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 #include "vm/signal_handler.h" | |
16 | |
17 namespace dart { | |
18 | |
19 #if defined(TARGET_OS_LINUX) || defined(TARGET_OS_MACOS) || \ | |
20 defined(TARGET_OS_ANDROID) | |
21 #define PROFILER_USES_SIGNALS | |
22 #endif | |
23 | |
24 DEFINE_FLAG(bool, profile, true, "Enable Sampling Profiler"); | |
25 | |
26 #if defined(PROFILER_USES_SIGNALS) | |
27 static void ProfileSignalAction(int signal, siginfo_t* info, void* context_); | |
28 #endif | |
29 | |
30 class ProfilerSampleStackWalker { | |
31 public: | |
32 ProfilerSampleStackWalker(Sample* sample, uintptr_t stack_lower, | |
33 uintptr_t stack_upper) : sample_(sample), | |
34 stack_lower_(stack_lower), | |
35 stack_upper_(stack_upper) { | |
36 } | |
37 | |
38 int walk(uintptr_t pc_, uintptr_t fp_) { | |
siva
2013/10/28 05:19:21
why underscrore for these param names?
Cutch
2013/11/04 20:36:05
Cleaned up.
| |
39 original_pc_ = pc_; | |
40 original_fp_ = fp_; | |
41 uword* pc = reinterpret_cast<uword*>(pc_); | |
42 uword* fp = reinterpret_cast<uword*>(fp_); | |
43 int i = 0; | |
44 for (; i < Sample::kNumStackFrames; i++) { | |
45 sample_->pcs[i] = reinterpret_cast<uintptr_t>(pc); | |
46 if (!ValidInstructionPointer(pc) || !ValidFramePointer(fp, i)) { | |
47 break; | |
48 } | |
49 pc = parent_pc(fp); | |
50 fp = parent_fp(fp); | |
51 } | |
52 return i; | |
53 } | |
54 | |
55 private: | |
56 uword* parent_pc(uword* fp) { | |
57 ASSERT(fp != NULL); | |
58 return reinterpret_cast<uword*>(*(fp+1)); | |
59 } | |
60 | |
61 uword* parent_fp(uword* fp) { | |
62 ASSERT(fp != NULL); | |
63 return reinterpret_cast<uword*>(*fp); | |
64 } | |
65 | |
66 bool ValidInstructionPointer(uword* pc) { | |
67 uintptr_t cursor = reinterpret_cast<uintptr_t>(pc); | |
68 return cursor != 0; | |
69 } | |
70 | |
71 bool ValidFramePointer(uword* fp, int i) { | |
72 uintptr_t cursor = reinterpret_cast<uintptr_t>(fp); | |
73 cursor += sizeof(fp); | |
74 bool r = cursor >= stack_lower_ && cursor <= stack_upper_; | |
75 return r; | |
76 } | |
77 | |
78 Sample* sample_; | |
79 uintptr_t original_fp_; | |
80 uintptr_t original_pc_; | |
81 uintptr_t stack_lower_; | |
82 uintptr_t stack_upper_; | |
siva
2013/10/28 05:19:21
DISALLOW stuff
Cutch
2013/11/04 20:36:05
Done.
| |
83 }; | |
84 | |
85 | |
86 bool ProfilerManager::initialized_ = false; | |
87 bool ProfilerManager::shutdown_ = false; | |
88 Monitor* ProfilerManager::monitor_ = NULL; | |
89 Isolate** ProfilerManager::isolates_ = NULL; | |
90 intptr_t ProfilerManager::isolates_capacity_ = 0; | |
91 intptr_t ProfilerManager::isolates_size_ = 0; | |
92 | |
93 | |
94 void ProfilerManager::InitOnce() { | |
95 if (!FLAG_profile) { | |
96 return; | |
97 } | |
98 NativeSymbolResolver::InitOnce(); | |
99 ASSERT(!initialized_); | |
100 monitor_ = new Monitor(); | |
101 initialized_ = true; | |
102 ResizeIsolates(16); | |
103 #if defined(PROFILER_USES_SIGNALS) | |
104 SignalHandler::Install(ProfileSignalAction); | |
105 #endif | |
106 Thread::Start(ThreadMain, 0); | |
107 } | |
108 | |
109 | |
110 void ProfilerManager::Shutdown() { | |
111 if (!FLAG_profile) { | |
112 return; | |
113 } | |
114 ScopedMonitorLock lock(monitor_); | |
115 shutdown_ = true; | |
116 for (intptr_t i = 0; i < isolates_size_; i++) { | |
117 Isolate* isolate = isolates_[i]; | |
siva
2013/10/28 05:19:21
ASSERT(isolate != NULL);
Cutch
2013/11/04 20:36:05
Done.
| |
118 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); | |
119 FreeIsolateProfilingData(isolate); | |
120 } | |
121 isolates_size_ = 0; | |
122 lock.Notify(); | |
123 NativeSymbolResolver::ShutdownOnce(); | |
124 } | |
125 | |
126 | |
127 void ProfilerManager::SetupIsolateForProfiling(Isolate* isolate) { | |
128 if (!FLAG_profile) { | |
129 return; | |
130 } | |
131 ASSERT(isolate != NULL); | |
132 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); | |
133 SampleBuffer* sample_buffer = new SampleBuffer(); | |
134 IsolateProfilerData* profiler_data = | |
135 new IsolateProfilerData(isolate, sample_buffer); | |
136 profiler_data->set_sample_interval(1000); | |
137 isolate->set_profiler_data(profiler_data); | |
138 } | |
139 | |
140 | |
141 void ProfilerManager::FreeIsolateProfilingData(Isolate* isolate) { | |
siva
2013/10/28 05:19:21
why not move the lock here
ScopedMutexLock profile
Cutch
2013/11/04 20:36:05
I've added a TODO. I need to think about this agai
| |
142 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
143 if (profiler_data == NULL) { | |
144 // Already freed. | |
145 return; | |
146 } | |
147 isolate->set_profiler_data(NULL); | |
148 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | |
149 ASSERT(sample_buffer != NULL); | |
150 delete sample_buffer; | |
151 delete profiler_data; | |
152 } | |
153 | |
154 | |
155 void ProfilerManager::ShutdownIsolate(Isolate* isolate) { | |
156 ASSERT(isolate != NULL); | |
157 if (!FLAG_profile) { | |
158 return; | |
159 } | |
160 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); | |
161 FreeIsolateProfilingData(isolate); | |
162 } | |
163 | |
164 | |
165 static void CollectSample(IsolateProfilerData* profiler_data, | |
166 uintptr_t pc, | |
167 uintptr_t fp, | |
168 uintptr_t stack_lower, | |
169 uintptr_t stack_upper) { | |
170 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | |
171 Sample* sample = sample_buffer->ReserveSample(); | |
172 ASSERT(sample != NULL); | |
173 sample->timestamp = OS::GetCurrentTimeMicros(); | |
174 // TODO(johnmccutchan): Make real use of vm_tags and runtime_tags. | |
siva
2013/10/28 05:19:21
open an issue and use the issue number in the TODO
Cutch
2013/11/04 20:36:05
Done.
| |
175 sample->vm_tags = Sample::kExecuting; | |
176 sample->runtime_tags = 0; | |
177 int64_t cpu_usage; | |
178 Thread::GetThreadCPUUsage(&cpu_usage); | |
179 sample->cpu_usage = profiler_data->set_and_delta_cpu_usage(cpu_usage); | |
180 ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper); | |
181 stackWalker.walk(pc, fp); | |
182 } | |
183 | |
184 | |
185 #if defined(PROFILER_USES_SIGNALS) | |
186 static void ProfileSignalAction(int signal, siginfo_t* info, void* context_) { | |
187 if (signal != SIGPROF) { | |
188 return; | |
189 } | |
190 ucontext_t* context = reinterpret_cast<ucontext_t*>(context_); | |
191 mcontext_t mcontext = context->uc_mcontext; | |
192 Isolate* isolate = Isolate::Current(); | |
193 if (isolate == NULL) { | |
194 return; | |
195 } | |
196 { | |
197 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); | |
198 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
199 if (profiler_data == NULL) { | |
200 return; | |
201 } | |
202 | |
203 uintptr_t stack_lower = isolate->stack_limit(); | |
204 uintptr_t stack_upper = stack_lower + isolate->GetSpecifiedStackSize(); | |
siva
2013/10/28 05:19:21
There is an implicit assumption here that the stac
Cutch
2013/11/04 20:36:05
Yes. When would this not be true?
| |
205 if (stack_lower == static_cast<uintptr_t>(~0)) { | |
206 stack_lower = isolate->saved_stack_limit(); | |
207 stack_upper = stack_lower + isolate->GetSpecifiedStackSize(); | |
208 } | |
209 if (stack_lower == static_cast<uintptr_t>(~0)) { | |
210 stack_lower = 0; | |
211 stack_upper = 0; | |
212 } | |
siva
2013/10/28 05:19:21
Not sure I understand the logic here.
Cutch
2013/11/04 20:36:05
I'm trying to get the address space bounds for the
| |
213 uintptr_t PC = SignalHandler::GetProgramCounter(mcontext); | |
214 uintptr_t FP = SignalHandler::GetFramePointer(mcontext); | |
215 stack_lower = SignalHandler::GetStackPointer(mcontext); | |
216 int64_t sample_time = OS::GetCurrentTimeMicros(); | |
217 profiler_data->SampledAt(sample_time); | |
218 CollectSample(profiler_data, PC, FP, stack_lower, stack_upper); | |
219 } | |
220 ProfilerManager::ScheduleIsolate(isolate); | |
221 } | |
222 #endif | |
223 | |
224 | |
225 void ProfilerManager::ScheduleIsolate(Isolate* isolate) { | |
226 if (!FLAG_profile) { | |
227 return; | |
228 } | |
229 ASSERT(initialized_); | |
230 ASSERT(isolate != NULL); | |
231 ScopedMonitorLock lock(monitor_); | |
232 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); | |
233 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
234 if (profiler_data == NULL) { | |
235 return; | |
236 } | |
237 profiler_data->Scheduled(OS::GetCurrentTimeMicros(), | |
238 Thread::GetCurrentThreadId()); | |
239 AddIsolate(isolate); | |
240 lock.Notify(); | |
241 } | |
242 | |
243 | |
244 void ProfilerManager::DescheduleIsolate(Isolate* isolate) { | |
245 if (!FLAG_profile) { | |
246 return; | |
247 } | |
248 ASSERT(initialized_); | |
249 ASSERT(isolate != NULL); | |
250 ScopedMonitorLock lock(monitor_); | |
251 intptr_t i = FindIsolate(isolate); | |
252 if (i < 0) { | |
253 // Not scheduled. | |
254 return; | |
255 } | |
256 { | |
257 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); | |
258 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
259 ASSERT(profiler_data != NULL); | |
260 profiler_data->Descheduled(); | |
261 } | |
262 RemoveIsolate(i); | |
263 lock.Notify(); | |
264 } | |
265 | |
266 | |
267 void PrintToJSONStream(Isolate* isolate, JSONStream* stream) { | |
268 ASSERT(isolate == Isolate::Current()); | |
269 { | |
270 // We can't get signals here. | |
271 } | |
272 UNIMPLEMENTED(); | |
273 } | |
274 | |
275 | |
276 void ProfilerManager::ResizeIsolates(intptr_t new_capacity) { | |
277 ASSERT(new_capacity > isolates_capacity_); | |
278 Isolate* isolate = NULL; | |
279 isolates_ = reinterpret_cast<Isolate**>( | |
280 realloc(isolates_, sizeof(isolate) * new_capacity)); | |
281 isolates_capacity_ = new_capacity; | |
282 } | |
283 | |
284 | |
285 void ProfilerManager::AddIsolate(Isolate* isolate) { | |
286 if (isolates_size_ == isolates_capacity_) { | |
287 ResizeIsolates(isolates_capacity_ == 0 ? 16 : isolates_capacity_ * 2); | |
288 } | |
289 isolates_[isolates_size_] = isolate; | |
290 isolates_size_++; | |
291 } | |
292 | |
293 | |
294 intptr_t ProfilerManager::FindIsolate(Isolate* isolate) { | |
295 for (intptr_t i = 0; i < isolates_size_; i++) { | |
296 if (isolates_[i] == isolate) { | |
297 return i; | |
298 } | |
299 } | |
300 return -1; | |
301 } | |
302 | |
303 | |
304 void ProfilerManager::RemoveIsolate(intptr_t i) { | |
305 ASSERT(i < isolates_size_); | |
306 intptr_t last = isolates_size_ - 1; | |
307 if (i != last) { | |
308 // Swap. | |
309 Isolate* temp = isolates_[last]; | |
310 isolates_[last] = isolates_[i]; | |
311 isolates_[i] = temp; | |
siva
2013/10/28 05:19:21
why do they have to be swapped why not just
isolat
Cutch
2013/11/04 20:36:05
Done.
| |
312 } | |
313 // Mark as NULL. | |
314 isolates_[last] = NULL; | |
315 // Pop. | |
316 isolates_size_--; | |
317 } | |
318 | |
319 | |
320 #if defined(PROFILER_USES_SIGNALS) | |
321 int64_t ProfilerManager::SampleAndRescheduleIsolates(int64_t current_time) { | |
322 if (isolates_size_ == 0) { | |
323 return 0; | |
324 } | |
325 static const int64_t max_time = 0x7fffffffffffffffLL; | |
326 int64_t lowest = max_time; | |
327 for (intptr_t i = 0; i < isolates_size_; i++) { | |
328 Isolate* isolate = isolates_[i]; | |
329 ScopedMutexLock isolate_lock(isolate->profiler_data_mutex()); | |
330 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
331 ASSERT(profiler_data != NULL); | |
332 if (profiler_data->ShouldSample(current_time)) { | |
333 pthread_kill(profiler_data->thread_id(), SIGPROF); | |
334 RemoveIsolate(i); | |
335 i--; // Remove moves the last element into i, this decrement cancels | |
336 // the increment in the for loop. | |
siva
2013/10/28 05:19:21
This is tricky, why not replace the for loop with
Cutch
2013/11/04 20:36:05
Done.
| |
337 continue; | |
338 } | |
339 if (profiler_data->CanExpire()) { | |
340 int64_t isolate_time_left = | |
341 profiler_data->TimeUntilExpiration(current_time); | |
342 if (isolate_time_left < 0) { | |
343 continue; | |
344 } | |
345 if (isolate_time_left < lowest) { | |
346 lowest = isolate_time_left; | |
347 } | |
348 } | |
349 } | |
350 if (isolates_size_ == 0) { | |
351 return 0; | |
352 } | |
353 if (lowest == max_time) { | |
354 return 0; | |
355 } | |
356 ASSERT(lowest != max_time); | |
357 ASSERT(lowest > 0); | |
358 return lowest; | |
359 } | |
360 #else | |
361 int64_t ProfilerManager::SampleAndRescheduleIsolates(int64_t current_time) { | |
362 if (isolates_size_ == 0) { | |
363 return 0; | |
364 } | |
365 static const int64_t max_time = 0x7fffffffffffffffLL; | |
366 int64_t lowest = max_time; | |
367 // TODO(johnmccutchan): Implement sampling loop on Windows. | |
368 if (isolates_size_ == 0) { | |
369 return 0; | |
370 } | |
371 if (lowest == max_time) { | |
372 return 0; | |
373 } | |
374 ASSERT(lowest != max_time); | |
375 ASSERT(lowest > 0); | |
376 return lowest; | |
377 } | |
378 #endif | |
siva
2013/10/28 05:19:21
I would prefer if we moved this code to indiviual
Cutch
2013/11/04 20:36:05
Agreed and Done.
| |
379 | |
380 | |
381 static const char* FindSymbolName(uintptr_t pc, bool* native_symbol) { | |
382 // TODO(johnmccutchan): Differentiate between symbols which can't be found | |
383 // and symbols which were GCed. (Heap::CodeContains). | |
384 ASSERT(native_symbol != NULL); | |
385 const char* symbol_name = "Unknown"; | |
386 *native_symbol = false; | |
387 const Code& code = Code::Handle(Code::LookupCode(pc)); | |
388 if (code.IsNull()) { | |
389 // Possibly a native symbol. | |
390 const char* native_name = | |
391 NativeSymbolResolver::LookupSymbolName(pc); | |
392 if (native_name != NULL) { | |
393 symbol_name = native_name; | |
394 *native_symbol = true; | |
395 } | |
396 } else { | |
397 const Function& function = Function::Handle(code.function()); | |
398 if (!function.IsNull()) { | |
399 const String& name = String::Handle(function.QualifiedUserVisibleName()); | |
400 if (!name.IsNull()) { | |
401 symbol_name = name.ToCString(); | |
402 } | |
403 } | |
404 } | |
405 return symbol_name; | |
406 } | |
407 | |
408 | |
409 void ProfilerManager::WriteTracing(Isolate* isolate, const char* name, | |
410 Dart_Port port) { | |
411 ASSERT(isolate == Isolate::Current()); | |
412 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); | |
413 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
414 if (profiler_data == NULL) { | |
415 return; | |
416 } | |
417 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | |
418 ASSERT(sample_buffer != NULL); | |
419 JSONStream stream(10 * MB); | |
420 intptr_t tid = reinterpret_cast<intptr_t>(sample_buffer); | |
421 intptr_t pid = 1; | |
422 { | |
423 JSONArray events(&stream); | |
424 { | |
425 JSONObject thread_name(&events); | |
426 thread_name.AddProperty("name", "thread_name"); | |
427 thread_name.AddProperty("ph", "M"); | |
428 thread_name.AddProperty("tid", tid); | |
429 thread_name.AddProperty("pid", pid); | |
430 { | |
431 JSONObject args(&thread_name, "args"); | |
432 args.AddProperty("name", name); | |
433 } | |
434 } | |
435 { | |
436 JSONObject process_name(&events); | |
437 process_name.AddProperty("name", "process_name"); | |
438 process_name.AddProperty("ph", "M"); | |
439 process_name.AddProperty("tid", tid); | |
440 process_name.AddProperty("pid", pid); | |
441 { | |
442 JSONObject args(&process_name, "args"); | |
443 args.AddProperty("name", "Dart VM"); | |
444 } | |
445 } | |
446 uint64_t last_time = 0; | |
447 for (Sample* i = sample_buffer->FirstSample(); | |
448 i != sample_buffer->LastSample(); | |
449 i = sample_buffer->NextSample(i)) { | |
450 if (last_time == 0) { | |
451 last_time = i->timestamp; | |
452 } | |
453 intptr_t delta = i->timestamp - last_time; | |
454 { | |
455 double percentage = static_cast<double>(i->cpu_usage) / | |
456 static_cast<double>(delta) * 100.0; | |
457 if (percentage != percentage) { | |
458 percentage = 0.0; | |
459 } | |
460 percentage = percentage < 0.0 ? 0.0 : percentage; | |
461 percentage = percentage > 100.0 ? 100.0 : percentage; | |
462 { | |
463 JSONObject cpu_usage(&events); | |
464 cpu_usage.AddProperty("name", "CPU Usage"); | |
465 cpu_usage.AddProperty("ph", "C"); | |
466 cpu_usage.AddProperty("tid", tid); | |
467 cpu_usage.AddProperty("pid", pid); | |
468 cpu_usage.AddProperty("ts", static_cast<double>(last_time)); | |
469 { | |
470 JSONObject args(&cpu_usage, "args"); | |
471 args.AddProperty("CPU", percentage); | |
472 } | |
473 } | |
474 { | |
475 JSONObject cpu_usage(&events); | |
476 cpu_usage.AddProperty("name", "CPU Usage"); | |
477 cpu_usage.AddProperty("ph", "C"); | |
478 cpu_usage.AddProperty("tid", tid); | |
479 cpu_usage.AddProperty("pid", pid); | |
480 cpu_usage.AddProperty("ts", static_cast<double>(i->timestamp)); | |
481 { | |
482 JSONObject args(&cpu_usage, "args"); | |
483 args.AddProperty("CPU", percentage); | |
484 } | |
485 } | |
486 } | |
487 for (int j = 0; j < Sample::kNumStackFrames; j++) { | |
488 if (i->pcs[j] == 0) { | |
489 continue; | |
490 } | |
491 bool native_symbol = false; | |
492 const char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol); | |
493 { | |
494 JSONObject begin(&events); | |
495 begin.AddProperty("ph", "B"); | |
496 begin.AddProperty("tid", tid); | |
497 begin.AddProperty("pid", pid); | |
498 begin.AddProperty("name", symbol_name); | |
499 begin.AddProperty("ts", static_cast<double>(last_time)); | |
500 } | |
501 if (native_symbol) { | |
502 NativeSymbolResolver::FreeSymbolName(symbol_name); | |
503 } | |
504 } | |
505 for (int j = Sample::kNumStackFrames-1; j >= 0; j--) { | |
506 if (i->pcs[j] == 0) { | |
507 continue; | |
508 } | |
509 bool native_symbol = false; | |
510 const char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol); | |
511 { | |
512 JSONObject end(&events); | |
513 end.AddProperty("ph", "E"); | |
514 end.AddProperty("tid", tid); | |
515 end.AddProperty("pid", pid); | |
516 end.AddProperty("name", symbol_name); | |
517 end.AddProperty("ts", static_cast<double>(i->timestamp)); | |
518 } | |
519 if (native_symbol) { | |
520 NativeSymbolResolver::FreeSymbolName(symbol_name); | |
521 } | |
522 } | |
523 last_time = i->timestamp; | |
524 } | |
525 } | |
526 char fname[1024]; | |
527 snprintf(fname, sizeof(fname)-1, "/tmp/isolate-%d.prof", | |
528 static_cast<int>(port)); | |
529 FILE* f = fopen(fname, "wb"); | |
530 ASSERT(f != NULL); | |
531 fputs(stream.ToCString(), f); | |
532 fclose(f); | |
533 } | |
534 | |
535 | |
536 void ProfilerManager::ThreadMain(uword parameters) { | |
537 ASSERT(initialized_); | |
538 ASSERT(FLAG_profile); | |
539 ScopedMonitorLock lock(monitor_); | |
540 while (!shutdown_) { | |
541 int64_t current_time = OS::GetCurrentTimeMicros(); | |
542 int64_t next_sample = SampleAndRescheduleIsolates(current_time); | |
543 lock.WaitMicros(next_sample); | |
544 } | |
545 } | |
546 | |
547 | |
548 IsolateProfilerData::IsolateProfilerData(Isolate* isolate, | |
549 SampleBuffer* sample_buffer) { | |
550 isolate_ = isolate; | |
551 sample_buffer_ = sample_buffer; | |
552 timer_expiration_micros_ = kNoExpirationTime; | |
553 last_sampled_micros_ = 0; | |
554 thread_id_ = 0; | |
555 } | |
556 | |
557 | |
558 IsolateProfilerData::~IsolateProfilerData() { | |
559 } | |
560 | |
561 | |
562 void IsolateProfilerData::SampledAt(int64_t current_time) { | |
563 last_sampled_micros_ = current_time; | |
564 } | |
565 | |
566 | |
567 void IsolateProfilerData::Scheduled(int64_t current_time, ThreadId thread_id) { | |
568 timer_expiration_micros_ = current_time + sample_interval_micros_; | |
569 Thread::GetThreadCPUUsage(&cpu_usage_); | |
570 thread_id_ = thread_id; | |
571 } | |
572 | |
573 | |
574 void IsolateProfilerData::Descheduled() { | |
575 cpu_usage_ = kDescheduledCpuUsage; | |
576 timer_expiration_micros_ = kNoExpirationTime; | |
577 thread_id_ = 0; | |
578 Sample* sample = sample_buffer_->ReserveSample(); | |
579 ASSERT(sample != NULL); | |
580 sample->timestamp = OS::GetCurrentTimeMicros(); | |
581 sample->cpu_usage = 0; | |
582 sample->vm_tags = Sample::kIdle; | |
583 } | |
584 | |
585 | |
586 const char* Sample::kLookupSymbol = "Symbol Not Looked Up"; | |
587 const char* Sample::kNoSymbol = "No Symbol Found"; | |
588 | |
589 Sample::Sample() { | |
590 timestamp = 0; | |
591 cpu_usage = 0; | |
592 for (int i = 0; i < kNumStackFrames; i++) { | |
593 pcs[i] = 0; | |
594 } | |
595 vm_tags = kIdle; | |
596 runtime_tags = 0; | |
597 } | |
598 | |
599 | |
600 SampleBuffer::SampleBuffer(intptr_t capacity) { | |
601 start_ = 0; | |
602 end_ = 0; | |
603 capacity_ = capacity; | |
604 samples_ = reinterpret_cast<Sample*>(calloc(capacity, sizeof(Sample))); | |
605 } | |
606 | |
607 | |
608 SampleBuffer::~SampleBuffer() { | |
609 if (samples_ != NULL) { | |
610 free(samples_); | |
611 samples_ = NULL; | |
612 } | |
613 } | |
614 | |
615 | |
616 Sample* SampleBuffer::ReserveSample() { | |
617 intptr_t index = end_; | |
618 end_ = WrapIncrement(end_); | |
619 if (end_ == start_) { | |
620 start_ = WrapIncrement(start_); | |
621 } | |
622 // Reset. | |
623 samples_[index] = Sample(); | |
624 return &samples_[index]; | |
625 } | |
626 | |
627 | |
628 Sample* SampleBuffer::FirstSample() const { | |
629 return &samples_[start_]; | |
630 } | |
631 | |
632 | |
633 Sample* SampleBuffer::NextSample(Sample* sample) const { | |
634 ASSERT(sample >= &samples_[0]); | |
635 ASSERT(sample < &samples_[capacity_]); | |
636 intptr_t index = sample - samples_; | |
637 index = WrapIncrement(index); | |
638 return &samples_[index]; | |
639 } | |
640 | |
641 | |
642 Sample* SampleBuffer::LastSample() const { | |
643 return &samples_[end_]; | |
644 } | |
645 | |
646 | |
647 intptr_t SampleBuffer::WrapIncrement(intptr_t i) const { | |
648 return (i + 1) % capacity_; | |
649 } | |
650 | |
651 | |
652 } // namespace dart | |
OLD | NEW |