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