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 a0e063a6e311dbb2ca1a5d39a0ec9f977ec911bd..17d7556559b7be9622c12feb2d573c0bd702fdd6 100644 |
--- a/content/renderer/devtools/v8_sampling_profiler.cc |
+++ b/content/renderer/devtools/v8_sampling_profiler.cc |
@@ -4,15 +4,150 @@ |
#include "content/renderer/devtools/v8_sampling_profiler.h" |
+#include "base/format_macros.h" |
+#include "base/strings/string_util.h" |
#include "base/synchronization/cancellation_flag.h" |
#include "base/threading/platform_thread.h" |
#include "base/trace_event/trace_event.h" |
+#include "base/trace_event/trace_event_argument.h" |
+#include "content/renderer/render_thread_impl.h" |
+#include "v8/include/v8.h" |
+ |
+using base::trace_event::TraceLog; |
+using base::trace_event::TracedValue; |
+using v8::Isolate; |
namespace content { |
+namespace { |
+ |
+std::string PtrToString(const void* value) { |
+ char buffer[20]; |
+ base::snprintf(buffer, sizeof(buffer), "0x%" PRIx64, |
+ static_cast<uint64>(reinterpret_cast<intptr_t>(value))); |
+ return buffer; |
+} |
+ |
+} // namespace |
+ |
+// The class implements a sampler responsible for sampling a single thread. |
+class Sampler { |
+ public: |
+ explicit Sampler(Isolate* isolate) : isolate_(isolate) { DCHECK(isolate_); } |
+ |
+ static scoped_ptr<Sampler> CreateForCurrentThread(); |
+ |
+ // These methods are called from the sampling thread. |
+ void Start(); |
+ void Stop(); |
+ void Sample(); |
+ |
+ private: |
+ static void InstallJitCodeEventHandler(Isolate* isolate, void* data); |
+ static void HandleJitCodeEvent(const v8::JitCodeEvent* event); |
+ static scoped_refptr<base::trace_event::ConvertableToTraceFormat> |
+ JitCodeEventToTraceFormat(const v8::JitCodeEvent* event); |
+ |
+ Isolate* isolate_; |
+}; |
+ |
+// static |
+scoped_ptr<Sampler> Sampler::CreateForCurrentThread() { |
+ return scoped_ptr<Sampler>(new Sampler(Isolate::GetCurrent())); |
+} |
+ |
+void Sampler::Start() { |
+ v8::JitCodeEventHandler handler = &HandleJitCodeEvent; |
+ 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::HandleJitCodeEvent(const v8::JitCodeEvent* event) { |
+ // Called on the sampled V8 thread. |
+ switch (event->type) { |
+ case v8::JitCodeEvent::CODE_ADDED: |
+ TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), |
+ "JitCodeAdded", TRACE_EVENT_SCOPE_THREAD, "data", |
+ JitCodeEventToTraceFormat(event)); |
+ break; |
+ |
+ case v8::JitCodeEvent::CODE_MOVED: |
+ TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), |
+ "JitCodeMoved", TRACE_EVENT_SCOPE_THREAD, "data", |
+ JitCodeEventToTraceFormat(event)); |
+ break; |
+ |
+ case v8::JitCodeEvent::CODE_REMOVED: |
+ TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), |
+ "JitCodeRemoved", TRACE_EVENT_SCOPE_THREAD, "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::trace_event::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)); |
+ 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; |
+} |
+ |
class V8SamplingThread : public base::PlatformThread::Delegate { |
public: |
- explicit V8SamplingThread(base::WaitableEvent* event); |
+ V8SamplingThread(Sampler*, base::WaitableEvent*); |
// Implementation of PlatformThread::Delegate: |
void ThreadMain() override; |
@@ -21,27 +156,71 @@ class V8SamplingThread : public base::PlatformThread::Delegate { |
void Stop(); |
private: |
+ void Sample(); |
+ void InstallSamplers(); |
+ void RemoveSamplers(); |
+ void StartSamplers(); |
+ void StopSamplers(); |
+ static void HandleJitCodeEvent(const v8::JitCodeEvent* event); |
+ |
+ Sampler* 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(Sampler* render_thread_sampler, |
+ base::WaitableEvent* event) |
+ : render_thread_sampler_(render_thread_sampler), |
+ 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 (Sampler* sampler : samplers_) { |
+ 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 (Sampler* sampler : samplers_) { |
+ sampler->Start(); |
+ } |
+} |
+ |
+void V8SamplingThread::StopSamplers() { |
+ for (Sampler* sampler : samplers_) { |
+ sampler->Stop(); |
+ } |
} |
void V8SamplingThread::Start() { |
@@ -55,27 +234,38 @@ void V8SamplingThread::Stop() { |
base::PlatformThread::Join(sampling_thread_handle_); |
} |
-V8SamplingProfiler::V8SamplingProfiler() : sampling_thread_(nullptr) { |
- // Force the "v8_cpu_profile" category to show up in the trace viewer. |
- base::trace_event::TraceLog::GetCategoryGroupEnabled( |
- TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile")); |
- base::trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this); |
+V8SamplingProfiler::V8SamplingProfiler(bool underTest) |
+ : sampling_thread_(nullptr), |
+ render_thread_sampler_(Sampler::CreateForCurrentThread()) { |
+ DCHECK(underTest || RenderThreadImpl::current()); |
+ // Force the "v8.cpu_profile" category to show up in the trace viewer. |
+ TraceLog::GetCategoryGroupEnabled( |
+ TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile")); |
+ TraceLog::GetInstance()->AddEnabledStateObserver(this); |
} |
V8SamplingProfiler::~V8SamplingProfiler() { |
- base::trace_event::TraceLog::GetInstance()->RemoveEnabledStateObserver(this); |
+ TraceLog::GetInstance()->RemoveEnabledStateObserver(this); |
DCHECK(!sampling_thread_.get()); |
} |
void V8SamplingProfiler::OnTraceLogEnabled() { |
bool enabled; |
TRACE_EVENT_CATEGORY_GROUP_ENABLED( |
- TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile"), &enabled); |
+ TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), &enabled); |
if (!enabled) |
return; |
+ |
+ // Do not enable sampling profiler in continuous mode, as losing |
+ // Jit code events may not be afforded. |
+ base::trace_event::TraceRecordMode record_mode = |
+ TraceLog::GetInstance()->GetCurrentTraceOptions().record_mode; |
+ if (record_mode == base::trace_event::TraceRecordMode::RECORD_CONTINUOUSLY) |
+ 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(); |
} |