blob: 2cd876a5a35b8418b047fd8d91658e16da179199 [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);
Wei Jina93b0bb2014-06-09 16:19:15 -070066 uint32_t dex_pc;
67 mirror::ArtMethod* method = thread->GetCurrentMethod(&dex_pc);
Dave Allison0aded082013-11-07 13:15:11 -080068 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 }
Wei Jina93b0bb2014-06-09 16:19:15 -070075 profiler->RecordMethod(method, dex_pc);
Dave Allison0aded082013-11-07 13:15:11 -080076}
77
Dave Allison0aded082013-11-07 13:15:11 -080078// A closure that is called by the thread checkpoint code.
79class SampleCheckpoint : public Closure {
80 public:
81 explicit SampleCheckpoint(BackgroundMethodSamplingProfiler* const profiler) :
82 profiler_(profiler) {}
83
84 virtual void Run(Thread* thread) NO_THREAD_SAFETY_ANALYSIS {
85 Thread* self = Thread::Current();
86 if (thread == nullptr) {
87 LOG(ERROR) << "Checkpoint with nullptr thread";
88 return;
89 }
90
91 // Grab the mutator lock (shared access).
92 ScopedObjectAccess soa(self);
93
94 // Grab a sample.
95 GetSample(thread, this->profiler_);
96
97 // And finally tell the barrier that we're done.
98 this->profiler_->GetBarrier().Pass(self);
99 }
100
101 private:
102 BackgroundMethodSamplingProfiler* const profiler_;
103};
104
105bool BackgroundMethodSamplingProfiler::ShuttingDown(Thread* self) {
106 MutexLock mu(self, *Locks::profiler_lock_);
107 return shutting_down_;
108}
109
110void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) {
111 Runtime* runtime = Runtime::Current();
112 BackgroundMethodSamplingProfiler* profiler =
113 reinterpret_cast<BackgroundMethodSamplingProfiler*>(arg);
114
115 // Add a random delay for the first time run so that we don't hammer the CPU
116 // with all profiles running at the same time.
117 const int kRandomDelayMaxSecs = 30;
118 const double kMaxBackoffSecs = 24*60*60; // Max backoff time.
119
120 srand(MicroTime() * getpid());
121 int startup_delay = rand() % kRandomDelayMaxSecs; // random delay for startup.
122
123
124 CHECK(runtime->AttachCurrentThread("Profiler", true, runtime->GetSystemThreadGroup(),
125 !runtime->IsCompiler()));
126
127 Thread* self = Thread::Current();
128
Calin Juravlec1b643c2014-05-30 23:44:11 +0100129 double backoff = 1.0;
Dave Allison0aded082013-11-07 13:15:11 -0800130 while (true) {
131 if (ShuttingDown(self)) {
132 break;
133 }
134
135 {
136 // wait until we need to run another profile
Calin Juravlec1b643c2014-05-30 23:44:11 +0100137 uint64_t delay_secs = profiler->options_.GetPeriodS() * backoff;
Dave Allison0aded082013-11-07 13:15:11 -0800138
139 // Add a startup delay to prevent all the profiles running at once.
140 delay_secs += startup_delay;
141
142 // Immediate startup for benchmarking?
Calin Juravlec1b643c2014-05-30 23:44:11 +0100143 if (profiler->options_.GetStartImmediately() && startup_delay > 0) {
Dave Allison0aded082013-11-07 13:15:11 -0800144 delay_secs = 0;
145 }
146
147 startup_delay = 0;
148
Brian Carlstrom4d466a82014-05-08 19:05:29 -0700149 VLOG(profiler) << "Delaying profile start for " << delay_secs << " secs";
Dave Allison0aded082013-11-07 13:15:11 -0800150 MutexLock mu(self, profiler->wait_lock_);
151 profiler->period_condition_.TimedWait(self, delay_secs * 1000, 0);
152
153 // Expand the backoff by its coefficient, but don't go beyond the max.
Calin Juravlec1b643c2014-05-30 23:44:11 +0100154 backoff = std::min(backoff * profiler->options_.GetBackoffCoefficient(), kMaxBackoffSecs);
Dave Allison0aded082013-11-07 13:15:11 -0800155 }
156
157 if (ShuttingDown(self)) {
158 break;
159 }
160
161
162 uint64_t start_us = MicroTime();
Calin Juravlec1b643c2014-05-30 23:44:11 +0100163 uint64_t end_us = start_us + profiler->options_.GetDurationS() * UINT64_C(1000000);
Dave Allison0aded082013-11-07 13:15:11 -0800164 uint64_t now_us = start_us;
165
Calin Juravlec1b643c2014-05-30 23:44:11 +0100166 VLOG(profiler) << "Starting profiling run now for "
167 << PrettyDuration((end_us - start_us) * 1000);
Dave Allison0aded082013-11-07 13:15:11 -0800168
169 SampleCheckpoint check_point(profiler);
170
Dave Allison39c3bfb2014-01-28 18:33:52 -0800171 size_t valid_samples = 0;
Dave Allison0aded082013-11-07 13:15:11 -0800172 while (now_us < end_us) {
173 if (ShuttingDown(self)) {
174 break;
175 }
176
Calin Juravlec1b643c2014-05-30 23:44:11 +0100177 usleep(profiler->options_.GetIntervalUs()); // Non-interruptible sleep.
Dave Allison0aded082013-11-07 13:15:11 -0800178
179 ThreadList* thread_list = runtime->GetThreadList();
180
181 profiler->profiler_barrier_->Init(self, 0);
Dave Allison39c3bfb2014-01-28 18:33:52 -0800182 size_t barrier_count = thread_list->RunCheckpointOnRunnableThreads(&check_point);
183
184 // All threads are suspended, nothing to do.
185 if (barrier_count == 0) {
186 now_us = MicroTime();
187 continue;
188 }
189
190 valid_samples += barrier_count;
Dave Allison0aded082013-11-07 13:15:11 -0800191
Wei Jin6a586912014-05-21 16:07:40 -0700192 ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
Dave Allison0aded082013-11-07 13:15:11 -0800193
194 // Wait for the barrier to be crossed by all runnable threads. This wait
195 // is done with a timeout so that we can detect problems with the checkpoint
196 // running code. We should never see this.
197 const uint32_t kWaitTimeoutMs = 10000;
198 const uint32_t kWaitTimeoutUs = kWaitTimeoutMs * 1000;
199
200 uint64_t waitstart_us = MicroTime();
201 // Wait for all threads to pass the barrier.
202 profiler->profiler_barrier_->Increment(self, barrier_count, kWaitTimeoutMs);
203 uint64_t waitend_us = MicroTime();
204 uint64_t waitdiff_us = waitend_us - waitstart_us;
205
206 // We should never get a timeout. If we do, it suggests a problem with the checkpoint
207 // code. Crash the process in this case.
208 CHECK_LT(waitdiff_us, kWaitTimeoutUs);
209
Dave Allison0aded082013-11-07 13:15:11 -0800210 // Update the current time.
211 now_us = MicroTime();
212 }
213
Wei Jin6a586912014-05-21 16:07:40 -0700214 if (valid_samples > 0) {
Dave Allison0aded082013-11-07 13:15:11 -0800215 // After the profile has been taken, write it out.
216 ScopedObjectAccess soa(self); // Acquire the mutator lock.
217 uint32_t size = profiler->WriteProfile();
Brian Carlstrom4d466a82014-05-08 19:05:29 -0700218 VLOG(profiler) << "Profile size: " << size;
Dave Allison0aded082013-11-07 13:15:11 -0800219 }
220 }
221
222 LOG(INFO) << "Profiler shutdown";
223 runtime->DetachCurrentThread();
224 return nullptr;
225}
226
227// Write out the profile file if we are generating a profile.
228uint32_t BackgroundMethodSamplingProfiler::WriteProfile() {
Calin Juravlec1b643c2014-05-30 23:44:11 +0100229 std::string full_name = output_filename_;
Brian Carlstrom4d466a82014-05-08 19:05:29 -0700230 VLOG(profiler) << "Saving profile to " << full_name;
Dave Allison0aded082013-11-07 13:15:11 -0800231
Dave Allison39c3bfb2014-01-28 18:33:52 -0800232 int fd = open(full_name.c_str(), O_RDWR);
233 if (fd < 0) {
234 // Open failed.
235 LOG(ERROR) << "Failed to open profile file " << full_name;
Dave Allison0aded082013-11-07 13:15:11 -0800236 return 0;
237 }
Dave Allison39c3bfb2014-01-28 18:33:52 -0800238
239 // Lock the file for exclusive access. This will block if another process is using
240 // the file.
241 int err = flock(fd, LOCK_EX);
242 if (err < 0) {
243 LOG(ERROR) << "Failed to lock profile file " << full_name;
244 return 0;
245 }
246
247 // Read the previous profile.
Wei Jina93b0bb2014-06-09 16:19:15 -0700248 profile_table_.ReadPrevious(fd, options_.GetProfileType());
Dave Allison39c3bfb2014-01-28 18:33:52 -0800249
250 // Move back to the start of the file.
251 lseek(fd, 0, SEEK_SET);
252
253 // Format the profile output and write to the file.
Dave Allison0aded082013-11-07 13:15:11 -0800254 std::ostringstream os;
255 uint32_t num_methods = DumpProfile(os);
256 std::string data(os.str());
Dave Allison39c3bfb2014-01-28 18:33:52 -0800257 const char *p = data.c_str();
258 size_t length = data.length();
259 size_t full_length = length;
260 do {
261 int n = ::write(fd, p, length);
262 p += n;
263 length -= n;
264 } while (length > 0);
265
266 // Truncate the file to the new length.
267 ftruncate(fd, full_length);
268
269 // Now unlock the file, allowing another process in.
270 err = flock(fd, LOCK_UN);
271 if (err < 0) {
272 LOG(ERROR) << "Failed to unlock profile file " << full_name;
273 }
274
275 // Done, close the file.
276 ::close(fd);
277
278 // Clean the profile for the next time.
279 CleanProfile();
280
Dave Allison0aded082013-11-07 13:15:11 -0800281 return num_methods;
282}
283
Calin Juravlec1b643c2014-05-30 23:44:11 +0100284bool BackgroundMethodSamplingProfiler::Start(
285 const std::string& output_filename, const ProfilerOptions& options) {
286 if (!options.IsEnabled()) {
287 LOG(INFO) << "Profiler disabled. To enable setprop dalvik.vm.profiler 1.";
288 return false;
289 }
290
291 CHECK(!output_filename.empty());
292
Dave Allison0aded082013-11-07 13:15:11 -0800293 Thread* self = Thread::Current();
294 {
295 MutexLock mu(self, *Locks::profiler_lock_);
296 // Don't start two profiler threads.
297 if (profiler_ != nullptr) {
Calin Juravlec1b643c2014-05-30 23:44:11 +0100298 return true;
Dave Allison0aded082013-11-07 13:15:11 -0800299 }
300 }
301
Calin Juravlec1b643c2014-05-30 23:44:11 +0100302 LOG(INFO) << "Starting profiler using output file: " << output_filename
303 << " and options: " << options;
Dave Allison0aded082013-11-07 13:15:11 -0800304 {
305 MutexLock mu(self, *Locks::profiler_lock_);
Calin Juravlec1b643c2014-05-30 23:44:11 +0100306 profiler_ = new BackgroundMethodSamplingProfiler(output_filename, options);
Dave Allison0aded082013-11-07 13:15:11 -0800307
308 CHECK_PTHREAD_CALL(pthread_create, (&profiler_pthread_, nullptr, &RunProfilerThread,
309 reinterpret_cast<void*>(profiler_)),
310 "Profiler thread");
311 }
Calin Juravlec1b643c2014-05-30 23:44:11 +0100312 return true;
Dave Allison0aded082013-11-07 13:15:11 -0800313}
314
315
316
317void BackgroundMethodSamplingProfiler::Stop() {
318 BackgroundMethodSamplingProfiler* profiler = nullptr;
319 pthread_t profiler_pthread = 0U;
320 {
321 MutexLock trace_mu(Thread::Current(), *Locks::profiler_lock_);
Wei Jin6a586912014-05-21 16:07:40 -0700322 CHECK(!shutting_down_);
Dave Allison0aded082013-11-07 13:15:11 -0800323 profiler = profiler_;
324 shutting_down_ = true;
325 profiler_pthread = profiler_pthread_;
326 }
327
328 // Now wake up the sampler thread if it sleeping.
329 {
330 MutexLock profile_mu(Thread::Current(), profiler->wait_lock_);
331 profiler->period_condition_.Signal(Thread::Current());
332 }
333 // Wait for the sample thread to stop.
334 CHECK_PTHREAD_CALL(pthread_join, (profiler_pthread, nullptr), "profiler thread shutdown");
335
336 {
337 MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
338 profiler_ = nullptr;
339 }
340 delete profiler;
341}
342
343
344void BackgroundMethodSamplingProfiler::Shutdown() {
345 Stop();
346}
347
Calin Juravlec1b643c2014-05-30 23:44:11 +0100348BackgroundMethodSamplingProfiler::BackgroundMethodSamplingProfiler(
349 const std::string& output_filename, const ProfilerOptions& options)
350 : output_filename_(output_filename),
351 options_(options),
Dave Allison0aded082013-11-07 13:15:11 -0800352 wait_lock_("Profile wait lock"),
353 period_condition_("Profile condition", wait_lock_),
354 profile_table_(wait_lock_),
355 profiler_barrier_(new Barrier(0)) {
356 // Populate the filtered_methods set.
357 // This is empty right now, but to add a method, do this:
358 //
359 // filtered_methods_.insert("void java.lang.Object.wait(long, int)");
360}
361
362// A method has been hit, record its invocation in the method map.
363// The mutator_lock must be held (shared) when this is called.
Wei Jina93b0bb2014-06-09 16:19:15 -0700364void BackgroundMethodSamplingProfiler::RecordMethod(mirror::ArtMethod* method, uint32_t dex_pc) {
Dave Allison0aded082013-11-07 13:15:11 -0800365 if (method == nullptr) {
366 profile_table_.NullMethod();
367 // Don't record a nullptr method.
368 return;
369 }
370
371 mirror::Class* cls = method->GetDeclaringClass();
372 if (cls != nullptr) {
373 if (cls->GetClassLoader() == nullptr) {
374 // Don't include things in the boot
375 profile_table_.BootMethod();
376 return;
377 }
378 }
379
380 bool is_filtered = false;
381
Mathieu Chartierbfd9a432014-05-21 17:43:44 -0700382 if (strcmp(method->GetName(), "<clinit>") == 0) {
Dave Allison0aded082013-11-07 13:15:11 -0800383 // always filter out class init
384 is_filtered = true;
385 }
386
387 // Filter out methods by name if there are any.
388 if (!is_filtered && filtered_methods_.size() > 0) {
389 std::string method_full_name = PrettyMethod(method);
390
391 // Don't include specific filtered methods.
392 is_filtered = filtered_methods_.count(method_full_name) != 0;
393 }
394
395 // Add to the profile table unless it is filtered out.
396 if (!is_filtered) {
Wei Jina93b0bb2014-06-09 16:19:15 -0700397 if (options_.GetProfileType() == kProfilerMethod) {
398 profile_table_.Put(method);
399 } else if (options_.GetProfileType() == kProfilerMethodAndDexPC) {
400 profile_table_.PutDexPC(method, dex_pc);
401 }
Dave Allison0aded082013-11-07 13:15:11 -0800402 }
403}
404
405// Clean out any recordings for the method traces.
406void BackgroundMethodSamplingProfiler::CleanProfile() {
407 profile_table_.Clear();
408}
409
410uint32_t BackgroundMethodSamplingProfiler::DumpProfile(std::ostream& os) {
Wei Jina93b0bb2014-06-09 16:19:15 -0700411 return profile_table_.Write(os, options_.GetProfileType());
Dave Allison0aded082013-11-07 13:15:11 -0800412}
413
414// Profile Table.
415// This holds a mapping of mirror::ArtMethod* to a count of how many times a sample
416// hit it at the top of the stack.
417ProfileSampleResults::ProfileSampleResults(Mutex& lock) : lock_(lock), num_samples_(0),
418 num_null_methods_(0),
419 num_boot_methods_(0) {
420 for (int i = 0; i < kHashSize; i++) {
421 table[i] = nullptr;
Wei Jina93b0bb2014-06-09 16:19:15 -0700422 dex_table[i] = nullptr;
Dave Allison0aded082013-11-07 13:15:11 -0800423 }
424}
425
426ProfileSampleResults::~ProfileSampleResults() {
Wei Jina93b0bb2014-06-09 16:19:15 -0700427 Clear();
Dave Allison0aded082013-11-07 13:15:11 -0800428}
429
Calin Juravlebb0b53f2014-05-23 17:33:29 +0100430// Add a method to the profile table. If it's the first time the method
Dave Allison0aded082013-11-07 13:15:11 -0800431// has been seen, add it with count=1, otherwise increment the count.
432void ProfileSampleResults::Put(mirror::ArtMethod* method) {
Wei Jina93b0bb2014-06-09 16:19:15 -0700433 MutexLock mu(Thread::Current(), lock_);
Dave Allison0aded082013-11-07 13:15:11 -0800434 uint32_t index = Hash(method);
435 if (table[index] == nullptr) {
436 table[index] = new Map();
437 }
438 Map::iterator i = table[index]->find(method);
439 if (i == table[index]->end()) {
440 (*table[index])[method] = 1;
441 } else {
442 i->second++;
443 }
444 num_samples_++;
Wei Jina93b0bb2014-06-09 16:19:15 -0700445}
446
447// Add a method with dex pc to the profile table
448void ProfileSampleResults::PutDexPC(mirror::ArtMethod* method, uint32_t dex_pc) {
449 MutexLock mu(Thread::Current(), lock_);
450 uint32_t index = Hash(method);
451 if (dex_table[index] == nullptr) {
452 dex_table[index] = new MethodDexPCMap();
453 }
454 MethodDexPCMap::iterator i = dex_table[index]->find(method);
455 if (i == dex_table[index]->end()) {
456 DexPCCountMap* dex_pc_map = new DexPCCountMap();
457 (*dex_pc_map)[dex_pc] = 1;
458 (*dex_table[index])[method] = dex_pc_map;
459 } else {
460 DexPCCountMap* dex_pc_count = i->second;
461 DexPCCountMap::iterator dex_pc_i = dex_pc_count->find(dex_pc);
462 if (dex_pc_i == dex_pc_count->end()) {
463 (*dex_pc_count)[dex_pc] = 1;
464 } else {
465 dex_pc_i->second++;
466 }
467 }
468 num_samples_++;
Dave Allison0aded082013-11-07 13:15:11 -0800469}
470
Dave Allison39c3bfb2014-01-28 18:33:52 -0800471// Write the profile table to the output stream. Also merge with the previous profile.
Wei Jina93b0bb2014-06-09 16:19:15 -0700472uint32_t ProfileSampleResults::Write(std::ostream& os, ProfileDataType type) {
Dave Allison0aded082013-11-07 13:15:11 -0800473 ScopedObjectAccess soa(Thread::Current());
Dave Allison39c3bfb2014-01-28 18:33:52 -0800474 num_samples_ += previous_num_samples_;
475 num_null_methods_ += previous_num_null_methods_;
476 num_boot_methods_ += previous_num_boot_methods_;
477
Calin Juravlec1b643c2014-05-30 23:44:11 +0100478 VLOG(profiler) << "Profile: "
479 << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_;
Dave Allison0aded082013-11-07 13:15:11 -0800480 os << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_ << "\n";
481 uint32_t num_methods = 0;
Wei Jina93b0bb2014-06-09 16:19:15 -0700482 if (type == kProfilerMethod) {
483 for (int i = 0 ; i < kHashSize; i++) {
484 Map *map = table[i];
485 if (map != nullptr) {
486 for (const auto &meth_iter : *map) {
487 mirror::ArtMethod *method = meth_iter.first;
488 std::string method_name = PrettyMethod(method);
Dave Allison39c3bfb2014-01-28 18:33:52 -0800489
Wei Jina93b0bb2014-06-09 16:19:15 -0700490 const DexFile::CodeItem* codeitem = method->GetCodeItem();
491 uint32_t method_size = 0;
492 if (codeitem != nullptr) {
493 method_size = codeitem->insns_size_in_code_units_;
494 }
495 uint32_t count = meth_iter.second;
Dave Allison39c3bfb2014-01-28 18:33:52 -0800496
Wei Jina93b0bb2014-06-09 16:19:15 -0700497 // Merge this profile entry with one from a previous run (if present). Also
498 // remove the previous entry.
499 PreviousProfile::iterator pi = previous_.find(method_name);
500 if (pi != previous_.end()) {
501 count += pi->second.count_;
502 previous_.erase(pi);
503 }
504 os << StringPrintf("%s/%u/%u\n", method_name.c_str(), count, method_size);
505 ++num_methods;
Dave Allison39c3bfb2014-01-28 18:33:52 -0800506 }
Wei Jina93b0bb2014-06-09 16:19:15 -0700507 }
508 }
509 } else if (type == kProfilerMethodAndDexPC) {
510 for (int i = 0 ; i < kHashSize; i++) {
511 MethodDexPCMap *dex_map = dex_table[i];
512 if (dex_map != nullptr) {
513 for (const auto &dex_pc_iter : *dex_map) {
514 mirror::ArtMethod *method = dex_pc_iter.first;
515 std::string method_name = PrettyMethod(method);
516
517 const DexFile::CodeItem* codeitem = method->GetCodeItem();
518 uint32_t method_size = 0;
519 if (codeitem != nullptr) {
520 method_size = codeitem->insns_size_in_code_units_;
521 }
522 DexPCCountMap* dex_pc_map = dex_pc_iter.second;
523 uint32_t total_count = 0;
524 for (const auto &dex_pc_i : *dex_pc_map) {
525 total_count += dex_pc_i.second;
526 }
527
528 PreviousProfile::iterator pi = previous_.find(method_name);
529 if (pi != previous_.end()) {
530 total_count += pi->second.count_;
531 DexPCCountMap* previous_dex_pc_map = pi->second.dex_pc_map_;
532 if (previous_dex_pc_map != nullptr) {
533 for (const auto &dex_pc_i : *previous_dex_pc_map) {
534 uint32_t dex_pc = dex_pc_i.first;
535 uint32_t count = dex_pc_i.second;
536 DexPCCountMap::iterator di = dex_pc_map->find(dex_pc);
537 if (di == dex_pc_map->end()) {
538 (*dex_pc_map)[dex_pc] = count;
539 } else {
540 di->second += count;
541 }
542 }
543 }
544 delete previous_dex_pc_map;
545 previous_.erase(pi);
546 }
547 std::vector<std::string> dex_pc_count_vector;
548 for (const auto &dex_pc_i : *dex_pc_map) {
549 dex_pc_count_vector.push_back(StringPrintf("%u:%u", dex_pc_i.first, dex_pc_i.second));
550 }
551 // We write out profile data with dex pc information in the following format:
552 // "method/total_count/size/[pc_1:count_1,pc_2:count_2,...]".
553 os << StringPrintf("%s/%u/%u/[%s]\n", method_name.c_str(), total_count,
554 method_size, Join(dex_pc_count_vector, ',').c_str());
555 ++num_methods;
556 }
Dave Allison39c3bfb2014-01-28 18:33:52 -0800557 }
Dave Allison0aded082013-11-07 13:15:11 -0800558 }
559 }
Dave Allison39c3bfb2014-01-28 18:33:52 -0800560
561 // Now we write out the remaining previous methods.
Wei Jina93b0bb2014-06-09 16:19:15 -0700562 for (const auto &pi : previous_) {
563 if (type == kProfilerMethod) {
564 os << StringPrintf("%s/%u/%u\n", pi.first.c_str(), pi.second.count_, pi.second.method_size_);
565 } else if (type == kProfilerMethodAndDexPC) {
566 os << StringPrintf("%s/%u/%u/[", pi.first.c_str(), pi.second.count_, pi.second.method_size_);
567 DexPCCountMap* previous_dex_pc_map = pi.second.dex_pc_map_;
568 if (previous_dex_pc_map != nullptr) {
569 std::vector<std::string> dex_pc_count_vector;
570 for (const auto &dex_pc_i : *previous_dex_pc_map) {
571 dex_pc_count_vector.push_back(StringPrintf("%u:%u", dex_pc_i.first, dex_pc_i.second));
572 }
573 os << Join(dex_pc_count_vector, ',');
574 }
575 os << "]\n";
576 }
Dave Allison39c3bfb2014-01-28 18:33:52 -0800577 ++num_methods;
578 }
Dave Allison0aded082013-11-07 13:15:11 -0800579 return num_methods;
580}
581
582void ProfileSampleResults::Clear() {
583 num_samples_ = 0;
584 num_null_methods_ = 0;
585 num_boot_methods_ = 0;
586 for (int i = 0; i < kHashSize; i++) {
Wei Jina93b0bb2014-06-09 16:19:15 -0700587 delete table[i];
588 table[i] = nullptr;
589 if (dex_table[i] != nullptr) {
590 for (auto &di : *dex_table[i]) {
591 delete di.second;
592 di.second = nullptr;
593 }
594 }
595 delete dex_table[i];
596 dex_table[i] = nullptr;
597 }
598 for (auto &pi : previous_) {
599 delete pi.second.dex_pc_map_;
600 pi.second.dex_pc_map_ = nullptr;
Dave Allison0aded082013-11-07 13:15:11 -0800601 }
Dave Allison39c3bfb2014-01-28 18:33:52 -0800602 previous_.clear();
Dave Allison0aded082013-11-07 13:15:11 -0800603}
604
605uint32_t ProfileSampleResults::Hash(mirror::ArtMethod* method) {
Ian Rogersef7d42f2014-01-06 12:55:46 -0800606 return (PointerToLowMemUInt32(method) >> 3) % kHashSize;
Dave Allison0aded082013-11-07 13:15:11 -0800607}
608
Dave Allison39c3bfb2014-01-28 18:33:52 -0800609// Read a single line into the given string. Returns true if everything OK, false
610// on EOF or error.
611static bool ReadProfileLine(int fd, std::string& line) {
612 char buf[4];
613 line.clear();
614 while (true) {
615 int n = read(fd, buf, 1); // TODO: could speed this up but is it worth it?
616 if (n != 1) {
617 return false;
618 }
619 if (buf[0] == '\n') {
620 break;
621 }
622 line += buf[0];
623 }
624 return true;
625}
626
Wei Jina93b0bb2014-06-09 16:19:15 -0700627void ProfileSampleResults::ReadPrevious(int fd, ProfileDataType type) {
Dave Allison39c3bfb2014-01-28 18:33:52 -0800628 // Reset counters.
629 previous_num_samples_ = previous_num_null_methods_ = previous_num_boot_methods_ = 0;
630
631 std::string line;
632
633 // The first line contains summary information.
634 if (!ReadProfileLine(fd, line)) {
635 return;
636 }
637 std::vector<std::string> summary_info;
638 Split(line, '/', summary_info);
639 if (summary_info.size() != 3) {
640 // Bad summary info. It should be count/nullcount/bootcount
641 return;
642 }
643 previous_num_samples_ = atoi(summary_info[0].c_str());
644 previous_num_null_methods_ = atoi(summary_info[1].c_str());
645 previous_num_boot_methods_ = atoi(summary_info[2].c_str());
646
Wei Jina93b0bb2014-06-09 16:19:15 -0700647 // Now read each line until the end of file. Each line consists of 3 or 4 fields separated by /
Dave Allison39c3bfb2014-01-28 18:33:52 -0800648 while (true) {
649 if (!ReadProfileLine(fd, line)) {
650 break;
651 }
652 std::vector<std::string> info;
653 Split(line, '/', info);
Wei Jina93b0bb2014-06-09 16:19:15 -0700654 if (info.size() != 3 && info.size() != 4) {
Dave Allison39c3bfb2014-01-28 18:33:52 -0800655 // Malformed.
656 break;
657 }
658 std::string methodname = info[0];
Wei Jina93b0bb2014-06-09 16:19:15 -0700659 uint32_t total_count = atoi(info[1].c_str());
Dave Allison39c3bfb2014-01-28 18:33:52 -0800660 uint32_t size = atoi(info[2].c_str());
Wei Jina93b0bb2014-06-09 16:19:15 -0700661 DexPCCountMap* dex_pc_map = nullptr;
662 if (type == kProfilerMethodAndDexPC && info.size() == 4) {
663 dex_pc_map = new DexPCCountMap();
664 std::string dex_pc_counts_str = info[3].substr(1, info[3].size() - 2);
665 std::vector<std::string> dex_pc_count_pairs;
666 Split(dex_pc_counts_str, ',', dex_pc_count_pairs);
667 for (uint32_t i = 0; i < dex_pc_count_pairs.size(); ++i) {
668 std::vector<std::string> dex_pc_count;
669 Split(dex_pc_count_pairs[i], ':', dex_pc_count);
670 uint32_t dex_pc = atoi(dex_pc_count[0].c_str());
671 uint32_t count = atoi(dex_pc_count[1].c_str());
672 (*dex_pc_map)[dex_pc] = count;
673 }
674 }
675 previous_[methodname] = PreviousValue(total_count, size, dex_pc_map);
Dave Allison39c3bfb2014-01-28 18:33:52 -0800676 }
677}
Dave Allison0aded082013-11-07 13:15:11 -0800678
Calin Juravlebb0b53f2014-05-23 17:33:29 +0100679bool ProfileFile::LoadFile(const std::string& fileName) {
Calin Juravle9dae5b42014-04-07 16:36:21 +0300680 LOG(VERBOSE) << "reading profile file " << fileName;
681 struct stat st;
682 int err = stat(fileName.c_str(), &st);
683 if (err == -1) {
684 LOG(VERBOSE) << "not found";
685 return false;
686 }
687 if (st.st_size == 0) {
Dave Allison644789f2014-04-10 13:06:10 -0700688 return false; // Empty profiles are invalid.
Calin Juravle9dae5b42014-04-07 16:36:21 +0300689 }
690 std::ifstream in(fileName.c_str());
691 if (!in) {
692 LOG(VERBOSE) << "profile file " << fileName << " exists but can't be opened";
693 LOG(VERBOSE) << "file owner: " << st.st_uid << ":" << st.st_gid;
694 LOG(VERBOSE) << "me: " << getuid() << ":" << getgid();
695 LOG(VERBOSE) << "file permissions: " << std::oct << st.st_mode;
696 LOG(VERBOSE) << "errno: " << errno;
697 return false;
698 }
699 // The first line contains summary information.
700 std::string line;
701 std::getline(in, line);
702 if (in.eof()) {
703 return false;
704 }
705 std::vector<std::string> summary_info;
706 Split(line, '/', summary_info);
707 if (summary_info.size() != 3) {
Calin Juravle19477a82014-06-06 12:24:21 +0100708 // Bad summary info. It should be total/null/boot.
Calin Juravle9dae5b42014-04-07 16:36:21 +0300709 return false;
710 }
Calin Juravle19477a82014-06-06 12:24:21 +0100711 // This is the number of hits in all profiled methods (without nullptr or boot methods)
712 uint32_t total_count = atoi(summary_info[0].c_str());
Calin Juravle9dae5b42014-04-07 16:36:21 +0300713
714 // Now read each line until the end of file. Each line consists of 3 fields separated by '/'.
715 // Store the info in descending order given by the most used methods.
716 typedef std::set<std::pair<int, std::vector<std::string>>> ProfileSet;
717 ProfileSet countSet;
718 while (!in.eof()) {
719 std::getline(in, line);
720 if (in.eof()) {
721 break;
722 }
723 std::vector<std::string> info;
724 Split(line, '/', info);
Wei Jina93b0bb2014-06-09 16:19:15 -0700725 if (info.size() != 3 && info.size() != 4) {
Calin Juravle9dae5b42014-04-07 16:36:21 +0300726 // Malformed.
Calin Juravlebb0b53f2014-05-23 17:33:29 +0100727 return false;
Calin Juravle9dae5b42014-04-07 16:36:21 +0300728 }
729 int count = atoi(info[1].c_str());
730 countSet.insert(std::make_pair(-count, info));
731 }
732
733 uint32_t curTotalCount = 0;
734 ProfileSet::iterator end = countSet.end();
735 const ProfileData* prevData = nullptr;
736 for (ProfileSet::iterator it = countSet.begin(); it != end ; it++) {
737 const std::string& methodname = it->second[0];
738 uint32_t count = -it->first;
739 uint32_t size = atoi(it->second[2].c_str());
740 double usedPercent = (count * 100.0) / total_count;
741
742 curTotalCount += count;
743 // Methods with the same count should be part of the same top K percentage bucket.
744 double topKPercentage = (prevData != nullptr) && (prevData->GetCount() == count)
745 ? prevData->GetTopKUsedPercentage()
746 : 100 * static_cast<double>(curTotalCount) / static_cast<double>(total_count);
747
748 // Add it to the profile map.
749 ProfileData curData = ProfileData(methodname, count, size, usedPercent, topKPercentage);
Calin Juravlebb0b53f2014-05-23 17:33:29 +0100750 profile_map_[methodname] = curData;
Calin Juravle9dae5b42014-04-07 16:36:21 +0300751 prevData = &curData;
752 }
753 return true;
754}
755
Calin Juravlebb0b53f2014-05-23 17:33:29 +0100756bool ProfileFile::GetProfileData(ProfileFile::ProfileData* data, const std::string& method_name) {
757 ProfileMap::iterator i = profile_map_.find(method_name);
758 if (i == profile_map_.end()) {
Calin Juravle9dae5b42014-04-07 16:36:21 +0300759 return false;
760 }
Calin Juravlebb0b53f2014-05-23 17:33:29 +0100761 *data = i->second;
762 return true;
763}
764
765bool ProfileFile::GetTopKSamples(std::set<std::string>& topKSamples, double topKPercentage) {
766 ProfileMap::iterator end = profile_map_.end();
767 for (ProfileMap::iterator it = profile_map_.begin(); it != end; it++) {
Calin Juravle9dae5b42014-04-07 16:36:21 +0300768 if (it->second.GetTopKUsedPercentage() < topKPercentage) {
769 topKSamples.insert(it->first);
770 }
771 }
772 return true;
773}
774
775} // namespace art