OLD | NEW |
---|---|
(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 | |
OLD | NEW |