blob: 00bb50179af61c0d7a6d50ad91700bf3fe77fcc8 [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
Dave Allison0aded082013-11-07 13:15:11 -080056// TODO: this profiler runs regardless of the state of the machine. Maybe we should use the
57// wakelock or something to modify the run characteristics. This can be done when we
58// have some performance data after it's been used for a while.
59
60
61// This is called from either a thread list traversal or from a checkpoint. Regardless
62// of which caller, the mutator lock must be held.
63static void GetSample(Thread* thread, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
64 BackgroundMethodSamplingProfiler* profiler =
65 reinterpret_cast<BackgroundMethodSamplingProfiler*>(arg);
66 mirror::ArtMethod* method = thread->GetCurrentMethod(nullptr);
67 if (false && method == nullptr) {
68 LOG(INFO) << "No current method available";
69 std::ostringstream os;
70 thread->Dump(os);
71 std::string data(os.str());
72 LOG(INFO) << data;
73 }
74 profiler->RecordMethod(method);
75}
76
Dave Allison0aded082013-11-07 13:15:11 -080077// A closure that is called by the thread checkpoint code.
78class SampleCheckpoint : public Closure {
79 public:
80 explicit SampleCheckpoint(BackgroundMethodSamplingProfiler* const profiler) :
81 profiler_(profiler) {}
82
83 virtual void Run(Thread* thread) NO_THREAD_SAFETY_ANALYSIS {
84 Thread* self = Thread::Current();
85 if (thread == nullptr) {
86 LOG(ERROR) << "Checkpoint with nullptr thread";
87 return;
88 }
89
90 // Grab the mutator lock (shared access).
91 ScopedObjectAccess soa(self);
92
93 // Grab a sample.
94 GetSample(thread, this->profiler_);
95
96 // And finally tell the barrier that we're done.
97 this->profiler_->GetBarrier().Pass(self);
98 }
99
100 private:
101 BackgroundMethodSamplingProfiler* const profiler_;
102};
103
104bool BackgroundMethodSamplingProfiler::ShuttingDown(Thread* self) {
105 MutexLock mu(self, *Locks::profiler_lock_);
106 return shutting_down_;
107}
108
109void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) {
110 Runtime* runtime = Runtime::Current();
111 BackgroundMethodSamplingProfiler* profiler =
112 reinterpret_cast<BackgroundMethodSamplingProfiler*>(arg);
113
114 // Add a random delay for the first time run so that we don't hammer the CPU
115 // with all profiles running at the same time.
116 const int kRandomDelayMaxSecs = 30;
117 const double kMaxBackoffSecs = 24*60*60; // Max backoff time.
118
119 srand(MicroTime() * getpid());
120 int startup_delay = rand() % kRandomDelayMaxSecs; // random delay for startup.
121
122
123 CHECK(runtime->AttachCurrentThread("Profiler", true, runtime->GetSystemThreadGroup(),
124 !runtime->IsCompiler()));
125
126 Thread* self = Thread::Current();
127
Calin Juravlec1b643c2014-05-30 23:44:11 +0100128 double backoff = 1.0;
Dave Allison0aded082013-11-07 13:15:11 -0800129 while (true) {
130 if (ShuttingDown(self)) {
131 break;
132 }
133
134 {
135 // wait until we need to run another profile
Calin Juravlec1b643c2014-05-30 23:44:11 +0100136 uint64_t delay_secs = profiler->options_.GetPeriodS() * backoff;
Dave Allison0aded082013-11-07 13:15:11 -0800137
138 // Add a startup delay to prevent all the profiles running at once.
139 delay_secs += startup_delay;
140
141 // Immediate startup for benchmarking?
Calin Juravlec1b643c2014-05-30 23:44:11 +0100142 if (profiler->options_.GetStartImmediately() && startup_delay > 0) {
Dave Allison0aded082013-11-07 13:15:11 -0800143 delay_secs = 0;
144 }
145
146 startup_delay = 0;
147
Brian Carlstrom4d466a82014-05-08 19:05:29 -0700148 VLOG(profiler) << "Delaying profile start for " << delay_secs << " secs";
Dave Allison0aded082013-11-07 13:15:11 -0800149 MutexLock mu(self, profiler->wait_lock_);
150 profiler->period_condition_.TimedWait(self, delay_secs * 1000, 0);
151
152 // Expand the backoff by its coefficient, but don't go beyond the max.
Calin Juravlec1b643c2014-05-30 23:44:11 +0100153 backoff = std::min(backoff * profiler->options_.GetBackoffCoefficient(), kMaxBackoffSecs);
Dave Allison0aded082013-11-07 13:15:11 -0800154 }
155
156 if (ShuttingDown(self)) {
157 break;
158 }
159
160
161 uint64_t start_us = MicroTime();
Calin Juravlec1b643c2014-05-30 23:44:11 +0100162 uint64_t end_us = start_us + profiler->options_.GetDurationS() * UINT64_C(1000000);
Dave Allison0aded082013-11-07 13:15:11 -0800163 uint64_t now_us = start_us;
164
Calin Juravlec1b643c2014-05-30 23:44:11 +0100165 VLOG(profiler) << "Starting profiling run now for "
166 << PrettyDuration((end_us - start_us) * 1000);
Dave Allison0aded082013-11-07 13:15:11 -0800167
168 SampleCheckpoint check_point(profiler);
169
Dave Allison39c3bfb2014-01-28 18:33:52 -0800170 size_t valid_samples = 0;
Dave Allison0aded082013-11-07 13:15:11 -0800171 while (now_us < end_us) {
172 if (ShuttingDown(self)) {
173 break;
174 }
175
Calin Juravlec1b643c2014-05-30 23:44:11 +0100176 usleep(profiler->options_.GetIntervalUs()); // Non-interruptible sleep.
Dave Allison0aded082013-11-07 13:15:11 -0800177
178 ThreadList* thread_list = runtime->GetThreadList();
179
180 profiler->profiler_barrier_->Init(self, 0);
Dave Allison39c3bfb2014-01-28 18:33:52 -0800181 size_t barrier_count = thread_list->RunCheckpointOnRunnableThreads(&check_point);
182
183 // All threads are suspended, nothing to do.
184 if (barrier_count == 0) {
185 now_us = MicroTime();
186 continue;
187 }
188
189 valid_samples += barrier_count;
Dave Allison0aded082013-11-07 13:15:11 -0800190
Wei Jin6a586912014-05-21 16:07:40 -0700191 ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
Dave Allison0aded082013-11-07 13:15:11 -0800192
193 // Wait for the barrier to be crossed by all runnable threads. This wait
194 // is done with a timeout so that we can detect problems with the checkpoint
195 // running code. We should never see this.
196 const uint32_t kWaitTimeoutMs = 10000;
197 const uint32_t kWaitTimeoutUs = kWaitTimeoutMs * 1000;
198
199 uint64_t waitstart_us = MicroTime();
200 // Wait for all threads to pass the barrier.
201 profiler->profiler_barrier_->Increment(self, barrier_count, kWaitTimeoutMs);
202 uint64_t waitend_us = MicroTime();
203 uint64_t waitdiff_us = waitend_us - waitstart_us;
204
205 // We should never get a timeout. If we do, it suggests a problem with the checkpoint
206 // code. Crash the process in this case.
207 CHECK_LT(waitdiff_us, kWaitTimeoutUs);
208
Dave Allison0aded082013-11-07 13:15:11 -0800209 // Update the current time.
210 now_us = MicroTime();
211 }
212
Wei Jin6a586912014-05-21 16:07:40 -0700213 if (valid_samples > 0) {
Dave Allison0aded082013-11-07 13:15:11 -0800214 // After the profile has been taken, write it out.
215 ScopedObjectAccess soa(self); // Acquire the mutator lock.
216 uint32_t size = profiler->WriteProfile();
Brian Carlstrom4d466a82014-05-08 19:05:29 -0700217 VLOG(profiler) << "Profile size: " << size;
Dave Allison0aded082013-11-07 13:15:11 -0800218 }
219 }
220
221 LOG(INFO) << "Profiler shutdown";
222 runtime->DetachCurrentThread();
223 return nullptr;
224}
225
226// Write out the profile file if we are generating a profile.
227uint32_t BackgroundMethodSamplingProfiler::WriteProfile() {
Calin Juravlec1b643c2014-05-30 23:44:11 +0100228 std::string full_name = output_filename_;
Brian Carlstrom4d466a82014-05-08 19:05:29 -0700229 VLOG(profiler) << "Saving profile to " << full_name;
Dave Allison0aded082013-11-07 13:15:11 -0800230
Dave Allison39c3bfb2014-01-28 18:33:52 -0800231 int fd = open(full_name.c_str(), O_RDWR);
232 if (fd < 0) {
233 // Open failed.
234 LOG(ERROR) << "Failed to open profile file " << full_name;
Dave Allison0aded082013-11-07 13:15:11 -0800235 return 0;
236 }
Dave Allison39c3bfb2014-01-28 18:33:52 -0800237
238 // Lock the file for exclusive access. This will block if another process is using
239 // the file.
240 int err = flock(fd, LOCK_EX);
241 if (err < 0) {
242 LOG(ERROR) << "Failed to lock profile file " << full_name;
243 return 0;
244 }
245
246 // Read the previous profile.
247 profile_table_.ReadPrevious(fd);
248
249 // Move back to the start of the file.
250 lseek(fd, 0, SEEK_SET);
251
252 // Format the profile output and write to the file.
Dave Allison0aded082013-11-07 13:15:11 -0800253 std::ostringstream os;
254 uint32_t num_methods = DumpProfile(os);
255 std::string data(os.str());
Dave Allison39c3bfb2014-01-28 18:33:52 -0800256 const char *p = data.c_str();
257 size_t length = data.length();
258 size_t full_length = length;
259 do {
260 int n = ::write(fd, p, length);
261 p += n;
262 length -= n;
263 } while (length > 0);
264
265 // Truncate the file to the new length.
266 ftruncate(fd, full_length);
267
268 // Now unlock the file, allowing another process in.
269 err = flock(fd, LOCK_UN);
270 if (err < 0) {
271 LOG(ERROR) << "Failed to unlock profile file " << full_name;
272 }
273
274 // Done, close the file.
275 ::close(fd);
276
277 // Clean the profile for the next time.
278 CleanProfile();
279
Dave Allison0aded082013-11-07 13:15:11 -0800280 return num_methods;
281}
282
Calin Juravlec1b643c2014-05-30 23:44:11 +0100283bool BackgroundMethodSamplingProfiler::Start(
284 const std::string& output_filename, const ProfilerOptions& options) {
285 if (!options.IsEnabled()) {
286 LOG(INFO) << "Profiler disabled. To enable setprop dalvik.vm.profiler 1.";
287 return false;
288 }
289
290 CHECK(!output_filename.empty());
291
Dave Allison0aded082013-11-07 13:15:11 -0800292 Thread* self = Thread::Current();
293 {
294 MutexLock mu(self, *Locks::profiler_lock_);
295 // Don't start two profiler threads.
296 if (profiler_ != nullptr) {
Calin Juravlec1b643c2014-05-30 23:44:11 +0100297 return true;
Dave Allison0aded082013-11-07 13:15:11 -0800298 }
299 }
300
Calin Juravlec1b643c2014-05-30 23:44:11 +0100301 LOG(INFO) << "Starting profiler using output file: " << output_filename
302 << " and options: " << options;
Dave Allison0aded082013-11-07 13:15:11 -0800303 {
304 MutexLock mu(self, *Locks::profiler_lock_);
Calin Juravlec1b643c2014-05-30 23:44:11 +0100305 profiler_ = new BackgroundMethodSamplingProfiler(output_filename, options);
Dave Allison0aded082013-11-07 13:15:11 -0800306
307 CHECK_PTHREAD_CALL(pthread_create, (&profiler_pthread_, nullptr, &RunProfilerThread,
308 reinterpret_cast<void*>(profiler_)),
309 "Profiler thread");
310 }
Calin Juravlec1b643c2014-05-30 23:44:11 +0100311 return true;
Dave Allison0aded082013-11-07 13:15:11 -0800312}
313
314
315
316void BackgroundMethodSamplingProfiler::Stop() {
317 BackgroundMethodSamplingProfiler* profiler = nullptr;
318 pthread_t profiler_pthread = 0U;
319 {
320 MutexLock trace_mu(Thread::Current(), *Locks::profiler_lock_);
Wei Jin6a586912014-05-21 16:07:40 -0700321 CHECK(!shutting_down_);
Dave Allison0aded082013-11-07 13:15:11 -0800322 profiler = profiler_;
323 shutting_down_ = true;
324 profiler_pthread = profiler_pthread_;
325 }
326
327 // Now wake up the sampler thread if it sleeping.
328 {
329 MutexLock profile_mu(Thread::Current(), profiler->wait_lock_);
330 profiler->period_condition_.Signal(Thread::Current());
331 }
332 // Wait for the sample thread to stop.
333 CHECK_PTHREAD_CALL(pthread_join, (profiler_pthread, nullptr), "profiler thread shutdown");
334
335 {
336 MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
337 profiler_ = nullptr;
338 }
339 delete profiler;
340}
341
342
343void BackgroundMethodSamplingProfiler::Shutdown() {
344 Stop();
345}
346
Calin Juravlec1b643c2014-05-30 23:44:11 +0100347BackgroundMethodSamplingProfiler::BackgroundMethodSamplingProfiler(
348 const std::string& output_filename, const ProfilerOptions& options)
349 : output_filename_(output_filename),
350 options_(options),
Dave Allison0aded082013-11-07 13:15:11 -0800351 wait_lock_("Profile wait lock"),
352 period_condition_("Profile condition", wait_lock_),
353 profile_table_(wait_lock_),
354 profiler_barrier_(new Barrier(0)) {
355 // Populate the filtered_methods set.
356 // This is empty right now, but to add a method, do this:
357 //
358 // filtered_methods_.insert("void java.lang.Object.wait(long, int)");
359}
360
361// A method has been hit, record its invocation in the method map.
362// The mutator_lock must be held (shared) when this is called.
363void BackgroundMethodSamplingProfiler::RecordMethod(mirror::ArtMethod* method) {
364 if (method == nullptr) {
365 profile_table_.NullMethod();
366 // Don't record a nullptr method.
367 return;
368 }
369
370 mirror::Class* cls = method->GetDeclaringClass();
371 if (cls != nullptr) {
372 if (cls->GetClassLoader() == nullptr) {
373 // Don't include things in the boot
374 profile_table_.BootMethod();
375 return;
376 }
377 }
378
379 bool is_filtered = false;
380
Mathieu Chartierbfd9a432014-05-21 17:43:44 -0700381 if (strcmp(method->GetName(), "<clinit>") == 0) {
Dave Allison0aded082013-11-07 13:15:11 -0800382 // always filter out class init
383 is_filtered = true;
384 }
385
386 // Filter out methods by name if there are any.
387 if (!is_filtered && filtered_methods_.size() > 0) {
388 std::string method_full_name = PrettyMethod(method);
389
390 // Don't include specific filtered methods.
391 is_filtered = filtered_methods_.count(method_full_name) != 0;
392 }
393
394 // Add to the profile table unless it is filtered out.
395 if (!is_filtered) {
396 profile_table_.Put(method);
397 }
398}
399
400// Clean out any recordings for the method traces.
401void BackgroundMethodSamplingProfiler::CleanProfile() {
402 profile_table_.Clear();
403}
404
405uint32_t BackgroundMethodSamplingProfiler::DumpProfile(std::ostream& os) {
406 return profile_table_.Write(os);
407}
408
409// Profile Table.
410// This holds a mapping of mirror::ArtMethod* to a count of how many times a sample
411// hit it at the top of the stack.
412ProfileSampleResults::ProfileSampleResults(Mutex& lock) : lock_(lock), num_samples_(0),
413 num_null_methods_(0),
414 num_boot_methods_(0) {
415 for (int i = 0; i < kHashSize; i++) {
416 table[i] = nullptr;
417 }
418}
419
420ProfileSampleResults::~ProfileSampleResults() {
421 for (int i = 0; i < kHashSize; i++) {
422 delete table[i];
423 }
424}
425
Calin Juravlebb0b53f2014-05-23 17:33:29 +0100426// Add a method to the profile table. If it's the first time the method
Dave Allison0aded082013-11-07 13:15:11 -0800427// has been seen, add it with count=1, otherwise increment the count.
428void ProfileSampleResults::Put(mirror::ArtMethod* method) {
429 lock_.Lock(Thread::Current());
430 uint32_t index = Hash(method);
431 if (table[index] == nullptr) {
432 table[index] = new Map();
433 }
434 Map::iterator i = table[index]->find(method);
435 if (i == table[index]->end()) {
436 (*table[index])[method] = 1;
437 } else {
438 i->second++;
439 }
440 num_samples_++;
441 lock_.Unlock(Thread::Current());
442}
443
Dave Allison39c3bfb2014-01-28 18:33:52 -0800444// Write the profile table to the output stream. Also merge with the previous profile.
Dave Allison0aded082013-11-07 13:15:11 -0800445uint32_t ProfileSampleResults::Write(std::ostream &os) {
446 ScopedObjectAccess soa(Thread::Current());
Dave Allison39c3bfb2014-01-28 18:33:52 -0800447 num_samples_ += previous_num_samples_;
448 num_null_methods_ += previous_num_null_methods_;
449 num_boot_methods_ += previous_num_boot_methods_;
450
Calin Juravlec1b643c2014-05-30 23:44:11 +0100451 VLOG(profiler) << "Profile: "
452 << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_;
Dave Allison0aded082013-11-07 13:15:11 -0800453 os << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_ << "\n";
454 uint32_t num_methods = 0;
455 for (int i = 0 ; i < kHashSize; i++) {
456 Map *map = table[i];
457 if (map != nullptr) {
458 for (const auto &meth_iter : *map) {
Dave Allison39c3bfb2014-01-28 18:33:52 -0800459 mirror::ArtMethod *method = meth_iter.first;
460 std::string method_name = PrettyMethod(method);
461
Mathieu Chartierbfd9a432014-05-21 17:43:44 -0700462 const DexFile::CodeItem* codeitem = method->GetCodeItem();
Dave Allison39c3bfb2014-01-28 18:33:52 -0800463 uint32_t method_size = 0;
464 if (codeitem != nullptr) {
465 method_size = codeitem->insns_size_in_code_units_;
466 }
467 uint32_t count = meth_iter.second;
468
469 // Merge this profile entry with one from a previous run (if present). Also
470 // remove the previous entry.
471 PreviousProfile::iterator pi = previous_.find(method_name);
472 if (pi != previous_.end()) {
473 count += pi->second.count_;
474 previous_.erase(pi);
475 }
476 os << StringPrintf("%s/%u/%u\n", method_name.c_str(), count, method_size);
477 ++num_methods;
478 }
Dave Allison0aded082013-11-07 13:15:11 -0800479 }
480 }
Dave Allison39c3bfb2014-01-28 18:33:52 -0800481
482 // Now we write out the remaining previous methods.
483 for (PreviousProfile::iterator pi = previous_.begin(); pi != previous_.end(); ++pi) {
484 os << StringPrintf("%s/%u/%u\n", pi->first.c_str(), pi->second.count_, pi->second.method_size_);
485 ++num_methods;
486 }
Dave Allison0aded082013-11-07 13:15:11 -0800487 return num_methods;
488}
489
490void ProfileSampleResults::Clear() {
491 num_samples_ = 0;
492 num_null_methods_ = 0;
493 num_boot_methods_ = 0;
494 for (int i = 0; i < kHashSize; i++) {
495 delete table[i];
496 table[i] = nullptr;
497 }
Dave Allison39c3bfb2014-01-28 18:33:52 -0800498 previous_.clear();
Dave Allison0aded082013-11-07 13:15:11 -0800499}
500
501uint32_t ProfileSampleResults::Hash(mirror::ArtMethod* method) {
Ian Rogersef7d42f2014-01-06 12:55:46 -0800502 return (PointerToLowMemUInt32(method) >> 3) % kHashSize;
Dave Allison0aded082013-11-07 13:15:11 -0800503}
504
Dave Allison39c3bfb2014-01-28 18:33:52 -0800505// Read a single line into the given string. Returns true if everything OK, false
506// on EOF or error.
507static bool ReadProfileLine(int fd, std::string& line) {
508 char buf[4];
509 line.clear();
510 while (true) {
511 int n = read(fd, buf, 1); // TODO: could speed this up but is it worth it?
512 if (n != 1) {
513 return false;
514 }
515 if (buf[0] == '\n') {
516 break;
517 }
518 line += buf[0];
519 }
520 return true;
521}
522
523void ProfileSampleResults::ReadPrevious(int fd) {
524 // Reset counters.
525 previous_num_samples_ = previous_num_null_methods_ = previous_num_boot_methods_ = 0;
526
527 std::string line;
528
529 // The first line contains summary information.
530 if (!ReadProfileLine(fd, line)) {
531 return;
532 }
533 std::vector<std::string> summary_info;
534 Split(line, '/', summary_info);
535 if (summary_info.size() != 3) {
536 // Bad summary info. It should be count/nullcount/bootcount
537 return;
538 }
539 previous_num_samples_ = atoi(summary_info[0].c_str());
540 previous_num_null_methods_ = atoi(summary_info[1].c_str());
541 previous_num_boot_methods_ = atoi(summary_info[2].c_str());
542
543 // Now read each line until the end of file. Each line consists of 3 fields separated by /
544 while (true) {
545 if (!ReadProfileLine(fd, line)) {
546 break;
547 }
548 std::vector<std::string> info;
549 Split(line, '/', info);
550 if (info.size() != 3) {
551 // Malformed.
552 break;
553 }
554 std::string methodname = info[0];
555 uint32_t count = atoi(info[1].c_str());
556 uint32_t size = atoi(info[2].c_str());
557 previous_[methodname] = PreviousValue(count, size);
558 }
559}
Dave Allison0aded082013-11-07 13:15:11 -0800560
Calin Juravlebb0b53f2014-05-23 17:33:29 +0100561bool ProfileFile::LoadFile(const std::string& fileName) {
Calin Juravle9dae5b42014-04-07 16:36:21 +0300562 LOG(VERBOSE) << "reading profile file " << fileName;
563 struct stat st;
564 int err = stat(fileName.c_str(), &st);
565 if (err == -1) {
566 LOG(VERBOSE) << "not found";
567 return false;
568 }
569 if (st.st_size == 0) {
Dave Allison644789f2014-04-10 13:06:10 -0700570 return false; // Empty profiles are invalid.
Calin Juravle9dae5b42014-04-07 16:36:21 +0300571 }
572 std::ifstream in(fileName.c_str());
573 if (!in) {
574 LOG(VERBOSE) << "profile file " << fileName << " exists but can't be opened";
575 LOG(VERBOSE) << "file owner: " << st.st_uid << ":" << st.st_gid;
576 LOG(VERBOSE) << "me: " << getuid() << ":" << getgid();
577 LOG(VERBOSE) << "file permissions: " << std::oct << st.st_mode;
578 LOG(VERBOSE) << "errno: " << errno;
579 return false;
580 }
581 // The first line contains summary information.
582 std::string line;
583 std::getline(in, line);
584 if (in.eof()) {
585 return false;
586 }
587 std::vector<std::string> summary_info;
588 Split(line, '/', summary_info);
589 if (summary_info.size() != 3) {
Calin Juravle19477a82014-06-06 12:24:21 +0100590 // Bad summary info. It should be total/null/boot.
Calin Juravle9dae5b42014-04-07 16:36:21 +0300591 return false;
592 }
Calin Juravle19477a82014-06-06 12:24:21 +0100593 // This is the number of hits in all profiled methods (without nullptr or boot methods)
594 uint32_t total_count = atoi(summary_info[0].c_str());
Calin Juravle9dae5b42014-04-07 16:36:21 +0300595
596 // Now read each line until the end of file. Each line consists of 3 fields separated by '/'.
597 // Store the info in descending order given by the most used methods.
598 typedef std::set<std::pair<int, std::vector<std::string>>> ProfileSet;
599 ProfileSet countSet;
600 while (!in.eof()) {
601 std::getline(in, line);
602 if (in.eof()) {
603 break;
604 }
605 std::vector<std::string> info;
606 Split(line, '/', info);
607 if (info.size() != 3) {
608 // Malformed.
Calin Juravlebb0b53f2014-05-23 17:33:29 +0100609 return false;
Calin Juravle9dae5b42014-04-07 16:36:21 +0300610 }
611 int count = atoi(info[1].c_str());
612 countSet.insert(std::make_pair(-count, info));
613 }
614
615 uint32_t curTotalCount = 0;
616 ProfileSet::iterator end = countSet.end();
617 const ProfileData* prevData = nullptr;
618 for (ProfileSet::iterator it = countSet.begin(); it != end ; it++) {
619 const std::string& methodname = it->second[0];
620 uint32_t count = -it->first;
621 uint32_t size = atoi(it->second[2].c_str());
622 double usedPercent = (count * 100.0) / total_count;
623
624 curTotalCount += count;
625 // Methods with the same count should be part of the same top K percentage bucket.
626 double topKPercentage = (prevData != nullptr) && (prevData->GetCount() == count)
627 ? prevData->GetTopKUsedPercentage()
628 : 100 * static_cast<double>(curTotalCount) / static_cast<double>(total_count);
629
630 // Add it to the profile map.
631 ProfileData curData = ProfileData(methodname, count, size, usedPercent, topKPercentage);
Calin Juravlebb0b53f2014-05-23 17:33:29 +0100632 profile_map_[methodname] = curData;
Calin Juravle9dae5b42014-04-07 16:36:21 +0300633 prevData = &curData;
634 }
635 return true;
636}
637
Calin Juravlebb0b53f2014-05-23 17:33:29 +0100638bool ProfileFile::GetProfileData(ProfileFile::ProfileData* data, const std::string& method_name) {
639 ProfileMap::iterator i = profile_map_.find(method_name);
640 if (i == profile_map_.end()) {
Calin Juravle9dae5b42014-04-07 16:36:21 +0300641 return false;
642 }
Calin Juravlebb0b53f2014-05-23 17:33:29 +0100643 *data = i->second;
644 return true;
645}
646
647bool ProfileFile::GetTopKSamples(std::set<std::string>& topKSamples, double topKPercentage) {
648 ProfileMap::iterator end = profile_map_.end();
649 for (ProfileMap::iterator it = profile_map_.begin(); it != end; it++) {
Calin Juravle9dae5b42014-04-07 16:36:21 +0300650 if (it->second.GetTopKUsedPercentage() < topKPercentage) {
651 topKSamples.insert(it->first);
652 }
653 }
654 return true;
655}
656
657} // namespace art