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