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