1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/devtools/v8_sampling_profiler.h"
7 #include "base/format_macros.h"
8 #include "base/strings/string_util.h"
9 #include "base/synchronization/cancellation_flag.h"
10 #include "base/threading/platform_thread.h"
11 #include "base/trace_event/trace_event.h"
12 #include "base/trace_event/trace_event_argument.h"
13 #include "content/renderer/render_thread_impl.h"
14 #include "v8/include/v8.h"
16 using base::trace_event::TraceLog
;
17 using base::trace_event::TracedValue
;
24 std::string
PtrToString(const void* value
) {
26 base::snprintf(buffer
, sizeof(buffer
), "0x%" PRIx64
,
27 static_cast<uint64
>(reinterpret_cast<intptr_t>(value
)));
33 // The class implements a sampler responsible for sampling a single thread.
36 explicit Sampler(Isolate
* isolate
) : isolate_(isolate
) { DCHECK(isolate_
); }
38 static scoped_ptr
<Sampler
> CreateForCurrentThread();
40 // These methods are called from the sampling thread.
46 static void InstallJitCodeEventHandler(Isolate
* isolate
, void* data
);
47 static void HandleJitCodeEvent(const v8::JitCodeEvent
* event
);
48 static scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
49 JitCodeEventToTraceFormat(const v8::JitCodeEvent
* event
);
55 scoped_ptr
<Sampler
> Sampler::CreateForCurrentThread() {
56 return scoped_ptr
<Sampler
>(new Sampler(Isolate::GetCurrent()));
59 void Sampler::Start() {
60 v8::JitCodeEventHandler handler
= &HandleJitCodeEvent
;
61 isolate_
->RequestInterrupt(&InstallJitCodeEventHandler
,
62 reinterpret_cast<void*>(handler
));
65 void Sampler::Stop() {
66 isolate_
->RequestInterrupt(&InstallJitCodeEventHandler
, nullptr);
69 void Sampler::Sample() {
73 void Sampler::InstallJitCodeEventHandler(Isolate
* isolate
, void* data
) {
74 // Called on the sampled V8 thread.
75 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"),
76 "Sampler::InstallJitCodeEventHandler");
77 v8::JitCodeEventHandler handler
=
78 reinterpret_cast<v8::JitCodeEventHandler
>(data
);
79 isolate
->SetJitCodeEventHandler(
80 v8::JitCodeEventOptions::kJitCodeEventEnumExisting
, handler
);
84 void Sampler::HandleJitCodeEvent(const v8::JitCodeEvent
* event
) {
85 // Called on the sampled V8 thread.
86 switch (event
->type
) {
87 case v8::JitCodeEvent::CODE_ADDED
:
88 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"),
89 "JitCodeAdded", TRACE_EVENT_SCOPE_THREAD
, "data",
90 JitCodeEventToTraceFormat(event
));
93 case v8::JitCodeEvent::CODE_MOVED
:
94 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"),
95 "JitCodeMoved", TRACE_EVENT_SCOPE_THREAD
, "data",
96 JitCodeEventToTraceFormat(event
));
99 case v8::JitCodeEvent::CODE_REMOVED
:
100 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"),
101 "JitCodeRemoved", TRACE_EVENT_SCOPE_THREAD
, "data",
102 JitCodeEventToTraceFormat(event
));
105 case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO
:
106 case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING
:
107 case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING
:
113 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
114 Sampler::JitCodeEventToTraceFormat(const v8::JitCodeEvent
* event
) {
115 // Called on the sampled thread.
116 switch (event
->type
) {
117 case v8::JitCodeEvent::CODE_ADDED
: {
118 scoped_refptr
<TracedValue
> data(new TracedValue());
119 data
->SetString("code_start", PtrToString(event
->code_start
));
120 data
->SetInteger("code_len", static_cast<unsigned>(event
->code_len
));
121 data
->SetString("name", std::string(event
->name
.str
, event
->name
.len
));
125 case v8::JitCodeEvent::CODE_MOVED
: {
126 scoped_refptr
<TracedValue
> data(new TracedValue());
127 data
->SetString("code_start", PtrToString(event
->code_start
));
128 data
->SetInteger("code_len", static_cast<unsigned>(event
->code_len
));
129 data
->SetString("new_code_start", PtrToString(event
->new_code_start
));
133 case v8::JitCodeEvent::CODE_REMOVED
: {
134 scoped_refptr
<TracedValue
> data(new TracedValue());
135 data
->SetString("code_start", PtrToString(event
->code_start
));
136 data
->SetInteger("code_len", static_cast<unsigned>(event
->code_len
));
140 case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO
:
141 case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING
:
142 case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING
:
148 class V8SamplingThread
: public base::PlatformThread::Delegate
{
150 V8SamplingThread(Sampler
*, base::WaitableEvent
*);
152 // Implementation of PlatformThread::Delegate:
153 void ThreadMain() override
;
160 void InstallSamplers();
161 void RemoveSamplers();
162 void StartSamplers();
164 static void HandleJitCodeEvent(const v8::JitCodeEvent
* event
);
166 Sampler
* render_thread_sampler_
;
167 base::CancellationFlag cancellation_flag_
;
168 base::WaitableEvent
* waitable_event_for_testing_
;
169 base::PlatformThreadHandle sampling_thread_handle_
;
170 std::vector
<Sampler
*> samplers_
;
172 DISALLOW_COPY_AND_ASSIGN(V8SamplingThread
);
175 V8SamplingThread::V8SamplingThread(Sampler
* render_thread_sampler
,
176 base::WaitableEvent
* event
)
177 : render_thread_sampler_(render_thread_sampler
),
178 waitable_event_for_testing_(event
) {
181 void V8SamplingThread::ThreadMain() {
182 base::PlatformThread::SetName("V8SamplingProfilerThread");
185 const int kSamplingFrequencyMicroseconds
= 1000;
186 while (!cancellation_flag_
.IsSet()) {
188 if (waitable_event_for_testing_
) {
189 waitable_event_for_testing_
->Signal();
191 base::PlatformThread::Sleep(
192 base::TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds
));
198 void V8SamplingThread::Sample() {
199 for (Sampler
* sampler
: samplers_
) {
204 void V8SamplingThread::InstallSamplers() {
205 // Note that the list does not own samplers.
206 samplers_
.push_back(render_thread_sampler_
);
207 // TODO: add worker samplers.
210 void V8SamplingThread::RemoveSamplers() {
214 void V8SamplingThread::StartSamplers() {
215 for (Sampler
* sampler
: samplers_
) {
220 void V8SamplingThread::StopSamplers() {
221 for (Sampler
* sampler
: samplers_
) {
226 void V8SamplingThread::Start() {
227 if (!base::PlatformThread::Create(0, this, &sampling_thread_handle_
)) {
228 DCHECK(false) << "failed to create thread";
232 void V8SamplingThread::Stop() {
233 cancellation_flag_
.Set();
234 base::PlatformThread::Join(sampling_thread_handle_
);
237 V8SamplingProfiler::V8SamplingProfiler(bool underTest
)
238 : sampling_thread_(nullptr),
239 render_thread_sampler_(Sampler::CreateForCurrentThread()) {
240 DCHECK(underTest
|| RenderThreadImpl::current());
241 // Force the "v8.cpu_profile" category to show up in the trace viewer.
242 TraceLog::GetCategoryGroupEnabled(
243 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"));
244 TraceLog::GetInstance()->AddEnabledStateObserver(this);
247 V8SamplingProfiler::~V8SamplingProfiler() {
248 TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
249 DCHECK(!sampling_thread_
.get());
252 void V8SamplingProfiler::OnTraceLogEnabled() {
254 TRACE_EVENT_CATEGORY_GROUP_ENABLED(
255 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), &enabled
);
259 // Do not enable sampling profiler in continuous mode, as losing
260 // Jit code events may not be afforded.
261 base::trace_event::TraceRecordMode record_mode
=
262 TraceLog::GetInstance()->GetCurrentTraceOptions().record_mode
;
263 if (record_mode
== base::trace_event::TraceRecordMode::RECORD_CONTINUOUSLY
)
266 DCHECK(!sampling_thread_
.get());
267 sampling_thread_
.reset(new V8SamplingThread(
268 render_thread_sampler_
.get(), waitable_event_for_testing_
.get()));
269 sampling_thread_
->Start();
272 void V8SamplingProfiler::OnTraceLogDisabled() {
273 if (!sampling_thread_
.get())
275 sampling_thread_
->Stop();
276 sampling_thread_
.reset();
279 void V8SamplingProfiler::EnableSamplingEventForTesting() {
280 waitable_event_for_testing_
.reset(new base::WaitableEvent(false, false));
283 void V8SamplingProfiler::WaitSamplingEventForTesting() {
284 waitable_event_for_testing_
->Wait();