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