| 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) | 7 #if defined(OS_POSIX) |
| 8 #include <signal.h> | 8 #include <signal.h> |
| 9 #define USE_SIGNALS | 9 #define USE_SIGNALS |
| 10 #endif | 10 #endif |
| 11 | 11 |
| 12 #if defined(OS_WIN) |
| 13 #include <windows.h> |
| 14 #endif |
| 15 |
| 12 #include "base/format_macros.h" | 16 #include "base/format_macros.h" |
| 13 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
| 14 #include "base/synchronization/cancellation_flag.h" | 18 #include "base/synchronization/cancellation_flag.h" |
| 15 #include "base/threading/platform_thread.h" | 19 #include "base/threading/platform_thread.h" |
| 16 #include "base/trace_event/trace_event.h" | 20 #include "base/trace_event/trace_event.h" |
| 17 #include "base/trace_event/trace_event_argument.h" | 21 #include "base/trace_event/trace_event_argument.h" |
| 18 #if defined(OS_WIN) | |
| 19 #include "base/win/scoped_handle.h" | |
| 20 #endif | |
| 21 #include "content/renderer/devtools/lock_free_circular_queue.h" | 22 #include "content/renderer/devtools/lock_free_circular_queue.h" |
| 22 #include "content/renderer/render_thread_impl.h" | 23 #include "content/renderer/render_thread_impl.h" |
| 23 #include "v8/include/v8.h" | 24 #include "v8/include/v8.h" |
| 24 | 25 |
| 25 using base::trace_event::ConvertableToTraceFormat; | 26 using base::trace_event::ConvertableToTraceFormat; |
| 26 using base::trace_event::TraceLog; | 27 using base::trace_event::TraceLog; |
| 27 using base::trace_event::TracedValue; | 28 using base::trace_event::TracedValue; |
| 28 using v8::Isolate; | 29 using v8::Isolate; |
| 29 | 30 |
| 30 namespace content { | 31 namespace content { |
| 31 | 32 |
| 32 namespace { | 33 namespace { |
| 33 | 34 |
| 34 #if defined(OS_WIN) | 35 class PlatformDataCommon { |
| 35 typedef base::win::ScopedHandle UniversalThreadHandle; | 36 public: |
| 36 #else | 37 base::PlatformThreadId thread_id() { return thread_id_; } |
| 37 typedef base::PlatformThreadHandle UniversalThreadHandle; | 38 |
| 39 protected: |
| 40 PlatformDataCommon() : thread_id_(base::PlatformThread::CurrentId()) {} |
| 41 |
| 42 private: |
| 43 base::PlatformThreadId thread_id_; |
| 44 }; |
| 45 |
| 46 #if defined(USE_SIGNALS) |
| 47 |
| 48 class PlatformData : public PlatformDataCommon { |
| 49 public: |
| 50 PlatformData() : vm_tid_(pthread_self()) {} |
| 51 pthread_t vm_tid() const { return vm_tid_; } |
| 52 |
| 53 private: |
| 54 pthread_t vm_tid_; |
| 55 }; |
| 56 |
| 57 #elif defined(OS_WIN) |
| 58 |
| 59 class PlatformData : public PlatformDataCommon { |
| 60 public: |
| 61 // Get a handle to the calling thread. This is the thread that we are |
| 62 // going to profile. We need to make a copy of the handle because we are |
| 63 // going to use it in the sampler thread. Using GetThreadHandle() will |
| 64 // not work in this case. We're using OpenThread because DuplicateHandle |
| 65 // doesn't work in Chrome's sandbox. |
| 66 PlatformData() |
| 67 : thread_handle_(::OpenThread(THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | |
| 68 THREAD_QUERY_INFORMATION, |
| 69 false, |
| 70 ::GetCurrentThreadId())) {} |
| 71 |
| 72 ~PlatformData() { |
| 73 if (thread_handle_ == NULL) |
| 74 return; |
| 75 ::CloseHandle(thread_handle_); |
| 76 thread_handle_ = NULL; |
| 77 } |
| 78 |
| 79 HANDLE thread_handle() { return thread_handle_; } |
| 80 |
| 81 private: |
| 82 HANDLE thread_handle_; |
| 83 }; |
| 38 #endif | 84 #endif |
| 39 | 85 |
| 40 std::string PtrToString(const void* value) { | 86 std::string PtrToString(const void* value) { |
| 41 return base::StringPrintf( | 87 return base::StringPrintf( |
| 42 "0x%" PRIx64, static_cast<uint64>(reinterpret_cast<intptr_t>(value))); | 88 "0x%" PRIx64, static_cast<uint64>(reinterpret_cast<intptr_t>(value))); |
| 43 } | 89 } |
| 44 | 90 |
| 45 class SampleRecord { | 91 class SampleRecord { |
| 46 public: | 92 public: |
| 47 static const int kMaxFramesCountLog2 = 8; | 93 static const int kMaxFramesCountLog2 = 8; |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 sample_events_to_collect_for_test_; | 184 sample_events_to_collect_for_test_; |
| 139 } | 185 } |
| 140 | 186 |
| 141 private: | 187 private: |
| 142 Sampler(); | 188 Sampler(); |
| 143 | 189 |
| 144 static void InstallJitCodeEventHandler(Isolate* isolate, void* data); | 190 static void InstallJitCodeEventHandler(Isolate* isolate, void* data); |
| 145 static void HandleJitCodeEvent(const v8::JitCodeEvent* event); | 191 static void HandleJitCodeEvent(const v8::JitCodeEvent* event); |
| 146 static scoped_refptr<ConvertableToTraceFormat> JitCodeEventToTraceFormat( | 192 static scoped_refptr<ConvertableToTraceFormat> JitCodeEventToTraceFormat( |
| 147 const v8::JitCodeEvent* event); | 193 const v8::JitCodeEvent* event); |
| 148 static UniversalThreadHandle GetCurrentThreadHandle(); | |
| 149 | 194 |
| 150 void InjectPendingEvents(); | 195 void InjectPendingEvents(); |
| 151 | 196 |
| 152 static const unsigned kNumberOfSamples = 10; | 197 static const unsigned kNumberOfSamples = 10; |
| 153 typedef LockFreeCircularQueue<SampleRecord, kNumberOfSamples> SamplingQueue; | 198 typedef LockFreeCircularQueue<SampleRecord, kNumberOfSamples> SamplingQueue; |
| 154 | 199 |
| 155 base::PlatformThreadId thread_id_; | 200 PlatformData platform_data_; |
| 156 UniversalThreadHandle thread_handle_; | |
| 157 Isolate* isolate_; | 201 Isolate* isolate_; |
| 158 scoped_ptr<SamplingQueue> samples_data_; | 202 scoped_ptr<SamplingQueue> samples_data_; |
| 159 base::subtle::Atomic32 code_added_events_count_; | 203 base::subtle::Atomic32 code_added_events_count_; |
| 160 base::subtle::Atomic32 samples_count_; | 204 base::subtle::Atomic32 samples_count_; |
| 161 int code_added_events_to_collect_for_test_; | 205 int code_added_events_to_collect_for_test_; |
| 162 int sample_events_to_collect_for_test_; | 206 int sample_events_to_collect_for_test_; |
| 163 | 207 |
| 164 static base::LazyInstance<base::ThreadLocalPointer<Sampler>>::Leaky | 208 static base::LazyInstance<base::ThreadLocalPointer<Sampler>>::Leaky |
| 165 tls_instance_; | 209 tls_instance_; |
| 166 }; | 210 }; |
| 167 | 211 |
| 168 base::LazyInstance<base::ThreadLocalPointer<Sampler>>::Leaky | 212 base::LazyInstance<base::ThreadLocalPointer<Sampler>>::Leaky |
| 169 Sampler::tls_instance_ = LAZY_INSTANCE_INITIALIZER; | 213 Sampler::tls_instance_ = LAZY_INSTANCE_INITIALIZER; |
| 170 | 214 |
| 171 Sampler::Sampler() | 215 Sampler::Sampler() |
| 172 : thread_id_(base::PlatformThread::CurrentId()), | 216 : isolate_(Isolate::GetCurrent()), |
| 173 thread_handle_(Sampler::GetCurrentThreadHandle()), | |
| 174 isolate_(Isolate::GetCurrent()), | |
| 175 code_added_events_count_(0), | 217 code_added_events_count_(0), |
| 176 samples_count_(0), | 218 samples_count_(0), |
| 177 code_added_events_to_collect_for_test_(0), | 219 code_added_events_to_collect_for_test_(0), |
| 178 sample_events_to_collect_for_test_(0) { | 220 sample_events_to_collect_for_test_(0) { |
| 179 DCHECK(isolate_); | 221 DCHECK(isolate_); |
| 180 DCHECK(!GetInstance()); | 222 DCHECK(!GetInstance()); |
| 181 tls_instance_.Pointer()->Set(this); | 223 tls_instance_.Pointer()->Set(this); |
| 182 } | 224 } |
| 183 | 225 |
| 184 Sampler::~Sampler() { | 226 Sampler::~Sampler() { |
| 185 DCHECK(GetInstance()); | 227 DCHECK(GetInstance()); |
| 186 tls_instance_.Pointer()->Set(nullptr); | 228 tls_instance_.Pointer()->Set(nullptr); |
| 187 } | 229 } |
| 188 | 230 |
| 189 // static | 231 // static |
| 190 scoped_ptr<Sampler> Sampler::CreateForCurrentThread() { | 232 scoped_ptr<Sampler> Sampler::CreateForCurrentThread() { |
| 191 return scoped_ptr<Sampler>(new Sampler()); | 233 return scoped_ptr<Sampler>(new Sampler()); |
| 192 } | 234 } |
| 193 | 235 |
| 194 // static | |
| 195 UniversalThreadHandle Sampler::GetCurrentThreadHandle() { | |
| 196 #ifdef OS_WIN | |
| 197 return base::win::ScopedHandle(::OpenThread( | |
| 198 THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, | |
| 199 false, base::PlatformThread::CurrentId())); | |
| 200 #else | |
| 201 return base::PlatformThread::CurrentHandle(); | |
| 202 #endif | |
| 203 } | |
| 204 | |
| 205 void Sampler::Start() { | 236 void Sampler::Start() { |
| 206 samples_data_.reset(new SamplingQueue()); | 237 samples_data_.reset(new SamplingQueue()); |
| 207 v8::JitCodeEventHandler handler = &HandleJitCodeEvent; | 238 v8::JitCodeEventHandler handler = &HandleJitCodeEvent; |
| 208 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, | 239 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, |
| 209 reinterpret_cast<void*>(handler)); | 240 reinterpret_cast<void*>(handler)); |
| 210 } | 241 } |
| 211 | 242 |
| 212 void Sampler::Stop() { | 243 void Sampler::Stop() { |
| 213 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, nullptr); | 244 isolate_->RequestInterrupt(&InstallJitCodeEventHandler, nullptr); |
| 214 samples_data_.reset(); | 245 samples_data_.reset(); |
| 215 } | 246 } |
| 216 | 247 |
| 217 #if ARCH_CPU_64_BITS | 248 #if ARCH_CPU_64_BITS |
| 218 #define REG_64_32(reg64, reg32) reg64 | 249 #define REG_64_32(reg64, reg32) reg64 |
| 219 #else | 250 #else |
| 220 #define REG_64_32(reg64, reg32) reg32 | 251 #define REG_64_32(reg64, reg32) reg32 |
| 221 #endif // ARCH_CPU_64_BITS | 252 #endif // ARCH_CPU_64_BITS |
| 222 | 253 |
| 223 void Sampler::Sample() { | 254 void Sampler::Sample() { |
| 224 #if defined(OS_WIN) | 255 #if defined(OS_WIN) |
| 225 const DWORD kSuspendFailed = static_cast<DWORD>(-1); | 256 const DWORD kSuspendFailed = static_cast<DWORD>(-1); |
| 226 if (::SuspendThread(thread_handle_.Get()) == kSuspendFailed) | 257 if (::SuspendThread(platform_data_.thread_handle()) == kSuspendFailed) |
| 227 return; | 258 return; |
| 228 CONTEXT context; | 259 CONTEXT context; |
| 229 memset(&context, 0, sizeof(context)); | 260 memset(&context, 0, sizeof(context)); |
| 230 context.ContextFlags = CONTEXT_FULL; | 261 context.ContextFlags = CONTEXT_FULL; |
| 231 if (::GetThreadContext(thread_handle_.Get(), &context) != 0) { | 262 if (::GetThreadContext(platform_data_.thread_handle(), &context) != 0) { |
| 232 v8::RegisterState state; | 263 v8::RegisterState state; |
| 233 state.pc = reinterpret_cast<void*>(context.REG_64_32(Rip, Eip)); | 264 state.pc = reinterpret_cast<void*>(context.REG_64_32(Rip, Eip)); |
| 234 state.sp = reinterpret_cast<void*>(context.REG_64_32(Rsp, Esp)); | 265 state.sp = reinterpret_cast<void*>(context.REG_64_32(Rsp, Esp)); |
| 235 state.fp = reinterpret_cast<void*>(context.REG_64_32(Rbp, Ebp)); | 266 state.fp = reinterpret_cast<void*>(context.REG_64_32(Rbp, Ebp)); |
| 236 // TODO(alph): It is not needed to buffer the events on Windows. | 267 // TODO(alph): It is not needed to buffer the events on Windows. |
| 237 // We can just collect and fire trace event right away. | 268 // We can just collect and fire trace event right away. |
| 238 DoSample(state); | 269 DoSample(state); |
| 239 } | 270 } |
| 240 ::ResumeThread(thread_handle_.Get()); | 271 ::ResumeThread(platform_data_.thread_handle()); |
| 241 #elif defined(USE_SIGNALS) | 272 #elif defined(USE_SIGNALS) |
| 242 int error = pthread_kill(thread_handle_.platform_handle(), SIGPROF); | 273 int error = pthread_kill(platform_data_.vm_tid(), SIGPROF); |
| 243 if (error) { | 274 if (error) { |
| 244 LOG(ERROR) << "pthread_kill failed with error " << error << " " | 275 LOG(ERROR) << "pthread_kill failed with error " << error << " " |
| 245 << strerror(error); | 276 << strerror(error); |
| 246 } | 277 } |
| 247 #endif | 278 #endif |
| 248 InjectPendingEvents(); | 279 InjectPendingEvents(); |
| 249 } | 280 } |
| 250 | 281 |
| 251 void Sampler::DoSample(const v8::RegisterState& state) { | 282 void Sampler::DoSample(const v8::RegisterState& state) { |
| 252 // Called in the sampled thread signal handler. | 283 // Called in the sampled thread signal handler. |
| 253 // Because of that it is not allowed to do any memory allocation here. | 284 // Because of that it is not allowed to do any memory allocation here. |
| 254 base::TimeTicks timestamp = base::TimeTicks::NowFromSystemTraceTime(); | 285 base::TimeTicks timestamp = base::TimeTicks::NowFromSystemTraceTime(); |
| 255 SampleRecord* record = samples_data_->StartEnqueue(); | 286 SampleRecord* record = samples_data_->StartEnqueue(); |
| 256 if (!record) | 287 if (!record) |
| 257 return; | 288 return; |
| 258 record->Collect(isolate_, timestamp, state); | 289 record->Collect(isolate_, timestamp, state); |
| 259 samples_data_->FinishEnqueue(); | 290 samples_data_->FinishEnqueue(); |
| 260 } | 291 } |
| 261 | 292 |
| 262 void Sampler::InjectPendingEvents() { | 293 void Sampler::InjectPendingEvents() { |
| 263 SampleRecord* record = samples_data_->Peek(); | 294 SampleRecord* record = samples_data_->Peek(); |
| 264 while (record) { | 295 while (record) { |
| 265 TRACE_EVENT_SAMPLE_WITH_TID_AND_TIMESTAMP1( | 296 TRACE_EVENT_SAMPLE_WITH_TID_AND_TIMESTAMP1( |
| 266 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), "V8Sample", thread_id_, | 297 TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), "V8Sample", |
| 298 platform_data_.thread_id(), |
| 267 (record->timestamp() - base::TimeTicks()).InMicroseconds(), "data", | 299 (record->timestamp() - base::TimeTicks()).InMicroseconds(), "data", |
| 268 record->ToTraceFormat()); | 300 record->ToTraceFormat()); |
| 269 samples_data_->Remove(); | 301 samples_data_->Remove(); |
| 270 record = samples_data_->Peek(); | 302 record = samples_data_->Peek(); |
| 271 base::subtle::NoBarrier_AtomicIncrement(&samples_count_, 1); | 303 base::subtle::NoBarrier_AtomicIncrement(&samples_count_, 1); |
| 272 } | 304 } |
| 273 } | 305 } |
| 274 | 306 |
| 275 // static | 307 // static |
| 276 void Sampler::InstallJitCodeEventHandler(Isolate* isolate, void* data) { | 308 void Sampler::InstallJitCodeEventHandler(Isolate* isolate, void* data) { |
| (...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 579 render_thread_sampler_->SetEventsToCollectForTest(code_added_events, | 611 render_thread_sampler_->SetEventsToCollectForTest(code_added_events, |
| 580 sample_events); | 612 sample_events); |
| 581 waitable_event_for_testing_.reset(new base::WaitableEvent(false, false)); | 613 waitable_event_for_testing_.reset(new base::WaitableEvent(false, false)); |
| 582 } | 614 } |
| 583 | 615 |
| 584 void V8SamplingProfiler::WaitSamplingEventForTesting() { | 616 void V8SamplingProfiler::WaitSamplingEventForTesting() { |
| 585 waitable_event_for_testing_->Wait(); | 617 waitable_event_for_testing_->Wait(); |
| 586 } | 618 } |
| 587 | 619 |
| 588 } // namespace blink | 620 } // namespace blink |
| OLD | NEW |