Chromium Code Reviews| Index: content/renderer/devtools/v8_sampling_profiler.cc |
| diff --git a/content/renderer/devtools/v8_sampling_profiler.cc b/content/renderer/devtools/v8_sampling_profiler.cc |
| index 3aff56ba16f54c6d91f11a6d160e8f9d55721481..c387edc3fc967ffc872e1c9cdf2b9281a5c703cd 100644 |
| --- a/content/renderer/devtools/v8_sampling_profiler.cc |
| +++ b/content/renderer/devtools/v8_sampling_profiler.cc |
| @@ -5,14 +5,154 @@ |
| #include "content/renderer/devtools/v8_sampling_profiler.h" |
| #include "base/debug/trace_event.h" |
| +#include "base/debug/trace_event_argument.h" |
| +#include "base/strings/string_util.h" |
| #include "base/synchronization/cancellation_flag.h" |
| #include "base/threading/platform_thread.h" |
| +#include "content/renderer/render_thread_impl.h" |
| +#include "v8/include/v8.h" |
| + |
| +using v8::Isolate; |
| +using base::debug::TracedValue; |
| namespace content { |
| +namespace { |
| + |
| +std::string PtrToString(const void* value) { |
| + char buffer[20]; |
| + base::snprintf(buffer, sizeof(buffer), "%p", value); |
| + return buffer; |
| +} |
| + |
| +// The class implements a sampler responsible for sampling a single thread. |
| +class Sampler { |
| + public: |
| + Sampler(base::PlatformThreadHandle handle, Isolate* isolate) |
| + : handle_(handle), isolate_(isolate) { |
| + DCHECK(isolate_); |
| + } |
| + virtual ~Sampler() {} |
| + |
| + void Start(); |
|
yurys
2015/01/14 13:41:36
Please add a comment that these methods can be cal
alph
2015/01/14 14:31:10
Done.
|
| + void Stop(); |
| + |
| + void Sample(); |
| + |
| + private: |
| + static void InstallJitCodeEventHandler(Isolate* isolate, void* data); |
| + static void JitCodeEventHandler(const v8::JitCodeEvent* event); |
| + static scoped_refptr<base::debug::ConvertableToTraceFormat> |
| + JitCodeEventToTraceFormat(const v8::JitCodeEvent* event); |
|
yurys
2015/01/14 13:41:37
Wrong indentation.
alph
2015/01/14 14:31:10
That's not me! That's git cl format
|
| + |
| + base::PlatformThreadHandle handle_; |
| + Isolate* isolate_; |
| +}; |
| + |
| +void Sampler::Start() { |
| + v8::JitCodeEventHandler handler = &JitCodeEventHandler; |
| + isolate_->RequestInterrupt(&InstallJitCodeEventHandler, |
| + reinterpret_cast<void*>(handler)); |
| +} |
| + |
| +void Sampler::Stop() { |
| + isolate_->RequestInterrupt(&InstallJitCodeEventHandler, nullptr); |
| +} |
| + |
| +void Sampler::Sample() { |
| +} |
| + |
| +// static |
| +void Sampler::InstallJitCodeEventHandler(Isolate* isolate, void* data) { |
| + // Called on the sampled V8 thread. |
| + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile"), |
| + "Sampler::InstallJitCodeEventHandler"); |
| + v8::JitCodeEventHandler handler = |
| + reinterpret_cast<v8::JitCodeEventHandler>(data); |
| + isolate->SetJitCodeEventHandler( |
| + v8::JitCodeEventOptions::kJitCodeEventEnumExisting, handler); |
| +} |
| + |
| +// static |
| +void Sampler::JitCodeEventHandler(const v8::JitCodeEvent* event) { |
| + // Called on the sampled V8 thread. |
| + switch (event->type) { |
| + case v8::JitCodeEvent::CODE_ADDED: |
| + TRACE_EVENT_SAMPLE_METADATA1(TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile"), |
|
yurys
2015/01/14 13:41:36
How does this relate to the stack trace format des
alph
2015/01/14 14:31:10
Yes, it's a different format. I'm not feeling comf
|
| + "JitCodeAdded", "data", |
| + JitCodeEventToTraceFormat(event)); |
| + break; |
| + |
| + case v8::JitCodeEvent::CODE_MOVED: |
| + TRACE_EVENT_SAMPLE_METADATA1(TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile"), |
|
yurys
2015/01/14 13:41:36
Why is this metadata event rather than instant one
alph
2015/01/14 14:31:10
I tried not to garble the trace view with the samp
|
| + "JitCodeMoved", "data", |
| + JitCodeEventToTraceFormat(event)); |
| + break; |
| + |
| + case v8::JitCodeEvent::CODE_REMOVED: |
| + TRACE_EVENT_SAMPLE_METADATA1(TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile"), |
| + "JitCodeRemoved", "data", |
| + JitCodeEventToTraceFormat(event)); |
| + break; |
| + |
| + case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: |
| + case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: |
| + case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: |
| + break; |
| + } |
| +} |
| + |
| +// static |
| +scoped_refptr<base::debug::ConvertableToTraceFormat> |
| +Sampler::JitCodeEventToTraceFormat(const v8::JitCodeEvent* event) { |
| + // Called on the sampled thread. |
| + switch (event->type) { |
| + case v8::JitCodeEvent::CODE_ADDED: { |
| + scoped_refptr<TracedValue> data(new TracedValue()); |
| + data->SetString("code_start", PtrToString(event->code_start)); |
|
yurys
2015/01/14 13:41:36
Do we want to see isolate identifier as one of the
alph
2015/01/14 14:31:10
It is not necessary. The code_start uniquely ident
|
| + data->SetInteger("code_len", static_cast<unsigned>(event->code_len)); |
| + data->SetString("name", std::string(event->name.str, event->name.len)); |
| + return data; |
| + } |
| + |
| + case v8::JitCodeEvent::CODE_MOVED: { |
| + scoped_refptr<TracedValue> data(new TracedValue()); |
| + data->SetString("code_start", PtrToString(event->code_start)); |
| + data->SetInteger("code_len", static_cast<unsigned>(event->code_len)); |
| + data->SetString("new_code_start", PtrToString(event->new_code_start)); |
| + return data; |
| + } |
| + |
| + case v8::JitCodeEvent::CODE_REMOVED: { |
| + scoped_refptr<TracedValue> data(new TracedValue()); |
| + data->SetString("code_start", PtrToString(event->code_start)); |
| + data->SetInteger("code_len", static_cast<unsigned>(event->code_len)); |
| + return data; |
| + } |
| + |
| + case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: |
| + case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: |
| + case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: |
| + return nullptr; |
| + } |
| + return nullptr; |
| +} |
| + |
| +} // namespace |
| + |
| +// Main thread sampler. Must be created on the main thread. |
| +class RenderThreadSampler : public Sampler { |
| + public: |
| + RenderThreadSampler() |
|
yurys
2015/01/14 13:41:36
Why do we need a subclass here? Wouldn't Sampler::
alph
2015/01/14 14:31:10
Done.
|
| + : Sampler(base::PlatformThread::CurrentHandle(), Isolate::GetCurrent()) { |
| + // Must be called on the render thread. |
| + DCHECK(RenderThreadImpl::current()); |
| + } |
| +}; |
| + |
| class V8SamplingThread : public base::PlatformThread::Delegate { |
| public: |
| - explicit V8SamplingThread(base::WaitableEvent* event); |
| + V8SamplingThread(RenderThreadSampler*, base::WaitableEvent*); |
| // Implementation of PlatformThread::Delegate: |
| void ThreadMain() override; |
| @@ -21,27 +161,71 @@ class V8SamplingThread : public base::PlatformThread::Delegate { |
| void Stop(); |
| private: |
| + void Sample(); |
| + void InstallSamplers(); |
| + void RemoveSamplers(); |
| + void StartSamplers(); |
| + void StopSamplers(); |
| + static void JitCodeEventHandler(const v8::JitCodeEvent* event); |
| + |
| + RenderThreadSampler* render_thread_sampler_; |
| base::CancellationFlag cancellation_flag_; |
| base::WaitableEvent* waitable_event_for_testing_; |
| base::PlatformThreadHandle sampling_thread_handle_; |
| + std::vector<Sampler*> samplers_; |
| DISALLOW_COPY_AND_ASSIGN(V8SamplingThread); |
| }; |
| -V8SamplingThread::V8SamplingThread(base::WaitableEvent* event) |
| - : waitable_event_for_testing_(event) { |
| +V8SamplingThread::V8SamplingThread(RenderThreadSampler* render_thread_sampler, |
| + base::WaitableEvent* event) |
| + : render_thread_sampler_(render_thread_sampler), |
|
yurys
2015/01/14 13:41:37
May be pass an array of all samplers?
alph
2015/01/14 14:31:10
I'm not yet sure who will be responsible for creat
|
| + waitable_event_for_testing_(event) { |
| } |
| void V8SamplingThread::ThreadMain() { |
| - base::PlatformThread::SetName("V8 Sampling Profiler Thread"); |
| + base::PlatformThread::SetName("V8SamplingProfilerThread"); |
| + InstallSamplers(); |
| + StartSamplers(); |
| const int kSamplingFrequencyMicroseconds = 1000; |
| while (!cancellation_flag_.IsSet()) { |
| + Sample(); |
| if (waitable_event_for_testing_) { |
| waitable_event_for_testing_->Signal(); |
| } |
| base::PlatformThread::Sleep( |
| base::TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds)); |
| } |
| + StopSamplers(); |
| + RemoveSamplers(); |
| +} |
| + |
| +void V8SamplingThread::Sample() { |
| + for (auto sampler : samplers_) { |
|
yurys
2015/01/14 13:41:36
auto -> Sampler* here and below
alph
2015/01/14 14:31:10
Done.
|
| + sampler->Sample(); |
| + } |
| +} |
| + |
| +void V8SamplingThread::InstallSamplers() { |
| + // Note that the list does not own samplers. |
| + samplers_.push_back(render_thread_sampler_); |
| + // TODO: add worker samplers. |
| +} |
| + |
| +void V8SamplingThread::RemoveSamplers() { |
| + samplers_.clear(); |
| +} |
| + |
| +void V8SamplingThread::StartSamplers() { |
| + for (auto sampler : samplers_) { |
| + sampler->Start(); |
| + } |
| +} |
| + |
| +void V8SamplingThread::StopSamplers() { |
| + for (auto sampler : samplers_) { |
| + sampler->Stop(); |
| + } |
| } |
| void V8SamplingThread::Start() { |
| @@ -55,7 +239,9 @@ void V8SamplingThread::Stop() { |
| base::PlatformThread::Join(sampling_thread_handle_); |
| } |
| -V8SamplingProfiler::V8SamplingProfiler() : sampling_thread_(nullptr) { |
| +V8SamplingProfiler::V8SamplingProfiler() |
| + : sampling_thread_(nullptr), |
| + render_thread_sampler_(new RenderThreadSampler()) { |
| // Force the "v8_cpu_profile" category to show up in the trace viewer. |
| base::debug::TraceLog::GetCategoryGroupEnabled( |
| TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile")); |
| @@ -74,8 +260,8 @@ void V8SamplingProfiler::OnTraceLogEnabled() { |
| if (!enabled) |
| return; |
| DCHECK(!sampling_thread_.get()); |
| - sampling_thread_.reset( |
| - new V8SamplingThread(waitable_event_for_testing_.get())); |
| + sampling_thread_.reset(new V8SamplingThread( |
| + render_thread_sampler_.get(), waitable_event_for_testing_.get())); |
| sampling_thread_->Start(); |
| } |