Line data Source code
1 : // Copyright 2015 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/inspector/v8-profiler-agent-impl.h"
6 :
7 : #include <vector>
8 :
9 : #include "src/base/atomicops.h"
10 : #include "src/inspector/protocol/Protocol.h"
11 : #include "src/inspector/string-util.h"
12 : #include "src/inspector/v8-debugger.h"
13 : #include "src/inspector/v8-inspector-impl.h"
14 : #include "src/inspector/v8-inspector-session-impl.h"
15 : #include "src/inspector/v8-stack-trace-impl.h"
16 :
17 : #include "include/v8-profiler.h"
18 :
19 : namespace v8_inspector {
20 :
21 : namespace ProfilerAgentState {
22 : static const char samplingInterval[] = "samplingInterval";
23 : static const char userInitiatedProfiling[] = "userInitiatedProfiling";
24 : static const char profilerEnabled[] = "profilerEnabled";
25 : static const char preciseCoverageStarted[] = "preciseCoverageStarted";
26 : static const char preciseCoverageCallCount[] = "preciseCoverageCallCount";
27 : }
28 :
29 : namespace {
30 :
31 : std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>>
32 111 : buildInspectorObjectForPositionTicks(const v8::CpuProfileNode* node) {
33 111 : unsigned lineCount = node->GetHitLineCount();
34 111 : if (!lineCount) return nullptr;
35 : auto array = protocol::Array<protocol::Profiler::PositionTickInfo>::create();
36 24 : std::vector<v8::CpuProfileNode::LineTick> entries(lineCount);
37 24 : if (node->GetLineTicks(&entries[0], lineCount)) {
38 24 : for (unsigned i = 0; i < lineCount; i++) {
39 : std::unique_ptr<protocol::Profiler::PositionTickInfo> line =
40 : protocol::Profiler::PositionTickInfo::create()
41 48 : .setLine(entries[i].line)
42 48 : .setTicks(entries[i].hit_count)
43 : .build();
44 24 : array->addItem(std::move(line));
45 : }
46 : }
47 : return array;
48 : }
49 :
50 111 : std::unique_ptr<protocol::Profiler::ProfileNode> buildInspectorObjectFor(
51 : v8::Isolate* isolate, const v8::CpuProfileNode* node) {
52 111 : v8::HandleScope handleScope(isolate);
53 : auto callFrame =
54 : protocol::Runtime::CallFrame::create()
55 333 : .setFunctionName(toProtocolString(node->GetFunctionName()))
56 222 : .setScriptId(String16::fromInteger(node->GetScriptId()))
57 222 : .setUrl(toProtocolString(node->GetScriptResourceName()))
58 111 : .setLineNumber(node->GetLineNumber() - 1)
59 111 : .setColumnNumber(node->GetColumnNumber() - 1)
60 : .build();
61 : auto result = protocol::Profiler::ProfileNode::create()
62 111 : .setCallFrame(std::move(callFrame))
63 111 : .setHitCount(node->GetHitCount())
64 111 : .setId(node->GetNodeId())
65 : .build();
66 :
67 111 : const int childrenCount = node->GetChildrenCount();
68 111 : if (childrenCount) {
69 : auto children = protocol::Array<int>::create();
70 132 : for (int i = 0; i < childrenCount; i++)
71 154 : children->addItem(node->GetChild(i)->GetNodeId());
72 : result->setChildren(std::move(children));
73 : }
74 :
75 111 : const char* deoptReason = node->GetBailoutReason();
76 111 : if (deoptReason && deoptReason[0] && strcmp(deoptReason, "no reason"))
77 0 : result->setDeoptReason(deoptReason);
78 :
79 111 : auto positionTicks = buildInspectorObjectForPositionTicks(node);
80 111 : if (positionTicks) result->setPositionTicks(std::move(positionTicks));
81 :
82 111 : return result;
83 : }
84 :
85 34 : std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples(
86 : v8::CpuProfile* v8profile) {
87 : auto array = protocol::Array<int>::create();
88 34 : int count = v8profile->GetSamplesCount();
89 136 : for (int i = 0; i < count; i++)
90 204 : array->addItem(v8profile->GetSample(i)->GetNodeId());
91 34 : return array;
92 : }
93 :
94 34 : std::unique_ptr<protocol::Array<int>> buildInspectorObjectForTimestamps(
95 : v8::CpuProfile* v8profile) {
96 : auto array = protocol::Array<int>::create();
97 34 : int count = v8profile->GetSamplesCount();
98 34 : uint64_t lastTime = v8profile->GetStartTime();
99 136 : for (int i = 0; i < count; i++) {
100 102 : uint64_t ts = v8profile->GetSampleTimestamp(i);
101 204 : array->addItem(static_cast<int>(ts - lastTime));
102 : lastTime = ts;
103 : }
104 34 : return array;
105 : }
106 :
107 111 : void flattenNodesTree(v8::Isolate* isolate, const v8::CpuProfileNode* node,
108 : protocol::Array<protocol::Profiler::ProfileNode>* list) {
109 222 : list->addItem(buildInspectorObjectFor(isolate, node));
110 111 : const int childrenCount = node->GetChildrenCount();
111 188 : for (int i = 0; i < childrenCount; i++)
112 77 : flattenNodesTree(isolate, node->GetChild(i), list);
113 111 : }
114 :
115 34 : std::unique_ptr<protocol::Profiler::Profile> createCPUProfile(
116 : v8::Isolate* isolate, v8::CpuProfile* v8profile) {
117 : auto nodes = protocol::Array<protocol::Profiler::ProfileNode>::create();
118 34 : flattenNodesTree(isolate, v8profile->GetTopDownRoot(), nodes.get());
119 : return protocol::Profiler::Profile::create()
120 34 : .setNodes(std::move(nodes))
121 34 : .setStartTime(static_cast<double>(v8profile->GetStartTime()))
122 34 : .setEndTime(static_cast<double>(v8profile->GetEndTime()))
123 68 : .setSamples(buildInspectorObjectForSamples(v8profile))
124 68 : .setTimeDeltas(buildInspectorObjectForTimestamps(v8profile))
125 34 : .build();
126 : }
127 :
128 50 : std::unique_ptr<protocol::Debugger::Location> currentDebugLocation(
129 : V8InspectorImpl* inspector) {
130 : std::unique_ptr<V8StackTraceImpl> callStack =
131 50 : inspector->debugger()->captureStackTrace(false /* fullStack */);
132 : auto location = protocol::Debugger::Location::create()
133 150 : .setScriptId(toString16(callStack->topScriptId()))
134 50 : .setLineNumber(callStack->topLineNumber())
135 : .build();
136 50 : location->setColumnNumber(callStack->topColumnNumber());
137 50 : return location;
138 : }
139 :
140 : volatile int s_lastProfileId = 0;
141 :
142 : } // namespace
143 :
144 6 : class V8ProfilerAgentImpl::ProfileDescriptor {
145 : public:
146 28 : ProfileDescriptor(const String16& id, const String16& title)
147 28 : : m_id(id), m_title(title) {}
148 : String16 m_id;
149 : String16 m_title;
150 : };
151 :
152 4573 : V8ProfilerAgentImpl::V8ProfilerAgentImpl(
153 : V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
154 : protocol::DictionaryValue* state)
155 : : m_session(session),
156 4573 : m_isolate(m_session->inspector()->isolate()),
157 : m_state(state),
158 18292 : m_frontend(frontendChannel) {}
159 :
160 13719 : V8ProfilerAgentImpl::~V8ProfilerAgentImpl() {
161 4573 : if (m_profiler) m_profiler->Dispose();
162 9146 : }
163 :
164 28 : void V8ProfilerAgentImpl::consoleProfile(const String16& title) {
165 28 : if (!m_enabled) return;
166 28 : String16 id = nextProfileId();
167 56 : m_startedProfiles.push_back(ProfileDescriptor(id, title));
168 28 : startProfiling(id);
169 : m_frontend.consoleProfileStarted(
170 84 : id, currentDebugLocation(m_session->inspector()), title);
171 : }
172 :
173 28 : void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) {
174 34 : if (!m_enabled) return;
175 : String16 id;
176 : String16 resolvedTitle;
177 : // Take last started profile if no title was passed.
178 28 : if (title.isEmpty()) {
179 48 : if (m_startedProfiles.empty()) return;
180 : id = m_startedProfiles.back().m_id;
181 : resolvedTitle = m_startedProfiles.back().m_title;
182 2 : m_startedProfiles.pop_back();
183 : } else {
184 20 : for (size_t i = 0; i < m_startedProfiles.size(); i++) {
185 20 : if (m_startedProfiles[i].m_title == title) {
186 : resolvedTitle = title;
187 : id = m_startedProfiles[i].m_id;
188 20 : m_startedProfiles.erase(m_startedProfiles.begin() + i);
189 20 : break;
190 : }
191 : }
192 20 : if (id.isEmpty()) return;
193 : }
194 : std::unique_ptr<protocol::Profiler::Profile> profile =
195 22 : stopProfiling(id, true);
196 22 : if (!profile) return;
197 : std::unique_ptr<protocol::Debugger::Location> location =
198 22 : currentDebugLocation(m_session->inspector());
199 : m_frontend.consoleProfileFinished(id, std::move(location), std::move(profile),
200 110 : resolvedTitle);
201 : }
202 :
203 80 : Response V8ProfilerAgentImpl::enable() {
204 80 : if (m_enabled) return Response::OK();
205 80 : m_enabled = true;
206 160 : m_state->setBoolean(ProfilerAgentState::profilerEnabled, true);
207 80 : return Response::OK();
208 : }
209 :
210 4639 : Response V8ProfilerAgentImpl::disable() {
211 4639 : if (!m_enabled) return Response::OK();
212 252 : for (size_t i = m_startedProfiles.size(); i > 0; --i)
213 18 : stopProfiling(m_startedProfiles[i - 1].m_id, false);
214 : m_startedProfiles.clear();
215 160 : stop(nullptr);
216 160 : stopPreciseCoverage();
217 : DCHECK(!m_profiler);
218 80 : m_enabled = false;
219 160 : m_state->setBoolean(ProfilerAgentState::profilerEnabled, false);
220 80 : return Response::OK();
221 : }
222 :
223 0 : Response V8ProfilerAgentImpl::setSamplingInterval(int interval) {
224 0 : if (m_profiler) {
225 0 : return Response::Error("Cannot change sampling interval when profiling.");
226 : }
227 0 : m_state->setInteger(ProfilerAgentState::samplingInterval, interval);
228 0 : return Response::OK();
229 : }
230 :
231 36 : void V8ProfilerAgentImpl::restore() {
232 : DCHECK(!m_enabled);
233 72 : if (!m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false))
234 36 : return;
235 0 : m_enabled = true;
236 : DCHECK(!m_profiler);
237 0 : if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling,
238 0 : false)) {
239 0 : start();
240 : }
241 0 : if (m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
242 0 : false)) {
243 : bool callCount = m_state->booleanProperty(
244 0 : ProfilerAgentState::preciseCoverageCallCount, false);
245 0 : startPreciseCoverage(Maybe<bool>(callCount));
246 : }
247 : }
248 :
249 24 : Response V8ProfilerAgentImpl::start() {
250 24 : if (m_recordingCPUProfile) return Response::OK();
251 30 : if (!m_enabled) return Response::Error("Profiler is not enabled");
252 18 : m_recordingCPUProfile = true;
253 36 : m_frontendInitiatedProfileId = nextProfileId();
254 18 : startProfiling(m_frontendInitiatedProfileId);
255 36 : m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, true);
256 18 : return Response::OK();
257 : }
258 :
259 104 : Response V8ProfilerAgentImpl::stop(
260 : std::unique_ptr<protocol::Profiler::Profile>* profile) {
261 104 : if (!m_recordingCPUProfile) {
262 172 : return Response::Error("No recording profiles found");
263 : }
264 18 : m_recordingCPUProfile = false;
265 : std::unique_ptr<protocol::Profiler::Profile> cpuProfile =
266 18 : stopProfiling(m_frontendInitiatedProfileId, !!profile);
267 18 : if (profile) {
268 : *profile = std::move(cpuProfile);
269 12 : if (!profile->get()) return Response::Error("Profile is not found");
270 : }
271 : m_frontendInitiatedProfileId = String16();
272 36 : m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false);
273 18 : return Response::OK();
274 : }
275 :
276 48 : Response V8ProfilerAgentImpl::startPreciseCoverage(Maybe<bool> callCount) {
277 48 : if (!m_enabled) return Response::Error("Profiler is not enabled");
278 : bool callCountValue = callCount.fromMaybe(false);
279 96 : m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted, true);
280 : m_state->setBoolean(ProfilerAgentState::preciseCoverageCallCount,
281 96 : callCountValue);
282 : v8::debug::Coverage::SelectMode(
283 : m_isolate, callCountValue ? v8::debug::Coverage::kPreciseCount
284 48 : : v8::debug::Coverage::kPreciseBinary);
285 48 : return Response::OK();
286 : }
287 :
288 128 : Response V8ProfilerAgentImpl::stopPreciseCoverage() {
289 128 : if (!m_enabled) return Response::Error("Profiler is not enabled");
290 256 : m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted, false);
291 256 : m_state->setBoolean(ProfilerAgentState::preciseCoverageCallCount, false);
292 128 : v8::debug::Coverage::SelectMode(m_isolate, v8::debug::Coverage::kBestEffort);
293 128 : return Response::OK();
294 : }
295 :
296 : namespace {
297 96 : Response coverageToProtocol(
298 : v8::Isolate* isolate, const v8::debug::Coverage& coverage,
299 : std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
300 : out_result) {
301 : std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> result =
302 : protocol::Array<protocol::Profiler::ScriptCoverage>::create();
303 120 : for (size_t i = 0; i < coverage.ScriptCount(); i++) {
304 120 : v8::debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
305 120 : v8::Local<v8::debug::Script> script = script_data.GetScript();
306 : std::unique_ptr<protocol::Array<protocol::Profiler::FunctionCoverage>>
307 : functions =
308 : protocol::Array<protocol::Profiler::FunctionCoverage>::create();
309 288 : for (size_t j = 0; j < script_data.FunctionCount(); j++) {
310 : v8::debug::Coverage::FunctionData function_data =
311 288 : script_data.GetFunctionData(j);
312 : std::unique_ptr<protocol::Array<protocol::Profiler::CoverageRange>>
313 : ranges = protocol::Array<protocol::Profiler::CoverageRange>::create();
314 : // At this point we only have per-function coverage data, so there is
315 : // only one range per function.
316 : ranges->addItem(protocol::Profiler::CoverageRange::create()
317 576 : .setStartOffset(function_data.StartOffset())
318 288 : .setEndOffset(function_data.EndOffset())
319 288 : .setCount(function_data.Count())
320 288 : .build());
321 : functions->addItem(
322 : protocol::Profiler::FunctionCoverage::create()
323 : .setFunctionName(toProtocolString(
324 1152 : function_data.Name().FromMaybe(v8::Local<v8::String>())))
325 : .setRanges(std::move(ranges))
326 288 : .build());
327 : }
328 : String16 url;
329 : v8::Local<v8::String> name;
330 240 : if (script->Name().ToLocal(&name) || script->SourceURL().ToLocal(&name)) {
331 240 : url = toProtocolString(name);
332 : }
333 : result->addItem(protocol::Profiler::ScriptCoverage::create()
334 360 : .setScriptId(String16::fromInteger(script->Id()))
335 : .setUrl(url)
336 : .setFunctions(std::move(functions))
337 120 : .build());
338 : }
339 : *out_result = std::move(result);
340 96 : return Response::OK();
341 : }
342 : } // anonymous namespace
343 :
344 66 : Response V8ProfilerAgentImpl::takePreciseCoverage(
345 : std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
346 : out_result) {
347 66 : if (!m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
348 132 : false)) {
349 12 : return Response::Error("Precise coverage has not been started.");
350 : }
351 60 : v8::HandleScope handle_scope(m_isolate);
352 120 : v8::debug::Coverage coverage = v8::debug::Coverage::CollectPrecise(m_isolate);
353 120 : return coverageToProtocol(m_isolate, coverage, out_result);
354 : }
355 :
356 36 : Response V8ProfilerAgentImpl::getBestEffortCoverage(
357 : std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
358 : out_result) {
359 36 : v8::HandleScope handle_scope(m_isolate);
360 : v8::debug::Coverage coverage =
361 72 : v8::debug::Coverage::CollectBestEffort(m_isolate);
362 72 : return coverageToProtocol(m_isolate, coverage, out_result);
363 : }
364 :
365 46 : String16 V8ProfilerAgentImpl::nextProfileId() {
366 : return String16::fromInteger(
367 46 : v8::base::NoBarrier_AtomicIncrement(&s_lastProfileId, 1));
368 : }
369 :
370 46 : void V8ProfilerAgentImpl::startProfiling(const String16& title) {
371 46 : v8::HandleScope handleScope(m_isolate);
372 46 : if (!m_startedProfilesCount) {
373 : DCHECK(!m_profiler);
374 32 : m_profiler = v8::CpuProfiler::New(m_isolate);
375 32 : m_profiler->SetIdle(m_idle);
376 : int interval =
377 64 : m_state->integerProperty(ProfilerAgentState::samplingInterval, 0);
378 32 : if (interval) m_profiler->SetSamplingInterval(interval);
379 : }
380 46 : ++m_startedProfilesCount;
381 46 : m_profiler->StartProfiling(toV8String(m_isolate, title), true);
382 46 : }
383 :
384 46 : std::unique_ptr<protocol::Profiler::Profile> V8ProfilerAgentImpl::stopProfiling(
385 : const String16& title, bool serialize) {
386 46 : v8::HandleScope handleScope(m_isolate);
387 : v8::CpuProfile* profile =
388 46 : m_profiler->StopProfiling(toV8String(m_isolate, title));
389 46 : std::unique_ptr<protocol::Profiler::Profile> result;
390 46 : if (profile) {
391 80 : if (serialize) result = createCPUProfile(m_isolate, profile);
392 46 : profile->Delete();
393 : }
394 46 : --m_startedProfilesCount;
395 46 : if (!m_startedProfilesCount) {
396 32 : m_profiler->Dispose();
397 32 : m_profiler = nullptr;
398 : }
399 46 : return result;
400 : }
401 :
402 0 : bool V8ProfilerAgentImpl::idleStarted() {
403 0 : m_idle = true;
404 0 : if (m_profiler) m_profiler->SetIdle(m_idle);
405 0 : return m_profiler;
406 : }
407 :
408 0 : bool V8ProfilerAgentImpl::idleFinished() {
409 0 : m_idle = false;
410 0 : if (m_profiler) m_profiler->SetIdle(m_idle);
411 0 : return m_profiler;
412 : }
413 :
414 : } // namespace v8_inspector
|