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();
450 TRACE_EVENT_CATEGORY_GROUP_ENABLED(
451 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile.hires"), &enabled_hires
);
452 const int kSamplingFrequencyMicroseconds
= enabled_hires
? 100 : 1000;
453 while (!cancellation_flag_
.IsSet()) {
455 if (waitable_event_for_testing_
&&
456 render_thread_sampler_
->EventsCollectedForTest()) {
457 waitable_event_for_testing_
->Signal();
459 // TODO(alph): make the samples firing interval not depend on the sample
461 base::PlatformThread::Sleep(
462 base::TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds
));
464 RestoreSignalHandler();
469 void V8SamplingThread::Sample() {
470 for (Sampler
* sampler
: samplers_
) {
475 void V8SamplingThread::InstallSamplers() {
476 // Note that the list does not own samplers.
477 samplers_
.push_back(render_thread_sampler_
);
478 // TODO: add worker samplers.
481 void V8SamplingThread::RemoveSamplers() {
485 void V8SamplingThread::StartSamplers() {
486 for (Sampler
* sampler
: samplers_
) {
491 void V8SamplingThread::StopSamplers() {
492 for (Sampler
* sampler
: samplers_
) {
498 void V8SamplingThread::InstallSignalHandler() {
500 // There must be the only one!
501 DCHECK(!signal_handler_installed_
);
503 sa
.sa_sigaction
= &HandleProfilerSignal
;
504 sigemptyset(&sa
.sa_mask
);
505 sa
.sa_flags
= SA_RESTART
| SA_SIGINFO
;
506 signal_handler_installed_
=
507 (sigaction(SIGPROF
, &sa
, &old_signal_handler_
) == 0);
512 void V8SamplingThread::RestoreSignalHandler() {
514 if (!signal_handler_installed_
)
516 sigaction(SIGPROF
, &old_signal_handler_
, 0);
517 signal_handler_installed_
= false;
523 void V8SamplingThread::HandleProfilerSignal(int signal
,
526 if (signal
!= SIGPROF
)
528 ucontext_t
* ucontext
= reinterpret_cast<ucontext_t
*>(context
);
529 mcontext_t
& mcontext
= ucontext
->uc_mcontext
;
530 v8::RegisterState state
;
532 #if defined(ARCH_CPU_ARM_FAMILY)
533 state
.pc
= reinterpret_cast<void*>(mcontext
.REG_64_32(pc
, arm_pc
));
534 state
.sp
= reinterpret_cast<void*>(mcontext
.REG_64_32(sp
, arm_sp
));
535 state
.fp
= reinterpret_cast<void*>(mcontext
.REG_64_32(regs
[29], arm_fp
));
537 #elif defined(ARCH_CPU_X86_FAMILY)
538 #if defined(OS_MACOSX)
539 state
.pc
= reinterpret_cast<void*>(mcontext
->__ss
.REG_64_32(__rip
, __eip
));
540 state
.sp
= reinterpret_cast<void*>(mcontext
->__ss
.REG_64_32(__rsp
, __esp
));
541 state
.fp
= reinterpret_cast<void*>(mcontext
->__ss
.REG_64_32(__rbp
, __ebp
));
544 reinterpret_cast<void*>(mcontext
.gregs
[REG_64_32(REG_RIP
, REG_EIP
)]);
546 reinterpret_cast<void*>(mcontext
.gregs
[REG_64_32(REG_RSP
, REG_ESP
)]);
548 reinterpret_cast<void*>(mcontext
.gregs
[REG_64_32(REG_RBP
, REG_EBP
)]);
550 #elif defined(ARCH_CPU_MIPS_FAMILY)
551 state
.pc
= reinterpret_cast<void*>(mcontext
.pc
);
552 state
.sp
= reinterpret_cast<void*>(mcontext
.gregs
[29]);
553 state
.fp
= reinterpret_cast<void*>(mcontext
.gregs
[30]);
554 #endif // ARCH_CPU_MIPS_FAMILY
556 Sampler::GetInstance()->DoSample(state
);
560 void V8SamplingThread::Start() {
561 if (!base::PlatformThread::Create(0, this, &sampling_thread_handle_
)) {
562 DCHECK(false) << "failed to create sampling thread";
566 void V8SamplingThread::Stop() {
567 cancellation_flag_
.Set();
568 base::PlatformThread::Join(sampling_thread_handle_
);
571 V8SamplingProfiler::V8SamplingProfiler(bool underTest
)
572 : sampling_thread_(nullptr),
573 render_thread_sampler_(Sampler::CreateForCurrentThread()),
574 task_runner_(base::ThreadTaskRunnerHandle::Get()) {
575 DCHECK(underTest
|| RenderThreadImpl::current());
576 // Force the "v8.cpu_profile*" categories to show up in the trace viewer.
577 TraceLog::GetCategoryGroupEnabled(
578 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"));
579 TraceLog::GetCategoryGroupEnabled(
580 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile.hires"));
581 TraceLog::GetInstance()->AddEnabledStateObserver(this);
584 V8SamplingProfiler::~V8SamplingProfiler() {
585 TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
586 DCHECK(!sampling_thread_
.get());
589 void V8SamplingProfiler::StartSamplingThread() {
590 DCHECK(!sampling_thread_
.get());
591 sampling_thread_
.reset(new V8SamplingThread(
592 render_thread_sampler_
.get(), waitable_event_for_testing_
.get()));
593 sampling_thread_
->Start();
596 void V8SamplingProfiler::StopSamplingThread() {
597 if (!sampling_thread_
.get())
599 sampling_thread_
->Stop();
600 sampling_thread_
.reset();
603 void V8SamplingProfiler::OnTraceLogEnabled() {
605 TRACE_EVENT_CATEGORY_GROUP_ENABLED(
606 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), &enabled
);
610 // Do not enable sampling profiler in continuous mode, as losing
611 // Jit code events may not be afforded.
612 // TODO(alph): add support of infinite recording of meta trace events.
613 base::trace_event::TraceRecordMode record_mode
=
614 TraceLog::GetInstance()->GetCurrentTraceConfig().GetTraceRecordMode();
615 if (record_mode
== base::trace_event::TraceRecordMode::RECORD_CONTINUOUSLY
)
618 task_runner_
->PostTask(FROM_HERE
,
619 base::Bind(&V8SamplingProfiler::StartSamplingThread
,
620 base::Unretained(this)));
623 void V8SamplingProfiler::OnTraceLogDisabled() {
624 task_runner_
->PostTask(FROM_HERE
,
625 base::Bind(&V8SamplingProfiler::StopSamplingThread
,
626 base::Unretained(this)));
629 void V8SamplingProfiler::EnableSamplingEventForTesting(int code_added_events
,
631 render_thread_sampler_
->SetEventsToCollectForTest(code_added_events
,
633 waitable_event_for_testing_
.reset(new base::WaitableEvent(false, false));
636 void V8SamplingProfiler::WaitSamplingEventForTesting() {
637 waitable_event_for_testing_
->Wait();