OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/devtools/v8_sampling_profiler.h" | 5 #include "content/renderer/devtools/v8_sampling_profiler.h" |
6 | 6 |
| 7 #include "base/format_macros.h" |
| 8 #include "base/strings/string_util.h" |
7 #include "base/synchronization/cancellation_flag.h" | 9 #include "base/synchronization/cancellation_flag.h" |
8 #include "base/threading/platform_thread.h" | 10 #include "base/threading/platform_thread.h" |
9 #include "base/trace_event/trace_event.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" |
| 15 |
| 16 using base::trace_event::TraceLog; |
| 17 using base::trace_event::TracedValue; |
| 18 using v8::Isolate; |
10 | 19 |
11 namespace content { | 20 namespace content { |
12 | 21 |
| 22 namespace { |
| 23 |
| 24 std::string PtrToString(const void* value) { |
| 25 char buffer[20]; |
| 26 base::snprintf(buffer, sizeof(buffer), "0x%" PRIx64, |
| 27 static_cast<uint64>(reinterpret_cast<intptr_t>(value))); |
| 28 return buffer; |
| 29 } |
| 30 |
| 31 } // namespace |
| 32 |
| 33 // The class implements a sampler responsible for sampling a single thread. |
| 34 class Sampler { |
| 35 public: |
| 36 explicit Sampler(Isolate* isolate) : isolate_(isolate) { DCHECK(isolate_); } |
| 37 |
| 38 static scoped_ptr<Sampler> CreateForCurrentThread(); |
| 39 |
| 40 // These methods are called from the sampling thread. |
| 41 void Start(); |
| 42 void Stop(); |
| 43 void Sample(); |
| 44 |
| 45 private: |
| 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); |
| 50 |
| 51 Isolate* isolate_; |
| 52 }; |
| 53 |
| 54 // static |
| 55 scoped_ptr<Sampler> Sampler::CreateForCurrentThread() { |
| 56 return scoped_ptr<Sampler>(new Sampler(Isolate::GetCurrent())); |
| 57 } |
| 58 |
| 59 void Sampler::Start() { |
| 60 v8::JitCodeEventHandler handler = &HandleJitCodeEvent; |
| 61 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, |
| 62 reinterpret_cast<void*>(handler)); |
| 63 } |
| 64 |
| 65 void Sampler::Stop() { |
| 66 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, nullptr); |
| 67 } |
| 68 |
| 69 void Sampler::Sample() { |
| 70 } |
| 71 |
| 72 // static |
| 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); |
| 81 } |
| 82 |
| 83 // static |
| 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)); |
| 91 break; |
| 92 |
| 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)); |
| 97 break; |
| 98 |
| 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)); |
| 103 break; |
| 104 |
| 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: |
| 108 break; |
| 109 } |
| 110 } |
| 111 |
| 112 // static |
| 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)); |
| 122 return data; |
| 123 } |
| 124 |
| 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)); |
| 130 return data; |
| 131 } |
| 132 |
| 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)); |
| 137 return data; |
| 138 } |
| 139 |
| 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: |
| 143 return nullptr; |
| 144 } |
| 145 return nullptr; |
| 146 } |
| 147 |
13 class V8SamplingThread : public base::PlatformThread::Delegate { | 148 class V8SamplingThread : public base::PlatformThread::Delegate { |
14 public: | 149 public: |
15 explicit V8SamplingThread(base::WaitableEvent* event); | 150 V8SamplingThread(Sampler*, base::WaitableEvent*); |
16 | 151 |
17 // Implementation of PlatformThread::Delegate: | 152 // Implementation of PlatformThread::Delegate: |
18 void ThreadMain() override; | 153 void ThreadMain() override; |
19 | 154 |
20 void Start(); | 155 void Start(); |
21 void Stop(); | 156 void Stop(); |
22 | 157 |
23 private: | 158 private: |
| 159 void Sample(); |
| 160 void InstallSamplers(); |
| 161 void RemoveSamplers(); |
| 162 void StartSamplers(); |
| 163 void StopSamplers(); |
| 164 static void HandleJitCodeEvent(const v8::JitCodeEvent* event); |
| 165 |
| 166 Sampler* render_thread_sampler_; |
24 base::CancellationFlag cancellation_flag_; | 167 base::CancellationFlag cancellation_flag_; |
25 base::WaitableEvent* waitable_event_for_testing_; | 168 base::WaitableEvent* waitable_event_for_testing_; |
26 base::PlatformThreadHandle sampling_thread_handle_; | 169 base::PlatformThreadHandle sampling_thread_handle_; |
| 170 std::vector<Sampler*> samplers_; |
27 | 171 |
28 DISALLOW_COPY_AND_ASSIGN(V8SamplingThread); | 172 DISALLOW_COPY_AND_ASSIGN(V8SamplingThread); |
29 }; | 173 }; |
30 | 174 |
31 V8SamplingThread::V8SamplingThread(base::WaitableEvent* event) | 175 V8SamplingThread::V8SamplingThread(Sampler* render_thread_sampler, |
32 : waitable_event_for_testing_(event) { | 176 base::WaitableEvent* event) |
| 177 : render_thread_sampler_(render_thread_sampler), |
| 178 waitable_event_for_testing_(event) { |
33 } | 179 } |
34 | 180 |
35 void V8SamplingThread::ThreadMain() { | 181 void V8SamplingThread::ThreadMain() { |
36 base::PlatformThread::SetName("V8 Sampling Profiler Thread"); | 182 base::PlatformThread::SetName("V8SamplingProfilerThread"); |
| 183 InstallSamplers(); |
| 184 StartSamplers(); |
37 const int kSamplingFrequencyMicroseconds = 1000; | 185 const int kSamplingFrequencyMicroseconds = 1000; |
38 while (!cancellation_flag_.IsSet()) { | 186 while (!cancellation_flag_.IsSet()) { |
| 187 Sample(); |
39 if (waitable_event_for_testing_) { | 188 if (waitable_event_for_testing_) { |
40 waitable_event_for_testing_->Signal(); | 189 waitable_event_for_testing_->Signal(); |
41 } | 190 } |
42 base::PlatformThread::Sleep( | 191 base::PlatformThread::Sleep( |
43 base::TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds)); | 192 base::TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds)); |
44 } | 193 } |
| 194 StopSamplers(); |
| 195 RemoveSamplers(); |
| 196 } |
| 197 |
| 198 void V8SamplingThread::Sample() { |
| 199 for (Sampler* sampler : samplers_) { |
| 200 sampler->Sample(); |
| 201 } |
| 202 } |
| 203 |
| 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. |
| 208 } |
| 209 |
| 210 void V8SamplingThread::RemoveSamplers() { |
| 211 samplers_.clear(); |
| 212 } |
| 213 |
| 214 void V8SamplingThread::StartSamplers() { |
| 215 for (Sampler* sampler : samplers_) { |
| 216 sampler->Start(); |
| 217 } |
| 218 } |
| 219 |
| 220 void V8SamplingThread::StopSamplers() { |
| 221 for (Sampler* sampler : samplers_) { |
| 222 sampler->Stop(); |
| 223 } |
45 } | 224 } |
46 | 225 |
47 void V8SamplingThread::Start() { | 226 void V8SamplingThread::Start() { |
48 if (!base::PlatformThread::Create(0, this, &sampling_thread_handle_)) { | 227 if (!base::PlatformThread::Create(0, this, &sampling_thread_handle_)) { |
49 DCHECK(false) << "failed to create thread"; | 228 DCHECK(false) << "failed to create thread"; |
50 } | 229 } |
51 } | 230 } |
52 | 231 |
53 void V8SamplingThread::Stop() { | 232 void V8SamplingThread::Stop() { |
54 cancellation_flag_.Set(); | 233 cancellation_flag_.Set(); |
55 base::PlatformThread::Join(sampling_thread_handle_); | 234 base::PlatformThread::Join(sampling_thread_handle_); |
56 } | 235 } |
57 | 236 |
58 V8SamplingProfiler::V8SamplingProfiler() : sampling_thread_(nullptr) { | 237 V8SamplingProfiler::V8SamplingProfiler(bool underTest) |
59 // Force the "v8_cpu_profile" category to show up in the trace viewer. | 238 : sampling_thread_(nullptr), |
60 base::trace_event::TraceLog::GetCategoryGroupEnabled( | 239 render_thread_sampler_(Sampler::CreateForCurrentThread()) { |
61 TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile")); | 240 DCHECK(underTest || RenderThreadImpl::current()); |
62 base::trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this); | 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); |
63 } | 245 } |
64 | 246 |
65 V8SamplingProfiler::~V8SamplingProfiler() { | 247 V8SamplingProfiler::~V8SamplingProfiler() { |
66 base::trace_event::TraceLog::GetInstance()->RemoveEnabledStateObserver(this); | 248 TraceLog::GetInstance()->RemoveEnabledStateObserver(this); |
67 DCHECK(!sampling_thread_.get()); | 249 DCHECK(!sampling_thread_.get()); |
68 } | 250 } |
69 | 251 |
70 void V8SamplingProfiler::OnTraceLogEnabled() { | 252 void V8SamplingProfiler::OnTraceLogEnabled() { |
71 bool enabled; | 253 bool enabled; |
72 TRACE_EVENT_CATEGORY_GROUP_ENABLED( | 254 TRACE_EVENT_CATEGORY_GROUP_ENABLED( |
73 TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile"), &enabled); | 255 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), &enabled); |
74 if (!enabled) | 256 if (!enabled) |
75 return; | 257 return; |
| 258 |
| 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) |
| 264 return; |
| 265 |
76 DCHECK(!sampling_thread_.get()); | 266 DCHECK(!sampling_thread_.get()); |
77 sampling_thread_.reset( | 267 sampling_thread_.reset(new V8SamplingThread( |
78 new V8SamplingThread(waitable_event_for_testing_.get())); | 268 render_thread_sampler_.get(), waitable_event_for_testing_.get())); |
79 sampling_thread_->Start(); | 269 sampling_thread_->Start(); |
80 } | 270 } |
81 | 271 |
82 void V8SamplingProfiler::OnTraceLogDisabled() { | 272 void V8SamplingProfiler::OnTraceLogDisabled() { |
83 if (!sampling_thread_.get()) | 273 if (!sampling_thread_.get()) |
84 return; | 274 return; |
85 sampling_thread_->Stop(); | 275 sampling_thread_->Stop(); |
86 sampling_thread_.reset(); | 276 sampling_thread_.reset(); |
87 } | 277 } |
88 | 278 |
89 void V8SamplingProfiler::EnableSamplingEventForTesting() { | 279 void V8SamplingProfiler::EnableSamplingEventForTesting() { |
90 waitable_event_for_testing_.reset(new base::WaitableEvent(false, false)); | 280 waitable_event_for_testing_.reset(new base::WaitableEvent(false, false)); |
91 } | 281 } |
92 | 282 |
93 void V8SamplingProfiler::WaitSamplingEventForTesting() { | 283 void V8SamplingProfiler::WaitSamplingEventForTesting() { |
94 waitable_event_for_testing_->Wait(); | 284 waitable_event_for_testing_->Wait(); |
95 } | 285 } |
96 | 286 |
97 } // namespace blink | 287 } // namespace blink |
OLD | NEW |