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 |