| 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 #if defined(OS_POSIX) |
| 8 #include <signal.h> |
| 9 #define USE_SIGNALS |
| 10 #endif |
| 11 |
| 7 #include "base/format_macros.h" | 12 #include "base/format_macros.h" |
| 8 #include "base/strings/string_util.h" | 13 #include "base/strings/stringprintf.h" |
| 9 #include "base/synchronization/cancellation_flag.h" | 14 #include "base/synchronization/cancellation_flag.h" |
| 10 #include "base/threading/platform_thread.h" | 15 #include "base/threading/platform_thread.h" |
| 11 #include "base/trace_event/trace_event.h" | 16 #include "base/trace_event/trace_event.h" |
| 12 #include "base/trace_event/trace_event_argument.h" | 17 #include "base/trace_event/trace_event_argument.h" |
| 18 #include "content/renderer/devtools/lock_free_circular_queue.h" |
| 13 #include "content/renderer/render_thread_impl.h" | 19 #include "content/renderer/render_thread_impl.h" |
| 14 #include "v8/include/v8.h" | 20 #include "v8/include/v8.h" |
| 15 | 21 |
| 22 using base::trace_event::ConvertableToTraceFormat; |
| 16 using base::trace_event::TraceLog; | 23 using base::trace_event::TraceLog; |
| 17 using base::trace_event::TracedValue; | 24 using base::trace_event::TracedValue; |
| 18 using v8::Isolate; | 25 using v8::Isolate; |
| 19 | 26 |
| 20 namespace content { | 27 namespace content { |
| 21 | 28 |
| 22 namespace { | 29 namespace { |
| 23 | 30 |
| 24 std::string PtrToString(const void* value) { | 31 std::string PtrToString(const void* value) { |
| 25 char buffer[20]; | 32 return base::StringPrintf( |
| 26 base::snprintf(buffer, sizeof(buffer), "0x%" PRIx64, | 33 "0x%" PRIx64, static_cast<uint64>(reinterpret_cast<intptr_t>(value))); |
| 27 static_cast<uint64>(reinterpret_cast<intptr_t>(value))); | 34 } |
| 28 return buffer; | 35 |
| 36 class SampleRecord { |
| 37 public: |
| 38 static const int kMaxFramesCountLog2 = 8; |
| 39 static const unsigned kMaxFramesCount = (1u << kMaxFramesCountLog2) - 1; |
| 40 |
| 41 SampleRecord() {} |
| 42 |
| 43 base::TimeTicks timestamp() const { return timestamp_; } |
| 44 void Collect(v8::Isolate* isolate, |
| 45 base::TimeTicks timestamp, |
| 46 const v8::RegisterState& state); |
| 47 scoped_refptr<ConvertableToTraceFormat> ToTraceFormat() const; |
| 48 |
| 49 private: |
| 50 base::TimeTicks timestamp_; |
| 51 unsigned vm_state_ : 4; |
| 52 unsigned frames_count_ : kMaxFramesCountLog2; |
| 53 const void* frames_[kMaxFramesCount]; |
| 54 |
| 55 DISALLOW_COPY_AND_ASSIGN(SampleRecord); |
| 56 }; |
| 57 |
| 58 void SampleRecord::Collect(v8::Isolate* isolate, |
| 59 base::TimeTicks timestamp, |
| 60 const v8::RegisterState& state) { |
| 61 v8::SampleInfo sample_info; |
| 62 isolate->GetStackSample(state, (void**)frames_, kMaxFramesCount, |
| 63 &sample_info); |
| 64 timestamp_ = timestamp; |
| 65 frames_count_ = sample_info.frames_count; |
| 66 vm_state_ = sample_info.vm_state; |
| 67 } |
| 68 |
| 69 scoped_refptr<ConvertableToTraceFormat> SampleRecord::ToTraceFormat() const { |
| 70 scoped_refptr<TracedValue> data(new TracedValue()); |
| 71 const char* vm_state = nullptr; |
| 72 switch (vm_state_) { |
| 73 case v8::StateTag::JS: |
| 74 vm_state = "js"; |
| 75 break; |
| 76 case v8::StateTag::GC: |
| 77 vm_state = "gc"; |
| 78 break; |
| 79 case v8::StateTag::COMPILER: |
| 80 vm_state = "compiler"; |
| 81 break; |
| 82 case v8::StateTag::OTHER: |
| 83 vm_state = "other"; |
| 84 break; |
| 85 case v8::StateTag::EXTERNAL: |
| 86 vm_state = "external"; |
| 87 break; |
| 88 case v8::StateTag::IDLE: |
| 89 vm_state = "idle"; |
| 90 break; |
| 91 default: |
| 92 NOTREACHED(); |
| 93 } |
| 94 data->SetString("vm_state", vm_state); |
| 95 data->BeginArray("stack"); |
| 96 for (unsigned i = 0; i < frames_count_; ++i) { |
| 97 data->AppendString(PtrToString(frames_[i])); |
| 98 } |
| 99 data->EndArray(); |
| 100 return data; |
| 29 } | 101 } |
| 30 | 102 |
| 31 } // namespace | 103 } // namespace |
| 32 | 104 |
| 33 // The class implements a sampler responsible for sampling a single thread. | 105 // The class implements a sampler responsible for sampling a single thread. |
| 34 class Sampler { | 106 class Sampler { |
| 35 public: | 107 public: |
| 36 explicit Sampler(Isolate* isolate) : isolate_(isolate) { DCHECK(isolate_); } | 108 ~Sampler(); |
| 37 | 109 |
| 38 static scoped_ptr<Sampler> CreateForCurrentThread(); | 110 static scoped_ptr<Sampler> CreateForCurrentThread(); |
| 111 static Sampler* GetInstance() { return tls_instance_.Pointer()->Get(); } |
| 39 | 112 |
| 40 // These methods are called from the sampling thread. | 113 // These methods are called from the sampling thread. |
| 41 void Start(); | 114 void Start(); |
| 42 void Stop(); | 115 void Stop(); |
| 43 void Sample(); | 116 void Sample(); |
| 44 | 117 |
| 118 void DoSample(const v8::RegisterState& state); |
| 119 |
| 120 bool EventsCollectedForTest() const { |
| 121 return base::subtle::NoBarrier_Load(&code_added_events_count_) != 0 || |
| 122 base::subtle::NoBarrier_Load(&samples_count_) != 0; |
| 123 } |
| 124 |
| 45 private: | 125 private: |
| 126 Sampler(); |
| 127 |
| 46 static void InstallJitCodeEventHandler(Isolate* isolate, void* data); | 128 static void InstallJitCodeEventHandler(Isolate* isolate, void* data); |
| 47 static void HandleJitCodeEvent(const v8::JitCodeEvent* event); | 129 static void HandleJitCodeEvent(const v8::JitCodeEvent* event); |
| 48 static scoped_refptr<base::trace_event::ConvertableToTraceFormat> | 130 static scoped_refptr<ConvertableToTraceFormat> JitCodeEventToTraceFormat( |
| 49 JitCodeEventToTraceFormat(const v8::JitCodeEvent* event); | 131 const v8::JitCodeEvent* event); |
| 132 static base::PlatformThreadHandle GetCurrentThreadHandle(); |
| 50 | 133 |
| 134 void InjectPendingEvents(); |
| 135 |
| 136 static const unsigned kNumberOfSamples = 10; |
| 137 typedef LockFreeCircularQueue<SampleRecord, kNumberOfSamples> SamplingQueue; |
| 138 |
| 139 base::PlatformThreadId thread_id_; |
| 140 base::PlatformThreadHandle thread_handle_; |
| 51 Isolate* isolate_; | 141 Isolate* isolate_; |
| 142 scoped_ptr<SamplingQueue> samples_data_; |
| 143 base::subtle::Atomic32 code_added_events_count_; |
| 144 base::subtle::Atomic32 samples_count_; |
| 145 |
| 146 static base::LazyInstance<base::ThreadLocalPointer<Sampler>>::Leaky |
| 147 tls_instance_; |
| 52 }; | 148 }; |
| 53 | 149 |
| 150 base::LazyInstance<base::ThreadLocalPointer<Sampler>>::Leaky |
| 151 Sampler::tls_instance_ = LAZY_INSTANCE_INITIALIZER; |
| 152 |
| 153 Sampler::Sampler() |
| 154 : thread_id_(base::PlatformThread::CurrentId()), |
| 155 thread_handle_(Sampler::GetCurrentThreadHandle()), |
| 156 isolate_(Isolate::GetCurrent()), |
| 157 code_added_events_count_(0), |
| 158 samples_count_(0) { |
| 159 DCHECK(isolate_); |
| 160 DCHECK(!GetInstance()); |
| 161 tls_instance_.Pointer()->Set(this); |
| 162 } |
| 163 |
| 164 Sampler::~Sampler() { |
| 165 DCHECK(GetInstance()); |
| 166 tls_instance_.Pointer()->Set(nullptr); |
| 167 } |
| 168 |
| 54 // static | 169 // static |
| 55 scoped_ptr<Sampler> Sampler::CreateForCurrentThread() { | 170 scoped_ptr<Sampler> Sampler::CreateForCurrentThread() { |
| 56 return scoped_ptr<Sampler>(new Sampler(Isolate::GetCurrent())); | 171 return scoped_ptr<Sampler>(new Sampler()); |
| 172 } |
| 173 |
| 174 // static |
| 175 base::PlatformThreadHandle Sampler::GetCurrentThreadHandle() { |
| 176 #ifdef OS_WIN |
| 177 // TODO(alph): Add Windows support. |
| 178 return base::PlatformThreadHandle(); |
| 179 #else |
| 180 return base::PlatformThread::CurrentHandle(); |
| 181 #endif |
| 57 } | 182 } |
| 58 | 183 |
| 59 void Sampler::Start() { | 184 void Sampler::Start() { |
| 185 samples_data_.reset(new SamplingQueue()); |
| 60 v8::JitCodeEventHandler handler = &HandleJitCodeEvent; | 186 v8::JitCodeEventHandler handler = &HandleJitCodeEvent; |
| 61 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, | 187 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, |
| 62 reinterpret_cast<void*>(handler)); | 188 reinterpret_cast<void*>(handler)); |
| 63 } | 189 } |
| 64 | 190 |
| 65 void Sampler::Stop() { | 191 void Sampler::Stop() { |
| 66 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, nullptr); | 192 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, nullptr); |
| 193 samples_data_.reset(); |
| 67 } | 194 } |
| 68 | 195 |
| 69 void Sampler::Sample() { | 196 void Sampler::Sample() { |
| 197 #if defined(USE_SIGNALS) |
| 198 int error = pthread_kill(thread_handle_.platform_handle(), SIGPROF); |
| 199 if (error) { |
| 200 LOG(ERROR) << "pthread_kill failed with error " << error << " " |
| 201 << strerror(error); |
| 202 } |
| 203 InjectPendingEvents(); |
| 204 #endif |
| 205 } |
| 206 |
| 207 void Sampler::DoSample(const v8::RegisterState& state) { |
| 208 // Called in the sampled thread signal handler. |
| 209 // Because of that it is not allowed to do any memory allocation here. |
| 210 base::TimeTicks timestamp = base::TimeTicks::NowFromSystemTraceTime(); |
| 211 SampleRecord* record = samples_data_->StartEnqueue(); |
| 212 if (!record) |
| 213 return; |
| 214 record->Collect(isolate_, timestamp, state); |
| 215 samples_data_->FinishEnqueue(); |
| 216 base::subtle::NoBarrier_AtomicIncrement(&samples_count_, 1); |
| 217 } |
| 218 |
| 219 void Sampler::InjectPendingEvents() { |
| 220 SampleRecord* record = samples_data_->Peek(); |
| 221 while (record) { |
| 222 TRACE_EVENT_SAMPLE_WITH_TID_AND_TIMESTAMP1( |
| 223 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), "V8Sample", thread_id_, |
| 224 (record->timestamp() - base::TimeTicks()).InMicroseconds(), "data", |
| 225 record->ToTraceFormat()); |
| 226 samples_data_->Remove(); |
| 227 record = samples_data_->Peek(); |
| 228 } |
| 70 } | 229 } |
| 71 | 230 |
| 72 // static | 231 // static |
| 73 void Sampler::InstallJitCodeEventHandler(Isolate* isolate, void* data) { | 232 void Sampler::InstallJitCodeEventHandler(Isolate* isolate, void* data) { |
| 74 // Called on the sampled V8 thread. | 233 // Called on the sampled V8 thread. |
| 75 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), | 234 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), |
| 76 "Sampler::InstallJitCodeEventHandler"); | 235 "Sampler::InstallJitCodeEventHandler"); |
| 77 v8::JitCodeEventHandler handler = | 236 v8::JitCodeEventHandler handler = |
| 78 reinterpret_cast<v8::JitCodeEventHandler>(data); | 237 reinterpret_cast<v8::JitCodeEventHandler>(data); |
| 79 isolate->SetJitCodeEventHandler( | 238 isolate->SetJitCodeEventHandler( |
| 80 v8::JitCodeEventOptions::kJitCodeEventEnumExisting, handler); | 239 v8::JitCodeEventOptions::kJitCodeEventEnumExisting, handler); |
| 81 } | 240 } |
| 82 | 241 |
| 83 // static | 242 // static |
| 84 void Sampler::HandleJitCodeEvent(const v8::JitCodeEvent* event) { | 243 void Sampler::HandleJitCodeEvent(const v8::JitCodeEvent* event) { |
| 85 // Called on the sampled V8 thread. | 244 // Called on the sampled V8 thread. |
| 245 Sampler* sampler = GetInstance(); |
| 246 // The sampler may have already been destroyed. |
| 247 // That's fine, we're not interested in these events anymore. |
| 248 if (!sampler) |
| 249 return; |
| 86 switch (event->type) { | 250 switch (event->type) { |
| 87 case v8::JitCodeEvent::CODE_ADDED: | 251 case v8::JitCodeEvent::CODE_ADDED: |
| 88 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), | 252 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), |
| 89 "JitCodeAdded", TRACE_EVENT_SCOPE_THREAD, "data", | 253 "JitCodeAdded", TRACE_EVENT_SCOPE_THREAD, "data", |
| 90 JitCodeEventToTraceFormat(event)); | 254 JitCodeEventToTraceFormat(event)); |
| 255 base::subtle::NoBarrier_AtomicIncrement( |
| 256 &sampler->code_added_events_count_, 1); |
| 91 break; | 257 break; |
| 92 | 258 |
| 93 case v8::JitCodeEvent::CODE_MOVED: | 259 case v8::JitCodeEvent::CODE_MOVED: |
| 94 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), | 260 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), |
| 95 "JitCodeMoved", TRACE_EVENT_SCOPE_THREAD, "data", | 261 "JitCodeMoved", TRACE_EVENT_SCOPE_THREAD, "data", |
| 96 JitCodeEventToTraceFormat(event)); | 262 JitCodeEventToTraceFormat(event)); |
| 97 break; | 263 break; |
| 98 | 264 |
| 99 case v8::JitCodeEvent::CODE_REMOVED: | 265 case v8::JitCodeEvent::CODE_REMOVED: |
| 100 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), | 266 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), |
| 101 "JitCodeRemoved", TRACE_EVENT_SCOPE_THREAD, "data", | 267 "JitCodeRemoved", TRACE_EVENT_SCOPE_THREAD, "data", |
| 102 JitCodeEventToTraceFormat(event)); | 268 JitCodeEventToTraceFormat(event)); |
| 103 break; | 269 break; |
| 104 | 270 |
| 105 case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: | 271 case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: |
| 106 case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: | 272 case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: |
| 107 case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: | 273 case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: |
| 108 break; | 274 break; |
| 109 } | 275 } |
| 110 } | 276 } |
| 111 | 277 |
| 112 // static | 278 // static |
| 113 scoped_refptr<base::trace_event::ConvertableToTraceFormat> | 279 scoped_refptr<ConvertableToTraceFormat> Sampler::JitCodeEventToTraceFormat( |
| 114 Sampler::JitCodeEventToTraceFormat(const v8::JitCodeEvent* event) { | 280 const v8::JitCodeEvent* event) { |
| 115 // Called on the sampled thread. | |
| 116 switch (event->type) { | 281 switch (event->type) { |
| 117 case v8::JitCodeEvent::CODE_ADDED: { | 282 case v8::JitCodeEvent::CODE_ADDED: { |
| 118 scoped_refptr<TracedValue> data(new TracedValue()); | 283 scoped_refptr<TracedValue> data(new TracedValue()); |
| 119 data->SetString("code_start", PtrToString(event->code_start)); | 284 data->SetString("code_start", PtrToString(event->code_start)); |
| 120 data->SetInteger("code_len", static_cast<unsigned>(event->code_len)); | 285 data->SetInteger("code_len", static_cast<unsigned>(event->code_len)); |
| 121 data->SetString("name", std::string(event->name.str, event->name.len)); | 286 data->SetString("name", std::string(event->name.str, event->name.len)); |
| 122 return data; | 287 return data; |
| 123 } | 288 } |
| 124 | 289 |
| 125 case v8::JitCodeEvent::CODE_MOVED: { | 290 case v8::JitCodeEvent::CODE_MOVED: { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 154 | 319 |
| 155 void Start(); | 320 void Start(); |
| 156 void Stop(); | 321 void Stop(); |
| 157 | 322 |
| 158 private: | 323 private: |
| 159 void Sample(); | 324 void Sample(); |
| 160 void InstallSamplers(); | 325 void InstallSamplers(); |
| 161 void RemoveSamplers(); | 326 void RemoveSamplers(); |
| 162 void StartSamplers(); | 327 void StartSamplers(); |
| 163 void StopSamplers(); | 328 void StopSamplers(); |
| 329 |
| 330 static void InstallSignalHandler(); |
| 331 static void RestoreSignalHandler(); |
| 332 #ifdef USE_SIGNALS |
| 333 static void HandleProfilerSignal(int signal, siginfo_t* info, void* context); |
| 334 #endif |
| 335 |
| 164 static void HandleJitCodeEvent(const v8::JitCodeEvent* event); | 336 static void HandleJitCodeEvent(const v8::JitCodeEvent* event); |
| 165 | 337 |
| 166 Sampler* render_thread_sampler_; | 338 Sampler* render_thread_sampler_; |
| 167 base::CancellationFlag cancellation_flag_; | 339 base::CancellationFlag cancellation_flag_; |
| 168 base::WaitableEvent* waitable_event_for_testing_; | 340 base::WaitableEvent* waitable_event_for_testing_; |
| 169 base::PlatformThreadHandle sampling_thread_handle_; | 341 base::PlatformThreadHandle sampling_thread_handle_; |
| 170 std::vector<Sampler*> samplers_; | 342 std::vector<Sampler*> samplers_; |
| 171 | 343 |
| 344 #ifdef USE_SIGNALS |
| 345 static bool signal_handler_installed_; |
| 346 static struct sigaction old_signal_handler_; |
| 347 #endif |
| 348 |
| 172 DISALLOW_COPY_AND_ASSIGN(V8SamplingThread); | 349 DISALLOW_COPY_AND_ASSIGN(V8SamplingThread); |
| 173 }; | 350 }; |
| 174 | 351 |
| 352 #ifdef USE_SIGNALS |
| 353 bool V8SamplingThread::signal_handler_installed_; |
| 354 struct sigaction V8SamplingThread::old_signal_handler_; |
| 355 #endif |
| 356 |
| 175 V8SamplingThread::V8SamplingThread(Sampler* render_thread_sampler, | 357 V8SamplingThread::V8SamplingThread(Sampler* render_thread_sampler, |
| 176 base::WaitableEvent* event) | 358 base::WaitableEvent* event) |
| 177 : render_thread_sampler_(render_thread_sampler), | 359 : render_thread_sampler_(render_thread_sampler), |
| 178 waitable_event_for_testing_(event) { | 360 waitable_event_for_testing_(event) { |
| 179 } | 361 } |
| 180 | 362 |
| 181 void V8SamplingThread::ThreadMain() { | 363 void V8SamplingThread::ThreadMain() { |
| 182 base::PlatformThread::SetName("V8SamplingProfilerThread"); | 364 base::PlatformThread::SetName("V8SamplingProfilerThread"); |
| 183 InstallSamplers(); | 365 InstallSamplers(); |
| 184 StartSamplers(); | 366 StartSamplers(); |
| 367 InstallSignalHandler(); |
| 185 const int kSamplingFrequencyMicroseconds = 1000; | 368 const int kSamplingFrequencyMicroseconds = 1000; |
| 186 while (!cancellation_flag_.IsSet()) { | 369 while (!cancellation_flag_.IsSet()) { |
| 187 Sample(); | 370 Sample(); |
| 188 if (waitable_event_for_testing_) { | 371 if (waitable_event_for_testing_ && |
| 372 render_thread_sampler_->EventsCollectedForTest()) { |
| 189 waitable_event_for_testing_->Signal(); | 373 waitable_event_for_testing_->Signal(); |
| 190 } | 374 } |
| 375 // TODO(alph): make the samples firing interval not depend on the sample |
| 376 // taking duration. |
| 191 base::PlatformThread::Sleep( | 377 base::PlatformThread::Sleep( |
| 192 base::TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds)); | 378 base::TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds)); |
| 193 } | 379 } |
| 380 RestoreSignalHandler(); |
| 194 StopSamplers(); | 381 StopSamplers(); |
| 195 RemoveSamplers(); | 382 RemoveSamplers(); |
| 196 } | 383 } |
| 197 | 384 |
| 198 void V8SamplingThread::Sample() { | 385 void V8SamplingThread::Sample() { |
| 199 for (Sampler* sampler : samplers_) { | 386 for (Sampler* sampler : samplers_) { |
| 200 sampler->Sample(); | 387 sampler->Sample(); |
| 201 } | 388 } |
| 202 } | 389 } |
| 203 | 390 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 216 sampler->Start(); | 403 sampler->Start(); |
| 217 } | 404 } |
| 218 } | 405 } |
| 219 | 406 |
| 220 void V8SamplingThread::StopSamplers() { | 407 void V8SamplingThread::StopSamplers() { |
| 221 for (Sampler* sampler : samplers_) { | 408 for (Sampler* sampler : samplers_) { |
| 222 sampler->Stop(); | 409 sampler->Stop(); |
| 223 } | 410 } |
| 224 } | 411 } |
| 225 | 412 |
| 413 // static |
| 414 void V8SamplingThread::InstallSignalHandler() { |
| 415 #ifdef USE_SIGNALS |
| 416 // There must be the only one! |
| 417 DCHECK(!signal_handler_installed_); |
| 418 struct sigaction sa; |
| 419 sa.sa_sigaction = &HandleProfilerSignal; |
| 420 sigemptyset(&sa.sa_mask); |
| 421 sa.sa_flags = SA_RESTART | SA_SIGINFO; |
| 422 signal_handler_installed_ = |
| 423 (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0); |
| 424 #endif |
| 425 } |
| 426 |
| 427 // static |
| 428 void V8SamplingThread::RestoreSignalHandler() { |
| 429 #ifdef USE_SIGNALS |
| 430 if (!signal_handler_installed_) |
| 431 return; |
| 432 sigaction(SIGPROF, &old_signal_handler_, 0); |
| 433 signal_handler_installed_ = false; |
| 434 #endif |
| 435 } |
| 436 |
| 437 #ifdef USE_SIGNALS |
| 438 // static |
| 439 void V8SamplingThread::HandleProfilerSignal(int signal, |
| 440 siginfo_t* info, |
| 441 void* context) { |
| 442 if (signal != SIGPROF) |
| 443 return; |
| 444 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); |
| 445 mcontext_t& mcontext = ucontext->uc_mcontext; |
| 446 v8::RegisterState state; |
| 447 |
| 448 #if defined(OS_ANDROID) |
| 449 // TODO(alph): Add support for Android |
| 450 ALLOW_UNUSED_LOCAL(mcontext); |
| 451 |
| 452 #elif defined(OS_MACOSX) |
| 453 #if ARCH_CPU_64_BITS |
| 454 state.pc = reinterpret_cast<void*>(mcontext->__ss.__rip); |
| 455 state.sp = reinterpret_cast<void*>(mcontext->__ss.__rsp); |
| 456 state.fp = reinterpret_cast<void*>(mcontext->__ss.__rbp); |
| 457 #elif ARCH_CPU_32_BITS |
| 458 state.pc = reinterpret_cast<void*>(mcontext->__ss.__eip); |
| 459 state.sp = reinterpret_cast<void*>(mcontext->__ss.__esp); |
| 460 state.fp = reinterpret_cast<void*>(mcontext->__ss.__ebp); |
| 461 #endif // ARCH_CPU_32_BITS |
| 462 |
| 463 #else |
| 464 #if ARCH_CPU_64_BITS |
| 465 state.pc = reinterpret_cast<void*>(mcontext.gregs[REG_RIP]); |
| 466 state.sp = reinterpret_cast<void*>(mcontext.gregs[REG_RSP]); |
| 467 state.fp = reinterpret_cast<void*>(mcontext.gregs[REG_RBP]); |
| 468 #elif ARCH_CPU_32_BITS |
| 469 state.pc = reinterpret_cast<void*>(mcontext.gregs[REG_EIP]); |
| 470 state.sp = reinterpret_cast<void*>(mcontext.gregs[REG_ESP]); |
| 471 state.fp = reinterpret_cast<void*>(mcontext.gregs[REG_EBP]); |
| 472 #endif // ARCH_CPU_32_BITS |
| 473 #endif |
| 474 Sampler::GetInstance()->DoSample(state); |
| 475 } |
| 476 #endif |
| 477 |
| 226 void V8SamplingThread::Start() { | 478 void V8SamplingThread::Start() { |
| 227 if (!base::PlatformThread::Create(0, this, &sampling_thread_handle_)) { | 479 if (!base::PlatformThread::Create(0, this, &sampling_thread_handle_)) { |
| 228 DCHECK(false) << "failed to create thread"; | 480 DCHECK(false) << "failed to create sampling thread"; |
| 229 } | 481 } |
| 230 } | 482 } |
| 231 | 483 |
| 232 void V8SamplingThread::Stop() { | 484 void V8SamplingThread::Stop() { |
| 233 cancellation_flag_.Set(); | 485 cancellation_flag_.Set(); |
| 234 base::PlatformThread::Join(sampling_thread_handle_); | 486 base::PlatformThread::Join(sampling_thread_handle_); |
| 235 } | 487 } |
| 236 | 488 |
| 237 V8SamplingProfiler::V8SamplingProfiler(bool underTest) | 489 V8SamplingProfiler::V8SamplingProfiler(bool underTest) |
| 238 : sampling_thread_(nullptr), | 490 : sampling_thread_(nullptr), |
| 239 render_thread_sampler_(Sampler::CreateForCurrentThread()) { | 491 render_thread_sampler_(Sampler::CreateForCurrentThread()), |
| 492 message_loop_proxy_(base::MessageLoopProxy::current()) { |
| 240 DCHECK(underTest || RenderThreadImpl::current()); | 493 DCHECK(underTest || RenderThreadImpl::current()); |
| 241 // Force the "v8.cpu_profile" category to show up in the trace viewer. | 494 // Force the "v8.cpu_profile" category to show up in the trace viewer. |
| 242 TraceLog::GetCategoryGroupEnabled( | 495 TraceLog::GetCategoryGroupEnabled( |
| 243 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile")); | 496 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile")); |
| 244 TraceLog::GetInstance()->AddEnabledStateObserver(this); | 497 TraceLog::GetInstance()->AddEnabledStateObserver(this); |
| 245 } | 498 } |
| 246 | 499 |
| 247 V8SamplingProfiler::~V8SamplingProfiler() { | 500 V8SamplingProfiler::~V8SamplingProfiler() { |
| 248 TraceLog::GetInstance()->RemoveEnabledStateObserver(this); | 501 TraceLog::GetInstance()->RemoveEnabledStateObserver(this); |
| 249 DCHECK(!sampling_thread_.get()); | 502 DCHECK(!sampling_thread_.get()); |
| 250 } | 503 } |
| 251 | 504 |
| 505 void V8SamplingProfiler::StartSamplingThread() { |
| 506 DCHECK(!sampling_thread_.get()); |
| 507 sampling_thread_.reset(new V8SamplingThread( |
| 508 render_thread_sampler_.get(), waitable_event_for_testing_.get())); |
| 509 sampling_thread_->Start(); |
| 510 } |
| 511 |
| 252 void V8SamplingProfiler::OnTraceLogEnabled() { | 512 void V8SamplingProfiler::OnTraceLogEnabled() { |
| 253 bool enabled; | 513 bool enabled; |
| 254 TRACE_EVENT_CATEGORY_GROUP_ENABLED( | 514 TRACE_EVENT_CATEGORY_GROUP_ENABLED( |
| 255 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), &enabled); | 515 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), &enabled); |
| 256 if (!enabled) | 516 if (!enabled) |
| 257 return; | 517 return; |
| 258 | 518 |
| 259 // Do not enable sampling profiler in continuous mode, as losing | 519 // Do not enable sampling profiler in continuous mode, as losing |
| 260 // Jit code events may not be afforded. | 520 // Jit code events may not be afforded. |
| 521 // TODO(alph): add support of infinite recording of meta trace events. |
| 261 base::trace_event::TraceRecordMode record_mode = | 522 base::trace_event::TraceRecordMode record_mode = |
| 262 TraceLog::GetInstance()->GetCurrentTraceOptions().record_mode; | 523 TraceLog::GetInstance()->GetCurrentTraceOptions().record_mode; |
| 263 if (record_mode == base::trace_event::TraceRecordMode::RECORD_CONTINUOUSLY) | 524 if (record_mode == base::trace_event::TraceRecordMode::RECORD_CONTINUOUSLY) |
| 264 return; | 525 return; |
| 265 | 526 |
| 266 DCHECK(!sampling_thread_.get()); | 527 message_loop_proxy_->PostTask( |
| 267 sampling_thread_.reset(new V8SamplingThread( | 528 FROM_HERE, base::Bind(&V8SamplingProfiler::StartSamplingThread, |
| 268 render_thread_sampler_.get(), waitable_event_for_testing_.get())); | 529 base::Unretained(this))); |
| 269 sampling_thread_->Start(); | |
| 270 } | 530 } |
| 271 | 531 |
| 272 void V8SamplingProfiler::OnTraceLogDisabled() { | 532 void V8SamplingProfiler::OnTraceLogDisabled() { |
| 273 if (!sampling_thread_.get()) | 533 if (!sampling_thread_.get()) |
| 274 return; | 534 return; |
| 275 sampling_thread_->Stop(); | 535 sampling_thread_->Stop(); |
| 276 sampling_thread_.reset(); | 536 sampling_thread_.reset(); |
| 277 } | 537 } |
| 278 | 538 |
| 279 void V8SamplingProfiler::EnableSamplingEventForTesting() { | 539 void V8SamplingProfiler::EnableSamplingEventForTesting() { |
| 280 waitable_event_for_testing_.reset(new base::WaitableEvent(false, false)); | 540 waitable_event_for_testing_.reset(new base::WaitableEvent(false, false)); |
| 281 } | 541 } |
| 282 | 542 |
| 283 void V8SamplingProfiler::WaitSamplingEventForTesting() { | 543 void V8SamplingProfiler::WaitSamplingEventForTesting() { |
| 284 waitable_event_for_testing_->Wait(); | 544 waitable_event_for_testing_->Wait(); |
| 285 } | 545 } |
| 286 | 546 |
| 287 } // namespace blink | 547 } // namespace blink |
| OLD | NEW |