blob: 223fe879c3bfec9c3bc4ffc4918a6b511792cdca [file] [log] [blame]
Dave Allison0aded082013-11-07 13:15:11 -08001/*
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
Calin Juravle9dae5b42014-04-07 16:36:21 +030019#include <fstream>
Dave Allison0aded082013-11-07 13:15:11 -080020#include <sys/uio.h>
Dave Allison39c3bfb2014-01-28 18:33:52 -080021#include <sys/file.h>
Dave Allison0aded082013-11-07 13:15:11 -080022
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 "object_utils.h"
36#include "os.h"
37#include "scoped_thread_state_change.h"
38#include "ScopedLocalRef.h"
39#include "thread.h"
40#include "thread_list.h"
Dave Allison4a7867b2014-01-30 17:44:12 -080041
42#ifdef HAVE_ANDROID_OS
43#include "cutils/properties.h"
44#endif
45
Dave Allison0aded082013-11-07 13:15:11 -080046#if !defined(ART_USE_PORTABLE_COMPILER)
47#include "entrypoints/quick/quick_entrypoints.h"
48#endif
49
50namespace art {
51
52BackgroundMethodSamplingProfiler* BackgroundMethodSamplingProfiler::profiler_ = nullptr;
53pthread_t BackgroundMethodSamplingProfiler::profiler_pthread_ = 0U;
54volatile bool BackgroundMethodSamplingProfiler::shutting_down_ = false;
55
56
57// TODO: this profiler runs regardless of the state of the machine. Maybe we should use the
58// wakelock or something to modify the run characteristics. This can be done when we
59// have some performance data after it's been used for a while.
60
61
62// This is called from either a thread list traversal or from a checkpoint. Regardless
63// of which caller, the mutator lock must be held.
64static void GetSample(Thread* thread, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
65 BackgroundMethodSamplingProfiler* profiler =
66 reinterpret_cast<BackgroundMethodSamplingProfiler*>(arg);
67 mirror::ArtMethod* method = thread->GetCurrentMethod(nullptr);
68 if (false && method == nullptr) {
69 LOG(INFO) << "No current method available";
70 std::ostringstream os;
71 thread->Dump(os);
72 std::string data(os.str());
73 LOG(INFO) << data;
74 }
75 profiler->RecordMethod(method);
76}
77
78
79
80// A closure that is called by the thread checkpoint code.
81class SampleCheckpoint : public Closure {
82 public:
83 explicit SampleCheckpoint(BackgroundMethodSamplingProfiler* const profiler) :
84 profiler_(profiler) {}
85
86 virtual void Run(Thread* thread) NO_THREAD_SAFETY_ANALYSIS {
87 Thread* self = Thread::Current();
88 if (thread == nullptr) {
89 LOG(ERROR) << "Checkpoint with nullptr thread";
90 return;
91 }
92
93 // Grab the mutator lock (shared access).
94 ScopedObjectAccess soa(self);
95
96 // Grab a sample.
97 GetSample(thread, this->profiler_);
98
99 // And finally tell the barrier that we're done.
100 this->profiler_->GetBarrier().Pass(self);
101 }
102
103 private:
104 BackgroundMethodSamplingProfiler* const profiler_;
105};
106
107bool BackgroundMethodSamplingProfiler::ShuttingDown(Thread* self) {
108 MutexLock mu(self, *Locks::profiler_lock_);
109 return shutting_down_;
110}
111
112void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) {
113 Runtime* runtime = Runtime::Current();
114 BackgroundMethodSamplingProfiler* profiler =
115 reinterpret_cast<BackgroundMethodSamplingProfiler*>(arg);
116
117 // Add a random delay for the first time run so that we don't hammer the CPU
118 // with all profiles running at the same time.
119 const int kRandomDelayMaxSecs = 30;
120 const double kMaxBackoffSecs = 24*60*60; // Max backoff time.
121
122 srand(MicroTime() * getpid());
123 int startup_delay = rand() % kRandomDelayMaxSecs; // random delay for startup.
124
125
126 CHECK(runtime->AttachCurrentThread("Profiler", true, runtime->GetSystemThreadGroup(),
127 !runtime->IsCompiler()));
128
129 Thread* self = Thread::Current();
130
131 while (true) {
132 if (ShuttingDown(self)) {
133 break;
134 }
135
136 {
137 // wait until we need to run another profile
138 uint64_t delay_secs = profiler->period_s_ * profiler->backoff_factor_;
139
140 // Add a startup delay to prevent all the profiles running at once.
141 delay_secs += startup_delay;
142
143 // Immediate startup for benchmarking?
144 if (profiler->start_immediately_ && startup_delay > 0) {
145 delay_secs = 0;
146 }
147
148 startup_delay = 0;
149
150 LOG(DEBUG) << "Delaying profile start for " << delay_secs << " secs";
151 MutexLock mu(self, profiler->wait_lock_);
152 profiler->period_condition_.TimedWait(self, delay_secs * 1000, 0);
153
154 // Expand the backoff by its coefficient, but don't go beyond the max.
155 double new_backoff = profiler->backoff_factor_ * profiler->backoff_coefficient_;
156 if (new_backoff < kMaxBackoffSecs) {
157 profiler->backoff_factor_ = new_backoff;
158 }
159 }
160
161 if (ShuttingDown(self)) {
162 break;
163 }
164
165
166 uint64_t start_us = MicroTime();
Ian Rogers0f678472014-03-10 16:18:37 -0700167 uint64_t end_us = start_us + profiler->duration_s_ * UINT64_C(1000000);
Dave Allison0aded082013-11-07 13:15:11 -0800168 uint64_t now_us = start_us;
169
170 LOG(DEBUG) << "Starting profiling run now for " << PrettyDuration((end_us - start_us) * 1000);
171
172
173 SampleCheckpoint check_point(profiler);
174
Dave Allison39c3bfb2014-01-28 18:33:52 -0800175 size_t valid_samples = 0;
Dave Allison0aded082013-11-07 13:15:11 -0800176 while (now_us < end_us) {
177 if (ShuttingDown(self)) {
178 break;
179 }
180
181 usleep(profiler->interval_us_); // Non-interruptible sleep.
182
183 ThreadList* thread_list = runtime->GetThreadList();
184
185 profiler->profiler_barrier_->Init(self, 0);
Dave Allison39c3bfb2014-01-28 18:33:52 -0800186 size_t barrier_count = thread_list->RunCheckpointOnRunnableThreads(&check_point);
187
188 // All threads are suspended, nothing to do.
189 if (barrier_count == 0) {
190 now_us = MicroTime();
191 continue;
192 }
193
194 valid_samples += barrier_count;
Dave Allison0aded082013-11-07 13:15:11 -0800195
196 ThreadState old_state = self->SetState(kWaitingForCheckPointsToRun);
197
198 // Wait for the barrier to be crossed by all runnable threads. This wait
199 // is done with a timeout so that we can detect problems with the checkpoint
200 // running code. We should never see this.
201 const uint32_t kWaitTimeoutMs = 10000;
202 const uint32_t kWaitTimeoutUs = kWaitTimeoutMs * 1000;
203
204 uint64_t waitstart_us = MicroTime();
205 // Wait for all threads to pass the barrier.
206 profiler->profiler_barrier_->Increment(self, barrier_count, kWaitTimeoutMs);
207 uint64_t waitend_us = MicroTime();
208 uint64_t waitdiff_us = waitend_us - waitstart_us;
209
210 // We should never get a timeout. If we do, it suggests a problem with the checkpoint
211 // code. Crash the process in this case.
212 CHECK_LT(waitdiff_us, kWaitTimeoutUs);
213
214 self->SetState(old_state);
215
216 // Update the current time.
217 now_us = MicroTime();
218 }
219
Dave Allison39c3bfb2014-01-28 18:33:52 -0800220 if (valid_samples > 0 && !ShuttingDown(self)) {
Dave Allison0aded082013-11-07 13:15:11 -0800221 // After the profile has been taken, write it out.
222 ScopedObjectAccess soa(self); // Acquire the mutator lock.
223 uint32_t size = profiler->WriteProfile();
224 LOG(DEBUG) << "Profile size: " << size;
225 }
226 }
227
228 LOG(INFO) << "Profiler shutdown";
229 runtime->DetachCurrentThread();
230 return nullptr;
231}
232
233// Write out the profile file if we are generating a profile.
234uint32_t BackgroundMethodSamplingProfiler::WriteProfile() {
Dave Allison0aded082013-11-07 13:15:11 -0800235 std::string full_name = profile_file_name_;
Dave Allison0aded082013-11-07 13:15:11 -0800236 LOG(DEBUG) << "Saving profile to " << full_name;
237
Dave Allison39c3bfb2014-01-28 18:33:52 -0800238 int fd = open(full_name.c_str(), O_RDWR);
239 if (fd < 0) {
240 // Open failed.
241 LOG(ERROR) << "Failed to open profile file " << full_name;
Dave Allison0aded082013-11-07 13:15:11 -0800242 return 0;
243 }
Dave Allison39c3bfb2014-01-28 18:33:52 -0800244
245 // Lock the file for exclusive access. This will block if another process is using
246 // the file.
247 int err = flock(fd, LOCK_EX);
248 if (err < 0) {
249 LOG(ERROR) << "Failed to lock profile file " << full_name;
250 return 0;
251 }
252
253 // Read the previous profile.
254 profile_table_.ReadPrevious(fd);
255
256 // Move back to the start of the file.
257 lseek(fd, 0, SEEK_SET);
258
259 // Format the profile output and write to the file.
Dave Allison0aded082013-11-07 13:15:11 -0800260 std::ostringstream os;
261 uint32_t num_methods = DumpProfile(os);
262 std::string data(os.str());
Dave Allison39c3bfb2014-01-28 18:33:52 -0800263 const char *p = data.c_str();
264 size_t length = data.length();
265 size_t full_length = length;
266 do {
267 int n = ::write(fd, p, length);
268 p += n;
269 length -= n;
270 } while (length > 0);
271
272 // Truncate the file to the new length.
273 ftruncate(fd, full_length);
274
275 // Now unlock the file, allowing another process in.
276 err = flock(fd, LOCK_UN);
277 if (err < 0) {
278 LOG(ERROR) << "Failed to unlock profile file " << full_name;
279 }
280
281 // Done, close the file.
282 ::close(fd);
283
284 // Clean the profile for the next time.
285 CleanProfile();
286
Dave Allison0aded082013-11-07 13:15:11 -0800287 return num_methods;
288}
289
290// Start a profile thread with the user-supplied arguments.
291void BackgroundMethodSamplingProfiler::Start(int period, int duration,
Dave Allison39c3bfb2014-01-28 18:33:52 -0800292 const std::string& profile_file_name, const std::string& procName,
293 int interval_us,
Dave Allison0aded082013-11-07 13:15:11 -0800294 double backoff_coefficient, bool startImmediately) {
295 Thread* self = Thread::Current();
296 {
297 MutexLock mu(self, *Locks::profiler_lock_);
298 // Don't start two profiler threads.
299 if (profiler_ != nullptr) {
300 return;
301 }
302 }
303
Dave Allison4a7867b2014-01-30 17:44:12 -0800304 // Only on target...
305#ifdef HAVE_ANDROID_OS
Dave Allison39c3bfb2014-01-28 18:33:52 -0800306 if (!startImmediately) {
307 // Switch off profiler if the dalvik.vm.profiler property has value 0.
308 char buf[PROP_VALUE_MAX];
309 property_get("dalvik.vm.profiler", buf, "0");
310 if (strcmp(buf, "0") == 0) {
311 LOG(INFO) << "Profiler disabled. To enable setprop dalvik.vm.profiler 1";
312 return;
313 }
Dave Allison4a7867b2014-01-30 17:44:12 -0800314 }
315#endif
316
Dave Allison0aded082013-11-07 13:15:11 -0800317 LOG(INFO) << "Starting profile with period " << period << "s, duration " << duration <<
318 "s, interval " << interval_us << "us. Profile file " << profile_file_name;
319
320 {
321 MutexLock mu(self, *Locks::profiler_lock_);
322 profiler_ = new BackgroundMethodSamplingProfiler(period, duration, profile_file_name,
Dave Allison39c3bfb2014-01-28 18:33:52 -0800323 procName,
Dave Allison0aded082013-11-07 13:15:11 -0800324 backoff_coefficient,
325 interval_us, startImmediately);
326
327 CHECK_PTHREAD_CALL(pthread_create, (&profiler_pthread_, nullptr, &RunProfilerThread,
328 reinterpret_cast<void*>(profiler_)),
329 "Profiler thread");
330 }
331}
332
333
334
335void BackgroundMethodSamplingProfiler::Stop() {
336 BackgroundMethodSamplingProfiler* profiler = nullptr;
337 pthread_t profiler_pthread = 0U;
338 {
339 MutexLock trace_mu(Thread::Current(), *Locks::profiler_lock_);
340 profiler = profiler_;
341 shutting_down_ = true;
342 profiler_pthread = profiler_pthread_;
343 }
344
345 // Now wake up the sampler thread if it sleeping.
346 {
347 MutexLock profile_mu(Thread::Current(), profiler->wait_lock_);
348 profiler->period_condition_.Signal(Thread::Current());
349 }
350 // Wait for the sample thread to stop.
351 CHECK_PTHREAD_CALL(pthread_join, (profiler_pthread, nullptr), "profiler thread shutdown");
352
353 {
354 MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
355 profiler_ = nullptr;
356 }
357 delete profiler;
358}
359
360
361void BackgroundMethodSamplingProfiler::Shutdown() {
362 Stop();
363}
364
365BackgroundMethodSamplingProfiler::BackgroundMethodSamplingProfiler(int period, int duration,
Dave Allison39c3bfb2014-01-28 18:33:52 -0800366 const std::string& profile_file_name,
367 const std::string& process_name,
Dave Allison0aded082013-11-07 13:15:11 -0800368 double backoff_coefficient, int interval_us, bool startImmediately)
Dave Allison39c3bfb2014-01-28 18:33:52 -0800369 : profile_file_name_(profile_file_name), process_name_(process_name),
Dave Allison0aded082013-11-07 13:15:11 -0800370 period_s_(period), start_immediately_(startImmediately),
371 interval_us_(interval_us), backoff_factor_(1.0),
372 backoff_coefficient_(backoff_coefficient), duration_s_(duration),
373 wait_lock_("Profile wait lock"),
374 period_condition_("Profile condition", wait_lock_),
375 profile_table_(wait_lock_),
376 profiler_barrier_(new Barrier(0)) {
377 // Populate the filtered_methods set.
378 // This is empty right now, but to add a method, do this:
379 //
380 // filtered_methods_.insert("void java.lang.Object.wait(long, int)");
381}
382
383// A method has been hit, record its invocation in the method map.
384// The mutator_lock must be held (shared) when this is called.
385void BackgroundMethodSamplingProfiler::RecordMethod(mirror::ArtMethod* method) {
386 if (method == nullptr) {
387 profile_table_.NullMethod();
388 // Don't record a nullptr method.
389 return;
390 }
391
392 mirror::Class* cls = method->GetDeclaringClass();
393 if (cls != nullptr) {
394 if (cls->GetClassLoader() == nullptr) {
395 // Don't include things in the boot
396 profile_table_.BootMethod();
397 return;
398 }
399 }
400
401 bool is_filtered = false;
402
403 MethodHelper mh(method);
404 if (strcmp(mh.GetName(), "<clinit>") == 0) {
405 // always filter out class init
406 is_filtered = true;
407 }
408
409 // Filter out methods by name if there are any.
410 if (!is_filtered && filtered_methods_.size() > 0) {
411 std::string method_full_name = PrettyMethod(method);
412
413 // Don't include specific filtered methods.
414 is_filtered = filtered_methods_.count(method_full_name) != 0;
415 }
416
417 // Add to the profile table unless it is filtered out.
418 if (!is_filtered) {
419 profile_table_.Put(method);
420 }
421}
422
423// Clean out any recordings for the method traces.
424void BackgroundMethodSamplingProfiler::CleanProfile() {
425 profile_table_.Clear();
426}
427
428uint32_t BackgroundMethodSamplingProfiler::DumpProfile(std::ostream& os) {
429 return profile_table_.Write(os);
430}
431
432// Profile Table.
433// This holds a mapping of mirror::ArtMethod* to a count of how many times a sample
434// hit it at the top of the stack.
435ProfileSampleResults::ProfileSampleResults(Mutex& lock) : lock_(lock), num_samples_(0),
436 num_null_methods_(0),
437 num_boot_methods_(0) {
438 for (int i = 0; i < kHashSize; i++) {
439 table[i] = nullptr;
440 }
441}
442
443ProfileSampleResults::~ProfileSampleResults() {
444 for (int i = 0; i < kHashSize; i++) {
445 delete table[i];
446 }
447}
448
449// Add a method to the profile table. If it the first time the method
450// has been seen, add it with count=1, otherwise increment the count.
451void ProfileSampleResults::Put(mirror::ArtMethod* method) {
452 lock_.Lock(Thread::Current());
453 uint32_t index = Hash(method);
454 if (table[index] == nullptr) {
455 table[index] = new Map();
456 }
457 Map::iterator i = table[index]->find(method);
458 if (i == table[index]->end()) {
459 (*table[index])[method] = 1;
460 } else {
461 i->second++;
462 }
463 num_samples_++;
464 lock_.Unlock(Thread::Current());
465}
466
Dave Allison39c3bfb2014-01-28 18:33:52 -0800467// Write the profile table to the output stream. Also merge with the previous profile.
Dave Allison0aded082013-11-07 13:15:11 -0800468uint32_t ProfileSampleResults::Write(std::ostream &os) {
469 ScopedObjectAccess soa(Thread::Current());
Dave Allison39c3bfb2014-01-28 18:33:52 -0800470 num_samples_ += previous_num_samples_;
471 num_null_methods_ += previous_num_null_methods_;
472 num_boot_methods_ += previous_num_boot_methods_;
473
Dave Allison0aded082013-11-07 13:15:11 -0800474 LOG(DEBUG) << "Profile: " << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_;
475 os << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_ << "\n";
476 uint32_t num_methods = 0;
477 for (int i = 0 ; i < kHashSize; i++) {
478 Map *map = table[i];
479 if (map != nullptr) {
480 for (const auto &meth_iter : *map) {
Dave Allison39c3bfb2014-01-28 18:33:52 -0800481 mirror::ArtMethod *method = meth_iter.first;
482 std::string method_name = PrettyMethod(method);
483
484 MethodHelper mh(method);
485 const DexFile::CodeItem* codeitem = mh.GetCodeItem();
486 uint32_t method_size = 0;
487 if (codeitem != nullptr) {
488 method_size = codeitem->insns_size_in_code_units_;
489 }
490 uint32_t count = meth_iter.second;
491
492 // Merge this profile entry with one from a previous run (if present). Also
493 // remove the previous entry.
494 PreviousProfile::iterator pi = previous_.find(method_name);
495 if (pi != previous_.end()) {
496 count += pi->second.count_;
497 previous_.erase(pi);
498 }
499 os << StringPrintf("%s/%u/%u\n", method_name.c_str(), count, method_size);
500 ++num_methods;
501 }
Dave Allison0aded082013-11-07 13:15:11 -0800502 }
503 }
Dave Allison39c3bfb2014-01-28 18:33:52 -0800504
505 // Now we write out the remaining previous methods.
506 for (PreviousProfile::iterator pi = previous_.begin(); pi != previous_.end(); ++pi) {
507 os << StringPrintf("%s/%u/%u\n", pi->first.c_str(), pi->second.count_, pi->second.method_size_);
508 ++num_methods;
509 }
Dave Allison0aded082013-11-07 13:15:11 -0800510 return num_methods;
511}
512
513void ProfileSampleResults::Clear() {
514 num_samples_ = 0;
515 num_null_methods_ = 0;
516 num_boot_methods_ = 0;
517 for (int i = 0; i < kHashSize; i++) {
518 delete table[i];
519 table[i] = nullptr;
520 }
Dave Allison39c3bfb2014-01-28 18:33:52 -0800521 previous_.clear();
Dave Allison0aded082013-11-07 13:15:11 -0800522}
523
524uint32_t ProfileSampleResults::Hash(mirror::ArtMethod* method) {
Ian Rogersef7d42f2014-01-06 12:55:46 -0800525 return (PointerToLowMemUInt32(method) >> 3) % kHashSize;
Dave Allison0aded082013-11-07 13:15:11 -0800526}
527
Dave Allison39c3bfb2014-01-28 18:33:52 -0800528// Read a single line into the given string. Returns true if everything OK, false
529// on EOF or error.
530static bool ReadProfileLine(int fd, std::string& line) {
531 char buf[4];
532 line.clear();
533 while (true) {
534 int n = read(fd, buf, 1); // TODO: could speed this up but is it worth it?
535 if (n != 1) {
536 return false;
537 }
538 if (buf[0] == '\n') {
539 break;
540 }
541 line += buf[0];
542 }
543 return true;
544}
545
546void ProfileSampleResults::ReadPrevious(int fd) {
547 // Reset counters.
548 previous_num_samples_ = previous_num_null_methods_ = previous_num_boot_methods_ = 0;
549
550 std::string line;
551
552 // The first line contains summary information.
553 if (!ReadProfileLine(fd, line)) {
554 return;
555 }
556 std::vector<std::string> summary_info;
557 Split(line, '/', summary_info);
558 if (summary_info.size() != 3) {
559 // Bad summary info. It should be count/nullcount/bootcount
560 return;
561 }
562 previous_num_samples_ = atoi(summary_info[0].c_str());
563 previous_num_null_methods_ = atoi(summary_info[1].c_str());
564 previous_num_boot_methods_ = atoi(summary_info[2].c_str());
565
566 // Now read each line until the end of file. Each line consists of 3 fields separated by /
567 while (true) {
568 if (!ReadProfileLine(fd, line)) {
569 break;
570 }
571 std::vector<std::string> info;
572 Split(line, '/', info);
573 if (info.size() != 3) {
574 // Malformed.
575 break;
576 }
577 std::string methodname = info[0];
578 uint32_t count = atoi(info[1].c_str());
579 uint32_t size = atoi(info[2].c_str());
580 previous_[methodname] = PreviousValue(count, size);
581 }
582}
Dave Allison0aded082013-11-07 13:15:11 -0800583
Calin Juravle9dae5b42014-04-07 16:36:21 +0300584bool ProfileHelper::LoadProfileMap(ProfileMap& profileMap, const std::string& fileName) {
585 LOG(VERBOSE) << "reading profile file " << fileName;
586 struct stat st;
587 int err = stat(fileName.c_str(), &st);
588 if (err == -1) {
589 LOG(VERBOSE) << "not found";
590 return false;
591 }
592 if (st.st_size == 0) {
593 return true; // empty profiles are ok.
594 }
595 std::ifstream in(fileName.c_str());
596 if (!in) {
597 LOG(VERBOSE) << "profile file " << fileName << " exists but can't be opened";
598 LOG(VERBOSE) << "file owner: " << st.st_uid << ":" << st.st_gid;
599 LOG(VERBOSE) << "me: " << getuid() << ":" << getgid();
600 LOG(VERBOSE) << "file permissions: " << std::oct << st.st_mode;
601 LOG(VERBOSE) << "errno: " << errno;
602 return false;
603 }
604 // The first line contains summary information.
605 std::string line;
606 std::getline(in, line);
607 if (in.eof()) {
608 return false;
609 }
610 std::vector<std::string> summary_info;
611 Split(line, '/', summary_info);
612 if (summary_info.size() != 3) {
613 // Bad summary info. It should be count/total/bootpath.
614 return false;
615 }
616 // This is the number of hits in all methods.
617 uint32_t total_count = 0;
618 for (int i = 0 ; i < 3; ++i) {
619 total_count += atoi(summary_info[i].c_str());
620 }
621
622 // Now read each line until the end of file. Each line consists of 3 fields separated by '/'.
623 // Store the info in descending order given by the most used methods.
624 typedef std::set<std::pair<int, std::vector<std::string>>> ProfileSet;
625 ProfileSet countSet;
626 while (!in.eof()) {
627 std::getline(in, line);
628 if (in.eof()) {
629 break;
630 }
631 std::vector<std::string> info;
632 Split(line, '/', info);
633 if (info.size() != 3) {
634 // Malformed.
635 break;
636 }
637 int count = atoi(info[1].c_str());
638 countSet.insert(std::make_pair(-count, info));
639 }
640
641 uint32_t curTotalCount = 0;
642 ProfileSet::iterator end = countSet.end();
643 const ProfileData* prevData = nullptr;
644 for (ProfileSet::iterator it = countSet.begin(); it != end ; it++) {
645 const std::string& methodname = it->second[0];
646 uint32_t count = -it->first;
647 uint32_t size = atoi(it->second[2].c_str());
648 double usedPercent = (count * 100.0) / total_count;
649
650 curTotalCount += count;
651 // Methods with the same count should be part of the same top K percentage bucket.
652 double topKPercentage = (prevData != nullptr) && (prevData->GetCount() == count)
653 ? prevData->GetTopKUsedPercentage()
654 : 100 * static_cast<double>(curTotalCount) / static_cast<double>(total_count);
655
656 // Add it to the profile map.
657 ProfileData curData = ProfileData(methodname, count, size, usedPercent, topKPercentage);
658 profileMap[methodname] = curData;
659 prevData = &curData;
660 }
661 return true;
662}
663
664bool ProfileHelper::LoadTopKSamples(std::set<std::string>& topKSamples, const std::string& fileName,
665 double topKPercentage) {
666 ProfileMap profileMap;
667 bool loadOk = LoadProfileMap(profileMap, fileName);
668 if (!loadOk) {
669 return false;
670 }
671 ProfileMap::iterator end = profileMap.end();
672 for (ProfileMap::iterator it = profileMap.begin(); it != end; it++) {
673 if (it->second.GetTopKUsedPercentage() < topKPercentage) {
674 topKSamples.insert(it->first);
675 }
676 }
677 return true;
678}
679
680} // namespace art