Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(489)

Side by Side Diff: runtime/vm/profiler.cc

Issue 25909002: Sampling profiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « runtime/vm/profiler.h ('k') | runtime/vm/profiler_android.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 #include <cstdio>
6
7 #include "platform/utils.h"
8
9 #include "vm/isolate.h"
10 #include "vm/json_stream.h"
11 #include "vm/native_symbol.h"
12 #include "vm/object.h"
13 #include "vm/os.h"
14 #include "vm/profiler.h"
15
16 namespace dart {
17
18 // Notes on locking and signal handling:
19
20 // The ProfilerManager has a single monitor (monitor_). This monitor guards
21 // access to the schedule list of isolates (isolates_, isolates_size_, etc).
22 //
23 // Each isolate has a mutex (profiler_data_mutex_) which protects access
24 // to the isolate's profiler data.
25 //
26 // Locks can be taken in this order:
27 // 1. ProfilerManager::monitor_
28 // 2. isolate->profiler_data_mutex_
29 // In other words, it is not acceptable to take ProfilerManager::monitor_
30 // after grabbing isolate->profiler_data_mutex_.
31 //
32 // ProfileManager::monitor_ taking entry points:
33 // InitOnce, Shutdown
34 // ProfilerManager::monitor_
35 // ScheduleIsolate, DescheduleIsolate.
36 // ProfilerManager::monitor_, isolate->profiler_data_mutex_
37 // ThreadMain
38 // isolate->profiler_data_mutex_ taking entry points:
39 // SetupIsolateForProfiling, FreeIsolateForProfiling.
40 // ProfilerManager::monitor_, isolate->profiler_data_mutex_
41 // ScheduleIsolate, DescheduleIsolate.
42 // ProfilerManager::monitor_, isolate->profiler_data_mutex_
43 // ProfileSignalAction
44 // isolate->profiler_data_mutex_
45 // ProfilerManager::monitor_, isolate->profiler_data_mutex_
46 //
47 // Signal handling and locking:
48 // On OSes with pthreads (Android, Linux, and Mac) we use signal delivery
49 // to interrupt the isolate running thread for sampling. After a thread
50 // is sent the SIGPROF, it is removed from the scheduled isolate list.
51 // Inside the signal handler, after the sample is taken, the isolate is
52 // added to the scheduled isolate list again. The side effect of this is
53 // that the signal handler must be able to acquire the isolate profiler data
54 // mutex and the profile manager monitor. When an isolate running thread
55 // (potential signal target) calls into an entry point which acquires
56 // ProfileManager::monitor_ signal delivery must be blocked. An example is
57 // Isolate::SetCurrent which blocks signal delivery while removing the old
58 // current isolate from the scheduled list and adding the new current isolate
59 // to the scheduled list.
60
61
62 DEFINE_FLAG(bool, profile, true, "Enable Sampling Profiler");
63
64 bool ProfilerManager::initialized_ = false;
65 bool ProfilerManager::shutdown_ = false;
66 Monitor* ProfilerManager::monitor_ = NULL;
67 Isolate** ProfilerManager::isolates_ = NULL;
68 intptr_t ProfilerManager::isolates_capacity_ = 0;
69 intptr_t ProfilerManager::isolates_size_ = 0;
70
71
72 void ProfilerManager::InitOnce() {
73 if (!FLAG_profile) {
74 return;
75 }
76 NativeSymbolResolver::InitOnce();
77 ASSERT(!initialized_);
78 monitor_ = new Monitor();
79 initialized_ = true;
80 ResizeIsolates(16);
81 Thread::Start(ThreadMain, 0);
82 }
83
84
85 void ProfilerManager::Shutdown() {
86 if (!FLAG_profile) {
87 return;
88 }
89 ScopedMonitor lock(monitor_);
90 shutdown_ = true;
91 for (intptr_t i = 0; i < isolates_size_; i++) {
92 Isolate* isolate = isolates_[i];
93 ASSERT(isolate != NULL);
94 FreeIsolateProfilingData(isolate);
95 }
96 isolates_size_ = 0;
97 free(isolates_);
98 isolates_ = NULL;
99 lock.Notify();
100 NativeSymbolResolver::ShutdownOnce();
101 }
102
103
104 void ProfilerManager::SetupIsolateForProfiling(Isolate* isolate) {
105 if (!FLAG_profile) {
106 return;
107 }
108 ASSERT(isolate != NULL);
109 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex());
110 SampleBuffer* sample_buffer = new SampleBuffer();
111 IsolateProfilerData* profiler_data =
112 new IsolateProfilerData(isolate, sample_buffer);
113 profiler_data->set_sample_interval_micros(1000);
114 isolate->set_profiler_data(profiler_data);
115 }
116
117
118 void ProfilerManager::FreeIsolateProfilingData(Isolate* isolate) {
119 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex());
120 IsolateProfilerData* profiler_data = isolate->profiler_data();
121 if (profiler_data == NULL) {
122 // Already freed.
123 return;
124 }
125 isolate->set_profiler_data(NULL);
126 SampleBuffer* sample_buffer = profiler_data->sample_buffer();
127 ASSERT(sample_buffer != NULL);
128 delete sample_buffer;
129 delete profiler_data;
130 }
131
132
133 void ProfilerManager::ShutdownIsolateForProfiling(Isolate* isolate) {
134 ASSERT(isolate != NULL);
135 if (!FLAG_profile) {
136 return;
137 }
138 FreeIsolateProfilingData(isolate);
139 }
140
141
142 void ProfilerManager::ScheduleIsolate(Isolate* isolate) {
143 if (!FLAG_profile) {
144 return;
145 }
146 ASSERT(initialized_);
147 ASSERT(isolate != NULL);
148 ScopedMonitor lock(monitor_);
149 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex());
150 IsolateProfilerData* profiler_data = isolate->profiler_data();
151 if (profiler_data == NULL) {
152 return;
153 }
154 profiler_data->Scheduled(OS::GetCurrentTimeMicros(),
155 Thread::GetCurrentThreadId());
156 AddIsolate(isolate);
157 lock.Notify();
158 }
159
160
161 void ProfilerManager::DescheduleIsolate(Isolate* isolate) {
162 if (!FLAG_profile) {
163 return;
164 }
165 ASSERT(initialized_);
166 ASSERT(isolate != NULL);
167 ScopedMonitor lock(monitor_);
168 intptr_t i = FindIsolate(isolate);
169 if (i < 0) {
170 // Not scheduled.
171 return;
172 }
173 {
174 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex());
175 IsolateProfilerData* profiler_data = isolate->profiler_data();
176 ASSERT(profiler_data != NULL);
177 profiler_data->Descheduled();
178 }
179 RemoveIsolate(i);
180 lock.Notify();
181 }
182
183
184 void PrintToJSONStream(Isolate* isolate, JSONStream* stream) {
185 ASSERT(isolate == Isolate::Current());
186 {
187 // We can't get signals here.
188 }
189 UNIMPLEMENTED();
190 }
191
192
193 void ProfilerManager::ResizeIsolates(intptr_t new_capacity) {
194 ASSERT(new_capacity < kMaxProfiledIsolates);
195 ASSERT(new_capacity > isolates_capacity_);
196 Isolate* isolate = NULL;
197 isolates_ = reinterpret_cast<Isolate**>(
198 realloc(isolates_, sizeof(isolate) * new_capacity));
199 isolates_capacity_ = new_capacity;
200 }
201
202
203 void ProfilerManager::AddIsolate(Isolate* isolate) {
204 // Must be called with monitor_ locked.
205 if (isolates_size_ == isolates_capacity_) {
206 ResizeIsolates(isolates_capacity_ == 0 ? 16 : isolates_capacity_ * 2);
207 }
208 isolates_[isolates_size_] = isolate;
209 isolates_size_++;
210 }
211
212
213 intptr_t ProfilerManager::FindIsolate(Isolate* isolate) {
214 // Must be called with monitor_ locked.
215 for (intptr_t i = 0; i < isolates_size_; i++) {
216 if (isolates_[i] == isolate) {
217 return i;
218 }
219 }
220 return -1;
221 }
222
223
224 void ProfilerManager::RemoveIsolate(intptr_t i) {
225 // Must be called with monitor_ locked.
226 ASSERT(i < isolates_size_);
227 intptr_t last = isolates_size_ - 1;
228 if (i != last) {
229 isolates_[i] = isolates_[last];
230 }
231 // Mark last as NULL.
232 isolates_[last] = NULL;
233 // Pop.
234 isolates_size_--;
235 }
236
237
238 static char* FindSymbolName(uintptr_t pc, bool* native_symbol) {
239 // TODO(johnmccutchan): Differentiate between symbols which can't be found
240 // and symbols which were GCed. (Heap::CodeContains).
241 ASSERT(native_symbol != NULL);
242 const char* symbol_name = "Unknown";
243 *native_symbol = false;
244 const Code& code = Code::Handle(Code::LookupCode(pc));
245 if (code.IsNull()) {
246 // Possibly a native symbol.
247 char* native_name = NativeSymbolResolver::LookupSymbolName(pc);
248 if (native_name != NULL) {
249 symbol_name = native_name;
250 *native_symbol = true;
251 }
252 } else {
253 const Function& function = Function::Handle(code.function());
254 if (!function.IsNull()) {
255 const String& name = String::Handle(function.QualifiedUserVisibleName());
256 if (!name.IsNull()) {
257 symbol_name = name.ToCString();
258 }
259 }
260 }
261 return const_cast<char*>(symbol_name);
262 }
263
264
265 void ProfilerManager::WriteTracing(Isolate* isolate, const char* name,
266 Dart_Port port) {
267 ASSERT(isolate == Isolate::Current());
268 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex());
269 IsolateProfilerData* profiler_data = isolate->profiler_data();
270 if (profiler_data == NULL) {
271 return;
272 }
273 SampleBuffer* sample_buffer = profiler_data->sample_buffer();
274 ASSERT(sample_buffer != NULL);
275 JSONStream stream(10 * MB);
276 intptr_t tid = reinterpret_cast<intptr_t>(sample_buffer);
277 intptr_t pid = 1;
278 {
279 JSONArray events(&stream);
280 {
281 JSONObject thread_name(&events);
282 thread_name.AddProperty("name", "thread_name");
283 thread_name.AddProperty("ph", "M");
284 thread_name.AddProperty("tid", tid);
285 thread_name.AddProperty("pid", pid);
286 {
287 JSONObject args(&thread_name, "args");
288 args.AddProperty("name", name);
289 }
290 }
291 {
292 JSONObject process_name(&events);
293 process_name.AddProperty("name", "process_name");
294 process_name.AddProperty("ph", "M");
295 process_name.AddProperty("tid", tid);
296 process_name.AddProperty("pid", pid);
297 {
298 JSONObject args(&process_name, "args");
299 args.AddProperty("name", "Dart VM");
300 }
301 }
302 uint64_t last_time = 0;
303 for (Sample* i = sample_buffer->FirstSample();
304 i != sample_buffer->LastSample();
305 i = sample_buffer->NextSample(i)) {
306 if (last_time == 0) {
307 last_time = i->timestamp;
308 }
309 intptr_t delta = i->timestamp - last_time;
310 {
311 double percentage = static_cast<double>(i->cpu_usage) /
312 static_cast<double>(delta) * 100.0;
313 if (percentage != percentage) {
314 percentage = 0.0;
315 }
316 percentage = percentage < 0.0 ? 0.0 : percentage;
317 percentage = percentage > 100.0 ? 100.0 : percentage;
318 {
319 JSONObject cpu_usage(&events);
320 cpu_usage.AddProperty("name", "CPU Usage");
321 cpu_usage.AddProperty("ph", "C");
322 cpu_usage.AddProperty("tid", tid);
323 cpu_usage.AddProperty("pid", pid);
324 cpu_usage.AddProperty("ts", static_cast<double>(last_time));
325 {
326 JSONObject args(&cpu_usage, "args");
327 args.AddProperty("CPU", percentage);
328 }
329 }
330 {
331 JSONObject cpu_usage(&events);
332 cpu_usage.AddProperty("name", "CPU Usage");
333 cpu_usage.AddProperty("ph", "C");
334 cpu_usage.AddProperty("tid", tid);
335 cpu_usage.AddProperty("pid", pid);
336 cpu_usage.AddProperty("ts", static_cast<double>(i->timestamp));
337 {
338 JSONObject args(&cpu_usage, "args");
339 args.AddProperty("CPU", percentage);
340 }
341 }
342 }
343 for (int j = 0; j < Sample::kNumStackFrames; j++) {
344 if (i->pcs[j] == 0) {
345 continue;
346 }
347 bool native_symbol = false;
348 char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol);
349 {
350 JSONObject begin(&events);
351 begin.AddProperty("ph", "B");
352 begin.AddProperty("tid", tid);
353 begin.AddProperty("pid", pid);
354 begin.AddProperty("name", symbol_name);
355 begin.AddProperty("ts", static_cast<double>(last_time));
356 }
357 if (native_symbol) {
358 NativeSymbolResolver::FreeSymbolName(symbol_name);
359 }
360 }
361 for (int j = Sample::kNumStackFrames-1; j >= 0; j--) {
362 if (i->pcs[j] == 0) {
363 continue;
364 }
365 bool native_symbol = false;
366 char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol);
367 {
368 JSONObject end(&events);
369 end.AddProperty("ph", "E");
370 end.AddProperty("tid", tid);
371 end.AddProperty("pid", pid);
372 end.AddProperty("name", symbol_name);
373 end.AddProperty("ts", static_cast<double>(i->timestamp));
374 }
375 if (native_symbol) {
376 NativeSymbolResolver::FreeSymbolName(symbol_name);
377 }
378 }
379 last_time = i->timestamp;
380 }
381 }
382 char fname[1024];
383 #if defined(TARGET_OS_WINDOWS)
384 snprintf(fname, sizeof(fname)-1, "c:\\tmp\\isolate-%d.prof",
385 static_cast<int>(port));
386 #else
387 snprintf(fname, sizeof(fname)-1, "/tmp/isolate-%d.prof",
388 static_cast<int>(port));
389 #endif
390 printf("%s\n", fname);
391 FILE* f = fopen(fname, "wb");
392 ASSERT(f != NULL);
393 fputs(stream.ToCString(), f);
394 fclose(f);
395 }
396
397
398
399
400
401 IsolateProfilerData::IsolateProfilerData(Isolate* isolate,
402 SampleBuffer* sample_buffer) {
403 isolate_ = isolate;
404 sample_buffer_ = sample_buffer;
405 timer_expiration_micros_ = kNoExpirationTime;
406 last_sampled_micros_ = 0;
407 thread_id_ = 0;
408 }
409
410
411 IsolateProfilerData::~IsolateProfilerData() {
412 }
413
414
415 void IsolateProfilerData::SampledAt(int64_t current_time) {
416 last_sampled_micros_ = current_time;
417 }
418
419
420 void IsolateProfilerData::Scheduled(int64_t current_time, ThreadId thread_id) {
421 timer_expiration_micros_ = current_time + sample_interval_micros_;
422 Thread::GetThreadCpuUsage(thread_id, &cpu_usage_);
423 thread_id_ = thread_id;
424 }
425
426
427 void IsolateProfilerData::Descheduled() {
428 // TODO(johnmccutchan): Track when we ran for a fraction of our sample
429 // interval and incorporate the time difference when scheduling the
430 // isolate again.
431 cpu_usage_ = kDescheduledCpuUsage;
432 timer_expiration_micros_ = kNoExpirationTime;
433 thread_id_ = 0;
434 Sample* sample = sample_buffer_->ReserveSample();
435 ASSERT(sample != NULL);
436 sample->timestamp = OS::GetCurrentTimeMicros();
437 sample->cpu_usage = 0;
438 sample->vm_tags = Sample::kIdle;
439 }
440
441
442 const char* Sample::kLookupSymbol = "Symbol Not Looked Up";
443 const char* Sample::kNoSymbol = "No Symbol Found";
444
445 Sample::Sample() {
446 timestamp = 0;
447 cpu_usage = 0;
448 for (int i = 0; i < kNumStackFrames; i++) {
449 pcs[i] = 0;
450 }
451 vm_tags = kIdle;
452 runtime_tags = 0;
453 }
454
455
456 SampleBuffer::SampleBuffer(intptr_t capacity) {
457 start_ = 0;
458 end_ = 0;
459 capacity_ = capacity;
460 samples_ = reinterpret_cast<Sample*>(calloc(capacity, sizeof(Sample)));
461 }
462
463
464 SampleBuffer::~SampleBuffer() {
465 if (samples_ != NULL) {
466 free(samples_);
467 samples_ = NULL;
468 }
469 }
470
471
472 Sample* SampleBuffer::ReserveSample() {
473 intptr_t index = end_;
474 end_ = WrapIncrement(end_);
475 if (end_ == start_) {
476 start_ = WrapIncrement(start_);
477 }
478 // Reset.
479 samples_[index] = Sample();
480 return &samples_[index];
481 }
482
483
484 Sample* SampleBuffer::FirstSample() const {
485 return &samples_[start_];
486 }
487
488
489 Sample* SampleBuffer::NextSample(Sample* sample) const {
490 ASSERT(sample >= &samples_[0]);
491 ASSERT(sample < &samples_[capacity_]);
492 intptr_t index = sample - samples_;
493 index = WrapIncrement(index);
494 return &samples_[index];
495 }
496
497
498 Sample* SampleBuffer::LastSample() const {
499 return &samples_[end_];
500 }
501
502
503 intptr_t SampleBuffer::WrapIncrement(intptr_t i) const {
504 return (i + 1) % capacity_;
505 }
506
507
508 ProfilerSampleStackWalker::ProfilerSampleStackWalker(Sample* sample,
509 uintptr_t stack_lower,
510 uintptr_t stack_upper,
511 uintptr_t pc,
512 uintptr_t fp,
513 uintptr_t sp) :
514 sample_(sample),
515 stack_lower_(stack_lower),
516 stack_upper_(stack_upper),
517 original_pc_(pc),
518 original_fp_(fp),
519 original_sp_(sp) {
520 ASSERT(sample_ != NULL);
521 }
522
523
524 int ProfilerSampleStackWalker::walk() {
525 uword* pc = reinterpret_cast<uword*>(original_pc_);
526 uword* fp = reinterpret_cast<uword*>(original_fp_);
527 int i = 0;
528 for (; i < Sample::kNumStackFrames; i++) {
529 sample_->pcs[i] = reinterpret_cast<uintptr_t>(pc);
530 if (!ValidFramePointer(fp)) {
531 break;
532 }
533 pc = CallerPC(fp);
534 uword* previous_fp = fp;
535 fp = CallerFP(fp);
536 if (fp <= previous_fp) {
537 // Frame pointers should only move to higher addresses.
538 break;
539 }
540 }
541 return i;
542 }
543
544
545 uword* ProfilerSampleStackWalker::CallerPC(uword* fp) {
546 ASSERT(fp != NULL);
547 return reinterpret_cast<uword*>(*(fp+1));
548 }
549
550
551 uword* ProfilerSampleStackWalker::CallerFP(uword* fp) {
552 ASSERT(fp != NULL);
553 return reinterpret_cast<uword*>(*fp);
554 }
555
556
557 bool ProfilerSampleStackWalker::ValidFramePointer(uword* fp) {
558 if (fp == NULL) {
559 return false;
560 }
561 uintptr_t cursor = reinterpret_cast<uintptr_t>(fp);
562 cursor += sizeof(fp);
563 bool r = cursor >= stack_lower_ && cursor <= stack_upper_;
564 return r;
565 }
566
567
568 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/profiler.h ('k') | runtime/vm/profiler_android.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698