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 #if defined(OS_POSIX) | |
| 8 #include <signal.h> | |
|
yurys
2015/03/18 12:49:03
You'll likely need <ucontext.h> and <mach/mach.h>
alph
2015/03/18 13:37:49
It compiles so far. I'll add these includes when i
| |
| 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" |
| 13 #include "content/renderer/render_thread_impl.h" | 18 #include "content/renderer/render_thread_impl.h" |
| 14 #include "v8/include/v8.h" | 19 #include "v8/include/v8.h" |
| 15 | 20 |
| 21 using base::trace_event::ConvertableToTraceFormat; | |
| 16 using base::trace_event::TraceLog; | 22 using base::trace_event::TraceLog; |
| 17 using base::trace_event::TracedValue; | 23 using base::trace_event::TracedValue; |
| 18 using v8::Isolate; | 24 using v8::Isolate; |
| 19 | 25 |
| 20 namespace content { | 26 namespace content { |
| 21 | 27 |
| 22 namespace { | 28 namespace { |
| 23 | 29 |
| 24 std::string PtrToString(const void* value) { | 30 std::string PtrToString(const void* value) { |
| 25 char buffer[20]; | 31 return base::StringPrintf( |
| 26 base::snprintf(buffer, sizeof(buffer), "0x%" PRIx64, | 32 "0x%" PRIx64, static_cast<uint64>(reinterpret_cast<intptr_t>(value))); |
| 27 static_cast<uint64>(reinterpret_cast<intptr_t>(value))); | 33 } |
| 28 return buffer; | 34 |
| 35 // Lock-free cache-friendly sampling circular queue for large | |
| 36 // records. Intended for fast transfer of large records between a | |
| 37 // single producer and a single consumer. If the queue is full, | |
| 38 // StartEnqueue will return nullptr. The queue is designed with | |
| 39 // a goal in mind to evade cache lines thrashing by preventing | |
| 40 // simultaneous reads and writes to adjanced memory locations. | |
| 41 template <typename T, unsigned Length> | |
| 42 class SamplingCircularQueue { | |
|
yurys
2015/03/18 12:49:04
Let's move it in a separate file.
alph
2015/03/18 13:37:49
Done.
| |
| 43 public: | |
| 44 // Executed on the application thread. | |
| 45 SamplingCircularQueue(); | |
| 46 ~SamplingCircularQueue(); | |
| 47 | |
| 48 // StartEnqueue returns a pointer to a memory location for storing the next | |
| 49 // record or nullptr if all entries are full at the moment. | |
| 50 T* StartEnqueue(); | |
| 51 // Notifies the queue that the producer has complete writing data into the | |
| 52 // memory returned by StartEnqueue and it can be passed to the consumer. | |
| 53 void FinishEnqueue(); | |
| 54 | |
| 55 // Executed on the consumer (analyzer) thread. | |
| 56 // Retrieves, but does not remove, the head of this queue, returning nullptr | |
| 57 // if this queue is empty. After the record had been read by a consumer, | |
| 58 // Remove must be called. | |
| 59 T* Peek(); | |
| 60 void Remove(); | |
| 61 | |
| 62 private: | |
| 63 // Reserved values for the entry marker. | |
| 64 enum { | |
| 65 kEmpty, // Marks clean (processed) entries. | |
| 66 kFull // Marks entries already filled by the producer but not yet | |
| 67 // completely processed by the consumer. | |
| 68 }; | |
| 69 | |
| 70 struct /*V8_ALIGNED(PROCESSOR_CACHE_LINE_SIZE)*/ Entry { | |
| 71 Entry() : marker(kEmpty) {} | |
| 72 T record; | |
| 73 base::subtle::Atomic32 marker; | |
| 74 }; | |
| 75 | |
| 76 Entry* Next(Entry* entry); | |
| 77 | |
| 78 Entry buffer_[Length]; | |
| 79 /*V8_ALIGNED(PROCESSOR_CACHE_LINE_SIZE)*/ Entry* enqueue_pos_; | |
| 80 /*V8_ALIGNED(PROCESSOR_CACHE_LINE_SIZE)*/ Entry* dequeue_pos_; | |
| 81 | |
| 82 DISALLOW_COPY_AND_ASSIGN(SamplingCircularQueue); | |
| 83 }; | |
| 84 | |
| 85 template <typename T, unsigned L> | |
| 86 SamplingCircularQueue<T, L>::SamplingCircularQueue() | |
| 87 : enqueue_pos_(buffer_), dequeue_pos_(buffer_) { | |
| 88 } | |
| 89 | |
| 90 template <typename T, unsigned L> | |
| 91 SamplingCircularQueue<T, L>::~SamplingCircularQueue() { | |
| 92 } | |
| 93 | |
| 94 template <typename T, unsigned L> | |
| 95 T* SamplingCircularQueue<T, L>::Peek() { | |
| 96 base::subtle::MemoryBarrier(); | |
| 97 if (base::subtle::Acquire_Load(&dequeue_pos_->marker) == kFull) { | |
| 98 return &dequeue_pos_->record; | |
| 99 } | |
| 100 return nullptr; | |
| 101 } | |
| 102 | |
| 103 template <typename T, unsigned L> | |
| 104 void SamplingCircularQueue<T, L>::Remove() { | |
| 105 base::subtle::Release_Store(&dequeue_pos_->marker, kEmpty); | |
| 106 dequeue_pos_ = Next(dequeue_pos_); | |
| 107 } | |
| 108 | |
| 109 template <typename T, unsigned L> | |
| 110 T* SamplingCircularQueue<T, L>::StartEnqueue() { | |
| 111 base::subtle::MemoryBarrier(); | |
| 112 if (base::subtle::Acquire_Load(&enqueue_pos_->marker) == kEmpty) { | |
| 113 return &enqueue_pos_->record; | |
| 114 } | |
| 115 return nullptr; | |
| 116 } | |
| 117 | |
| 118 template <typename T, unsigned L> | |
| 119 void SamplingCircularQueue<T, L>::FinishEnqueue() { | |
| 120 base::subtle::Release_Store(&enqueue_pos_->marker, kFull); | |
| 121 enqueue_pos_ = Next(enqueue_pos_); | |
| 122 } | |
| 123 | |
| 124 template <typename T, unsigned L> | |
| 125 typename SamplingCircularQueue<T, L>::Entry* SamplingCircularQueue<T, L>::Next( | |
| 126 Entry* entry) { | |
| 127 Entry* next = entry + 1; | |
| 128 if (next == &buffer_[L]) | |
| 129 return buffer_; | |
| 130 return next; | |
| 131 } | |
| 132 | |
| 133 class SampleRecord { | |
| 134 public: | |
| 135 static const int kMaxFramesCountLog2 = 8; | |
| 136 static const unsigned kMaxFramesCount = (1u << kMaxFramesCountLog2) - 1; | |
| 137 | |
| 138 SampleRecord() {} | |
| 139 | |
| 140 base::TimeTicks timestamp() const { return timestamp_; } | |
| 141 void Collect(v8::Isolate* isolate, | |
| 142 base::TimeTicks timestamp, | |
| 143 const v8::RegisterState& state); | |
| 144 scoped_refptr<ConvertableToTraceFormat> ToTraceFormat() const; | |
| 145 | |
| 146 private: | |
| 147 base::TimeTicks timestamp_; | |
| 148 unsigned vm_state_ : 4; | |
| 149 unsigned frames_count_ : kMaxFramesCountLog2; | |
| 150 const void* frames_[kMaxFramesCount]; | |
| 151 | |
| 152 DISALLOW_COPY_AND_ASSIGN(SampleRecord); | |
| 153 }; | |
| 154 | |
| 155 void SampleRecord::Collect(v8::Isolate* isolate, | |
| 156 base::TimeTicks timestamp, | |
| 157 const v8::RegisterState& state) { | |
| 158 v8::SampleInfo sample_info; | |
| 159 isolate->GetStackSample(state, (void**)frames_, kMaxFramesCount, | |
| 160 &sample_info); | |
| 161 timestamp_ = timestamp; | |
| 162 frames_count_ = sample_info.frames_count; | |
| 163 vm_state_ = sample_info.vm_state; | |
| 164 } | |
| 165 | |
| 166 scoped_refptr<ConvertableToTraceFormat> SampleRecord::ToTraceFormat() const { | |
| 167 scoped_refptr<TracedValue> data(new TracedValue()); | |
| 168 const char* vm_state = nullptr; | |
| 169 switch (vm_state_) { | |
| 170 case v8::StateTag::JS: | |
| 171 vm_state = "js"; | |
| 172 break; | |
| 173 case v8::StateTag::GC: | |
| 174 vm_state = "gc"; | |
| 175 break; | |
| 176 case v8::StateTag::COMPILER: | |
| 177 vm_state = "compiler"; | |
| 178 break; | |
| 179 case v8::StateTag::OTHER: | |
| 180 vm_state = "other"; | |
| 181 break; | |
| 182 case v8::StateTag::EXTERNAL: | |
| 183 vm_state = "external"; | |
| 184 break; | |
| 185 case v8::StateTag::IDLE: | |
| 186 vm_state = "idle"; | |
| 187 break; | |
| 188 default: | |
| 189 NOTREACHED(); | |
| 190 } | |
| 191 data->SetString("vm_state", vm_state); | |
| 192 data->BeginArray("stack"); | |
| 193 for (unsigned i = 0; i < frames_count_; ++i) { | |
| 194 data->AppendString(PtrToString(frames_[i])); | |
| 195 } | |
| 196 data->EndArray(); | |
| 197 return data; | |
| 29 } | 198 } |
| 30 | 199 |
| 31 } // namespace | 200 } // namespace |
| 32 | 201 |
| 33 // The class implements a sampler responsible for sampling a single thread. | 202 // The class implements a sampler responsible for sampling a single thread. |
| 34 class Sampler { | 203 class Sampler { |
| 35 public: | 204 public: |
| 36 explicit Sampler(Isolate* isolate) : isolate_(isolate) { DCHECK(isolate_); } | 205 Sampler(base::PlatformThreadHandle thread_handle, |
| 206 base::PlatformThreadId thread_id, | |
| 207 Isolate* isolate); | |
| 208 ~Sampler(); | |
| 37 | 209 |
| 38 static scoped_ptr<Sampler> CreateForCurrentThread(); | 210 static scoped_ptr<Sampler> CreateForCurrentThread(); |
| 211 static Sampler* GetInstance() { return tls_instance_.Pointer()->Get(); } | |
| 39 | 212 |
| 40 // These methods are called from the sampling thread. | 213 // These methods are called from the sampling thread. |
| 41 void Start(); | 214 void Start(); |
| 42 void Stop(); | 215 void Stop(); |
| 43 void Sample(); | 216 void Sample(); |
| 44 | 217 |
| 218 void DoSample(const v8::RegisterState& state); | |
| 219 | |
| 220 bool EventsCollectedForTest() const { | |
| 221 return base::subtle::NoBarrier_Load(&code_added_events_count_) != 0 || | |
| 222 base::subtle::NoBarrier_Load(&samples_count_) != 0; | |
| 223 } | |
| 224 | |
| 45 private: | 225 private: |
| 46 static void InstallJitCodeEventHandler(Isolate* isolate, void* data); | 226 static void InstallJitCodeEventHandler(Isolate* isolate, void* data); |
| 47 static void HandleJitCodeEvent(const v8::JitCodeEvent* event); | 227 static void HandleJitCodeEvent(const v8::JitCodeEvent* event); |
| 48 static scoped_refptr<base::trace_event::ConvertableToTraceFormat> | 228 static scoped_refptr<ConvertableToTraceFormat> JitCodeEventToTraceFormat( |
| 49 JitCodeEventToTraceFormat(const v8::JitCodeEvent* event); | 229 const v8::JitCodeEvent* event); |
| 50 | 230 |
| 231 void InjectPendingEvents(); | |
| 232 | |
| 233 static const unsigned kNumberOfSamples = 10; | |
| 234 typedef SamplingCircularQueue<SampleRecord, kNumberOfSamples> SamplingQueue; | |
| 235 | |
| 236 base::PlatformThreadId thread_id_; | |
| 237 base::PlatformThreadHandle thread_handle_; | |
| 51 Isolate* isolate_; | 238 Isolate* isolate_; |
| 239 scoped_ptr<SamplingQueue> samples_data_; | |
| 240 base::subtle::Atomic32 code_added_events_count_; | |
| 241 base::subtle::Atomic32 samples_count_; | |
| 242 | |
| 243 static base::LazyInstance<base::ThreadLocalPointer<Sampler>>::Leaky | |
| 244 tls_instance_; | |
| 52 }; | 245 }; |
| 53 | 246 |
| 247 base::LazyInstance<base::ThreadLocalPointer<Sampler>>::Leaky | |
| 248 Sampler::tls_instance_ = LAZY_INSTANCE_INITIALIZER; | |
| 249 | |
| 250 Sampler::Sampler(base::PlatformThreadHandle thread_handle, | |
| 251 base::PlatformThreadId thread_id, | |
| 252 Isolate* isolate) | |
| 253 : thread_id_(thread_id), | |
| 254 thread_handle_(thread_handle), | |
| 255 isolate_(isolate), | |
| 256 code_added_events_count_(0), | |
| 257 samples_count_(0) { | |
| 258 DCHECK(isolate_); | |
| 259 DCHECK(!GetInstance()); | |
| 260 tls_instance_.Pointer()->Set(this); | |
| 261 } | |
| 262 | |
| 263 Sampler::~Sampler() { | |
| 264 DCHECK(GetInstance()); | |
| 265 tls_instance_.Pointer()->Set(nullptr); | |
|
yurys
2015/03/18 12:49:03
It is not safe to access TLS storage in signal han
alph
2015/03/18 13:37:49
It should be safe provided that:
- the tls variabl
| |
| 266 } | |
| 267 | |
| 54 // static | 268 // static |
| 55 scoped_ptr<Sampler> Sampler::CreateForCurrentThread() { | 269 scoped_ptr<Sampler> Sampler::CreateForCurrentThread() { |
| 56 return scoped_ptr<Sampler>(new Sampler(Isolate::GetCurrent())); | 270 return scoped_ptr<Sampler>(new Sampler(base::PlatformThread::CurrentHandle(), |
| 271 base::PlatformThread::CurrentId(), | |
| 272 Isolate::GetCurrent())); | |
| 57 } | 273 } |
| 58 | 274 |
| 59 void Sampler::Start() { | 275 void Sampler::Start() { |
| 276 samples_data_.reset(new SamplingQueue()); | |
| 60 v8::JitCodeEventHandler handler = &HandleJitCodeEvent; | 277 v8::JitCodeEventHandler handler = &HandleJitCodeEvent; |
| 61 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, | 278 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, |
| 62 reinterpret_cast<void*>(handler)); | 279 reinterpret_cast<void*>(handler)); |
| 63 } | 280 } |
| 64 | 281 |
| 65 void Sampler::Stop() { | 282 void Sampler::Stop() { |
| 66 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, nullptr); | 283 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, nullptr); |
| 284 samples_data_.reset(); | |
| 67 } | 285 } |
| 68 | 286 |
| 69 void Sampler::Sample() { | 287 void Sampler::Sample() { |
| 288 #if defined(USE_SIGNALS) | |
| 289 int error = pthread_kill(thread_handle_.platform_handle(), SIGPROF); | |
| 290 if (error) { | |
| 291 LOG(ERROR) << "pthread_kill failed with error " << error << " " | |
| 292 << strerror(error); | |
| 293 } | |
| 294 InjectPendingEvents(); | |
| 295 #endif | |
| 296 } | |
| 297 | |
| 298 void Sampler::DoSample(const v8::RegisterState& state) { | |
| 299 // Called in the sampled thread signal handler. | |
| 300 // Because of that it is not allowed to do any memory allocation here. | |
| 301 base::TimeTicks timestamp = base::TimeTicks::NowFromSystemTraceTime(); | |
| 302 SampleRecord* record = samples_data_->StartEnqueue(); | |
| 303 if (!record) { | |
| 304 LOG(ERROR) << "No space left in the sampling buffer"; | |
|
yurys
2015/03/18 12:49:04
I'm pretty much sure it will allocate.
alph
2015/03/18 13:37:49
Acknowledged.
| |
| 305 return; | |
| 306 } | |
| 307 record->Collect(isolate_, timestamp, state); | |
| 308 samples_data_->FinishEnqueue(); | |
| 309 base::subtle::NoBarrier_AtomicIncrement(&samples_count_, 1); | |
| 310 } | |
| 311 | |
| 312 void Sampler::InjectPendingEvents() { | |
| 313 SampleRecord* record = samples_data_->Peek(); | |
| 314 while (record) { | |
| 315 TRACE_EVENT_SAMPLE_WITH_TID_AND_TIMESTAMP1( | |
| 316 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), "V8Sample", thread_id_, | |
| 317 (record->timestamp() - base::TimeTicks()).InMicroseconds(), "data", | |
| 318 record->ToTraceFormat()); | |
| 319 samples_data_->Remove(); | |
| 320 record = samples_data_->Peek(); | |
| 321 } | |
| 70 } | 322 } |
| 71 | 323 |
| 72 // static | 324 // static |
| 73 void Sampler::InstallJitCodeEventHandler(Isolate* isolate, void* data) { | 325 void Sampler::InstallJitCodeEventHandler(Isolate* isolate, void* data) { |
| 74 // Called on the sampled V8 thread. | 326 // Called on the sampled V8 thread. |
| 75 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), | 327 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), |
| 76 "Sampler::InstallJitCodeEventHandler"); | 328 "Sampler::InstallJitCodeEventHandler"); |
| 77 v8::JitCodeEventHandler handler = | 329 v8::JitCodeEventHandler handler = |
| 78 reinterpret_cast<v8::JitCodeEventHandler>(data); | 330 reinterpret_cast<v8::JitCodeEventHandler>(data); |
| 79 isolate->SetJitCodeEventHandler( | 331 isolate->SetJitCodeEventHandler( |
| 80 v8::JitCodeEventOptions::kJitCodeEventEnumExisting, handler); | 332 v8::JitCodeEventOptions::kJitCodeEventEnumExisting, handler); |
| 81 } | 333 } |
| 82 | 334 |
| 83 // static | 335 // static |
| 84 void Sampler::HandleJitCodeEvent(const v8::JitCodeEvent* event) { | 336 void Sampler::HandleJitCodeEvent(const v8::JitCodeEvent* event) { |
| 85 // Called on the sampled V8 thread. | 337 // Called on the sampled V8 thread. |
| 338 Sampler* sampler = GetInstance(); | |
| 339 // The sampler may have already been destroyed. | |
| 340 // That's fine, we're not interested in these events anymore. | |
| 341 if (!sampler) | |
| 342 return; | |
| 86 switch (event->type) { | 343 switch (event->type) { |
| 87 case v8::JitCodeEvent::CODE_ADDED: | 344 case v8::JitCodeEvent::CODE_ADDED: |
| 88 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), | 345 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), |
| 89 "JitCodeAdded", TRACE_EVENT_SCOPE_THREAD, "data", | 346 "JitCodeAdded", TRACE_EVENT_SCOPE_THREAD, "data", |
| 90 JitCodeEventToTraceFormat(event)); | 347 JitCodeEventToTraceFormat(event)); |
| 348 base::subtle::NoBarrier_AtomicIncrement( | |
| 349 &sampler->code_added_events_count_, 1); | |
| 91 break; | 350 break; |
| 92 | 351 |
| 93 case v8::JitCodeEvent::CODE_MOVED: | 352 case v8::JitCodeEvent::CODE_MOVED: |
| 94 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), | 353 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), |
| 95 "JitCodeMoved", TRACE_EVENT_SCOPE_THREAD, "data", | 354 "JitCodeMoved", TRACE_EVENT_SCOPE_THREAD, "data", |
| 96 JitCodeEventToTraceFormat(event)); | 355 JitCodeEventToTraceFormat(event)); |
| 97 break; | 356 break; |
| 98 | 357 |
| 99 case v8::JitCodeEvent::CODE_REMOVED: | 358 case v8::JitCodeEvent::CODE_REMOVED: |
| 100 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), | 359 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), |
| 101 "JitCodeRemoved", TRACE_EVENT_SCOPE_THREAD, "data", | 360 "JitCodeRemoved", TRACE_EVENT_SCOPE_THREAD, "data", |
| 102 JitCodeEventToTraceFormat(event)); | 361 JitCodeEventToTraceFormat(event)); |
| 103 break; | 362 break; |
| 104 | 363 |
| 105 case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: | 364 case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: |
| 106 case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: | 365 case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: |
| 107 case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: | 366 case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: |
| 108 break; | 367 break; |
| 109 } | 368 } |
| 110 } | 369 } |
| 111 | 370 |
| 112 // static | 371 // static |
| 113 scoped_refptr<base::trace_event::ConvertableToTraceFormat> | 372 scoped_refptr<ConvertableToTraceFormat> Sampler::JitCodeEventToTraceFormat( |
| 114 Sampler::JitCodeEventToTraceFormat(const v8::JitCodeEvent* event) { | 373 const v8::JitCodeEvent* event) { |
| 115 // Called on the sampled thread. | |
| 116 switch (event->type) { | 374 switch (event->type) { |
| 117 case v8::JitCodeEvent::CODE_ADDED: { | 375 case v8::JitCodeEvent::CODE_ADDED: { |
| 118 scoped_refptr<TracedValue> data(new TracedValue()); | 376 scoped_refptr<TracedValue> data(new TracedValue()); |
| 119 data->SetString("code_start", PtrToString(event->code_start)); | 377 data->SetString("code_start", PtrToString(event->code_start)); |
| 120 data->SetInteger("code_len", static_cast<unsigned>(event->code_len)); | 378 data->SetInteger("code_len", static_cast<unsigned>(event->code_len)); |
| 121 data->SetString("name", std::string(event->name.str, event->name.len)); | 379 data->SetString("name", std::string(event->name.str, event->name.len)); |
| 122 return data; | 380 return data; |
| 123 } | 381 } |
| 124 | 382 |
| 125 case v8::JitCodeEvent::CODE_MOVED: { | 383 case v8::JitCodeEvent::CODE_MOVED: { |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 154 | 412 |
| 155 void Start(); | 413 void Start(); |
| 156 void Stop(); | 414 void Stop(); |
| 157 | 415 |
| 158 private: | 416 private: |
| 159 void Sample(); | 417 void Sample(); |
| 160 void InstallSamplers(); | 418 void InstallSamplers(); |
| 161 void RemoveSamplers(); | 419 void RemoveSamplers(); |
| 162 void StartSamplers(); | 420 void StartSamplers(); |
| 163 void StopSamplers(); | 421 void StopSamplers(); |
| 422 | |
| 423 static void InstallSignalHandler(); | |
| 424 static void RestoreSignalHandler(); | |
| 425 #ifdef USE_SIGNALS | |
| 426 static void HandleProfilerSignal(int signal, siginfo_t* info, void* context); | |
| 427 #endif | |
| 428 | |
| 164 static void HandleJitCodeEvent(const v8::JitCodeEvent* event); | 429 static void HandleJitCodeEvent(const v8::JitCodeEvent* event); |
| 165 | 430 |
| 166 Sampler* render_thread_sampler_; | 431 Sampler* render_thread_sampler_; |
| 167 base::CancellationFlag cancellation_flag_; | 432 base::CancellationFlag cancellation_flag_; |
| 168 base::WaitableEvent* waitable_event_for_testing_; | 433 base::WaitableEvent* waitable_event_for_testing_; |
| 169 base::PlatformThreadHandle sampling_thread_handle_; | 434 base::PlatformThreadHandle sampling_thread_handle_; |
| 170 std::vector<Sampler*> samplers_; | 435 std::vector<Sampler*> samplers_; |
| 171 | 436 |
| 437 static bool signal_handler_installed_; | |
|
yurys
2015/03/18 12:49:03
Should be behind #ifdef
alph
2015/03/18 13:37:49
Done.
| |
| 438 #ifdef USE_SIGNALS | |
| 439 static struct sigaction old_signal_handler_; | |
| 440 #endif | |
| 441 | |
| 172 DISALLOW_COPY_AND_ASSIGN(V8SamplingThread); | 442 DISALLOW_COPY_AND_ASSIGN(V8SamplingThread); |
| 173 }; | 443 }; |
| 174 | 444 |
| 445 bool V8SamplingThread::signal_handler_installed_; | |
| 446 #ifdef USE_SIGNALS | |
| 447 struct sigaction V8SamplingThread::old_signal_handler_; | |
| 448 #endif | |
| 449 | |
| 175 V8SamplingThread::V8SamplingThread(Sampler* render_thread_sampler, | 450 V8SamplingThread::V8SamplingThread(Sampler* render_thread_sampler, |
| 176 base::WaitableEvent* event) | 451 base::WaitableEvent* event) |
| 177 : render_thread_sampler_(render_thread_sampler), | 452 : render_thread_sampler_(render_thread_sampler), |
| 178 waitable_event_for_testing_(event) { | 453 waitable_event_for_testing_(event) { |
| 179 } | 454 } |
| 180 | 455 |
| 181 void V8SamplingThread::ThreadMain() { | 456 void V8SamplingThread::ThreadMain() { |
| 182 base::PlatformThread::SetName("V8SamplingProfilerThread"); | 457 base::PlatformThread::SetName("V8SamplingProfilerThread"); |
| 183 InstallSamplers(); | 458 InstallSamplers(); |
| 184 StartSamplers(); | 459 StartSamplers(); |
| 460 InstallSignalHandler(); | |
| 185 const int kSamplingFrequencyMicroseconds = 1000; | 461 const int kSamplingFrequencyMicroseconds = 1000; |
| 186 while (!cancellation_flag_.IsSet()) { | 462 while (!cancellation_flag_.IsSet()) { |
| 187 Sample(); | 463 Sample(); |
| 188 if (waitable_event_for_testing_) { | 464 if (waitable_event_for_testing_ && |
| 465 render_thread_sampler_->EventsCollectedForTest()) { | |
| 189 waitable_event_for_testing_->Signal(); | 466 waitable_event_for_testing_->Signal(); |
| 190 } | 467 } |
| 468 // TODO(alph): make the samples firing interval not depend on the sample | |
| 469 // taking duration. | |
| 191 base::PlatformThread::Sleep( | 470 base::PlatformThread::Sleep( |
| 192 base::TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds)); | 471 base::TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds)); |
| 193 } | 472 } |
| 473 RestoreSignalHandler(); | |
| 194 StopSamplers(); | 474 StopSamplers(); |
| 195 RemoveSamplers(); | 475 RemoveSamplers(); |
| 196 } | 476 } |
| 197 | 477 |
| 198 void V8SamplingThread::Sample() { | 478 void V8SamplingThread::Sample() { |
| 199 for (Sampler* sampler : samplers_) { | 479 for (Sampler* sampler : samplers_) { |
| 200 sampler->Sample(); | 480 sampler->Sample(); |
| 201 } | 481 } |
| 202 } | 482 } |
| 203 | 483 |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 216 sampler->Start(); | 496 sampler->Start(); |
| 217 } | 497 } |
| 218 } | 498 } |
| 219 | 499 |
| 220 void V8SamplingThread::StopSamplers() { | 500 void V8SamplingThread::StopSamplers() { |
| 221 for (Sampler* sampler : samplers_) { | 501 for (Sampler* sampler : samplers_) { |
| 222 sampler->Stop(); | 502 sampler->Stop(); |
| 223 } | 503 } |
| 224 } | 504 } |
| 225 | 505 |
| 506 // static | |
| 507 void V8SamplingThread::InstallSignalHandler() { | |
| 508 #ifdef USE_SIGNALS | |
| 509 // There must be the only one! | |
| 510 DCHECK(!signal_handler_installed_); | |
| 511 struct sigaction sa; | |
| 512 sa.sa_sigaction = &HandleProfilerSignal; | |
| 513 sigemptyset(&sa.sa_mask); | |
| 514 sa.sa_flags = SA_RESTART | SA_SIGINFO; | |
| 515 signal_handler_installed_ = | |
| 516 (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0); | |
| 517 #endif | |
| 518 } | |
| 519 | |
| 520 // static | |
| 521 void V8SamplingThread::RestoreSignalHandler() { | |
| 522 #ifdef USE_SIGNALS | |
| 523 if (!signal_handler_installed_) | |
| 524 return; | |
| 525 sigaction(SIGPROF, &old_signal_handler_, 0); | |
| 526 signal_handler_installed_ = false; | |
| 527 #endif | |
| 528 } | |
| 529 | |
| 530 #ifdef USE_SIGNALS | |
| 531 // static | |
| 532 void V8SamplingThread::HandleProfilerSignal(int signal, | |
| 533 siginfo_t* info, | |
| 534 void* context) { | |
| 535 if (signal != SIGPROF) | |
| 536 return; | |
| 537 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); | |
| 538 mcontext_t& mcontext = ucontext->uc_mcontext; | |
| 539 v8::RegisterState state; | |
| 540 | |
| 541 #if defined(OS_ANDROID) | |
| 542 // TODO(alph): Add support for Android | |
| 543 ALLOW_UNUSED_LOCAL(mcontext); | |
| 544 | |
| 545 #elif defined(OS_MACOSX) | |
| 546 #if ARCH_CPU_64_BITS | |
| 547 state.pc = reinterpret_cast<void*>(mcontext->__ss.__rip); | |
| 548 state.sp = reinterpret_cast<void*>(mcontext->__ss.__rsp); | |
| 549 state.fp = reinterpret_cast<void*>(mcontext->__ss.__rbp); | |
| 550 #elif ARCH_CPU_32_BITS | |
| 551 state.pc = reinterpret_cast<void*>(mcontext->__ss.__eip); | |
| 552 state.sp = reinterpret_cast<void*>(mcontext->__ss.__esp); | |
| 553 state.fp = reinterpret_cast<void*>(mcontext->__ss.__ebp); | |
| 554 #endif // ARCH_CPU_32_BITS | |
| 555 | |
| 556 #else | |
| 557 #if ARCH_CPU_64_BITS | |
| 558 state.pc = reinterpret_cast<void*>(mcontext.gregs[REG_RIP]); | |
| 559 state.sp = reinterpret_cast<void*>(mcontext.gregs[REG_RSP]); | |
| 560 state.fp = reinterpret_cast<void*>(mcontext.gregs[REG_RBP]); | |
| 561 #elif ARCH_CPU_32_BITS | |
| 562 state.pc = reinterpret_cast<void*>(mcontext.gregs[REG_EIP]); | |
| 563 state.sp = reinterpret_cast<void*>(mcontext.gregs[REG_ESP]); | |
| 564 state.fp = reinterpret_cast<void*>(mcontext.gregs[REG_EBP]); | |
| 565 #endif // ARCH_CPU_32_BITS | |
| 566 #endif | |
| 567 Sampler::GetInstance()->DoSample(state); | |
| 568 } | |
| 569 #endif | |
| 570 | |
| 226 void V8SamplingThread::Start() { | 571 void V8SamplingThread::Start() { |
| 227 if (!base::PlatformThread::Create(0, this, &sampling_thread_handle_)) { | 572 if (!base::PlatformThread::Create(0, this, &sampling_thread_handle_)) { |
| 228 DCHECK(false) << "failed to create thread"; | 573 DCHECK(false) << "failed to create sampling thread"; |
| 229 } | 574 } |
| 230 } | 575 } |
| 231 | 576 |
| 232 void V8SamplingThread::Stop() { | 577 void V8SamplingThread::Stop() { |
| 233 cancellation_flag_.Set(); | 578 cancellation_flag_.Set(); |
| 234 base::PlatformThread::Join(sampling_thread_handle_); | 579 base::PlatformThread::Join(sampling_thread_handle_); |
| 235 } | 580 } |
| 236 | 581 |
| 237 V8SamplingProfiler::V8SamplingProfiler(bool underTest) | 582 V8SamplingProfiler::V8SamplingProfiler(bool underTest) |
| 238 : sampling_thread_(nullptr), | 583 : sampling_thread_(nullptr), |
| 239 render_thread_sampler_(Sampler::CreateForCurrentThread()) { | 584 render_thread_sampler_(Sampler::CreateForCurrentThread()), |
| 585 message_loop_proxy_(base::MessageLoopProxy::current()) { | |
| 240 DCHECK(underTest || RenderThreadImpl::current()); | 586 DCHECK(underTest || RenderThreadImpl::current()); |
| 241 // Force the "v8.cpu_profile" category to show up in the trace viewer. | 587 // Force the "v8.cpu_profile" category to show up in the trace viewer. |
| 242 TraceLog::GetCategoryGroupEnabled( | 588 TraceLog::GetCategoryGroupEnabled( |
| 243 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile")); | 589 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile")); |
| 244 TraceLog::GetInstance()->AddEnabledStateObserver(this); | 590 TraceLog::GetInstance()->AddEnabledStateObserver(this); |
| 245 } | 591 } |
| 246 | 592 |
| 247 V8SamplingProfiler::~V8SamplingProfiler() { | 593 V8SamplingProfiler::~V8SamplingProfiler() { |
| 248 TraceLog::GetInstance()->RemoveEnabledStateObserver(this); | 594 TraceLog::GetInstance()->RemoveEnabledStateObserver(this); |
| 249 DCHECK(!sampling_thread_.get()); | 595 DCHECK(!sampling_thread_.get()); |
| 250 } | 596 } |
| 251 | 597 |
| 598 void V8SamplingProfiler::StartSamplingThread() { | |
| 599 DCHECK(!sampling_thread_.get()); | |
| 600 sampling_thread_.reset(new V8SamplingThread( | |
| 601 render_thread_sampler_.get(), waitable_event_for_testing_.get())); | |
| 602 sampling_thread_->Start(); | |
| 603 } | |
| 604 | |
| 252 void V8SamplingProfiler::OnTraceLogEnabled() { | 605 void V8SamplingProfiler::OnTraceLogEnabled() { |
| 253 bool enabled; | 606 bool enabled; |
| 254 TRACE_EVENT_CATEGORY_GROUP_ENABLED( | 607 TRACE_EVENT_CATEGORY_GROUP_ENABLED( |
| 255 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), &enabled); | 608 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), &enabled); |
| 256 if (!enabled) | 609 if (!enabled) |
| 257 return; | 610 return; |
| 258 | 611 |
| 259 // Do not enable sampling profiler in continuous mode, as losing | 612 // Do not enable sampling profiler in continuous mode, as losing |
| 260 // Jit code events may not be afforded. | 613 // Jit code events may not be afforded. |
| 614 // TODO(alph): add support of infinite recording of meta trace events. | |
| 261 base::trace_event::TraceRecordMode record_mode = | 615 base::trace_event::TraceRecordMode record_mode = |
| 262 TraceLog::GetInstance()->GetCurrentTraceOptions().record_mode; | 616 TraceLog::GetInstance()->GetCurrentTraceOptions().record_mode; |
| 263 if (record_mode == base::trace_event::TraceRecordMode::RECORD_CONTINUOUSLY) | 617 if (record_mode == base::trace_event::TraceRecordMode::RECORD_CONTINUOUSLY) |
| 264 return; | 618 return; |
| 265 | 619 |
| 266 DCHECK(!sampling_thread_.get()); | 620 message_loop_proxy_->PostTask( |
| 267 sampling_thread_.reset(new V8SamplingThread( | 621 FROM_HERE, base::Bind(&V8SamplingProfiler::StartSamplingThread, |
| 268 render_thread_sampler_.get(), waitable_event_for_testing_.get())); | 622 base::Unretained(this))); |
| 269 sampling_thread_->Start(); | |
| 270 } | 623 } |
| 271 | 624 |
| 272 void V8SamplingProfiler::OnTraceLogDisabled() { | 625 void V8SamplingProfiler::OnTraceLogDisabled() { |
| 273 if (!sampling_thread_.get()) | 626 if (!sampling_thread_.get()) |
| 274 return; | 627 return; |
| 275 sampling_thread_->Stop(); | 628 sampling_thread_->Stop(); |
| 276 sampling_thread_.reset(); | 629 sampling_thread_.reset(); |
| 277 } | 630 } |
| 278 | 631 |
| 279 void V8SamplingProfiler::EnableSamplingEventForTesting() { | 632 void V8SamplingProfiler::EnableSamplingEventForTesting() { |
| 280 waitable_event_for_testing_.reset(new base::WaitableEvent(false, false)); | 633 waitable_event_for_testing_.reset(new base::WaitableEvent(false, false)); |
| 281 } | 634 } |
| 282 | 635 |
| 283 void V8SamplingProfiler::WaitSamplingEventForTesting() { | 636 void V8SamplingProfiler::WaitSamplingEventForTesting() { |
| 284 waitable_event_for_testing_->Wait(); | 637 waitable_event_for_testing_->Wait(); |
| 285 } | 638 } |
| 286 | 639 |
| 287 } // namespace blink | 640 } // namespace blink |
| OLD | NEW |