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

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
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 DEFINE_FLAG(bool, profile, true, "Enable Sampling Profiler");
19
20 bool ProfilerManager::initialized_ = false;
21 bool ProfilerManager::shutdown_ = false;
22 Monitor* ProfilerManager::monitor_ = NULL;
23 Isolate** ProfilerManager::isolates_ = NULL;
24 intptr_t ProfilerManager::isolates_capacity_ = 0;
25 intptr_t ProfilerManager::isolates_size_ = 0;
26
27
28 void ProfilerManager::InitOnce() {
29 if (!FLAG_profile) {
30 return;
31 }
32 NativeSymbolResolver::InitOnce();
33 ASSERT(!initialized_);
34 monitor_ = new Monitor();
35 initialized_ = true;
36 ResizeIsolates(16);
37 Thread::Start(ThreadMain, 0);
38 }
39
40
41 void ProfilerManager::Shutdown() {
42 if (!FLAG_profile) {
43 return;
44 }
45 ScopedMonitorLock lock(monitor_);
46 shutdown_ = true;
47 for (intptr_t i = 0; i < isolates_size_; i++) {
48 Isolate* isolate = isolates_[i];
49 ASSERT(isolate != NULL);
50 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex());
51 FreeIsolateProfilingData(isolate);
52 }
53 isolates_size_ = 0;
siva 2013/11/11 03:51:54 a free of isolates_ also needs to be done here?
Cutch 2013/11/18 20:48:54 Done.
54 lock.Notify();
55 NativeSymbolResolver::ShutdownOnce();
56 }
57
58
59 void ProfilerManager::SetupIsolateForProfiling(Isolate* isolate) {
60 if (!FLAG_profile) {
61 return;
62 }
63 ASSERT(isolate != NULL);
64 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex());
65 SampleBuffer* sample_buffer = new SampleBuffer();
66 IsolateProfilerData* profiler_data =
67 new IsolateProfilerData(isolate, sample_buffer);
68 profiler_data->set_sample_interval(1000);
69 isolate->set_profiler_data(profiler_data);
70 }
71
72
73 void ProfilerManager::FreeIsolateProfilingData(Isolate* isolate) {
74 // TODO(johnmccutchan): Move ScopedMutexLock here and remove from
75 // two call sites.
76 IsolateProfilerData* profiler_data = isolate->profiler_data();
77 if (profiler_data == NULL) {
78 // Already freed.
79 return;
80 }
81 isolate->set_profiler_data(NULL);
82 SampleBuffer* sample_buffer = profiler_data->sample_buffer();
83 ASSERT(sample_buffer != NULL);
84 delete sample_buffer;
85 delete profiler_data;
86 }
87
88
89 void ProfilerManager::ShutdownIsolateForProfiling(Isolate* isolate) {
90 ASSERT(isolate != NULL);
91 if (!FLAG_profile) {
92 return;
93 }
94 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex());
95 FreeIsolateProfilingData(isolate);
96 }
97
98
99 void ProfilerManager::ScheduleIsolate(Isolate* isolate) {
100 if (!FLAG_profile) {
101 return;
102 }
103 ASSERT(initialized_);
104 ASSERT(isolate != NULL);
105 ScopedMonitorLock lock(monitor_);
106 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex());
siva 2013/11/11 03:51:54 Maybe later you should consider adding a locking o
107 IsolateProfilerData* profiler_data = isolate->profiler_data();
108 if (profiler_data == NULL) {
109 return;
110 }
111 profiler_data->Scheduled(OS::GetCurrentTimeMicros(),
112 Thread::GetCurrentThreadId());
113 AddIsolate(isolate);
114 lock.Notify();
115 }
116
117
118 void ProfilerManager::DescheduleIsolate(Isolate* isolate) {
119 if (!FLAG_profile) {
120 return;
121 }
122 ASSERT(initialized_);
123 ASSERT(isolate != NULL);
124 ScopedMonitorLock lock(monitor_);
125 intptr_t i = FindIsolate(isolate);
126 if (i < 0) {
127 // Not scheduled.
128 return;
129 }
130 {
131 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex());
132 IsolateProfilerData* profiler_data = isolate->profiler_data();
133 ASSERT(profiler_data != NULL);
134 profiler_data->Descheduled();
135 }
136 RemoveIsolate(i);
137 lock.Notify();
138 }
139
140
141 void PrintToJSONStream(Isolate* isolate, JSONStream* stream) {
142 ASSERT(isolate == Isolate::Current());
143 {
144 // We can't get signals here.
145 }
146 UNIMPLEMENTED();
147 }
148
149
150 void ProfilerManager::ResizeIsolates(intptr_t new_capacity) {
151 ASSERT(new_capacity > isolates_capacity_);
152 Isolate* isolate = NULL;
153 isolates_ = reinterpret_cast<Isolate**>(
154 realloc(isolates_, sizeof(isolate) * new_capacity));
siva 2013/11/11 03:51:54 we need to ensure that this does not overflow, may
Cutch 2013/11/18 20:48:54 Done. For now I just assert we aren't monitoring m
155 isolates_capacity_ = new_capacity;
156 }
157
158
159 void ProfilerManager::AddIsolate(Isolate* isolate) {
siva 2013/11/11 03:51:54 you should add a comment here and in FindIsolate,
Cutch 2013/11/18 20:48:54 Done.
160 if (isolates_size_ == isolates_capacity_) {
161 ResizeIsolates(isolates_capacity_ == 0 ? 16 : isolates_capacity_ * 2);
162 }
163 isolates_[isolates_size_] = isolate;
164 isolates_size_++;
165 }
166
167
168 intptr_t ProfilerManager::FindIsolate(Isolate* isolate) {
169 for (intptr_t i = 0; i < isolates_size_; i++) {
170 if (isolates_[i] == isolate) {
171 return i;
172 }
173 }
174 return -1;
175 }
176
177
178 void ProfilerManager::RemoveIsolate(intptr_t i) {
179 ASSERT(i < isolates_size_);
180 intptr_t last = isolates_size_ - 1;
181 if (i != last) {
182 isolates_[i] = isolates_[last];
183 }
184 // Mark last as NULL.
185 isolates_[last] = NULL;
186 // Pop.
187 isolates_size_--;
188 }
189
190
191 static char* FindSymbolName(uintptr_t pc, bool* native_symbol) {
192 // TODO(johnmccutchan): Differentiate between symbols which can't be found
193 // and symbols which were GCed. (Heap::CodeContains).
194 ASSERT(native_symbol != NULL);
195 const char* symbol_name = "Unknown";
196 *native_symbol = false;
197 const Code& code = Code::Handle(Code::LookupCode(pc));
198 if (code.IsNull()) {
199 // Possibly a native symbol.
200 char* native_name = NativeSymbolResolver::LookupSymbolName(pc);
201 if (native_name != NULL) {
202 symbol_name = native_name;
203 *native_symbol = true;
204 }
205 } else {
206 const Function& function = Function::Handle(code.function());
207 if (!function.IsNull()) {
208 const String& name = String::Handle(function.QualifiedUserVisibleName());
209 if (!name.IsNull()) {
210 symbol_name = name.ToCString();
211 }
212 }
213 }
214 return const_cast<char*>(symbol_name);
215 }
216
217
218 void ProfilerManager::WriteTracing(Isolate* isolate, const char* name,
219 Dart_Port port) {
220 ASSERT(isolate == Isolate::Current());
221 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex());
222 IsolateProfilerData* profiler_data = isolate->profiler_data();
223 if (profiler_data == NULL) {
224 return;
225 }
226 SampleBuffer* sample_buffer = profiler_data->sample_buffer();
227 ASSERT(sample_buffer != NULL);
228 JSONStream stream(10 * MB);
229 intptr_t tid = reinterpret_cast<intptr_t>(sample_buffer);
230 intptr_t pid = 1;
231 {
232 JSONArray events(&stream);
233 {
234 JSONObject thread_name(&events);
235 thread_name.AddProperty("name", "thread_name");
236 thread_name.AddProperty("ph", "M");
237 thread_name.AddProperty("tid", tid);
238 thread_name.AddProperty("pid", pid);
239 {
240 JSONObject args(&thread_name, "args");
241 args.AddProperty("name", name);
242 }
243 }
244 {
245 JSONObject process_name(&events);
246 process_name.AddProperty("name", "process_name");
247 process_name.AddProperty("ph", "M");
248 process_name.AddProperty("tid", tid);
249 process_name.AddProperty("pid", pid);
250 {
251 JSONObject args(&process_name, "args");
252 args.AddProperty("name", "Dart VM");
253 }
254 }
255 uint64_t last_time = 0;
256 for (Sample* i = sample_buffer->FirstSample();
257 i != sample_buffer->LastSample();
258 i = sample_buffer->NextSample(i)) {
259 if (last_time == 0) {
260 last_time = i->timestamp;
261 }
262 intptr_t delta = i->timestamp - last_time;
263 {
264 double percentage = static_cast<double>(i->cpu_usage) /
265 static_cast<double>(delta) * 100.0;
266 if (percentage != percentage) {
267 percentage = 0.0;
268 }
269 percentage = percentage < 0.0 ? 0.0 : percentage;
270 percentage = percentage > 100.0 ? 100.0 : percentage;
271 {
272 JSONObject cpu_usage(&events);
273 cpu_usage.AddProperty("name", "CPU Usage");
274 cpu_usage.AddProperty("ph", "C");
275 cpu_usage.AddProperty("tid", tid);
276 cpu_usage.AddProperty("pid", pid);
277 cpu_usage.AddProperty("ts", static_cast<double>(last_time));
278 {
279 JSONObject args(&cpu_usage, "args");
280 args.AddProperty("CPU", percentage);
281 }
282 }
283 {
284 JSONObject cpu_usage(&events);
285 cpu_usage.AddProperty("name", "CPU Usage");
286 cpu_usage.AddProperty("ph", "C");
287 cpu_usage.AddProperty("tid", tid);
288 cpu_usage.AddProperty("pid", pid);
289 cpu_usage.AddProperty("ts", static_cast<double>(i->timestamp));
290 {
291 JSONObject args(&cpu_usage, "args");
292 args.AddProperty("CPU", percentage);
293 }
294 }
295 }
296 for (int j = 0; j < Sample::kNumStackFrames; j++) {
297 if (i->pcs[j] == 0) {
298 continue;
299 }
300 bool native_symbol = false;
301 char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol);
302 {
303 JSONObject begin(&events);
304 begin.AddProperty("ph", "B");
305 begin.AddProperty("tid", tid);
306 begin.AddProperty("pid", pid);
307 begin.AddProperty("name", symbol_name);
308 begin.AddProperty("ts", static_cast<double>(last_time));
309 }
310 if (native_symbol) {
311 NativeSymbolResolver::FreeSymbolName(symbol_name);
312 }
313 }
314 for (int j = Sample::kNumStackFrames-1; j >= 0; j--) {
315 if (i->pcs[j] == 0) {
316 continue;
317 }
318 bool native_symbol = false;
319 char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol);
320 {
321 JSONObject end(&events);
322 end.AddProperty("ph", "E");
323 end.AddProperty("tid", tid);
324 end.AddProperty("pid", pid);
325 end.AddProperty("name", symbol_name);
326 end.AddProperty("ts", static_cast<double>(i->timestamp));
327 }
328 if (native_symbol) {
329 NativeSymbolResolver::FreeSymbolName(symbol_name);
330 }
331 }
332 last_time = i->timestamp;
333 }
334 }
335 char fname[1024];
336 #if defined(TARGET_OS_WINDOWS)
337 snprintf(fname, sizeof(fname)-1, "c:\\tmp\\isolate-%d.prof",
338 static_cast<int>(port));
339 #else
340 snprintf(fname, sizeof(fname)-1, "/tmp/isolate-%d.prof",
341 static_cast<int>(port));
342 #endif
343 printf("%s\n", fname);
344 FILE* f = fopen(fname, "wb");
345 ASSERT(f != NULL);
346 fputs(stream.ToCString(), f);
347 fclose(f);
348 }
349
350
351
352
353
354 IsolateProfilerData::IsolateProfilerData(Isolate* isolate,
355 SampleBuffer* sample_buffer) {
356 isolate_ = isolate;
357 sample_buffer_ = sample_buffer;
358 timer_expiration_micros_ = kNoExpirationTime;
359 last_sampled_micros_ = 0;
360 thread_id_ = 0;
361 }
362
363
364 IsolateProfilerData::~IsolateProfilerData() {
365 }
366
367
368 void IsolateProfilerData::SampledAt(int64_t current_time) {
369 last_sampled_micros_ = current_time;
370 }
371
372
373 void IsolateProfilerData::Scheduled(int64_t current_time, ThreadId thread_id) {
374 timer_expiration_micros_ = current_time + sample_interval_micros_;
375 Thread::GetThreadCpuUsage(thread_id, &cpu_usage_);
376 thread_id_ = thread_id;
377 }
378
379
380 void IsolateProfilerData::Descheduled() {
381 cpu_usage_ = kDescheduledCpuUsage;
382 timer_expiration_micros_ = kNoExpirationTime;
383 thread_id_ = 0;
384 Sample* sample = sample_buffer_->ReserveSample();
385 ASSERT(sample != NULL);
386 sample->timestamp = OS::GetCurrentTimeMicros();
387 sample->cpu_usage = 0;
388 sample->vm_tags = Sample::kIdle;
389 }
390
391
392 const char* Sample::kLookupSymbol = "Symbol Not Looked Up";
393 const char* Sample::kNoSymbol = "No Symbol Found";
394
395 Sample::Sample() {
396 timestamp = 0;
397 cpu_usage = 0;
398 for (int i = 0; i < kNumStackFrames; i++) {
399 pcs[i] = 0;
400 }
401 vm_tags = kIdle;
402 runtime_tags = 0;
403 }
404
405
406 SampleBuffer::SampleBuffer(intptr_t capacity) {
407 start_ = 0;
408 end_ = 0;
409 capacity_ = capacity;
410 samples_ = reinterpret_cast<Sample*>(calloc(capacity, sizeof(Sample)));
411 }
412
413
414 SampleBuffer::~SampleBuffer() {
415 if (samples_ != NULL) {
416 free(samples_);
417 samples_ = NULL;
418 }
419 }
420
421
422 Sample* SampleBuffer::ReserveSample() {
423 intptr_t index = end_;
424 end_ = WrapIncrement(end_);
425 if (end_ == start_) {
426 start_ = WrapIncrement(start_);
427 }
428 // Reset.
429 samples_[index] = Sample();
430 return &samples_[index];
431 }
432
433
434 Sample* SampleBuffer::FirstSample() const {
435 return &samples_[start_];
436 }
437
438
439 Sample* SampleBuffer::NextSample(Sample* sample) const {
440 ASSERT(sample >= &samples_[0]);
441 ASSERT(sample < &samples_[capacity_]);
442 intptr_t index = sample - samples_;
443 index = WrapIncrement(index);
444 return &samples_[index];
445 }
446
447
448 Sample* SampleBuffer::LastSample() const {
449 return &samples_[end_];
450 }
451
452
453 intptr_t SampleBuffer::WrapIncrement(intptr_t i) const {
454 return (i + 1) % capacity_;
455 }
456
457
458 ProfilerSampleStackWalker::ProfilerSampleStackWalker(Sample* sample,
459 uintptr_t stack_lower,
460 uintptr_t stack_upper,
461 uintptr_t pc,
462 uintptr_t fp,
463 uintptr_t sp) :
464 sample_(sample),
465 stack_lower_(stack_lower),
466 stack_upper_(stack_upper),
467 original_pc_(pc),
468 original_fp_(fp),
469 original_sp_(sp) {
470 ASSERT(sample_ != NULL);
471 }
472
473
474 int ProfilerSampleStackWalker::walk() {
475 uword* pc = reinterpret_cast<uword*>(original_pc_);
476 uword* fp = reinterpret_cast<uword*>(original_fp_);
477 int i = 0;
478 for (; i < Sample::kNumStackFrames; i++) {
479 sample_->pcs[i] = reinterpret_cast<uintptr_t>(pc);
480 if (!ValidInstructionPointer(pc) || !ValidFramePointer(fp)) {
481 break;
482 }
483 pc = CallerPC(fp);
484 uword* previous_fp = fp;
485 fp = CallerFP(fp);
486 if (fp <= previous_fp) {
487 // Frame pointers should only move to higher addresses.
488 break;
489 }
490 }
491 return i;
492 }
493
494
495 uword* ProfilerSampleStackWalker::CallerPC(uword* fp) {
496 ASSERT(fp != NULL);
497 return reinterpret_cast<uword*>(*(fp+1));
498 }
499
500
501 uword* ProfilerSampleStackWalker::CallerFP(uword* fp) {
502 ASSERT(fp != NULL);
503 return reinterpret_cast<uword*>(*fp);
504 }
505
506
507 bool ProfilerSampleStackWalker::ValidInstructionPointer(uword* pc) {
508 uintptr_t cursor = reinterpret_cast<uintptr_t>(pc);
509 return cursor != 0;
siva 2013/11/11 03:51:54 Seems simplistic...
Cutch 2013/11/18 20:48:54 Done.
510 }
511
512
513 bool ProfilerSampleStackWalker::ValidFramePointer(uword* fp) {
514 uintptr_t cursor = reinterpret_cast<uintptr_t>(fp);
515 cursor += sizeof(fp);
516 bool r = cursor >= stack_lower_ && cursor <= stack_upper_;
517 return r;
518 }
519
520
521 } // namespace dart
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698