Fixit: Fork base::TimeTicks --> TimeTicks + ThreadTicks + TraceTicks
[chromium-blink-merge.git] / content / renderer / devtools / v8_sampling_profiler.cc
blob8a6275b730428da36652de041960e8cbd448e424
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 #if defined(OS_POSIX)
8 #include <signal.h>
9 #define USE_SIGNALS
10 #endif
12 #if defined(OS_WIN)
13 #include <windows.h>
14 #endif
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;
31 using v8::Isolate;
33 namespace content {
35 namespace {
37 class PlatformDataCommon {
38 public:
39 base::PlatformThreadId thread_id() { return thread_id_; }
41 protected:
42 PlatformDataCommon() : thread_id_(base::PlatformThread::CurrentId()) {}
44 private:
45 base::PlatformThreadId thread_id_;
48 #if defined(USE_SIGNALS)
50 class PlatformData : public PlatformDataCommon {
51 public:
52 PlatformData() : vm_tid_(pthread_self()) {}
53 pthread_t vm_tid() const { return vm_tid_; }
55 private:
56 pthread_t vm_tid_;
59 #elif defined(OS_WIN)
61 class PlatformData : public PlatformDataCommon {
62 public:
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.
68 PlatformData()
69 : thread_handle_(::OpenThread(THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME |
70 THREAD_QUERY_INFORMATION,
71 false,
72 ::GetCurrentThreadId())) {}
74 ~PlatformData() {
75 if (thread_handle_ == NULL)
76 return;
77 ::CloseHandle(thread_handle_);
78 thread_handle_ = NULL;
81 HANDLE thread_handle() { return thread_handle_; }
83 private:
84 HANDLE thread_handle_;
86 #endif
88 std::string PtrToString(const void* value) {
89 return base::StringPrintf(
90 "0x%" PRIx64, static_cast<uint64>(reinterpret_cast<intptr_t>(value)));
93 class SampleRecord {
94 public:
95 static const int kMaxFramesCountLog2 = 8;
96 static const unsigned kMaxFramesCount = (1u << kMaxFramesCountLog2) - 1;
98 SampleRecord() {}
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;
106 private:
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,
120 &sample_info);
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;
129 switch (vm_state_) {
130 case v8::StateTag::JS:
131 vm_state = "js";
132 break;
133 case v8::StateTag::GC:
134 vm_state = "gc";
135 break;
136 case v8::StateTag::COMPILER:
137 vm_state = "compiler";
138 break;
139 case v8::StateTag::OTHER:
140 vm_state = "other";
141 break;
142 case v8::StateTag::EXTERNAL:
143 vm_state = "external";
144 break;
145 case v8::StateTag::IDLE:
146 vm_state = "idle";
147 break;
148 default:
149 NOTREACHED();
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]));
156 data->EndArray();
157 return data;
160 } // namespace
162 // The class implements a sampler responsible for sampling a single thread.
163 class Sampler {
164 public:
165 ~Sampler();
167 static scoped_ptr<Sampler> CreateForCurrentThread();
168 static Sampler* GetInstance() { return tls_instance_.Pointer()->Get(); }
170 // These methods are called from the sampling thread.
171 void Start();
172 void Stop();
173 void Sample();
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_;
189 private:
190 Sampler();
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_;
203 Isolate* isolate_;
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
211 tls_instance_;
214 base::LazyInstance<base::ThreadLocalPointer<Sampler>>::Leaky
215 Sampler::tls_instance_ = LAZY_INSTANCE_INITIALIZER;
217 Sampler::Sampler()
218 : isolate_(Isolate::GetCurrent()),
219 code_added_events_count_(0),
220 samples_count_(0),
221 code_added_events_to_collect_for_test_(0),
222 sample_events_to_collect_for_test_(0) {
223 DCHECK(isolate_);
224 DCHECK(!GetInstance());
225 tls_instance_.Pointer()->Set(this);
228 Sampler::~Sampler() {
229 DCHECK(GetInstance());
230 tls_instance_.Pointer()->Set(nullptr);
233 // static
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();
250 #if ARCH_CPU_64_BITS
251 #define REG_64_32(reg64, reg32) reg64
252 #else
253 #define REG_64_32(reg64, reg32) reg32
254 #endif // ARCH_CPU_64_BITS
256 void Sampler::Sample() {
257 #if defined(OS_WIN)
258 const DWORD kSuspendFailed = static_cast<DWORD>(-1);
259 if (::SuspendThread(platform_data_.thread_handle()) == kSuspendFailed)
260 return;
261 CONTEXT context;
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.
271 DoSample(state);
273 ::ResumeThread(platform_data_.thread_handle());
274 #elif defined(USE_SIGNALS)
275 int error = pthread_kill(platform_data_.vm_tid(), SIGPROF);
276 if (error) {
277 LOG(ERROR) << "pthread_kill failed with error " << error << " "
278 << strerror(error);
280 #endif
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();
289 if (!record)
290 return;
291 record->Collect(isolate_, timestamp, state);
292 samples_data_->FinishEnqueue();
295 void Sampler::InjectPendingEvents() {
296 SampleRecord* record = samples_data_->Peek();
297 while (record) {
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);
309 // static
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);
320 // static
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.
326 if (!sampler)
327 return;
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);
335 break;
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));
341 break;
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));
347 break;
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:
352 break;
356 // static
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());
368 return data;
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));
376 return data;
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));
383 return data;
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:
389 return nullptr;
391 return nullptr;
394 class V8SamplingThread : public base::PlatformThread::Delegate {
395 public:
396 V8SamplingThread(Sampler*, base::WaitableEvent*);
398 // Implementation of PlatformThread::Delegate:
399 void ThreadMain() override;
401 void Start();
402 void Stop();
404 private:
405 void Sample();
406 void InstallSamplers();
407 void RemoveSamplers();
408 void StartSamplers();
409 void StopSamplers();
411 static void InstallSignalHandler();
412 static void RestoreSignalHandler();
413 #ifdef USE_SIGNALS
414 static void HandleProfilerSignal(int signal, siginfo_t* info, void* context);
415 #endif
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_;
425 #ifdef USE_SIGNALS
426 static bool signal_handler_installed_;
427 static struct sigaction old_signal_handler_;
428 #endif
430 DISALLOW_COPY_AND_ASSIGN(V8SamplingThread);
433 #ifdef USE_SIGNALS
434 bool V8SamplingThread::signal_handler_installed_;
435 struct sigaction V8SamplingThread::old_signal_handler_;
436 #endif
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");
446 InstallSamplers();
447 StartSamplers();
448 InstallSignalHandler();
449 const int kSamplingFrequencyMicroseconds = 1000;
450 while (!cancellation_flag_.IsSet()) {
451 Sample();
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
457 // taking duration.
458 base::PlatformThread::Sleep(
459 base::TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds));
461 RestoreSignalHandler();
462 StopSamplers();
463 RemoveSamplers();
466 void V8SamplingThread::Sample() {
467 for (Sampler* sampler : samplers_) {
468 sampler->Sample();
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() {
479 samplers_.clear();
482 void V8SamplingThread::StartSamplers() {
483 for (Sampler* sampler : samplers_) {
484 sampler->Start();
488 void V8SamplingThread::StopSamplers() {
489 for (Sampler* sampler : samplers_) {
490 sampler->Stop();
494 // static
495 void V8SamplingThread::InstallSignalHandler() {
496 #ifdef USE_SIGNALS
497 // There must be the only one!
498 DCHECK(!signal_handler_installed_);
499 struct sigaction sa;
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);
505 #endif
508 // static
509 void V8SamplingThread::RestoreSignalHandler() {
510 #ifdef USE_SIGNALS
511 if (!signal_handler_installed_)
512 return;
513 sigaction(SIGPROF, &old_signal_handler_, 0);
514 signal_handler_installed_ = false;
515 #endif
518 #ifdef USE_SIGNALS
519 // static
520 void V8SamplingThread::HandleProfilerSignal(int signal,
521 siginfo_t* info,
522 void* context) {
523 if (signal != SIGPROF)
524 return;
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));
539 #else
540 state.pc =
541 reinterpret_cast<void*>(mcontext.gregs[REG_64_32(REG_RIP, REG_EIP)]);
542 state.sp =
543 reinterpret_cast<void*>(mcontext.gregs[REG_64_32(REG_RSP, REG_ESP)]);
544 state.fp =
545 reinterpret_cast<void*>(mcontext.gregs[REG_64_32(REG_RBP, REG_EBP)]);
546 #endif // OS_MACOS
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);
555 #endif
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() {
592 bool enabled;
593 TRACE_EVENT_CATEGORY_GROUP_ENABLED(
594 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), &enabled);
595 if (!enabled)
596 return;
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)
604 return;
606 task_runner_->PostTask(FROM_HERE,
607 base::Bind(&V8SamplingProfiler::StartSamplingThread,
608 base::Unretained(this)));
611 void V8SamplingProfiler::OnTraceLogDisabled() {
612 if (!sampling_thread_.get())
613 return;
614 sampling_thread_->Stop();
615 sampling_thread_.reset();
618 void V8SamplingProfiler::EnableSamplingEventForTesting(int code_added_events,
619 int sample_events) {
620 render_thread_sampler_->SetEventsToCollectForTest(code_added_events,
621 sample_events);
622 waitable_event_for_testing_.reset(new base::WaitableEvent(false, false));
625 void V8SamplingProfiler::WaitSamplingEventForTesting() {
626 waitable_event_for_testing_->Wait();
629 } // namespace blink