v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
v8-profiler-agent-impl.cc
Go to the documentation of this file.
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
6
7#include <vector>
8
10#include "src/base/atomicops.h"
13#include "src/inspector/protocol/Protocol.h"
19
20namespace v8_inspector {
21
22namespace ProfilerAgentState {
23static const char samplingInterval[] = "samplingInterval";
24static const char userInitiatedProfiling[] = "userInitiatedProfiling";
25static const char profilerEnabled[] = "profilerEnabled";
26static const char preciseCoverageStarted[] = "preciseCoverageStarted";
27static const char preciseCoverageCallCount[] = "preciseCoverageCallCount";
28static const char preciseCoverageDetailed[] = "preciseCoverageDetailed";
30 "preciseCoverageAllowTriggeredUpdates";
31} // namespace ProfilerAgentState
32
33namespace {
34
35String16 resourceNameToUrl(V8InspectorImpl* inspector,
36 v8::Local<v8::String> v8Name) {
37 String16 name = toProtocolString(inspector->isolate(), v8Name);
38 if (!inspector) return name;
39 std::unique_ptr<StringBuffer> url =
40 inspector->client()->resourceNameToUrl(toStringView(name));
41 return url ? toString16(url->string()) : name;
42}
43
44std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>>
45buildInspectorObjectForPositionTicks(const v8::CpuProfileNode* node) {
46 unsigned lineCount = node->GetHitLineCount();
47 if (!lineCount) return nullptr;
48 auto array =
49 std::make_unique<protocol::Array<protocol::Profiler::PositionTickInfo>>();
50 std::vector<v8::CpuProfileNode::LineTick> entries(lineCount);
51 if (node->GetLineTicks(&entries[0], lineCount)) {
52 for (unsigned i = 0; i < lineCount; i++) {
53 std::unique_ptr<protocol::Profiler::PositionTickInfo> line =
54 protocol::Profiler::PositionTickInfo::create()
55 .setLine(entries[i].line)
56 .setTicks(entries[i].hit_count)
57 .build();
58 array->emplace_back(std::move(line));
59 }
60 }
61 return array;
62}
63
64std::unique_ptr<protocol::Profiler::ProfileNode> buildInspectorObjectFor(
65 V8InspectorImpl* inspector, const v8::CpuProfileNode* node) {
66 v8::Isolate* isolate = inspector->isolate();
67 v8::HandleScope handleScope(isolate);
68 auto callFrame =
69 protocol::Runtime::CallFrame::create()
70 .setFunctionName(toProtocolString(isolate, node->GetFunctionName()))
71 .setScriptId(String16::fromInteger(node->GetScriptId()))
72 .setUrl(resourceNameToUrl(inspector, node->GetScriptResourceName()))
73 .setLineNumber(node->GetLineNumber() - 1)
74 .setColumnNumber(node->GetColumnNumber() - 1)
75 .build();
76 auto result = protocol::Profiler::ProfileNode::create()
77 .setCallFrame(std::move(callFrame))
78 .setHitCount(node->GetHitCount())
79 .setId(node->GetNodeId())
80 .build();
81
82 const int childrenCount = node->GetChildrenCount();
83 if (childrenCount) {
84 auto children = std::make_unique<protocol::Array<int>>();
85 for (int i = 0; i < childrenCount; i++)
86 children->emplace_back(node->GetChild(i)->GetNodeId());
87 result->setChildren(std::move(children));
88 }
89
90 const char* deoptReason = node->GetBailoutReason();
91 if (deoptReason && deoptReason[0] && strcmp(deoptReason, "no reason"))
92 result->setDeoptReason(deoptReason);
93
94 auto positionTicks = buildInspectorObjectForPositionTicks(node);
95 if (positionTicks) result->setPositionTicks(std::move(positionTicks));
96
97 return result;
98}
99
100std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples(
101 v8::CpuProfile* v8profile) {
102 auto array = std::make_unique<protocol::Array<int>>();
103 int count = v8profile->GetSamplesCount();
104 for (int i = 0; i < count; i++)
105 array->emplace_back(v8profile->GetSample(i)->GetNodeId());
106 return array;
107}
108
109std::unique_ptr<protocol::Array<int>> buildInspectorObjectForTimestamps(
110 v8::CpuProfile* v8profile) {
111 auto array = std::make_unique<protocol::Array<int>>();
112 int count = v8profile->GetSamplesCount();
113 uint64_t lastTime = v8profile->GetStartTime();
114 for (int i = 0; i < count; i++) {
115 uint64_t ts = v8profile->GetSampleTimestamp(i);
116 array->emplace_back(static_cast<int>(ts - lastTime));
117 lastTime = ts;
118 }
119 return array;
120}
121
122void flattenNodesTree(V8InspectorImpl* inspector,
123 const v8::CpuProfileNode* node,
124 protocol::Array<protocol::Profiler::ProfileNode>* list) {
125 list->emplace_back(buildInspectorObjectFor(inspector, node));
126 const int childrenCount = node->GetChildrenCount();
127 for (int i = 0; i < childrenCount; i++)
128 flattenNodesTree(inspector, node->GetChild(i), list);
129}
130
131std::unique_ptr<protocol::Profiler::Profile> createCPUProfile(
132 V8InspectorImpl* inspector, v8::CpuProfile* v8profile) {
133 auto nodes =
134 std::make_unique<protocol::Array<protocol::Profiler::ProfileNode>>();
135 flattenNodesTree(inspector, v8profile->GetTopDownRoot(), nodes.get());
136 return protocol::Profiler::Profile::create()
137 .setNodes(std::move(nodes))
138 .setStartTime(static_cast<double>(v8profile->GetStartTime()))
139 .setEndTime(static_cast<double>(v8profile->GetEndTime()))
140 .setSamples(buildInspectorObjectForSamples(v8profile))
141 .setTimeDeltas(buildInspectorObjectForTimestamps(v8profile))
142 .build();
143}
144
145std::unique_ptr<protocol::Debugger::Location> currentDebugLocation(
146 V8InspectorImpl* inspector) {
147 auto stackTrace = V8StackTraceImpl::capture(inspector->debugger(), 1);
148 CHECK(stackTrace);
149 CHECK(!stackTrace->isEmpty());
150 return protocol::Debugger::Location::create()
151 .setScriptId(String16::fromInteger(stackTrace->topScriptId()))
152 .setLineNumber(stackTrace->topLineNumber())
153 .setColumnNumber(stackTrace->topColumnNumber())
154 .build();
155}
156
157volatile int s_lastProfileId = 0;
158
159} // namespace
160
162 public:
163 ProfileDescriptor(const String16& id, const String16& title)
164 : m_id(id), m_title(title) {}
167};
168
170 V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
171 protocol::DictionaryValue* state)
172 : m_session(session),
173 m_isolate(m_session->inspector()->isolate()),
174 m_state(state),
175 m_frontend(frontendChannel) {}
176
180
182 if (!m_enabled) return;
183 String16 id = nextProfileId();
184 m_startedProfiles.push_back(ProfileDescriptor(id, title));
185 startProfiling(id);
186 m_frontend.consoleProfileStarted(
187 id, currentDebugLocation(m_session->inspector()), title);
188}
189
191 if (!m_enabled) return;
192 String16 id;
193 String16 resolvedTitle;
194 // Take last started profile if no title was passed.
195 if (title.isEmpty()) {
196 if (m_startedProfiles.empty()) return;
197 id = m_startedProfiles.back().m_id;
198 resolvedTitle = m_startedProfiles.back().m_title;
199 m_startedProfiles.pop_back();
200 } else {
201 for (size_t i = 0; i < m_startedProfiles.size(); i++) {
202 if (m_startedProfiles[i].m_title == title) {
203 resolvedTitle = title;
204 id = m_startedProfiles[i].m_id;
205 m_startedProfiles.erase(m_startedProfiles.begin() + i);
206 break;
207 }
208 }
209 if (id.isEmpty()) return;
210 }
211 std::unique_ptr<protocol::Profiler::Profile> profile =
212 stopProfiling(id, true);
213 if (!profile) return;
214 m_frontend.consoleProfileFinished(
215 id, currentDebugLocation(m_session->inspector()), std::move(profile),
216 resolvedTitle);
217}
218
220 if (!m_enabled) {
221 m_enabled = true;
223 }
224
225 return Response::Success();
226}
227
229 if (m_enabled) {
230 for (size_t i = m_startedProfiles.size(); i > 0; --i)
231 stopProfiling(m_startedProfiles[i - 1].m_id, false);
232 m_startedProfiles.clear();
233 stop(nullptr);
236 m_enabled = false;
238 }
239
240 return Response::Success();
241}
242
244 if (m_profiler) {
245 return Response::ServerError(
246 "Cannot change sampling interval when profiling.");
247 }
248 m_state->setInteger(ProfilerAgentState::samplingInterval, interval);
249 return Response::Success();
250}
251
254 if (m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false)) {
255 m_enabled = true;
258 false)) {
259 start();
260 }
262 false)) {
263 bool callCount = m_state->booleanProperty(
265 bool detailed = m_state->booleanProperty(
267 bool updatesAllowed = m_state->booleanProperty(
269 double timestamp;
270 startPreciseCoverage(std::optional<bool>(callCount),
271 std::optional<bool>(detailed),
272 std::optional<bool>(updatesAllowed), &timestamp);
273 }
274 }
275}
276
278 if (m_recordingCPUProfile) return Response::Success();
279 if (!m_enabled) return Response::ServerError("Profiler is not enabled");
284 return Response::Success();
285}
286
288 std::unique_ptr<protocol::Profiler::Profile>* profile) {
290 return Response::ServerError("No recording profiles found");
291 }
292 m_recordingCPUProfile = false;
293 std::unique_ptr<protocol::Profiler::Profile> cpuProfile =
295 if (profile) {
296 *profile = std::move(cpuProfile);
297 if (!*profile) return Response::ServerError("Profile is not found");
298 }
301 return Response::Success();
302}
303
305 std::optional<bool> callCount, std::optional<bool> detailed,
306 std::optional<bool> allowTriggeredUpdates, double* out_timestamp) {
307 if (!m_enabled) return Response::ServerError("Profiler is not enabled");
308 *out_timestamp = v8::base::TimeTicks::Now().since_origin().InSecondsF();
309 bool callCountValue = callCount.value_or(false);
310 bool detailedValue = detailed.value_or(false);
311 bool allowTriggeredUpdatesValue = allowTriggeredUpdates.value_or(false);
314 callCountValue);
316 detailedValue);
318 allowTriggeredUpdatesValue);
319 // BlockCount is a superset of PreciseCount. It includes block-granularity
320 // coverage data if it exists (at the time of writing, that's the case for
321 // each function recompiled after the BlockCount mode has been set); and
322 // function-granularity coverage data otherwise.
323 using C = v8::debug::Coverage;
324 using Mode = v8::debug::CoverageMode;
325 Mode mode = callCountValue
326 ? (detailedValue ? Mode::kBlockCount : Mode::kPreciseCount)
327 : (detailedValue ? Mode::kBlockBinary : Mode::kPreciseBinary);
328 C::SelectMode(m_isolate, mode);
329 return Response::Success();
330}
331
333 if (!m_enabled) return Response::ServerError("Profiler is not enabled");
339 return Response::Success();
340}
341
342namespace {
343std::unique_ptr<protocol::Profiler::CoverageRange> createCoverageRange(
344 int start, int end, int count) {
345 return protocol::Profiler::CoverageRange::create()
346 .setStartOffset(start)
347 .setEndOffset(end)
348 .setCount(count)
349 .build();
350}
351
352Response coverageToProtocol(
353 V8InspectorImpl* inspector, const v8::debug::Coverage& coverage,
354 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
355 out_result) {
356 auto result =
357 std::make_unique<protocol::Array<protocol::Profiler::ScriptCoverage>>();
358 v8::Isolate* isolate = inspector->isolate();
359 for (size_t i = 0; i < coverage.ScriptCount(); i++) {
360 v8::debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
361 v8::Local<v8::debug::Script> script = script_data.GetScript();
362 auto functions = std::make_unique<
363 protocol::Array<protocol::Profiler::FunctionCoverage>>();
364 for (size_t j = 0; j < script_data.FunctionCount(); j++) {
366 script_data.GetFunctionData(j);
367 auto ranges = std::make_unique<
368 protocol::Array<protocol::Profiler::CoverageRange>>();
369
370 // Add function range.
371 ranges->emplace_back(createCoverageRange(function_data.StartOffset(),
372 function_data.EndOffset(),
373 function_data.Count()));
374
375 // Process inner blocks.
376 for (size_t k = 0; k < function_data.BlockCount(); k++) {
378 function_data.GetBlockData(k);
379 ranges->emplace_back(createCoverageRange(block_data.StartOffset(),
380 block_data.EndOffset(),
381 block_data.Count()));
382 }
383
384 functions->emplace_back(
385 protocol::Profiler::FunctionCoverage::create()
386 .setFunctionName(toProtocolString(
387 isolate,
388 function_data.Name().FromMaybe(v8::Local<v8::String>())))
389 .setRanges(std::move(ranges))
390 .setIsBlockCoverage(function_data.HasBlockCoverage())
391 .build());
392 }
393 String16 url;
395 if (script->SourceURL().ToLocal(&name) && name->Length()) {
396 url = toProtocolString(isolate, name);
397 } else if (script->Name().ToLocal(&name) && name->Length()) {
398 url = resourceNameToUrl(inspector, name);
399 }
400 result->emplace_back(protocol::Profiler::ScriptCoverage::create()
401 .setScriptId(String16::fromInteger(script->Id()))
402 .setUrl(url)
403 .setFunctions(std::move(functions))
404 .build());
405 }
406 *out_result = std::move(result);
407 return Response::Success();
408}
409} // anonymous namespace
410
412 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
413 out_result,
414 double* out_timestamp) {
416 false)) {
417 return Response::ServerError("Precise coverage has not been started.");
418 }
419 v8::HandleScope handle_scope(m_isolate);
421 *out_timestamp = v8::base::TimeTicks::Now().since_origin().InSecondsF();
422 return coverageToProtocol(m_session->inspector(), coverage, out_result);
423}
424
426 const String16& occasion) {
428 false)) {
429 return;
430 }
431 if (!m_state->booleanProperty(
433 return;
434 }
435 v8::HandleScope handle_scope(m_isolate);
437 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>
438 out_result;
439 coverageToProtocol(m_session->inspector(), coverage, &out_result);
441 m_frontend.preciseCoverageDeltaUpdate(now, occasion, std::move(out_result));
442}
443
445 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
446 out_result) {
447 v8::HandleScope handle_scope(m_isolate);
448 v8::debug::Coverage coverage =
450 return coverageToProtocol(m_session->inspector(), coverage, out_result);
451}
452
457
459 v8::HandleScope handleScope(m_isolate);
463 int interval =
465 if (interval) m_profiler->SetSamplingInterval(interval);
466 }
469}
470
471std::unique_ptr<protocol::Profiler::Profile> V8ProfilerAgentImpl::stopProfiling(
472 const String16& title, bool serialize) {
473 v8::HandleScope handleScope(m_isolate);
474 v8::CpuProfile* profile =
476 std::unique_ptr<protocol::Profiler::Profile> result;
477 if (profile) {
478 if (serialize) result = createCPUProfile(m_session->inspector(), profile);
479 profile->Delete();
480 }
484 m_profiler = nullptr;
485 }
486 return result;
487}
488
489} // namespace v8_inspector
const char * name
Definition builtins.cc:39
const CpuProfileNode * GetTopDownRoot() const
Definition api.cc:11334
int GetSamplesCount() const
Definition api.cc:11387
int64_t GetEndTime() const
Definition api.cc:11367
const CpuProfileNode * GetSample(int index) const
Definition api.cc:11339
int64_t GetSampleTimestamp(int index) const
Definition api.cc:11347
int64_t GetStartTime() const
Definition api.cc:11362
CpuProfilingStatus StartProfiling(Local< String > title, CpuProfilingOptions options, std::unique_ptr< DiscardedSamplesDelegate > delegate=nullptr)
Definition api.cc:11475
static CpuProfiler * New(Isolate *isolate, CpuProfilingNamingMode=kDebugNaming, CpuProfilingLoggingMode=kLazyLogging)
Definition api.cc:11391
CpuProfile * StopProfiling(Local< String > title)
Definition api.cc:11493
void Dispose()
Definition api.cc:11420
void SetSamplingInterval(int us)
Definition api.cc:11431
double InSecondsF() const
Definition time.cc:210
static TimeTicks Now()
Definition time.cc:736
constexpr TimeDelta since_origin() const
Definition time.h:295
BlockData GetBlockData(size_t i) const
MaybeLocal< String > Name() const
FunctionData GetFunctionData(size_t i) const
Local< debug::Script > GetScript() const
static void SelectMode(Isolate *isolate, CoverageMode mode)
static Coverage CollectPrecise(Isolate *isolate)
static Coverage CollectBestEffort(Isolate *isolate)
static String16 fromInteger(int)
Definition string-16.cc:71
bool isEmpty() const
Definition string-16.h:59
virtual std::unique_ptr< StringBuffer > resourceNameToUrl(const StringView &resourceName)
v8::Isolate * isolate() const
ProfileDescriptor(const String16 &id, const String16 &title)
Response takePreciseCoverage(std::unique_ptr< protocol::Array< protocol::Profiler::ScriptCoverage > > *out_result, double *out_timestamp) override
void startProfiling(const String16 &title)
Response startPreciseCoverage(std::optional< bool > binary, std::optional< bool > detailed, std::optional< bool > allow_triggered_updates, double *out_timestamp) override
protocol::Profiler::Frontend m_frontend
Response getBestEffortCoverage(std::unique_ptr< protocol::Array< protocol::Profiler::ScriptCoverage > > *out_result) override
void consoleProfile(const String16 &title)
V8ProfilerAgentImpl(V8InspectorSessionImpl *, protocol::FrontendChannel *, protocol::DictionaryValue *state)
std::vector< ProfileDescriptor > m_startedProfiles
protocol::DictionaryValue * m_state
std::unique_ptr< protocol::Profiler::Profile > stopProfiling(const String16 &title, bool serialize)
void triggerPreciseCoverageDeltaUpdate(const String16 &occasion)
Response stop(std::unique_ptr< protocol::Profiler::Profile > *) override
void consoleProfileEnd(const String16 &title)
static std::unique_ptr< V8StackTraceImpl > capture(V8Debugger *, int maxStackSize)
int start
uint32_t count
int end
std::vector< std::unique_ptr< InstanceTypeTree > > children
ZoneVector< RpoNumber > & result
ZoneVector< Entry > entries
Atomic32 Relaxed_AtomicIncrement(volatile Atomic32 *ptr, Atomic32 increment)
Definition atomicops.h:140
static const char preciseCoverageAllowTriggeredUpdates[]
String16 toProtocolString(v8::Isolate *isolate, v8::Local< v8::String > value)
StringView toStringView(const String16 &string)
v8::Local< v8::String > toV8String(v8::Isolate *isolate, const String16 &string)
String16 toString16(const StringView &string)
#define CHECK(condition)
Definition logging.h:124
#define DCHECK(condition)
Definition logging.h:482
v8::Isolate * m_isolate
V8InspectorSessionImpl * m_session
protocol::HeapProfiler::Frontend * m_frontend