Chromium Code Reviews| Index: runtime/vm/profiler.cc |
| diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc |
| index 8de6d12d59a7f87dfdecde35bbfd7d5cc1fb6b45..ab826841741a848313758c7bb9457df001c8131e 100644 |
| --- a/runtime/vm/profiler.cc |
| +++ b/runtime/vm/profiler.cc |
| @@ -6,6 +6,7 @@ |
| #include "platform/utils.h" |
| +#include "vm/atomic.h" |
| #include "vm/isolate.h" |
| #include "vm/json_stream.h" |
| #include "vm/native_symbol.h" |
| @@ -16,48 +17,6 @@ |
| namespace dart { |
| -// Notes on locking and signal handling: |
| -// |
| -// The ProfilerManager has a single monitor (monitor_). This monitor guards |
| -// access to the schedule list of isolates (isolates_, isolates_size_, etc). |
| -// |
| -// Each isolate has a mutex (profiler_data_mutex_) which protects access |
| -// to the isolate's profiler data. |
| -// |
| -// Locks can be taken in this order: |
| -// 1. ProfilerManager::monitor_ |
| -// 2. isolate->profiler_data_mutex_ |
| -// In other words, it is not acceptable to take ProfilerManager::monitor_ |
| -// after grabbing isolate->profiler_data_mutex_. |
| -// |
| -// ProfileManager::monitor_ taking entry points: |
| -// InitOnce, Shutdown |
| -// ProfilerManager::monitor_ |
| -// ScheduleIsolate, DescheduleIsolate. |
| -// ProfilerManager::monitor_, isolate->profiler_data_mutex_ |
| -// ThreadMain |
| -// isolate->profiler_data_mutex_ taking entry points: |
| -// SetupIsolateForProfiling, FreeIsolateForProfiling. |
| -// ProfilerManager::monitor_, isolate->profiler_data_mutex_ |
| -// ScheduleIsolate, DescheduleIsolate. |
| -// ProfilerManager::monitor_, isolate->profiler_data_mutex_ |
| -// ProfileSignalAction |
| -// isolate->profiler_data_mutex_ |
| -// ProfilerManager::monitor_, isolate->profiler_data_mutex_ |
| -// |
| -// Signal handling and locking: |
| -// On OSes with pthreads (Android, Linux, and Mac) we use signal delivery |
| -// to interrupt the isolate running thread for sampling. After a thread |
| -// is sent the SIGPROF, it is removed from the scheduled isolate list. |
| -// Inside the signal handler, after the sample is taken, the isolate is |
| -// added to the scheduled isolate list again. The side effect of this is |
| -// that the signal handler must be able to acquire the isolate profiler data |
| -// mutex and the profile manager monitor. When an isolate running thread |
| -// (potential signal target) calls into an entry point which acquires |
| -// ProfileManager::monitor_ signal delivery must be blocked. An example is |
| -// ProfileManager::ScheduleIsolate which blocks signal delivery while removing |
| -// the scheduling the isolate. |
| -// |
| // Notes on stack frame walking: |
| // |
| @@ -68,291 +27,181 @@ namespace dart { |
| // fail (sometimes leading to a crash). |
| // |
| -DEFINE_FLAG(bool, profile, false, "Enable Sampling Profiler"); |
| +DEFINE_FLAG(bool, profile, true, "Enable Sampling Profiler"); |
| DEFINE_FLAG(bool, trace_profiled_isolates, false, "Trace profiled isolates."); |
| -bool ProfilerManager::initialized_ = false; |
| -bool ProfilerManager::shutdown_ = false; |
| -bool ProfilerManager::thread_running_ = false; |
| -Monitor* ProfilerManager::monitor_ = NULL; |
| -Monitor* ProfilerManager::start_stop_monitor_ = NULL; |
| -Isolate** ProfilerManager::isolates_ = NULL; |
| -intptr_t ProfilerManager::isolates_capacity_ = 0; |
| -intptr_t ProfilerManager::isolates_size_ = 0; |
| +bool Profiler::initialized_ = false; |
| +Monitor* Profiler::monitor_ = NULL; |
| +SampleBuffer* Profiler::sample_buffer_ = NULL; |
| - |
| -void ProfilerManager::InitOnce() { |
| -#if defined(USING_SIMULATOR) |
| - // Force disable of profiling on simulator. |
| - FLAG_profile = false; |
| -#endif |
| -#if defined(TARGET_OS_WINDOWS) |
| - // Force disable of profiling on Windows. |
| - FLAG_profile = false; |
| -#endif |
| +void Profiler::InitOnce() { |
| if (!FLAG_profile) { |
| return; |
| } |
| - NativeSymbolResolver::InitOnce(); |
| ASSERT(!initialized_); |
| - monitor_ = new Monitor(); |
| - start_stop_monitor_ = new Monitor(); |
| initialized_ = true; |
| - ResizeIsolates(16); |
| - if (FLAG_trace_profiled_isolates) { |
| - OS::Print("ProfilerManager starting up.\n"); |
| - } |
| - { |
| - ScopedMonitor startup_lock(start_stop_monitor_); |
| - Thread::Start(ThreadMain, 0); |
| - while (!thread_running_) { |
| - // Wait until profiler thread has started up. |
| - startup_lock.Wait(); |
| - } |
| - } |
| - if (FLAG_trace_profiled_isolates) { |
| - OS::Print("ProfilerManager running.\n"); |
| - } |
| + monitor_ = new Monitor(); |
| + sample_buffer_ = new SampleBuffer(); |
| + NativeSymbolResolver::InitOnce(); |
| } |
| -void ProfilerManager::Shutdown() { |
| +void Profiler::Shutdown() { |
| if (!FLAG_profile) { |
| return; |
| } |
| ASSERT(initialized_); |
| - if (FLAG_trace_profiled_isolates) { |
| - OS::Print("ProfilerManager shutting down.\n"); |
| - } |
| - intptr_t size_at_shutdown = 0; |
| - { |
| - ScopedSignalBlocker ssb; |
| - { |
| - ScopedMonitor lock(monitor_); |
| - shutdown_ = true; |
| - size_at_shutdown = isolates_size_; |
| - isolates_size_ = 0; |
| - free(isolates_); |
| - isolates_ = NULL; |
| - lock.Notify(); |
| - } |
| - } |
| NativeSymbolResolver::ShutdownOnce(); |
| - { |
| - ScopedMonitor shutdown_lock(start_stop_monitor_); |
| - while (thread_running_) { |
| - // Wait until profiler thread has exited. |
| - shutdown_lock.Wait(); |
| - } |
| - } |
| - if (FLAG_trace_profiled_isolates) { |
| - OS::Print("ProfilerManager shut down (%" Pd ").\n", size_at_shutdown); |
| - } |
| } |
| -void ProfilerManager::SetupIsolateForProfiling(Isolate* isolate) { |
| +void Profiler::InitIsolateForProfiling(Isolate* isolate, bool shared_buffer) { |
| if (!FLAG_profile) { |
| return; |
| } |
| ASSERT(isolate != NULL); |
| + ASSERT(sample_buffer_ != NULL); |
| + MonitorLocker ml(monitor_); |
| { |
| - ScopedSignalBlocker ssb; |
| - { |
| - ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); |
| - SampleBuffer* sample_buffer = new SampleBuffer(); |
| - ASSERT(sample_buffer != NULL); |
| - IsolateProfilerData* profiler_data = |
| - new IsolateProfilerData(isolate, sample_buffer); |
| - ASSERT(profiler_data != NULL); |
| - profiler_data->set_sample_interval_micros(1000); |
| - isolate->set_profiler_data(profiler_data); |
| - if (FLAG_trace_profiled_isolates) { |
| - OS::Print("ProfilerManager Setup Isolate %p %s %p\n", |
| - isolate, |
| - isolate->name(), |
| - reinterpret_cast<void*>(Thread::GetCurrentThreadId())); |
| - } |
| + MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
| + SampleBuffer* sample_buffer = sample_buffer_; |
| + if (!shared_buffer) { |
| + sample_buffer = new SampleBuffer(); |
| + } |
| + IsolateProfilerData* profiler_data = |
| + new IsolateProfilerData(sample_buffer, !shared_buffer); |
| + ASSERT(profiler_data != NULL); |
| + isolate->set_profiler_data(profiler_data); |
| + if (FLAG_trace_profiled_isolates) { |
| + OS::Print("Profiler Setup %p %s\n", isolate, isolate->name()); |
| } |
| } |
| } |
| -void ProfilerManager::FreeIsolateProfilingData(Isolate* isolate) { |
| - ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); |
| - IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| - if (profiler_data == NULL) { |
| - // Already freed. |
| - return; |
| - } |
| - isolate->set_profiler_data(NULL); |
| - SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| - ASSERT(sample_buffer != NULL); |
| - profiler_data->set_sample_buffer(NULL); |
| - delete sample_buffer; |
| - delete profiler_data; |
| - if (FLAG_trace_profiled_isolates) { |
| - OS::Print("ProfilerManager Shutdown Isolate %p %s %p\n", |
| - isolate, |
| - isolate->name(), |
| - reinterpret_cast<void*>(Thread::GetCurrentThreadId())); |
| - } |
| -} |
| - |
| - |
| -void ProfilerManager::ShutdownIsolateForProfiling(Isolate* isolate) { |
| +void Profiler::ShutdownIsolateForProfiling(Isolate* isolate) { |
|
siva
2013/12/11 02:52:21
The names InitIsolateForProfiling and ShutdownIsol
Cutch
2013/12/11 17:44:56
Done.
|
| ASSERT(isolate != NULL); |
| if (!FLAG_profile) { |
| return; |
| } |
| + // We do not have a current isolate. |
| + ASSERT(Isolate::Current() == NULL); |
| + MonitorLocker ml(monitor_); |
| { |
| - ScopedSignalBlocker ssb; |
| - FreeIsolateProfilingData(isolate); |
| - } |
| -} |
| - |
| - |
| -void ProfilerManager::ScheduleIsolateHelper(Isolate* isolate) { |
| - ScopedMonitor lock(monitor_); |
| - { |
| - if (shutdown_) { |
| - // Shutdown. |
| - return; |
| - } |
| - ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); |
| + MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
| IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| if (profiler_data == NULL) { |
| + // Already freed. |
| return; |
| } |
| - profiler_data->Scheduled(OS::GetCurrentTimeMicros(), |
| - Thread::GetCurrentThreadId()); |
| - } |
| - intptr_t i = FindIsolate(isolate); |
| - if (i >= 0) { |
| - // Already scheduled. |
| - return; |
| + isolate->set_profiler_data(NULL); |
| + profiler_data->set_sample_buffer(NULL); |
|
siva
2013/12/11 02:52:21
if shared_buffer is not true how is the per isolat
Cutch
2013/12/11 17:44:56
IsolateProfilerData tracks whether it owns the buf
|
| + delete profiler_data; |
| + if (FLAG_trace_profiled_isolates) { |
| + OS::Print("Profiler Shutdown %p %s\n", isolate, isolate->name()); |
| + } |
| } |
| - AddIsolate(isolate); |
| - lock.Notify(); |
| } |
| -void ProfilerManager::ScheduleIsolate(Isolate* isolate, bool inside_signal) { |
| +void Profiler::BeginExecution(Isolate* isolate) { |
| + if (isolate == NULL) { |
| + return; |
| + } |
| if (!FLAG_profile) { |
| return; |
| } |
| - ASSERT(initialized_); |
| - ASSERT(isolate != NULL); |
| - if (!inside_signal) { |
| - ScopedSignalBlocker ssb; |
| - { |
| - ScheduleIsolateHelper(isolate); |
| - } |
| - } else { |
| - // Do not need a signal blocker inside a signal handler. |
| - { |
| - ScheduleIsolateHelper(isolate); |
| - } |
| + IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| + if (profiler_data == NULL) { |
| + return; |
| } |
| + SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| + if (sample_buffer == NULL) { |
| + return; |
| + } |
|
siva
2013/12/11 02:52:21
Are the two checks above more an assertion, i.e:
Cutch
2013/12/11 17:44:56
No. BeginExecution can be called before an isolate
|
| + Sample* sample = sample_buffer->ReserveSample(); |
| + sample->Init(Sample::kIsolateStart, isolate, OS::GetCurrentTimeMicros(), |
| + Thread::GetCurrentThreadId()); |
| + ThreadInterrupter::Register(RecordSampleInterruptCallback, isolate); |
| } |
| -void ProfilerManager::DescheduleIsolate(Isolate* isolate) { |
| +void Profiler::EndExecution(Isolate* isolate) { |
| + if (isolate == NULL) { |
| + return; |
| + } |
| if (!FLAG_profile) { |
| return; |
| } |
| - ASSERT(initialized_); |
| - ASSERT(isolate != NULL); |
| - { |
| - ScopedSignalBlocker ssb; |
| - { |
| - ScopedMonitor lock(monitor_); |
| - if (shutdown_) { |
| - // Shutdown. |
| - return; |
| - } |
| - intptr_t i = FindIsolate(isolate); |
| - if (i < 0) { |
| - // Not scheduled. |
| - return; |
| - } |
| - { |
| - ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); |
| - IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| - if (profiler_data != NULL) { |
| - profiler_data->Descheduled(); |
| - } |
| - } |
| - RemoveIsolate(i); |
| - lock.Notify(); |
| - } |
| + ThreadInterrupter::Unregister(); |
| + IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| + if (profiler_data == NULL) { |
| + return; |
| } |
| -} |
| - |
| - |
| -void PrintToJSONStream(Isolate* isolate, JSONStream* stream) { |
| - ASSERT(isolate == Isolate::Current()); |
| - { |
| - // We can't get signals here. |
| + SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| + if (sample_buffer == NULL) { |
| + return; |
| } |
|
siva
2013/12/11 02:52:21
Ditto comment about ASSERT vs hard checks.
|
| - UNIMPLEMENTED(); |
| + Sample* sample = sample_buffer->ReserveSample(); |
| + sample->Init(Sample::kIsolateStop, isolate, OS::GetCurrentTimeMicros(), |
| + Thread::GetCurrentThreadId()); |
| } |
| -void ProfilerManager::ResizeIsolates(intptr_t new_capacity) { |
| - ASSERT(new_capacity < kMaxProfiledIsolates); |
| - ASSERT(new_capacity > isolates_capacity_); |
| - Isolate* isolate = NULL; |
| - isolates_ = reinterpret_cast<Isolate**>( |
| - realloc(isolates_, sizeof(isolate) * new_capacity)); |
| - isolates_capacity_ = new_capacity; |
| -} |
| - |
| - |
| -void ProfilerManager::AddIsolate(Isolate* isolate) { |
| - // Must be called with monitor_ locked. |
| - if (isolates_ == NULL) { |
| - // We are shutting down. |
| +void Profiler::RecordTickInterruptCallback(const InterruptedThreadState& state, |
| + void* data) { |
| + Isolate* isolate = reinterpret_cast<Isolate*>(data); |
| + if (isolate == NULL) { |
| return; |
| } |
| - if (isolates_size_ == isolates_capacity_) { |
| - ResizeIsolates(isolates_capacity_ == 0 ? 16 : isolates_capacity_ * 2); |
| + IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| + if (profiler_data == NULL) { |
| + return; |
| } |
| - isolates_[isolates_size_] = isolate; |
| - isolates_size_++; |
| + SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| + if (sample_buffer == NULL) { |
| + return; |
| + } |
| + Sample* sample = sample_buffer->ReserveSample(); |
| + sample->Init(Sample::kIsolateSample, isolate, OS::GetCurrentTimeMicros(), |
| + state.tid); |
| } |
| -intptr_t ProfilerManager::FindIsolate(Isolate* isolate) { |
| - // Must be called with monitor_ locked. |
| - if (isolates_ == NULL) { |
| - // We are shutting down. |
| - return -1; |
| +void Profiler::RecordSampleInterruptCallback( |
| + const InterruptedThreadState& state, |
| + void* data) { |
| + Isolate* isolate = reinterpret_cast<Isolate*>(data); |
| + if (isolate == NULL) { |
| + return; |
| } |
| - for (intptr_t i = 0; i < isolates_size_; i++) { |
| - if (isolates_[i] == isolate) { |
| - return i; |
| - } |
| + IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| + if (profiler_data == NULL) { |
| + return; |
| } |
| - return -1; |
| -} |
| - |
| - |
| -void ProfilerManager::RemoveIsolate(intptr_t i) { |
| - // Must be called with monitor_ locked. |
| - if (isolates_ == NULL) { |
| - // We are shutting down. |
| + SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| + if (sample_buffer == NULL) { |
| return; |
| } |
| - ASSERT(i < isolates_size_); |
| - intptr_t last = isolates_size_ - 1; |
| - if (i != last) { |
| - isolates_[i] = isolates_[last]; |
| + Sample* sample = sample_buffer->ReserveSample(); |
| + sample->Init(Sample::kIsolateSample, isolate, OS::GetCurrentTimeMicros(), |
| + state.tid); |
| + uintptr_t stack_lower = 0; |
| + uintptr_t stack_upper = 0; |
| + isolate->GetStackBounds(&stack_lower, &stack_upper); |
| + if ((stack_lower == 0) || (stack_upper == 0)) { |
| + stack_lower = 0; |
| + stack_upper = 0; |
| } |
| - // Mark last as NULL. |
| - isolates_[last] = NULL; |
| - // Pop. |
| - isolates_size_--; |
| + ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper, |
| + state.pc, state.fp, state.sp); |
| + stackWalker.walk(); |
| +} |
| + |
| + |
| +void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream) { |
| + ASSERT(isolate == Isolate::Current()); |
| + UNIMPLEMENTED(); |
| } |
| @@ -362,6 +211,9 @@ static char* FindSymbolName(uintptr_t pc, bool* native_symbol) { |
| ASSERT(native_symbol != NULL); |
| const char* symbol_name = "Unknown"; |
| *native_symbol = false; |
| + if (pc == 0) { |
| + return const_cast<char*>(Sample::kNoFrame); |
| + } |
| const Code& code = Code::Handle(Code::LookupCode(pc)); |
| if (code.IsNull()) { |
| // Possibly a native symbol. |
| @@ -383,203 +235,162 @@ static char* FindSymbolName(uintptr_t pc, bool* native_symbol) { |
| } |
| -void ProfilerManager::WriteTracing(Isolate* isolate, const char* name, |
| - Dart_Port port) { |
| - ASSERT(isolate == Isolate::Current()); |
| - { |
| - ScopedSignalBlocker ssb; |
| - { |
| - ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); |
| - IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| - if (profiler_data == NULL) { |
| - return; |
| - } |
| - SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| - ASSERT(sample_buffer != NULL); |
| - JSONStream stream(10 * MB); |
| - intptr_t tid = reinterpret_cast<intptr_t>(sample_buffer); |
| - intptr_t pid = 1; |
| - { |
| - JSONArray events(&stream); |
| +void Profiler::WriteTracingSample(Isolate* isolate, intptr_t pid, |
| + Sample* sample, JSONArray& events) { |
| + Sample::SampleType type = sample->type; |
| + intptr_t tid = Thread::ThreadIdToIntPtr(sample->tid); |
| + double timestamp = static_cast<double>(sample->timestamp); |
| + const char* isolate_name = isolate->name(); |
| + switch (type) { |
| + case Sample::kIsolateStart: { |
| + JSONObject begin(&events); |
| + begin.AddProperty("ph", "B"); |
| + begin.AddProperty("tid", tid); |
| + begin.AddProperty("pid", pid); |
| + begin.AddProperty("name", isolate_name); |
| + begin.AddProperty("ts", timestamp); |
| + } |
| + break; |
| + case Sample::kIsolateStop: { |
| + JSONObject begin(&events); |
| + begin.AddProperty("ph", "E"); |
| + begin.AddProperty("tid", tid); |
| + begin.AddProperty("pid", pid); |
| + begin.AddProperty("name", isolate_name); |
| + begin.AddProperty("ts", timestamp); |
| + } |
| + break; |
| + case Sample::kIsolateSample: |
| + // Write "B" events. |
| + for (int i = Sample::kNumStackFrames - 1; i >= 0; i--) { |
| + bool native_symbol = false; |
| + char* symbol_name = FindSymbolName(sample->pcs[i], &native_symbol); |
| { |
| - JSONObject thread_name(&events); |
| - thread_name.AddProperty("name", "thread_name"); |
| - thread_name.AddProperty("ph", "M"); |
| - thread_name.AddProperty("tid", tid); |
| - thread_name.AddProperty("pid", pid); |
| - { |
| - JSONObject args(&thread_name, "args"); |
| - args.AddProperty("name", name); |
| - } |
| + JSONObject begin(&events); |
| + begin.AddProperty("ph", "B"); |
| + begin.AddProperty("tid", tid); |
| + begin.AddProperty("pid", pid); |
| + begin.AddProperty("name", symbol_name); |
| + begin.AddProperty("ts", timestamp); |
| } |
| + if (native_symbol) { |
| + NativeSymbolResolver::FreeSymbolName(symbol_name); |
| + } |
| + } |
| + // Write "E" events. |
| + for (int i = 0; i < Sample::kNumStackFrames; i++) { |
| + bool native_symbol = false; |
| + char* symbol_name = FindSymbolName(sample->pcs[i], &native_symbol); |
| { |
| - JSONObject process_name(&events); |
| - process_name.AddProperty("name", "process_name"); |
| - process_name.AddProperty("ph", "M"); |
| - process_name.AddProperty("tid", tid); |
| - process_name.AddProperty("pid", pid); |
| - { |
| - JSONObject args(&process_name, "args"); |
| - args.AddProperty("name", "Dart VM"); |
| - } |
| + JSONObject begin(&events); |
| + begin.AddProperty("ph", "E"); |
| + begin.AddProperty("tid", tid); |
| + begin.AddProperty("pid", pid); |
| + begin.AddProperty("name", symbol_name); |
| + begin.AddProperty("ts", timestamp); |
| } |
| - uint64_t last_time = 0; |
| - for (Sample* i = sample_buffer->FirstSample(); |
| - i != sample_buffer->LastSample(); |
| - i = sample_buffer->NextSample(i)) { |
| - if (last_time == 0) { |
| - last_time = i->timestamp; |
| - } |
| - intptr_t delta = i->timestamp - last_time; |
| - { |
| - double percentage = static_cast<double>(i->cpu_usage) / |
| - static_cast<double>(delta) * 100.0; |
| - if (percentage != percentage) { |
| - percentage = 0.0; |
| - } |
| - percentage = percentage < 0.0 ? 0.0 : percentage; |
| - percentage = percentage > 100.0 ? 100.0 : percentage; |
| - { |
| - JSONObject cpu_usage(&events); |
| - cpu_usage.AddProperty("name", "CPU Usage"); |
| - cpu_usage.AddProperty("ph", "C"); |
| - cpu_usage.AddProperty("tid", tid); |
| - cpu_usage.AddProperty("pid", pid); |
| - cpu_usage.AddProperty("ts", static_cast<double>(last_time)); |
| - { |
| - JSONObject args(&cpu_usage, "args"); |
| - args.AddProperty("CPU", percentage); |
| - } |
| - } |
| - { |
| - JSONObject cpu_usage(&events); |
| - cpu_usage.AddProperty("name", "CPU Usage"); |
| - cpu_usage.AddProperty("ph", "C"); |
| - cpu_usage.AddProperty("tid", tid); |
| - cpu_usage.AddProperty("pid", pid); |
| - cpu_usage.AddProperty("ts", static_cast<double>(i->timestamp)); |
| - { |
| - JSONObject args(&cpu_usage, "args"); |
| - args.AddProperty("CPU", percentage); |
| - } |
| - } |
| - } |
| - for (int j = 0; j < Sample::kNumStackFrames; j++) { |
| - if (i->pcs[j] == 0) { |
| - continue; |
| - } |
| - bool native_symbol = false; |
| - char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol); |
| - { |
| - JSONObject begin(&events); |
| - begin.AddProperty("ph", "B"); |
| - begin.AddProperty("tid", tid); |
| - begin.AddProperty("pid", pid); |
| - begin.AddProperty("name", symbol_name); |
| - begin.AddProperty("ts", static_cast<double>(last_time)); |
| - } |
| - if (native_symbol) { |
| - NativeSymbolResolver::FreeSymbolName(symbol_name); |
| - } |
| - } |
| - for (int j = Sample::kNumStackFrames-1; j >= 0; j--) { |
| - if (i->pcs[j] == 0) { |
| - continue; |
| - } |
| - bool native_symbol = false; |
| - char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol); |
| - { |
| - JSONObject end(&events); |
| - end.AddProperty("ph", "E"); |
| - end.AddProperty("tid", tid); |
| - end.AddProperty("pid", pid); |
| - end.AddProperty("name", symbol_name); |
| - end.AddProperty("ts", static_cast<double>(i->timestamp)); |
| - } |
| - if (native_symbol) { |
| - NativeSymbolResolver::FreeSymbolName(symbol_name); |
| - } |
| - } |
| - last_time = i->timestamp; |
| + if (native_symbol) { |
| + NativeSymbolResolver::FreeSymbolName(symbol_name); |
| } |
| } |
| - char fname[1024]; |
| - #if defined(TARGET_OS_WINDOWS) |
| - snprintf(fname, sizeof(fname)-1, "c:\\tmp\\isolate-%d.prof", |
| - static_cast<int>(port)); |
| - #else |
| - snprintf(fname, sizeof(fname)-1, "/tmp/isolate-%d.prof", |
| - static_cast<int>(port)); |
| - #endif |
| - printf("%s\n", fname); |
| - FILE* f = fopen(fname, "wb"); |
| - ASSERT(f != NULL); |
| - fputs(stream.ToCString(), f); |
| - fclose(f); |
| - } |
| + break; |
| + default: |
| + UNIMPLEMENTED(); |
| } |
| } |
| -IsolateProfilerData::IsolateProfilerData(Isolate* isolate, |
| - SampleBuffer* sample_buffer) { |
| - isolate_ = isolate; |
| - sample_buffer_ = sample_buffer; |
| - timer_expiration_micros_ = kNoExpirationTime; |
| - last_sampled_micros_ = 0; |
| - thread_id_ = 0; |
| -} |
| - |
| - |
| -IsolateProfilerData::~IsolateProfilerData() { |
| -} |
| - |
| - |
| -void IsolateProfilerData::SampledAt(int64_t current_time) { |
| - last_sampled_micros_ = current_time; |
| +void Profiler::WriteTracing(Isolate* isolate) { |
| + // We will be looking up code objects within the isolate. |
| + ASSERT(Isolate::Current() != NULL); |
| + // We do not want to be interrupted while processing the buffer. |
| + EndExecution(isolate); |
| + Dart_Port port = isolate->main_port(); |
| + MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
| + IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| + if (profiler_data == NULL) { |
| + return; |
| + } |
| + SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| + ASSERT(sample_buffer != NULL); |
| + JSONStream stream(10 * MB); |
| + intptr_t pid = OS::ProcessId(); |
| + { |
| + JSONArray events(&stream); |
| + { |
| + JSONObject process_name(&events); |
| + process_name.AddProperty("name", "process_name"); |
| + process_name.AddProperty("ph", "M"); |
| + process_name.AddProperty("pid", pid); |
| + { |
| + JSONObject args(&process_name, "args"); |
| + args.AddProperty("name", "Dart VM"); |
| + } |
| + } |
| + for (intptr_t i = 0; i < sample_buffer->capacity(); i++) { |
| + Sample* sample = sample_buffer->GetSample(i); |
| + if (sample->isolate != isolate) { |
| + continue; |
| + } |
| + if (sample->timestamp == 0) { |
| + continue; |
| + } |
| + WriteTracingSample(isolate, pid, sample, events); |
| + } |
| + } |
| + char fname[1024]; |
| +#if defined(TARGET_OS_WINDOWS) |
| + snprintf(fname, sizeof(fname)-1, "c:\\tmp\\isolate-%d-%d.prof", |
| + static_cast<int>(pid), static_cast<int>(port)); |
| +#else |
| + snprintf(fname, sizeof(fname)-1, "/tmp/isolate-%d-%d.prof", |
| + static_cast<int>(pid), static_cast<int>(port)); |
| +#endif |
| + FILE* f = fopen(fname, "wb"); |
| + ASSERT(f != NULL); |
| + fputs(stream.ToCString(), f); |
| + fclose(f); |
|
siva
2013/12/11 02:52:21
We don't operate on files directly like this insid
Cutch
2013/12/11 17:44:56
Done.
|
| + BeginExecution(isolate); |
| } |
| -void IsolateProfilerData::Scheduled(int64_t current_time, ThreadId thread_id) { |
| - timer_expiration_micros_ = current_time + sample_interval_micros_; |
| - thread_id_ = thread_id; |
| - Thread::GetThreadCpuUsage(thread_id_, &cpu_usage_); |
| +IsolateProfilerData::IsolateProfilerData(SampleBuffer* sample_buffer, |
| + bool own_sample_buffer) { |
| + sample_buffer_ = sample_buffer; |
| + own_sample_buffer_ = own_sample_buffer; |
| } |
| -void IsolateProfilerData::Descheduled() { |
| - // TODO(johnmccutchan): Track when we ran for a fraction of our sample |
| - // interval and incorporate the time difference when scheduling the |
| - // isolate again. |
| - cpu_usage_ = kDescheduledCpuUsage; |
| - timer_expiration_micros_ = kNoExpirationTime; |
| - Sample* sample = sample_buffer_->ReserveSample(); |
| - ASSERT(sample != NULL); |
| - sample->timestamp = OS::GetCurrentTimeMicros(); |
| - sample->cpu_usage = 0; |
| - sample->vm_tags = Sample::kIdle; |
| +IsolateProfilerData::~IsolateProfilerData() { |
| + if (own_sample_buffer_) { |
| + delete sample_buffer_; |
| + sample_buffer_ = NULL; |
| + own_sample_buffer_ = false; |
| + } |
| } |
| const char* Sample::kLookupSymbol = "Symbol Not Looked Up"; |
| const char* Sample::kNoSymbol = "No Symbol Found"; |
| - |
| -Sample::Sample() { |
| - timestamp = 0; |
| - cpu_usage = 0; |
| - for (int i = 0; i < kNumStackFrames; i++) { |
| +const char* Sample::kNoFrame = "<no frame>"; |
| + |
| +void Sample::Init(SampleType type, Isolate* isolate, int64_t timestamp, |
| + ThreadId tid) { |
| + this->timestamp = timestamp; |
| + this->tid = tid; |
| + this->isolate = isolate; |
| + for (intptr_t i = 0; i < kNumStackFrames; i++) { |
| pcs[i] = 0; |
| } |
| - vm_tags = kIdle; |
| + this->type = type; |
| + vm_tags = 0; |
| runtime_tags = 0; |
| } |
| - |
| SampleBuffer::SampleBuffer(intptr_t capacity) { |
| - start_ = 0; |
| - end_ = 0; |
| capacity_ = capacity; |
| samples_ = reinterpret_cast<Sample*>(calloc(capacity, sizeof(Sample))); |
| + cursor_ = 0; |
| } |
| @@ -587,8 +398,7 @@ SampleBuffer::~SampleBuffer() { |
| if (samples_ != NULL) { |
| free(samples_); |
| samples_ = NULL; |
| - start_ = 0; |
| - end_ = 0; |
| + cursor_ = 0; |
| capacity_ = 0; |
| } |
| } |
| @@ -596,40 +406,10 @@ SampleBuffer::~SampleBuffer() { |
| Sample* SampleBuffer::ReserveSample() { |
| ASSERT(samples_ != NULL); |
| - intptr_t index = end_; |
| - end_ = WrapIncrement(end_); |
| - if (end_ == start_) { |
| - start_ = WrapIncrement(start_); |
| - } |
| - ASSERT(index >= 0); |
| - ASSERT(index < capacity_); |
| - // Reset. |
| - samples_[index] = Sample(); |
| - return &samples_[index]; |
| -} |
| - |
| - |
| -Sample* SampleBuffer::FirstSample() const { |
| - return &samples_[start_]; |
| -} |
| - |
| - |
| -Sample* SampleBuffer::NextSample(Sample* sample) const { |
| - ASSERT(sample >= &samples_[0]); |
| - ASSERT(sample < &samples_[capacity_]); |
| - intptr_t index = sample - samples_; |
| - index = WrapIncrement(index); |
| - return &samples_[index]; |
| -} |
| - |
| - |
| -Sample* SampleBuffer::LastSample() const { |
| - return &samples_[end_]; |
| -} |
| - |
| - |
| -intptr_t SampleBuffer::WrapIncrement(intptr_t i) const { |
| - return (i + 1) % capacity_; |
| + uintptr_t cursor = AtomicOperations::FetchAndIncrement(&cursor_); |
|
siva
2013/12/11 02:52:21
what happens when cursor overflows?
Also there is
Cutch
2013/12/11 17:44:56
We wrap around back to zero.
|
| + // Map back into sample buffer range. |
| + cursor = cursor % capacity_; |
| + return &samples_[cursor]; |
| } |
| @@ -652,6 +432,7 @@ ProfilerSampleStackWalker::ProfilerSampleStackWalker(Sample* sample, |
| int ProfilerSampleStackWalker::walk() { |
| uword* pc = reinterpret_cast<uword*>(original_pc_); |
| +#define WALK_STACK |
| #if defined(WALK_STACK) |
| uword* fp = reinterpret_cast<uword*>(original_fp_); |
| uword* previous_fp = fp; |