Chromium Code Reviews| 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/debug/trace_event.h" | 7 #include "base/debug/trace_event.h" |
| 8 #include "base/debug/trace_event_argument.h" | |
| 9 #include "base/strings/string_util.h" | |
| 8 #include "base/synchronization/cancellation_flag.h" | 10 #include "base/synchronization/cancellation_flag.h" |
| 9 #include "base/threading/platform_thread.h" | 11 #include "base/threading/platform_thread.h" |
| 12 #include "content/renderer/render_thread_impl.h" | |
| 13 #include "v8/include/v8.h" | |
| 14 | |
| 15 using v8::Isolate; | |
| 16 using base::debug::TracedValue; | |
| 10 | 17 |
| 11 namespace content { | 18 namespace content { |
| 12 | 19 |
| 20 namespace { | |
| 21 | |
| 22 std::string PtrToString(const void* value) { | |
| 23 char buffer[20]; | |
| 24 base::snprintf(buffer, sizeof(buffer), "%p", value); | |
| 25 return buffer; | |
| 26 } | |
| 27 | |
| 28 // The class implements a sampler responsible for sampling a single thread. | |
| 29 class Sampler { | |
| 30 public: | |
| 31 Sampler(base::PlatformThreadHandle handle, Isolate* isolate) | |
| 32 : handle_(handle), isolate_(isolate) { | |
| 33 DCHECK(isolate_); | |
| 34 } | |
| 35 virtual ~Sampler() {} | |
| 36 | |
| 37 void Start(); | |
|
yurys
2015/01/14 13:41:36
Please add a comment that these methods can be cal
alph
2015/01/14 14:31:10
Done.
| |
| 38 void Stop(); | |
| 39 | |
| 40 void Sample(); | |
| 41 | |
| 42 private: | |
| 43 static void InstallJitCodeEventHandler(Isolate* isolate, void* data); | |
| 44 static void JitCodeEventHandler(const v8::JitCodeEvent* event); | |
| 45 static scoped_refptr<base::debug::ConvertableToTraceFormat> | |
| 46 JitCodeEventToTraceFormat(const v8::JitCodeEvent* event); | |
|
yurys
2015/01/14 13:41:37
Wrong indentation.
alph
2015/01/14 14:31:10
That's not me! That's git cl format
| |
| 47 | |
| 48 base::PlatformThreadHandle handle_; | |
| 49 Isolate* isolate_; | |
| 50 }; | |
| 51 | |
| 52 void Sampler::Start() { | |
| 53 v8::JitCodeEventHandler handler = &JitCodeEventHandler; | |
| 54 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, | |
| 55 reinterpret_cast<void*>(handler)); | |
| 56 } | |
| 57 | |
| 58 void Sampler::Stop() { | |
| 59 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, nullptr); | |
| 60 } | |
| 61 | |
| 62 void Sampler::Sample() { | |
| 63 } | |
| 64 | |
| 65 // static | |
| 66 void Sampler::InstallJitCodeEventHandler(Isolate* isolate, void* data) { | |
| 67 // Called on the sampled V8 thread. | |
| 68 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile"), | |
| 69 "Sampler::InstallJitCodeEventHandler"); | |
| 70 v8::JitCodeEventHandler handler = | |
| 71 reinterpret_cast<v8::JitCodeEventHandler>(data); | |
| 72 isolate->SetJitCodeEventHandler( | |
| 73 v8::JitCodeEventOptions::kJitCodeEventEnumExisting, handler); | |
| 74 } | |
| 75 | |
| 76 // static | |
| 77 void Sampler::JitCodeEventHandler(const v8::JitCodeEvent* event) { | |
| 78 // Called on the sampled V8 thread. | |
| 79 switch (event->type) { | |
| 80 case v8::JitCodeEvent::CODE_ADDED: | |
| 81 TRACE_EVENT_SAMPLE_METADATA1(TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile"), | |
|
yurys
2015/01/14 13:41:36
How does this relate to the stack trace format des
alph
2015/01/14 14:31:10
Yes, it's a different format. I'm not feeling comf
| |
| 82 "JitCodeAdded", "data", | |
| 83 JitCodeEventToTraceFormat(event)); | |
| 84 break; | |
| 85 | |
| 86 case v8::JitCodeEvent::CODE_MOVED: | |
| 87 TRACE_EVENT_SAMPLE_METADATA1(TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile"), | |
|
yurys
2015/01/14 13:41:36
Why is this metadata event rather than instant one
alph
2015/01/14 14:31:10
I tried not to garble the trace view with the samp
| |
| 88 "JitCodeMoved", "data", | |
| 89 JitCodeEventToTraceFormat(event)); | |
| 90 break; | |
| 91 | |
| 92 case v8::JitCodeEvent::CODE_REMOVED: | |
| 93 TRACE_EVENT_SAMPLE_METADATA1(TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile"), | |
| 94 "JitCodeRemoved", "data", | |
| 95 JitCodeEventToTraceFormat(event)); | |
| 96 break; | |
| 97 | |
| 98 case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: | |
| 99 case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: | |
| 100 case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: | |
| 101 break; | |
| 102 } | |
| 103 } | |
| 104 | |
| 105 // static | |
| 106 scoped_refptr<base::debug::ConvertableToTraceFormat> | |
| 107 Sampler::JitCodeEventToTraceFormat(const v8::JitCodeEvent* event) { | |
| 108 // Called on the sampled thread. | |
| 109 switch (event->type) { | |
| 110 case v8::JitCodeEvent::CODE_ADDED: { | |
| 111 scoped_refptr<TracedValue> data(new TracedValue()); | |
| 112 data->SetString("code_start", PtrToString(event->code_start)); | |
|
yurys
2015/01/14 13:41:36
Do we want to see isolate identifier as one of the
alph
2015/01/14 14:31:10
It is not necessary. The code_start uniquely ident
| |
| 113 data->SetInteger("code_len", static_cast<unsigned>(event->code_len)); | |
| 114 data->SetString("name", std::string(event->name.str, event->name.len)); | |
| 115 return data; | |
| 116 } | |
| 117 | |
| 118 case v8::JitCodeEvent::CODE_MOVED: { | |
| 119 scoped_refptr<TracedValue> data(new TracedValue()); | |
| 120 data->SetString("code_start", PtrToString(event->code_start)); | |
| 121 data->SetInteger("code_len", static_cast<unsigned>(event->code_len)); | |
| 122 data->SetString("new_code_start", PtrToString(event->new_code_start)); | |
| 123 return data; | |
| 124 } | |
| 125 | |
| 126 case v8::JitCodeEvent::CODE_REMOVED: { | |
| 127 scoped_refptr<TracedValue> data(new TracedValue()); | |
| 128 data->SetString("code_start", PtrToString(event->code_start)); | |
| 129 data->SetInteger("code_len", static_cast<unsigned>(event->code_len)); | |
| 130 return data; | |
| 131 } | |
| 132 | |
| 133 case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: | |
| 134 case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: | |
| 135 case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: | |
| 136 return nullptr; | |
| 137 } | |
| 138 return nullptr; | |
| 139 } | |
| 140 | |
| 141 } // namespace | |
| 142 | |
| 143 // Main thread sampler. Must be created on the main thread. | |
| 144 class RenderThreadSampler : public Sampler { | |
| 145 public: | |
| 146 RenderThreadSampler() | |
|
yurys
2015/01/14 13:41:36
Why do we need a subclass here? Wouldn't Sampler::
alph
2015/01/14 14:31:10
Done.
| |
| 147 : Sampler(base::PlatformThread::CurrentHandle(), Isolate::GetCurrent()) { | |
| 148 // Must be called on the render thread. | |
| 149 DCHECK(RenderThreadImpl::current()); | |
| 150 } | |
| 151 }; | |
| 152 | |
| 13 class V8SamplingThread : public base::PlatformThread::Delegate { | 153 class V8SamplingThread : public base::PlatformThread::Delegate { |
| 14 public: | 154 public: |
| 15 explicit V8SamplingThread(base::WaitableEvent* event); | 155 V8SamplingThread(RenderThreadSampler*, base::WaitableEvent*); |
| 16 | 156 |
| 17 // Implementation of PlatformThread::Delegate: | 157 // Implementation of PlatformThread::Delegate: |
| 18 void ThreadMain() override; | 158 void ThreadMain() override; |
| 19 | 159 |
| 20 void Start(); | 160 void Start(); |
| 21 void Stop(); | 161 void Stop(); |
| 22 | 162 |
| 23 private: | 163 private: |
| 164 void Sample(); | |
| 165 void InstallSamplers(); | |
| 166 void RemoveSamplers(); | |
| 167 void StartSamplers(); | |
| 168 void StopSamplers(); | |
| 169 static void JitCodeEventHandler(const v8::JitCodeEvent* event); | |
| 170 | |
| 171 RenderThreadSampler* render_thread_sampler_; | |
| 24 base::CancellationFlag cancellation_flag_; | 172 base::CancellationFlag cancellation_flag_; |
| 25 base::WaitableEvent* waitable_event_for_testing_; | 173 base::WaitableEvent* waitable_event_for_testing_; |
| 26 base::PlatformThreadHandle sampling_thread_handle_; | 174 base::PlatformThreadHandle sampling_thread_handle_; |
| 175 std::vector<Sampler*> samplers_; | |
| 27 | 176 |
| 28 DISALLOW_COPY_AND_ASSIGN(V8SamplingThread); | 177 DISALLOW_COPY_AND_ASSIGN(V8SamplingThread); |
| 29 }; | 178 }; |
| 30 | 179 |
| 31 V8SamplingThread::V8SamplingThread(base::WaitableEvent* event) | 180 V8SamplingThread::V8SamplingThread(RenderThreadSampler* render_thread_sampler, |
| 32 : waitable_event_for_testing_(event) { | 181 base::WaitableEvent* event) |
| 182 : render_thread_sampler_(render_thread_sampler), | |
|
yurys
2015/01/14 13:41:37
May be pass an array of all samplers?
alph
2015/01/14 14:31:10
I'm not yet sure who will be responsible for creat
| |
| 183 waitable_event_for_testing_(event) { | |
| 33 } | 184 } |
| 34 | 185 |
| 35 void V8SamplingThread::ThreadMain() { | 186 void V8SamplingThread::ThreadMain() { |
| 36 base::PlatformThread::SetName("V8 Sampling Profiler Thread"); | 187 base::PlatformThread::SetName("V8SamplingProfilerThread"); |
| 188 InstallSamplers(); | |
| 189 StartSamplers(); | |
| 37 const int kSamplingFrequencyMicroseconds = 1000; | 190 const int kSamplingFrequencyMicroseconds = 1000; |
| 38 while (!cancellation_flag_.IsSet()) { | 191 while (!cancellation_flag_.IsSet()) { |
| 192 Sample(); | |
| 39 if (waitable_event_for_testing_) { | 193 if (waitable_event_for_testing_) { |
| 40 waitable_event_for_testing_->Signal(); | 194 waitable_event_for_testing_->Signal(); |
| 41 } | 195 } |
| 42 base::PlatformThread::Sleep( | 196 base::PlatformThread::Sleep( |
| 43 base::TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds)); | 197 base::TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds)); |
| 44 } | 198 } |
| 199 StopSamplers(); | |
| 200 RemoveSamplers(); | |
| 201 } | |
| 202 | |
| 203 void V8SamplingThread::Sample() { | |
| 204 for (auto sampler : samplers_) { | |
|
yurys
2015/01/14 13:41:36
auto -> Sampler* here and below
alph
2015/01/14 14:31:10
Done.
| |
| 205 sampler->Sample(); | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 void V8SamplingThread::InstallSamplers() { | |
| 210 // Note that the list does not own samplers. | |
| 211 samplers_.push_back(render_thread_sampler_); | |
| 212 // TODO: add worker samplers. | |
| 213 } | |
| 214 | |
| 215 void V8SamplingThread::RemoveSamplers() { | |
| 216 samplers_.clear(); | |
| 217 } | |
| 218 | |
| 219 void V8SamplingThread::StartSamplers() { | |
| 220 for (auto sampler : samplers_) { | |
| 221 sampler->Start(); | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 void V8SamplingThread::StopSamplers() { | |
| 226 for (auto sampler : samplers_) { | |
| 227 sampler->Stop(); | |
| 228 } | |
| 45 } | 229 } |
| 46 | 230 |
| 47 void V8SamplingThread::Start() { | 231 void V8SamplingThread::Start() { |
| 48 if (!base::PlatformThread::Create(0, this, &sampling_thread_handle_)) { | 232 if (!base::PlatformThread::Create(0, this, &sampling_thread_handle_)) { |
| 49 DCHECK(false) << "failed to create thread"; | 233 DCHECK(false) << "failed to create thread"; |
| 50 } | 234 } |
| 51 } | 235 } |
| 52 | 236 |
| 53 void V8SamplingThread::Stop() { | 237 void V8SamplingThread::Stop() { |
| 54 cancellation_flag_.Set(); | 238 cancellation_flag_.Set(); |
| 55 base::PlatformThread::Join(sampling_thread_handle_); | 239 base::PlatformThread::Join(sampling_thread_handle_); |
| 56 } | 240 } |
| 57 | 241 |
| 58 V8SamplingProfiler::V8SamplingProfiler() : sampling_thread_(nullptr) { | 242 V8SamplingProfiler::V8SamplingProfiler() |
| 243 : sampling_thread_(nullptr), | |
| 244 render_thread_sampler_(new RenderThreadSampler()) { | |
| 59 // Force the "v8_cpu_profile" category to show up in the trace viewer. | 245 // Force the "v8_cpu_profile" category to show up in the trace viewer. |
| 60 base::debug::TraceLog::GetCategoryGroupEnabled( | 246 base::debug::TraceLog::GetCategoryGroupEnabled( |
| 61 TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile")); | 247 TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile")); |
| 62 base::debug::TraceLog::GetInstance()->AddEnabledStateObserver(this); | 248 base::debug::TraceLog::GetInstance()->AddEnabledStateObserver(this); |
| 63 } | 249 } |
| 64 | 250 |
| 65 V8SamplingProfiler::~V8SamplingProfiler() { | 251 V8SamplingProfiler::~V8SamplingProfiler() { |
| 66 base::debug::TraceLog::GetInstance()->RemoveEnabledStateObserver(this); | 252 base::debug::TraceLog::GetInstance()->RemoveEnabledStateObserver(this); |
| 67 DCHECK(!sampling_thread_.get()); | 253 DCHECK(!sampling_thread_.get()); |
| 68 } | 254 } |
| 69 | 255 |
| 70 void V8SamplingProfiler::OnTraceLogEnabled() { | 256 void V8SamplingProfiler::OnTraceLogEnabled() { |
| 71 bool enabled; | 257 bool enabled; |
| 72 TRACE_EVENT_CATEGORY_GROUP_ENABLED( | 258 TRACE_EVENT_CATEGORY_GROUP_ENABLED( |
| 73 TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile"), &enabled); | 259 TRACE_DISABLED_BY_DEFAULT("v8_cpu_profile"), &enabled); |
| 74 if (!enabled) | 260 if (!enabled) |
| 75 return; | 261 return; |
| 76 DCHECK(!sampling_thread_.get()); | 262 DCHECK(!sampling_thread_.get()); |
| 77 sampling_thread_.reset( | 263 sampling_thread_.reset(new V8SamplingThread( |
| 78 new V8SamplingThread(waitable_event_for_testing_.get())); | 264 render_thread_sampler_.get(), waitable_event_for_testing_.get())); |
| 79 sampling_thread_->Start(); | 265 sampling_thread_->Start(); |
| 80 } | 266 } |
| 81 | 267 |
| 82 void V8SamplingProfiler::OnTraceLogDisabled() { | 268 void V8SamplingProfiler::OnTraceLogDisabled() { |
| 83 if (!sampling_thread_.get()) | 269 if (!sampling_thread_.get()) |
| 84 return; | 270 return; |
| 85 sampling_thread_->Stop(); | 271 sampling_thread_->Stop(); |
| 86 sampling_thread_.reset(); | 272 sampling_thread_.reset(); |
| 87 } | 273 } |
| 88 | 274 |
| 89 void V8SamplingProfiler::EnableSamplingEventForTesting() { | 275 void V8SamplingProfiler::EnableSamplingEventForTesting() { |
| 90 waitable_event_for_testing_.reset(new base::WaitableEvent(false, false)); | 276 waitable_event_for_testing_.reset(new base::WaitableEvent(false, false)); |
| 91 } | 277 } |
| 92 | 278 |
| 93 void V8SamplingProfiler::WaitSamplingEventForTesting() { | 279 void V8SamplingProfiler::WaitSamplingEventForTesting() { |
| 94 waitable_event_for_testing_->Wait(); | 280 waitable_event_for_testing_->Wait(); |
| 95 } | 281 } |
| 96 | 282 |
| 97 } // namespace blink | 283 } // namespace blink |
| OLD | NEW |