| Index: runtime/vm/profiler.cc
|
| diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
|
| index 8de6d12d59a7f87dfdecde35bbfd7d5cc1fb6b45..b08b2cd2a1e25a5b3198c3bbfa8078c1d4eb3b08 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"
|
| @@ -13,346 +14,206 @@
|
| #include "vm/os.h"
|
| #include "vm/profiler.h"
|
| #include "vm/signal_handler.h"
|
| +#include "vm/simulator.h"
|
|
|
| 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:
|
| //
|
| // The sampling profiler will collect up to Sample::kNumStackFrames stack frames
|
| // The stack frame walking code uses the frame pointer to traverse the stack.
|
| // If the VM is compiled without frame pointers (which is the default on
|
| -// recent GCC versions with optimizing enabled) the stack walking code will
|
| +// recent GCC versions with optimizing enabled) the stack walking code may
|
| // fail (sometimes leading to a crash).
|
| //
|
|
|
| -DEFINE_FLAG(bool, profile, false, "Enable Sampling Profiler");
|
| +#if defined(USING_SIMULATOR) || defined(TARGET_OS_WINDOWS) || \
|
| + defined(TARGET_OS_MACOS) || defined(TARGET_OS_ANDROID)
|
| + DEFINE_FLAG(bool, profile, false, "Enable Sampling Profiler");
|
| +#else
|
| + DEFINE_FLAG(bool, profile, true, "Enable Sampling Profiler");
|
| +#endif
|
| DEFINE_FLAG(bool, trace_profiled_isolates, false, "Trace profiled isolates.");
|
| +DEFINE_FLAG(charp, profile_dir, NULL,
|
| + "Enable writing profile data into specified directory.");
|
|
|
| -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();
|
| + ThreadInterrupter::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();
|
| - }
|
| - }
|
| + ThreadInterrupter::Shutdown();
|
| 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::InitProfilingForIsolate(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::ShutdownProfilingForIsolate(Isolate* isolate) {
|
| 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);
|
| + 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;
|
| }
|
| + 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;
|
| }
|
| - UNIMPLEMENTED();
|
| -}
|
| -
|
| -
|
| -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;
|
| + Sample* sample = sample_buffer->ReserveSample();
|
| + sample->Init(Sample::kIsolateStop, isolate, OS::GetCurrentTimeMicros(),
|
| + Thread::GetCurrentThreadId());
|
| }
|
|
|
|
|
| -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 +223,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 +247,181 @@ 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) {
|
| + if (isolate == NULL) {
|
| + return;
|
| + }
|
| + if (!FLAG_profile) {
|
| + return;
|
| + }
|
| + ASSERT(initialized_);
|
| + if (FLAG_profile_dir == NULL) {
|
| + return;
|
| + }
|
| + Dart_FileOpenCallback file_open = Isolate::file_open_callback();
|
| + Dart_FileCloseCallback file_close = Isolate::file_close_callback();
|
| + Dart_FileWriteCallback file_write = Isolate::file_write_callback();
|
| + if ((file_open == NULL) || (file_close == NULL) || (file_write == NULL)) {
|
| + // Embedder has not provided necessary callbacks.
|
| + return;
|
| + }
|
| + // 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);
|
| + 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);
|
| + }
|
| + }
|
| + const char* format = "%s/dart-profile-%" Pd "-%" Pd ".json";
|
| + intptr_t len = OS::SNPrint(NULL, 0, format,
|
| + FLAG_profile_dir, pid, isolate->main_port());
|
| + char* filename = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
|
| + OS::SNPrint(filename, len + 1, format,
|
| + FLAG_profile_dir, pid, isolate->main_port());
|
| + void* f = file_open(filename, true);
|
| + if (f == NULL) {
|
| + // Cannot write.
|
| + return;
|
| + }
|
| + TextBuffer* buffer = stream.buffer();
|
| + ASSERT(buffer != NULL);
|
| + file_write(buffer->buf(), buffer->length(), f);
|
| + file_close(f);
|
| + 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 +429,7 @@ SampleBuffer::~SampleBuffer() {
|
| if (samples_ != NULL) {
|
| free(samples_);
|
| samples_ = NULL;
|
| - start_ = 0;
|
| - end_ = 0;
|
| + cursor_ = 0;
|
| capacity_ = 0;
|
| }
|
| }
|
| @@ -596,40 +437,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_);
|
| + // Map back into sample buffer range.
|
| + cursor = cursor % capacity_;
|
| + return &samples_[cursor];
|
| }
|
|
|
|
|
| @@ -652,6 +463,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;
|
|
|