1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "profiler.h" 18 19 #include <fstream> 20 #include <sys/uio.h> 21 #include <sys/file.h> 22 23 #include "base/stl_util.h" 24 #include "base/unix_file/fd_file.h" 25 #include "class_linker.h" 26 #include "common_throws.h" 27 #include "debugger.h" 28 #include "dex_file-inl.h" 29 #include "instrumentation.h" 30 #include "mirror/art_method-inl.h" 31 #include "mirror/class-inl.h" 32 #include "mirror/dex_cache.h" 33 #include "mirror/object_array-inl.h" 34 #include "mirror/object-inl.h" 35 #include "os.h" 36 #include "scoped_thread_state_change.h" 37 #include "ScopedLocalRef.h" 38 #include "thread.h" 39 #include "thread_list.h" 40 41 #ifdef HAVE_ANDROID_OS 42 #include "cutils/properties.h" 43 #endif 44 45 #if !defined(ART_USE_PORTABLE_COMPILER) 46 #include "entrypoints/quick/quick_entrypoints.h" 47 #endif 48 49 namespace art { 50 51 BackgroundMethodSamplingProfiler* BackgroundMethodSamplingProfiler::profiler_ = nullptr; 52 pthread_t BackgroundMethodSamplingProfiler::profiler_pthread_ = 0U; 53 volatile bool BackgroundMethodSamplingProfiler::shutting_down_ = false; 54 55 // TODO: this profiler runs regardless of the state of the machine. Maybe we should use the 56 // wakelock or something to modify the run characteristics. This can be done when we 57 // have some performance data after it's been used for a while. 58 59 // Walk through the method within depth of max_depth_ on the Java stack 60 class BoundedStackVisitor : public StackVisitor { 61 public: 62 BoundedStackVisitor(std::vector<std::pair<mirror::ArtMethod*, uint32_t>>* stack, 63 Thread* thread, uint32_t max_depth) 64 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) 65 : StackVisitor(thread, NULL), stack_(stack), max_depth_(max_depth), depth_(0) { 66 } 67 68 bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 69 mirror::ArtMethod* m = GetMethod(); 70 if (m->IsRuntimeMethod()) { 71 return true; 72 } 73 uint32_t dex_pc_ = GetDexPc(); 74 stack_->push_back(std::make_pair(m, dex_pc_)); 75 ++depth_; 76 if (depth_ < max_depth_) { 77 return true; 78 } else { 79 return false; 80 } 81 } 82 83 private: 84 std::vector<std::pair<mirror::ArtMethod*, uint32_t>>* stack_; 85 const uint32_t max_depth_; 86 uint32_t depth_; 87 }; 88 89 // This is called from either a thread list traversal or from a checkpoint. Regardless 90 // of which caller, the mutator lock must be held. 91 static void GetSample(Thread* thread, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 92 BackgroundMethodSamplingProfiler* profiler = 93 reinterpret_cast<BackgroundMethodSamplingProfiler*>(arg); 94 const ProfilerOptions profile_options = profiler->GetProfilerOptions(); 95 switch (profile_options.GetProfileType()) { 96 case kProfilerMethod: { 97 mirror::ArtMethod* method = thread->GetCurrentMethod(nullptr); 98 if (false && method == nullptr) { 99 LOG(INFO) << "No current method available"; 100 std::ostringstream os; 101 thread->Dump(os); 102 std::string data(os.str()); 103 LOG(INFO) << data; 104 } 105 profiler->RecordMethod(method); 106 break; 107 } 108 case kProfilerBoundedStack: { 109 std::vector<InstructionLocation> stack; 110 uint32_t max_depth = profile_options.GetMaxStackDepth(); 111 BoundedStackVisitor bounded_stack_visitor(&stack, thread, max_depth); 112 bounded_stack_visitor.WalkStack(); 113 profiler->RecordStack(stack); 114 break; 115 } 116 default: 117 LOG(INFO) << "This profile type is not implemented."; 118 } 119 } 120 121 // A closure that is called by the thread checkpoint code. 122 class SampleCheckpoint : public Closure { 123 public: 124 explicit SampleCheckpoint(BackgroundMethodSamplingProfiler* const profiler) : 125 profiler_(profiler) {} 126 127 virtual void Run(Thread* thread) NO_THREAD_SAFETY_ANALYSIS { 128 Thread* self = Thread::Current(); 129 if (thread == nullptr) { 130 LOG(ERROR) << "Checkpoint with nullptr thread"; 131 return; 132 } 133 134 // Grab the mutator lock (shared access). 135 ScopedObjectAccess soa(self); 136 137 // Grab a sample. 138 GetSample(thread, this->profiler_); 139 140 // And finally tell the barrier that we're done. 141 this->profiler_->GetBarrier().Pass(self); 142 } 143 144 private: 145 BackgroundMethodSamplingProfiler* const profiler_; 146 }; 147 148 bool BackgroundMethodSamplingProfiler::ShuttingDown(Thread* self) { 149 MutexLock mu(self, *Locks::profiler_lock_); 150 return shutting_down_; 151 } 152 153 void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) { 154 Runtime* runtime = Runtime::Current(); 155 BackgroundMethodSamplingProfiler* profiler = 156 reinterpret_cast<BackgroundMethodSamplingProfiler*>(arg); 157 158 // Add a random delay for the first time run so that we don't hammer the CPU 159 // with all profiles running at the same time. 160 const int kRandomDelayMaxSecs = 30; 161 const double kMaxBackoffSecs = 24*60*60; // Max backoff time. 162 163 srand(MicroTime() * getpid()); 164 int startup_delay = rand() % kRandomDelayMaxSecs; // random delay for startup. 165 166 167 CHECK(runtime->AttachCurrentThread("Profiler", true, runtime->GetSystemThreadGroup(), 168 !runtime->IsCompiler())); 169 170 Thread* self = Thread::Current(); 171 172 double backoff = 1.0; 173 while (true) { 174 if (ShuttingDown(self)) { 175 break; 176 } 177 178 { 179 // wait until we need to run another profile 180 uint64_t delay_secs = profiler->options_.GetPeriodS() * backoff; 181 182 // Add a startup delay to prevent all the profiles running at once. 183 delay_secs += startup_delay; 184 185 // Immediate startup for benchmarking? 186 if (profiler->options_.GetStartImmediately() && startup_delay > 0) { 187 delay_secs = 0; 188 } 189 190 startup_delay = 0; 191 192 VLOG(profiler) << "Delaying profile start for " << delay_secs << " secs"; 193 MutexLock mu(self, profiler->wait_lock_); 194 profiler->period_condition_.TimedWait(self, delay_secs * 1000, 0); 195 196 // Expand the backoff by its coefficient, but don't go beyond the max. 197 backoff = std::min(backoff * profiler->options_.GetBackoffCoefficient(), kMaxBackoffSecs); 198 } 199 200 if (ShuttingDown(self)) { 201 break; 202 } 203 204 205 uint64_t start_us = MicroTime(); 206 uint64_t end_us = start_us + profiler->options_.GetDurationS() * UINT64_C(1000000); 207 uint64_t now_us = start_us; 208 209 VLOG(profiler) << "Starting profiling run now for " 210 << PrettyDuration((end_us - start_us) * 1000); 211 212 SampleCheckpoint check_point(profiler); 213 214 size_t valid_samples = 0; 215 while (now_us < end_us) { 216 if (ShuttingDown(self)) { 217 break; 218 } 219 220 usleep(profiler->options_.GetIntervalUs()); // Non-interruptible sleep. 221 222 ThreadList* thread_list = runtime->GetThreadList(); 223 224 profiler->profiler_barrier_->Init(self, 0); 225 size_t barrier_count = thread_list->RunCheckpointOnRunnableThreads(&check_point); 226 227 // All threads are suspended, nothing to do. 228 if (barrier_count == 0) { 229 now_us = MicroTime(); 230 continue; 231 } 232 233 valid_samples += barrier_count; 234 235 ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); 236 237 // Wait for the barrier to be crossed by all runnable threads. This wait 238 // is done with a timeout so that we can detect problems with the checkpoint 239 // running code. We should never see this. 240 const uint32_t kWaitTimeoutMs = 10000; 241 const uint32_t kWaitTimeoutUs = kWaitTimeoutMs * 1000; 242 243 uint64_t waitstart_us = MicroTime(); 244 // Wait for all threads to pass the barrier. 245 profiler->profiler_barrier_->Increment(self, barrier_count, kWaitTimeoutMs); 246 uint64_t waitend_us = MicroTime(); 247 uint64_t waitdiff_us = waitend_us - waitstart_us; 248 249 // We should never get a timeout. If we do, it suggests a problem with the checkpoint 250 // code. Crash the process in this case. 251 CHECK_LT(waitdiff_us, kWaitTimeoutUs); 252 253 // Update the current time. 254 now_us = MicroTime(); 255 } 256 257 if (valid_samples > 0) { 258 // After the profile has been taken, write it out. 259 ScopedObjectAccess soa(self); // Acquire the mutator lock. 260 uint32_t size = profiler->WriteProfile(); 261 VLOG(profiler) << "Profile size: " << size; 262 } 263 } 264 265 LOG(INFO) << "Profiler shutdown"; 266 runtime->DetachCurrentThread(); 267 return nullptr; 268 } 269 270 // Write out the profile file if we are generating a profile. 271 uint32_t BackgroundMethodSamplingProfiler::WriteProfile() { 272 std::string full_name = output_filename_; 273 VLOG(profiler) << "Saving profile to " << full_name; 274 275 int fd = open(full_name.c_str(), O_RDWR); 276 if (fd < 0) { 277 // Open failed. 278 LOG(ERROR) << "Failed to open profile file " << full_name; 279 return 0; 280 } 281 282 // Lock the file for exclusive access. This will block if another process is using 283 // the file. 284 int err = flock(fd, LOCK_EX); 285 if (err < 0) { 286 LOG(ERROR) << "Failed to lock profile file " << full_name; 287 return 0; 288 } 289 290 // Read the previous profile. 291 profile_table_.ReadPrevious(fd, options_.GetProfileType()); 292 293 // Move back to the start of the file. 294 lseek(fd, 0, SEEK_SET); 295 296 // Format the profile output and write to the file. 297 std::ostringstream os; 298 uint32_t num_methods = DumpProfile(os); 299 std::string data(os.str()); 300 const char *p = data.c_str(); 301 size_t length = data.length(); 302 size_t full_length = length; 303 do { 304 int n = ::write(fd, p, length); 305 p += n; 306 length -= n; 307 } while (length > 0); 308 309 // Truncate the file to the new length. 310 ftruncate(fd, full_length); 311 312 // Now unlock the file, allowing another process in. 313 err = flock(fd, LOCK_UN); 314 if (err < 0) { 315 LOG(ERROR) << "Failed to unlock profile file " << full_name; 316 } 317 318 // Done, close the file. 319 ::close(fd); 320 321 // Clean the profile for the next time. 322 CleanProfile(); 323 324 return num_methods; 325 } 326 327 bool BackgroundMethodSamplingProfiler::Start( 328 const std::string& output_filename, const ProfilerOptions& options) { 329 if (!options.IsEnabled()) { 330 return false; 331 } 332 333 CHECK(!output_filename.empty()); 334 335 Thread* self = Thread::Current(); 336 { 337 MutexLock mu(self, *Locks::profiler_lock_); 338 // Don't start two profiler threads. 339 if (profiler_ != nullptr) { 340 return true; 341 } 342 } 343 344 LOG(INFO) << "Starting profiler using output file: " << output_filename 345 << " and options: " << options; 346 { 347 MutexLock mu(self, *Locks::profiler_lock_); 348 profiler_ = new BackgroundMethodSamplingProfiler(output_filename, options); 349 350 CHECK_PTHREAD_CALL(pthread_create, (&profiler_pthread_, nullptr, &RunProfilerThread, 351 reinterpret_cast<void*>(profiler_)), 352 "Profiler thread"); 353 } 354 return true; 355 } 356 357 358 359 void BackgroundMethodSamplingProfiler::Stop() { 360 BackgroundMethodSamplingProfiler* profiler = nullptr; 361 pthread_t profiler_pthread = 0U; 362 { 363 MutexLock trace_mu(Thread::Current(), *Locks::profiler_lock_); 364 CHECK(!shutting_down_); 365 profiler = profiler_; 366 shutting_down_ = true; 367 profiler_pthread = profiler_pthread_; 368 } 369 370 // Now wake up the sampler thread if it sleeping. 371 { 372 MutexLock profile_mu(Thread::Current(), profiler->wait_lock_); 373 profiler->period_condition_.Signal(Thread::Current()); 374 } 375 // Wait for the sample thread to stop. 376 CHECK_PTHREAD_CALL(pthread_join, (profiler_pthread, nullptr), "profiler thread shutdown"); 377 378 { 379 MutexLock mu(Thread::Current(), *Locks::profiler_lock_); 380 profiler_ = nullptr; 381 } 382 delete profiler; 383 } 384 385 386 void BackgroundMethodSamplingProfiler::Shutdown() { 387 Stop(); 388 } 389 390 BackgroundMethodSamplingProfiler::BackgroundMethodSamplingProfiler( 391 const std::string& output_filename, const ProfilerOptions& options) 392 : output_filename_(output_filename), 393 options_(options), 394 wait_lock_("Profile wait lock"), 395 period_condition_("Profile condition", wait_lock_), 396 profile_table_(wait_lock_), 397 profiler_barrier_(new Barrier(0)) { 398 // Populate the filtered_methods set. 399 // This is empty right now, but to add a method, do this: 400 // 401 // filtered_methods_.insert("void java.lang.Object.wait(long, int)"); 402 } 403 404 // Filter out methods the profiler doesn't want to record. 405 // We require mutator lock since some statistics will be updated here. 406 bool BackgroundMethodSamplingProfiler::ProcessMethod(mirror::ArtMethod* method) { 407 if (method == nullptr) { 408 profile_table_.NullMethod(); 409 // Don't record a nullptr method. 410 return false; 411 } 412 413 mirror::Class* cls = method->GetDeclaringClass(); 414 if (cls != nullptr) { 415 if (cls->GetClassLoader() == nullptr) { 416 // Don't include things in the boot 417 profile_table_.BootMethod(); 418 return false; 419 } 420 } 421 422 bool is_filtered = false; 423 424 if (strcmp(method->GetName(), "<clinit>") == 0) { 425 // always filter out class init 426 is_filtered = true; 427 } 428 429 // Filter out methods by name if there are any. 430 if (!is_filtered && filtered_methods_.size() > 0) { 431 std::string method_full_name = PrettyMethod(method); 432 433 // Don't include specific filtered methods. 434 is_filtered = filtered_methods_.count(method_full_name) != 0; 435 } 436 return !is_filtered; 437 } 438 439 // A method has been hit, record its invocation in the method map. 440 // The mutator_lock must be held (shared) when this is called. 441 void BackgroundMethodSamplingProfiler::RecordMethod(mirror::ArtMethod* method) { 442 // Add to the profile table unless it is filtered out. 443 if (ProcessMethod(method)) { 444 profile_table_.Put(method); 445 } 446 } 447 448 // Record the current bounded stack into sampling results. 449 void BackgroundMethodSamplingProfiler::RecordStack(const std::vector<InstructionLocation>& stack) { 450 if (stack.size() == 0) { 451 return; 452 } 453 // Get the method on top of the stack. We use this method to perform filtering. 454 mirror::ArtMethod* method = stack.front().first; 455 if (ProcessMethod(method)) { 456 profile_table_.PutStack(stack); 457 } 458 } 459 460 // Clean out any recordings for the method traces. 461 void BackgroundMethodSamplingProfiler::CleanProfile() { 462 profile_table_.Clear(); 463 } 464 465 uint32_t BackgroundMethodSamplingProfiler::DumpProfile(std::ostream& os) { 466 return profile_table_.Write(os, options_.GetProfileType()); 467 } 468 469 // Profile Table. 470 // This holds a mapping of mirror::ArtMethod* to a count of how many times a sample 471 // hit it at the top of the stack. 472 ProfileSampleResults::ProfileSampleResults(Mutex& lock) : lock_(lock), num_samples_(0), 473 num_null_methods_(0), 474 num_boot_methods_(0) { 475 for (int i = 0; i < kHashSize; i++) { 476 table[i] = nullptr; 477 } 478 method_context_table = nullptr; 479 stack_trie_root_ = nullptr; 480 } 481 482 ProfileSampleResults::~ProfileSampleResults() { 483 Clear(); 484 } 485 486 // Add a method to the profile table. If it's the first time the method 487 // has been seen, add it with count=1, otherwise increment the count. 488 void ProfileSampleResults::Put(mirror::ArtMethod* method) { 489 MutexLock mu(Thread::Current(), lock_); 490 uint32_t index = Hash(method); 491 if (table[index] == nullptr) { 492 table[index] = new Map(); 493 } 494 Map::iterator i = table[index]->find(method); 495 if (i == table[index]->end()) { 496 (*table[index])[method] = 1; 497 } else { 498 i->second++; 499 } 500 num_samples_++; 501 } 502 503 // Add a bounded stack to the profile table. Only the count of the method on 504 // top of the frame will be increased. 505 void ProfileSampleResults::PutStack(const std::vector<InstructionLocation>& stack) { 506 MutexLock mu(Thread::Current(), lock_); 507 ScopedObjectAccess soa(Thread::Current()); 508 if (stack_trie_root_ == nullptr) { 509 // The root of the stack trie is a dummy node so that we don't have to maintain 510 // a collection of tries. 511 stack_trie_root_ = new StackTrieNode(); 512 } 513 514 StackTrieNode* current = stack_trie_root_; 515 if (stack.size() == 0) { 516 current->IncreaseCount(); 517 return; 518 } 519 520 for (std::vector<InstructionLocation>::const_reverse_iterator iter = stack.rbegin(); 521 iter != stack.rend(); ++iter) { 522 InstructionLocation inst_loc = *iter; 523 mirror::ArtMethod* method = inst_loc.first; 524 if (method == nullptr) { 525 // skip null method 526 continue; 527 } 528 uint32_t dex_pc = inst_loc.second; 529 uint32_t method_idx = method->GetDexMethodIndex(); 530 const DexFile* dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile(); 531 MethodReference method_ref(dex_file, method_idx); 532 StackTrieNode* child = current->FindChild(method_ref, dex_pc); 533 if (child != nullptr) { 534 current = child; 535 } else { 536 uint32_t method_size = 0; 537 const DexFile::CodeItem* codeitem = method->GetCodeItem(); 538 if (codeitem != nullptr) { 539 method_size = codeitem->insns_size_in_code_units_; 540 } 541 StackTrieNode* new_node = new StackTrieNode(method_ref, dex_pc, method_size, current); 542 current->AppendChild(new_node); 543 current = new_node; 544 } 545 } 546 547 if (current != stack_trie_root_ && current->GetCount() == 0) { 548 // Insert into method_context table; 549 if (method_context_table == nullptr) { 550 method_context_table = new MethodContextMap(); 551 } 552 MethodReference method = current->GetMethod(); 553 MethodContextMap::iterator i = method_context_table->find(method); 554 if (i == method_context_table->end()) { 555 TrieNodeSet* node_set = new TrieNodeSet(); 556 node_set->insert(current); 557 (*method_context_table)[method] = node_set; 558 } else { 559 TrieNodeSet* node_set = i->second; 560 node_set->insert(current); 561 } 562 } 563 current->IncreaseCount(); 564 num_samples_++; 565 } 566 567 // Write the profile table to the output stream. Also merge with the previous profile. 568 uint32_t ProfileSampleResults::Write(std::ostream& os, ProfileDataType type) { 569 ScopedObjectAccess soa(Thread::Current()); 570 num_samples_ += previous_num_samples_; 571 num_null_methods_ += previous_num_null_methods_; 572 num_boot_methods_ += previous_num_boot_methods_; 573 574 VLOG(profiler) << "Profile: " 575 << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_; 576 os << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_ << "\n"; 577 uint32_t num_methods = 0; 578 if (type == kProfilerMethod) { 579 for (int i = 0 ; i < kHashSize; i++) { 580 Map *map = table[i]; 581 if (map != nullptr) { 582 for (const auto &meth_iter : *map) { 583 mirror::ArtMethod *method = meth_iter.first; 584 std::string method_name = PrettyMethod(method); 585 586 const DexFile::CodeItem* codeitem = method->GetCodeItem(); 587 uint32_t method_size = 0; 588 if (codeitem != nullptr) { 589 method_size = codeitem->insns_size_in_code_units_; 590 } 591 uint32_t count = meth_iter.second; 592 593 // Merge this profile entry with one from a previous run (if present). Also 594 // remove the previous entry. 595 PreviousProfile::iterator pi = previous_.find(method_name); 596 if (pi != previous_.end()) { 597 count += pi->second.count_; 598 previous_.erase(pi); 599 } 600 os << StringPrintf("%s/%u/%u\n", method_name.c_str(), count, method_size); 601 ++num_methods; 602 } 603 } 604 } 605 } else if (type == kProfilerBoundedStack) { 606 if (method_context_table != nullptr) { 607 for (const auto &method_iter : *method_context_table) { 608 MethodReference method = method_iter.first; 609 TrieNodeSet* node_set = method_iter.second; 610 std::string method_name = PrettyMethod(method.dex_method_index, *(method.dex_file)); 611 uint32_t method_size = 0; 612 uint32_t total_count = 0; 613 PreviousContextMap new_context_map; 614 for (const auto &trie_node_i : *node_set) { 615 StackTrieNode* node = trie_node_i; 616 method_size = node->GetMethodSize(); 617 uint32_t count = node->GetCount(); 618 uint32_t dexpc = node->GetDexPC(); 619 total_count += count; 620 621 StackTrieNode* current = node->GetParent(); 622 // We go backward on the trie to retrieve context and dex_pc until the dummy root. 623 // The format of the context is "method_1@pc_1@method_2@pc_2 (at) ..." 624 std::vector<std::string> context_vector; 625 while (current != nullptr && current->GetParent() != nullptr) { 626 context_vector.push_back(StringPrintf("%s@%u", 627 PrettyMethod(current->GetMethod().dex_method_index, *(current->GetMethod().dex_file)).c_str(), 628 current->GetDexPC())); 629 current = current->GetParent(); 630 } 631 std::string context_sig = Join(context_vector, '@'); 632 new_context_map[std::make_pair(dexpc, context_sig)] = count; 633 } 634 635 PreviousProfile::iterator pi = previous_.find(method_name); 636 if (pi != previous_.end()) { 637 total_count += pi->second.count_; 638 PreviousContextMap* previous_context_map = pi->second.context_map_; 639 if (previous_context_map != nullptr) { 640 for (const auto &context_i : *previous_context_map) { 641 uint32_t count = context_i.second; 642 PreviousContextMap::iterator ci = new_context_map.find(context_i.first); 643 if (ci == new_context_map.end()) { 644 new_context_map[context_i.first] = count; 645 } else { 646 ci->second += count; 647 } 648 } 649 } 650 delete previous_context_map; 651 previous_.erase(pi); 652 } 653 // We write out profile data with dex pc and context information in the following format: 654 // "method/total_count/size/[pc_1:count_1:context_1#pc_2:count_2:context_2#...]". 655 std::vector<std::string> context_count_vector; 656 for (const auto &context_i : new_context_map) { 657 context_count_vector.push_back(StringPrintf("%u:%u:%s", context_i.first.first, 658 context_i.second, context_i.first.second.c_str())); 659 } 660 os << StringPrintf("%s/%u/%u/[%s]\n", method_name.c_str(), total_count, 661 method_size, Join(context_count_vector, '#').c_str()); 662 ++num_methods; 663 } 664 } 665 } 666 667 // Now we write out the remaining previous methods. 668 for (const auto &pi : previous_) { 669 if (type == kProfilerMethod) { 670 os << StringPrintf("%s/%u/%u\n", pi.first.c_str(), pi.second.count_, pi.second.method_size_); 671 } else if (type == kProfilerBoundedStack) { 672 os << StringPrintf("%s/%u/%u/[", pi.first.c_str(), pi.second.count_, pi.second.method_size_); 673 PreviousContextMap* previous_context_map = pi.second.context_map_; 674 if (previous_context_map != nullptr) { 675 std::vector<std::string> context_count_vector; 676 for (const auto &context_i : *previous_context_map) { 677 context_count_vector.push_back(StringPrintf("%u:%u:%s", context_i.first.first, 678 context_i.second, context_i.first.second.c_str())); 679 } 680 os << Join(context_count_vector, '#'); 681 } 682 os << "]\n"; 683 } 684 ++num_methods; 685 } 686 return num_methods; 687 } 688 689 void ProfileSampleResults::Clear() { 690 num_samples_ = 0; 691 num_null_methods_ = 0; 692 num_boot_methods_ = 0; 693 for (int i = 0; i < kHashSize; i++) { 694 delete table[i]; 695 table[i] = nullptr; 696 } 697 if (stack_trie_root_ != nullptr) { 698 stack_trie_root_->DeleteChildren(); 699 delete stack_trie_root_; 700 stack_trie_root_ = nullptr; 701 if (method_context_table != nullptr) { 702 delete method_context_table; 703 method_context_table = nullptr; 704 } 705 } 706 for (auto &pi : previous_) { 707 if (pi.second.context_map_ != nullptr) { 708 delete pi.second.context_map_; 709 pi.second.context_map_ = nullptr; 710 } 711 } 712 previous_.clear(); 713 } 714 715 uint32_t ProfileSampleResults::Hash(mirror::ArtMethod* method) { 716 return (PointerToLowMemUInt32(method) >> 3) % kHashSize; 717 } 718 719 // Read a single line into the given string. Returns true if everything OK, false 720 // on EOF or error. 721 static bool ReadProfileLine(int fd, std::string& line) { 722 char buf[4]; 723 line.clear(); 724 while (true) { 725 int n = read(fd, buf, 1); // TODO: could speed this up but is it worth it? 726 if (n != 1) { 727 return false; 728 } 729 if (buf[0] == '\n') { 730 break; 731 } 732 line += buf[0]; 733 } 734 return true; 735 } 736 737 void ProfileSampleResults::ReadPrevious(int fd, ProfileDataType type) { 738 // Reset counters. 739 previous_num_samples_ = previous_num_null_methods_ = previous_num_boot_methods_ = 0; 740 741 std::string line; 742 743 // The first line contains summary information. 744 if (!ReadProfileLine(fd, line)) { 745 return; 746 } 747 std::vector<std::string> summary_info; 748 Split(line, '/', summary_info); 749 if (summary_info.size() != 3) { 750 // Bad summary info. It should be count/nullcount/bootcount 751 return; 752 } 753 previous_num_samples_ = strtoul(summary_info[0].c_str(), nullptr, 10); 754 previous_num_null_methods_ = strtoul(summary_info[1].c_str(), nullptr, 10); 755 previous_num_boot_methods_ = strtoul(summary_info[2].c_str(), nullptr, 10); 756 757 // Now read each line until the end of file. Each line consists of 3 or 4 fields separated by / 758 while (true) { 759 if (!ReadProfileLine(fd, line)) { 760 break; 761 } 762 std::vector<std::string> info; 763 Split(line, '/', info); 764 if (info.size() != 3 && info.size() != 4) { 765 // Malformed. 766 break; 767 } 768 std::string methodname = info[0]; 769 uint32_t total_count = strtoul(info[1].c_str(), nullptr, 10); 770 uint32_t size = strtoul(info[2].c_str(), nullptr, 10); 771 PreviousContextMap* context_map = nullptr; 772 if (type == kProfilerBoundedStack && info.size() == 4) { 773 context_map = new PreviousContextMap(); 774 std::string context_counts_str = info[3].substr(1, info[3].size() - 2); 775 std::vector<std::string> context_count_pairs; 776 Split(context_counts_str, '#', context_count_pairs); 777 for (uint32_t i = 0; i < context_count_pairs.size(); ++i) { 778 std::vector<std::string> context_count; 779 Split(context_count_pairs[i], ':', context_count); 780 if (context_count.size() == 2) { 781 // Handles the situtation when the profile file doesn't contain context information. 782 uint32_t dexpc = strtoul(context_count[0].c_str(), nullptr, 10); 783 uint32_t count = strtoul(context_count[1].c_str(), nullptr, 10); 784 (*context_map)[std::make_pair(dexpc, "")] = count; 785 } else { 786 // Handles the situtation when the profile file contains context information. 787 uint32_t dexpc = strtoul(context_count[0].c_str(), nullptr, 10); 788 uint32_t count = strtoul(context_count[1].c_str(), nullptr, 10); 789 std::string context = context_count[2]; 790 (*context_map)[std::make_pair(dexpc, context)] = count; 791 } 792 } 793 } 794 previous_[methodname] = PreviousValue(total_count, size, context_map); 795 } 796 } 797 798 bool ProfileFile::LoadFile(const std::string& fileName) { 799 LOG(VERBOSE) << "reading profile file " << fileName; 800 struct stat st; 801 int err = stat(fileName.c_str(), &st); 802 if (err == -1) { 803 LOG(VERBOSE) << "not found"; 804 return false; 805 } 806 if (st.st_size == 0) { 807 return false; // Empty profiles are invalid. 808 } 809 std::ifstream in(fileName.c_str()); 810 if (!in) { 811 LOG(VERBOSE) << "profile file " << fileName << " exists but can't be opened"; 812 LOG(VERBOSE) << "file owner: " << st.st_uid << ":" << st.st_gid; 813 LOG(VERBOSE) << "me: " << getuid() << ":" << getgid(); 814 LOG(VERBOSE) << "file permissions: " << std::oct << st.st_mode; 815 LOG(VERBOSE) << "errno: " << errno; 816 return false; 817 } 818 // The first line contains summary information. 819 std::string line; 820 std::getline(in, line); 821 if (in.eof()) { 822 return false; 823 } 824 std::vector<std::string> summary_info; 825 Split(line, '/', summary_info); 826 if (summary_info.size() != 3) { 827 // Bad summary info. It should be total/null/boot. 828 return false; 829 } 830 // This is the number of hits in all profiled methods (without nullptr or boot methods) 831 uint32_t total_count = strtoul(summary_info[0].c_str(), nullptr, 10); 832 833 // Now read each line until the end of file. Each line consists of 3 fields separated by '/'. 834 // Store the info in descending order given by the most used methods. 835 typedef std::set<std::pair<int, std::vector<std::string>>> ProfileSet; 836 ProfileSet countSet; 837 while (!in.eof()) { 838 std::getline(in, line); 839 if (in.eof()) { 840 break; 841 } 842 std::vector<std::string> info; 843 Split(line, '/', info); 844 if (info.size() != 3 && info.size() != 4) { 845 // Malformed. 846 return false; 847 } 848 int count = atoi(info[1].c_str()); 849 countSet.insert(std::make_pair(-count, info)); 850 } 851 852 uint32_t curTotalCount = 0; 853 ProfileSet::iterator end = countSet.end(); 854 const ProfileData* prevData = nullptr; 855 for (ProfileSet::iterator it = countSet.begin(); it != end ; it++) { 856 const std::string& methodname = it->second[0]; 857 uint32_t count = -it->first; 858 uint32_t size = strtoul(it->second[2].c_str(), nullptr, 10); 859 double usedPercent = (count * 100.0) / total_count; 860 861 curTotalCount += count; 862 // Methods with the same count should be part of the same top K percentage bucket. 863 double topKPercentage = (prevData != nullptr) && (prevData->GetCount() == count) 864 ? prevData->GetTopKUsedPercentage() 865 : 100 * static_cast<double>(curTotalCount) / static_cast<double>(total_count); 866 867 // Add it to the profile map. 868 ProfileData curData = ProfileData(methodname, count, size, usedPercent, topKPercentage); 869 profile_map_[methodname] = curData; 870 prevData = &curData; 871 } 872 return true; 873 } 874 875 bool ProfileFile::GetProfileData(ProfileFile::ProfileData* data, const std::string& method_name) { 876 ProfileMap::iterator i = profile_map_.find(method_name); 877 if (i == profile_map_.end()) { 878 return false; 879 } 880 *data = i->second; 881 return true; 882 } 883 884 bool ProfileFile::GetTopKSamples(std::set<std::string>& topKSamples, double topKPercentage) { 885 ProfileMap::iterator end = profile_map_.end(); 886 for (ProfileMap::iterator it = profile_map_.begin(); it != end; it++) { 887 if (it->second.GetTopKUsedPercentage() < topKPercentage) { 888 topKSamples.insert(it->first); 889 } 890 } 891 return true; 892 } 893 894 StackTrieNode* StackTrieNode::FindChild(MethodReference method, uint32_t dex_pc) { 895 if (children_.size() == 0) { 896 return nullptr; 897 } 898 // Create a dummy node for searching. 899 StackTrieNode* node = new StackTrieNode(method, dex_pc, 0, nullptr); 900 std::set<StackTrieNode*, StackTrieNodeComparator>::iterator i = children_.find(node); 901 delete node; 902 return (i == children_.end()) ? nullptr : *i; 903 } 904 905 void StackTrieNode::DeleteChildren() { 906 for (auto &child : children_) { 907 if (child != nullptr) { 908 child->DeleteChildren(); 909 delete child; 910 } 911 } 912 } 913 914 } // namespace art 915