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"
16 #include "base/format_macros.h"
17 #include "base/location.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/synchronization/cancellation_flag.h"
20 #include "base/thread_task_runner_handle.h"
21 #include "base/threading/platform_thread.h"
22 #include "base/trace_event/trace_event.h"
23 #include "base/trace_event/trace_event_argument.h"
24 #include "content/renderer/devtools/lock_free_circular_queue.h"
25 #include "content/renderer/render_thread_impl.h"
26 #include "v8/include/v8.h"
28 using base::trace_event::ConvertableToTraceFormat
;
29 using base::trace_event::TraceLog
;
30 using base::trace_event::TracedValue
;
37 class PlatformDataCommon
{
39 base::PlatformThreadId
thread_id() { return thread_id_
; }
42 PlatformDataCommon() : thread_id_(base::PlatformThread::CurrentId()) {}
45 base::PlatformThreadId thread_id_
;
48 #if defined(USE_SIGNALS)
50 class PlatformData
: public PlatformDataCommon
{
52 PlatformData() : vm_tid_(pthread_self()) {}
53 pthread_t
vm_tid() const { return vm_tid_
; }
61 class PlatformData
: public PlatformDataCommon
{
63 // Get a handle to the calling thread. This is the thread that we are
64 // going to profile. We need to make a copy of the handle because we are
65 // going to use it in the sampler thread. Using GetThreadHandle() will
66 // not work in this case. We're using OpenThread because DuplicateHandle
67 // doesn't work in Chrome's sandbox.
69 : thread_handle_(::OpenThread(THREAD_GET_CONTEXT
| THREAD_SUSPEND_RESUME
|
70 THREAD_QUERY_INFORMATION
,
72 ::GetCurrentThreadId())) {}
75 if (thread_handle_
== NULL
)
77 ::CloseHandle(thread_handle_
);
78 thread_handle_
= NULL
;
81 HANDLE
thread_handle() { return thread_handle_
; }
84 HANDLE thread_handle_
;
88 std::string
PtrToString(const void* value
) {
89 return base::StringPrintf(
90 "0x%" PRIx64
, static_cast<uint64
>(reinterpret_cast<intptr_t>(value
)));
95 static const int kMaxFramesCountLog2
= 8;
96 static const unsigned kMaxFramesCount
= (1u << kMaxFramesCountLog2
) - 1;
100 base::TraceTicks
timestamp() const { return timestamp_
; }
101 void Collect(v8::Isolate
* isolate
,
102 base::TraceTicks timestamp
,
103 const v8::RegisterState
& state
);
104 scoped_refptr
<ConvertableToTraceFormat
> ToTraceFormat() const;
107 base::TraceTicks timestamp_
;
108 unsigned vm_state_
: 4;
109 unsigned frames_count_
: kMaxFramesCountLog2
;
110 const void* frames_
[kMaxFramesCount
];
112 DISALLOW_COPY_AND_ASSIGN(SampleRecord
);
115 void SampleRecord::Collect(v8::Isolate
* isolate
,
116 base::TraceTicks timestamp
,
117 const v8::RegisterState
& state
) {
118 v8::SampleInfo sample_info
;
119 isolate
->GetStackSample(state
, (void**)frames_
, kMaxFramesCount
,
121 timestamp_
= timestamp
;
122 frames_count_
= sample_info
.frames_count
;
123 vm_state_
= sample_info
.vm_state
;
126 scoped_refptr
<ConvertableToTraceFormat
> SampleRecord::ToTraceFormat() const {
127 scoped_refptr
<TracedValue
> data(new TracedValue());
128 const char* vm_state
= nullptr;
130 case v8::StateTag::JS
:
133 case v8::StateTag::GC
:
136 case v8::StateTag::COMPILER
:
137 vm_state
= "compiler";
139 case v8::StateTag::OTHER
:
142 case v8::StateTag::EXTERNAL
:
143 vm_state
= "external";
145 case v8::StateTag::IDLE
:
151 data
->SetString("vm_state", vm_state
);
152 data
->BeginArray("stack");
153 for (unsigned i
= 0; i
< frames_count_
; ++i
) {
154 data
->AppendString(PtrToString(frames_
[i
]));
162 // The class implements a sampler responsible for sampling a single thread.
167 static scoped_ptr
<Sampler
> CreateForCurrentThread();
168 static Sampler
* GetInstance() { return tls_instance_
.Pointer()->Get(); }
170 // These methods are called from the sampling thread.
175 void DoSample(const v8::RegisterState
& state
);
177 void SetEventsToCollectForTest(int code_added_events
, int sample_events
) {
178 code_added_events_to_collect_for_test_
= code_added_events
;
179 sample_events_to_collect_for_test_
= sample_events
;
182 bool EventsCollectedForTest() const {
183 return base::subtle::NoBarrier_Load(&code_added_events_count_
) >=
184 code_added_events_to_collect_for_test_
&&
185 base::subtle::NoBarrier_Load(&samples_count_
) >=
186 sample_events_to_collect_for_test_
;
192 static void InstallJitCodeEventHandler(Isolate
* isolate
, void* data
);
193 static void HandleJitCodeEvent(const v8::JitCodeEvent
* event
);
194 static scoped_refptr
<ConvertableToTraceFormat
> JitCodeEventToTraceFormat(
195 const v8::JitCodeEvent
* event
);
197 void InjectPendingEvents();
199 static const unsigned kNumberOfSamples
= 10;
200 typedef LockFreeCircularQueue
<SampleRecord
, kNumberOfSamples
> SamplingQueue
;
202 PlatformData platform_data_
;
204 scoped_ptr
<SamplingQueue
> samples_data_
;
205 base::subtle::Atomic32 code_added_events_count_
;
206 base::subtle::Atomic32 samples_count_
;
207 int code_added_events_to_collect_for_test_
;
208 int sample_events_to_collect_for_test_
;
210 static base::LazyInstance
<base::ThreadLocalPointer
<Sampler
>>::Leaky
214 base::LazyInstance
<base::ThreadLocalPointer
<Sampler
>>::Leaky
215 Sampler::tls_instance_
= LAZY_INSTANCE_INITIALIZER
;
218 : isolate_(Isolate::GetCurrent()),
219 code_added_events_count_(0),
221 code_added_events_to_collect_for_test_(0),
222 sample_events_to_collect_for_test_(0) {
224 DCHECK(!GetInstance());
225 tls_instance_
.Pointer()->Set(this);
228 Sampler::~Sampler() {
229 DCHECK(GetInstance());
230 tls_instance_
.Pointer()->Set(nullptr);
234 scoped_ptr
<Sampler
> Sampler::CreateForCurrentThread() {
235 return scoped_ptr
<Sampler
>(new Sampler());
238 void Sampler::Start() {
239 samples_data_
.reset(new SamplingQueue());
240 v8::JitCodeEventHandler handler
= &HandleJitCodeEvent
;
241 isolate_
->RequestInterrupt(&InstallJitCodeEventHandler
,
242 reinterpret_cast<void*>(handler
));
245 void Sampler::Stop() {
246 isolate_
->RequestInterrupt(&InstallJitCodeEventHandler
, nullptr);
247 samples_data_
.reset();
251 #define REG_64_32(reg64, reg32) reg64
253 #define REG_64_32(reg64, reg32) reg32
254 #endif // ARCH_CPU_64_BITS
256 void Sampler::Sample() {
258 const DWORD kSuspendFailed
= static_cast<DWORD
>(-1);
259 if (::SuspendThread(platform_data_
.thread_handle()) == kSuspendFailed
)
262 memset(&context
, 0, sizeof(context
));
263 context
.ContextFlags
= CONTEXT_FULL
;
264 if (::GetThreadContext(platform_data_
.thread_handle(), &context
) != 0) {
265 v8::RegisterState state
;
266 state
.pc
= reinterpret_cast<void*>(context
.REG_64_32(Rip
, Eip
));
267 state
.sp
= reinterpret_cast<void*>(context
.REG_64_32(Rsp
, Esp
));
268 state
.fp
= reinterpret_cast<void*>(context
.REG_64_32(Rbp
, Ebp
));
269 // TODO(alph): It is not needed to buffer the events on Windows.
270 // We can just collect and fire trace event right away.
273 ::ResumeThread(platform_data_
.thread_handle());
274 #elif defined(USE_SIGNALS)
275 int error
= pthread_kill(platform_data_
.vm_tid(), SIGPROF
);
277 LOG(ERROR
) << "pthread_kill failed with error " << error
<< " "
281 InjectPendingEvents();
284 void Sampler::DoSample(const v8::RegisterState
& state
) {
285 // Called in the sampled thread signal handler.
286 // Because of that it is not allowed to do any memory allocation here.
287 base::TraceTicks timestamp
= base::TraceTicks::Now();
288 SampleRecord
* record
= samples_data_
->StartEnqueue();
291 record
->Collect(isolate_
, timestamp
, state
);
292 samples_data_
->FinishEnqueue();
295 void Sampler::InjectPendingEvents() {
296 SampleRecord
* record
= samples_data_
->Peek();
298 TRACE_EVENT_SAMPLE_WITH_TID_AND_TIMESTAMP1(
299 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), "V8Sample",
300 platform_data_
.thread_id(),
301 (record
->timestamp() - base::TraceTicks()).InMicroseconds(), "data",
302 record
->ToTraceFormat());
303 samples_data_
->Remove();
304 record
= samples_data_
->Peek();
305 base::subtle::NoBarrier_AtomicIncrement(&samples_count_
, 1);
310 void Sampler::InstallJitCodeEventHandler(Isolate
* isolate
, void* data
) {
311 // Called on the sampled V8 thread.
312 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"),
313 "Sampler::InstallJitCodeEventHandler");
314 v8::JitCodeEventHandler handler
=
315 reinterpret_cast<v8::JitCodeEventHandler
>(data
);
316 isolate
->SetJitCodeEventHandler(
317 v8::JitCodeEventOptions::kJitCodeEventEnumExisting
, handler
);
321 void Sampler::HandleJitCodeEvent(const v8::JitCodeEvent
* event
) {
322 // Called on the sampled V8 thread.
323 Sampler
* sampler
= GetInstance();
324 // The sampler may have already been destroyed.
325 // That's fine, we're not interested in these events anymore.
328 switch (event
->type
) {
329 case v8::JitCodeEvent::CODE_ADDED
:
330 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"),
331 "JitCodeAdded", TRACE_EVENT_SCOPE_THREAD
, "data",
332 JitCodeEventToTraceFormat(event
));
333 base::subtle::NoBarrier_AtomicIncrement(
334 &sampler
->code_added_events_count_
, 1);
337 case v8::JitCodeEvent::CODE_MOVED
:
338 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"),
339 "JitCodeMoved", TRACE_EVENT_SCOPE_THREAD
, "data",
340 JitCodeEventToTraceFormat(event
));
343 case v8::JitCodeEvent::CODE_REMOVED
:
344 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"),
345 "JitCodeRemoved", TRACE_EVENT_SCOPE_THREAD
, "data",
346 JitCodeEventToTraceFormat(event
));
349 case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO
:
350 case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING
:
351 case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING
:
357 scoped_refptr
<ConvertableToTraceFormat
> Sampler::JitCodeEventToTraceFormat(
358 const v8::JitCodeEvent
* event
) {
359 switch (event
->type
) {
360 case v8::JitCodeEvent::CODE_ADDED
: {
361 scoped_refptr
<TracedValue
> data(new TracedValue());
362 data
->SetString("code_start", PtrToString(event
->code_start
));
363 data
->SetInteger("code_len", static_cast<unsigned>(event
->code_len
));
364 data
->SetString("name", std::string(event
->name
.str
, event
->name
.len
));
365 if (!event
->script
.IsEmpty()) {
366 data
->SetInteger("script_id", event
->script
->GetId());
371 case v8::JitCodeEvent::CODE_MOVED
: {
372 scoped_refptr
<TracedValue
> data(new TracedValue());
373 data
->SetString("code_start", PtrToString(event
->code_start
));
374 data
->SetInteger("code_len", static_cast<unsigned>(event
->code_len
));
375 data
->SetString("new_code_start", PtrToString(event
->new_code_start
));
379 case v8::JitCodeEvent::CODE_REMOVED
: {
380 scoped_refptr
<TracedValue
> data(new TracedValue());
381 data
->SetString("code_start", PtrToString(event
->code_start
));
382 data
->SetInteger("code_len", static_cast<unsigned>(event
->code_len
));
386 case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO
:
387 case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING
:
388 case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING
:
394 class V8SamplingThread
: public base::PlatformThread::Delegate
{
396 V8SamplingThread(Sampler
*, base::WaitableEvent
*);
398 // Implementation of PlatformThread::Delegate:
399 void ThreadMain() override
;
406 void InstallSamplers();
407 void RemoveSamplers();
408 void StartSamplers();
411 static void InstallSignalHandler();
412 static void RestoreSignalHandler();
414 static void HandleProfilerSignal(int signal
, siginfo_t
* info
, void* context
);
417 static void HandleJitCodeEvent(const v8::JitCodeEvent
* event
);
419 Sampler
* render_thread_sampler_
;
420 base::CancellationFlag cancellation_flag_
;
421 base::WaitableEvent
* waitable_event_for_testing_
;
422 base::PlatformThreadHandle sampling_thread_handle_
;
423 std::vector
<Sampler
*> samplers_
;
426 static bool signal_handler_installed_
;
427 static struct sigaction old_signal_handler_
;
430 DISALLOW_COPY_AND_ASSIGN(V8SamplingThread
);
434 bool V8SamplingThread::signal_handler_installed_
;
435 struct sigaction
V8SamplingThread::old_signal_handler_
;
438 V8SamplingThread::V8SamplingThread(Sampler
* render_thread_sampler
,
439 base::WaitableEvent
* event
)
440 : render_thread_sampler_(render_thread_sampler
),
441 waitable_event_for_testing_(event
) {
444 void V8SamplingThread::ThreadMain() {
445 base::PlatformThread::SetName("V8SamplingProfilerThread");
448 InstallSignalHandler();
449 const int kSamplingFrequencyMicroseconds
= 1000;
450 while (!cancellation_flag_
.IsSet()) {
452 if (waitable_event_for_testing_
&&
453 render_thread_sampler_
->EventsCollectedForTest()) {
454 waitable_event_for_testing_
->Signal();
456 // TODO(alph): make the samples firing interval not depend on the sample
458 base::PlatformThread::Sleep(
459 base::TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds
));
461 RestoreSignalHandler();
466 void V8SamplingThread::Sample() {
467 for (Sampler
* sampler
: samplers_
) {
472 void V8SamplingThread::InstallSamplers() {
473 // Note that the list does not own samplers.
474 samplers_
.push_back(render_thread_sampler_
);
475 // TODO: add worker samplers.
478 void V8SamplingThread::RemoveSamplers() {
482 void V8SamplingThread::StartSamplers() {
483 for (Sampler
* sampler
: samplers_
) {
488 void V8SamplingThread::StopSamplers() {
489 for (Sampler
* sampler
: samplers_
) {
495 void V8SamplingThread::InstallSignalHandler() {
497 // There must be the only one!
498 DCHECK(!signal_handler_installed_
);
500 sa
.sa_sigaction
= &HandleProfilerSignal
;
501 sigemptyset(&sa
.sa_mask
);
502 sa
.sa_flags
= SA_RESTART
| SA_SIGINFO
;
503 signal_handler_installed_
=
504 (sigaction(SIGPROF
, &sa
, &old_signal_handler_
) == 0);
509 void V8SamplingThread::RestoreSignalHandler() {
511 if (!signal_handler_installed_
)
513 sigaction(SIGPROF
, &old_signal_handler_
, 0);
514 signal_handler_installed_
= false;
520 void V8SamplingThread::HandleProfilerSignal(int signal
,
523 if (signal
!= SIGPROF
)
525 ucontext_t
* ucontext
= reinterpret_cast<ucontext_t
*>(context
);
526 mcontext_t
& mcontext
= ucontext
->uc_mcontext
;
527 v8::RegisterState state
;
529 #if defined(ARCH_CPU_ARM_FAMILY)
530 state
.pc
= reinterpret_cast<void*>(mcontext
.REG_64_32(pc
, arm_pc
));
531 state
.sp
= reinterpret_cast<void*>(mcontext
.REG_64_32(sp
, arm_sp
));
532 state
.fp
= reinterpret_cast<void*>(mcontext
.REG_64_32(regs
[29], arm_fp
));
534 #elif defined(ARCH_CPU_X86_FAMILY)
535 #if defined(OS_MACOSX)
536 state
.pc
= reinterpret_cast<void*>(mcontext
->__ss
.REG_64_32(__rip
, __eip
));
537 state
.sp
= reinterpret_cast<void*>(mcontext
->__ss
.REG_64_32(__rsp
, __esp
));
538 state
.fp
= reinterpret_cast<void*>(mcontext
->__ss
.REG_64_32(__rbp
, __ebp
));
541 reinterpret_cast<void*>(mcontext
.gregs
[REG_64_32(REG_RIP
, REG_EIP
)]);
543 reinterpret_cast<void*>(mcontext
.gregs
[REG_64_32(REG_RSP
, REG_ESP
)]);
545 reinterpret_cast<void*>(mcontext
.gregs
[REG_64_32(REG_RBP
, REG_EBP
)]);
547 #elif defined(ARCH_CPU_MIPS_FAMILY)
548 state
.pc
= reinterpret_cast<void*>(mcontext
.pc
);
549 state
.sp
= reinterpret_cast<void*>(mcontext
.gregs
[29]);
550 state
.fp
= reinterpret_cast<void*>(mcontext
.gregs
[30]);
551 #endif // ARCH_CPU_MIPS_FAMILY
553 Sampler::GetInstance()->DoSample(state
);
557 void V8SamplingThread::Start() {
558 if (!base::PlatformThread::Create(0, this, &sampling_thread_handle_
)) {
559 DCHECK(false) << "failed to create sampling thread";
563 void V8SamplingThread::Stop() {
564 cancellation_flag_
.Set();
565 base::PlatformThread::Join(sampling_thread_handle_
);
568 V8SamplingProfiler::V8SamplingProfiler(bool underTest
)
569 : sampling_thread_(nullptr),
570 render_thread_sampler_(Sampler::CreateForCurrentThread()),
571 task_runner_(base::ThreadTaskRunnerHandle::Get()) {
572 DCHECK(underTest
|| RenderThreadImpl::current());
573 // Force the "v8.cpu_profile" category to show up in the trace viewer.
574 TraceLog::GetCategoryGroupEnabled(
575 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"));
576 TraceLog::GetInstance()->AddEnabledStateObserver(this);
579 V8SamplingProfiler::~V8SamplingProfiler() {
580 TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
581 DCHECK(!sampling_thread_
.get());
584 void V8SamplingProfiler::StartSamplingThread() {
585 DCHECK(!sampling_thread_
.get());
586 sampling_thread_
.reset(new V8SamplingThread(
587 render_thread_sampler_
.get(), waitable_event_for_testing_
.get()));
588 sampling_thread_
->Start();
591 void V8SamplingProfiler::OnTraceLogEnabled() {
593 TRACE_EVENT_CATEGORY_GROUP_ENABLED(
594 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), &enabled
);
598 // Do not enable sampling profiler in continuous mode, as losing
599 // Jit code events may not be afforded.
600 // TODO(alph): add support of infinite recording of meta trace events.
601 base::trace_event::TraceRecordMode record_mode
=
602 TraceLog::GetInstance()->GetCurrentTraceOptions().record_mode
;
603 if (record_mode
== base::trace_event::TraceRecordMode::RECORD_CONTINUOUSLY
)
606 task_runner_
->PostTask(FROM_HERE
,
607 base::Bind(&V8SamplingProfiler::StartSamplingThread
,
608 base::Unretained(this)));
611 void V8SamplingProfiler::OnTraceLogDisabled() {
612 if (!sampling_thread_
.get())
614 sampling_thread_
->Stop();
615 sampling_thread_
.reset();
618 void V8SamplingProfiler::EnableSamplingEventForTesting(int code_added_events
,
620 render_thread_sampler_
->SetEventsToCollectForTest(code_added_events
,
622 waitable_event_for_testing_
.reset(new base::WaitableEvent(false, false));
625 void V8SamplingProfiler::WaitSamplingEventForTesting() {
626 waitable_event_for_testing_
->Wait();