5 #include "src/inspector/v8-profiler-agent-impl.h" 9 #include "src/base/atomicops.h" 10 #include "src/debug/debug-interface.h" 11 #include "src/flags.h" 12 #include "src/inspector/protocol/Protocol.h" 13 #include "src/inspector/string-util.h" 14 #include "src/inspector/v8-debugger.h" 15 #include "src/inspector/v8-inspector-impl.h" 16 #include "src/inspector/v8-inspector-session-impl.h" 17 #include "src/inspector/v8-stack-trace-impl.h" 19 #include "include/v8-profiler.h" 23 namespace ProfilerAgentState {
24 static const char samplingInterval[] =
"samplingInterval";
25 static const char userInitiatedProfiling[] =
"userInitiatedProfiling";
26 static const char profilerEnabled[] =
"profilerEnabled";
27 static const char preciseCoverageStarted[] =
"preciseCoverageStarted";
28 static const char preciseCoverageCallCount[] =
"preciseCoverageCallCount";
29 static const char preciseCoverageDetailed[] =
"preciseCoverageDetailed";
30 static const char typeProfileStarted[] =
"typeProfileStarted";
35 String16 resourceNameToUrl(V8InspectorImpl* inspector,
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;
44 std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>>
46 unsigned lineCount = node->GetHitLineCount();
47 if (!lineCount)
return nullptr;
48 auto array = protocol::Array<protocol::Profiler::PositionTickInfo>::create();
49 std::vector<v8::CpuProfileNode::LineTick> entries(lineCount);
50 if (node->GetLineTicks(&entries[0], lineCount)) {
51 for (
unsigned i = 0;
i < lineCount;
i++) {
52 std::unique_ptr<protocol::Profiler::PositionTickInfo> line =
53 protocol::Profiler::PositionTickInfo::create()
54 .setLine(entries[
i].line)
55 .setTicks(entries[
i].hit_count)
57 array->addItem(std::move(line));
63 std::unique_ptr<protocol::Profiler::ProfileNode> buildInspectorObjectFor(
65 v8::Isolate* isolate = inspector->isolate();
68 protocol::Runtime::CallFrame::create()
69 .setFunctionName(toProtocolString(isolate, node->GetFunctionName()))
70 .setScriptId(String16::fromInteger(node->GetScriptId()))
71 .setUrl(resourceNameToUrl(inspector, node->GetScriptResourceName()))
72 .setLineNumber(node->GetLineNumber() - 1)
73 .setColumnNumber(node->GetColumnNumber() - 1)
75 auto result = protocol::Profiler::ProfileNode::create()
76 .setCallFrame(std::move(callFrame))
77 .setHitCount(node->GetHitCount())
78 .setId(node->GetNodeId())
81 const int childrenCount = node->GetChildrenCount();
83 auto children = protocol::Array<int>::create();
84 for (
int i = 0;
i < childrenCount;
i++)
85 children->addItem(node->GetChild(
i)->GetNodeId());
86 result->setChildren(std::move(children));
89 const char* deoptReason = node->GetBailoutReason();
90 if (deoptReason && deoptReason[0] && strcmp(deoptReason,
"no reason"))
91 result->setDeoptReason(deoptReason);
93 auto positionTicks = buildInspectorObjectForPositionTicks(node);
94 if (positionTicks) result->setPositionTicks(std::move(positionTicks));
99 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples(
101 auto array = protocol::Array<int>::create();
103 for (
int i = 0;
i < count;
i++)
108 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForTimestamps(
110 auto array = protocol::Array<int>::create();
113 for (
int i = 0;
i < count;
i++) {
115 array->addItem(static_cast<int>(ts - lastTime));
121 void flattenNodesTree(V8InspectorImpl* inspector,
123 protocol::Array<protocol::Profiler::ProfileNode>* list) {
124 list->addItem(buildInspectorObjectFor(inspector, node));
125 const int childrenCount = node->GetChildrenCount();
126 for (
int i = 0;
i < childrenCount;
i++)
127 flattenNodesTree(inspector, node->GetChild(
i), list);
130 std::unique_ptr<protocol::Profiler::Profile> createCPUProfile(
132 auto nodes = protocol::Array<protocol::Profiler::ProfileNode>::create();
133 flattenNodesTree(inspector, v8profile->
GetTopDownRoot(), nodes.get());
134 return protocol::Profiler::Profile::create()
135 .setNodes(std::move(nodes))
136 .setStartTime(static_cast<double>(v8profile->
GetStartTime()))
137 .setEndTime(static_cast<double>(v8profile->
GetEndTime()))
138 .setSamples(buildInspectorObjectForSamples(v8profile))
139 .setTimeDeltas(buildInspectorObjectForTimestamps(v8profile))
143 std::unique_ptr<protocol::Debugger::Location> currentDebugLocation(
144 V8InspectorImpl* inspector) {
145 std::unique_ptr<V8StackTraceImpl> callStack =
146 inspector->debugger()->captureStackTrace(
false );
147 auto location = protocol::Debugger::Location::create()
148 .setScriptId(toString16(callStack->topScriptId()))
149 .setLineNumber(callStack->topLineNumber())
151 location->setColumnNumber(callStack->topColumnNumber());
155 volatile int s_lastProfileId = 0;
162 : m_id(
id), m_title(title) {}
167 V8ProfilerAgentImpl::V8ProfilerAgentImpl(
169 protocol::DictionaryValue* state)
170 : m_session(session),
171 m_isolate(m_session->inspector()->isolate()),
173 m_frontend(frontendChannel) {}
175 V8ProfilerAgentImpl::~V8ProfilerAgentImpl() {
176 if (m_profiler) m_profiler->
Dispose();
179 void V8ProfilerAgentImpl::consoleProfile(
const String16& title) {
180 if (!m_enabled)
return;
181 String16
id = nextProfileId();
182 m_startedProfiles.push_back(ProfileDescriptor(
id, title));
184 m_frontend.consoleProfileStarted(
185 id, currentDebugLocation(m_session->inspector()), title);
188 void V8ProfilerAgentImpl::consoleProfileEnd(
const String16& title) {
189 if (!m_enabled)
return;
191 String16 resolvedTitle;
193 if (title.isEmpty()) {
194 if (m_startedProfiles.empty())
return;
195 id = m_startedProfiles.back().m_id;
196 resolvedTitle = m_startedProfiles.back().m_title;
197 m_startedProfiles.pop_back();
199 for (
size_t i = 0;
i < m_startedProfiles.size();
i++) {
200 if (m_startedProfiles[
i].m_title == title) {
201 resolvedTitle = title;
202 id = m_startedProfiles[
i].m_id;
203 m_startedProfiles.erase(m_startedProfiles.begin() +
i);
207 if (
id.isEmpty())
return;
209 std::unique_ptr<protocol::Profiler::Profile> profile =
210 stopProfiling(
id,
true);
211 if (!profile)
return;
212 std::unique_ptr<protocol::Debugger::Location> location =
213 currentDebugLocation(m_session->inspector());
214 m_frontend.consoleProfileFinished(
id, std::move(location), std::move(profile),
218 Response V8ProfilerAgentImpl::enable() {
219 if (m_enabled)
return Response::OK();
221 m_state->setBoolean(ProfilerAgentState::profilerEnabled,
true);
222 return Response::OK();
225 Response V8ProfilerAgentImpl::disable() {
226 if (!m_enabled)
return Response::OK();
227 for (
size_t i = m_startedProfiles.size();
i > 0; --
i)
228 stopProfiling(m_startedProfiles[
i - 1].m_id,
false);
229 m_startedProfiles.clear();
231 stopPreciseCoverage();
234 m_state->setBoolean(ProfilerAgentState::profilerEnabled,
false);
235 return Response::OK();
238 Response V8ProfilerAgentImpl::setSamplingInterval(
int interval) {
240 return Response::Error(
"Cannot change sampling interval when profiling.");
242 m_state->setInteger(ProfilerAgentState::samplingInterval, interval);
243 return Response::OK();
246 void V8ProfilerAgentImpl::restore() {
248 if (!m_state->booleanProperty(ProfilerAgentState::profilerEnabled,
false))
252 if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling,
256 if (m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
258 bool callCount = m_state->booleanProperty(
259 ProfilerAgentState::preciseCoverageCallCount,
false);
260 bool detailed = m_state->booleanProperty(
261 ProfilerAgentState::preciseCoverageDetailed,
false);
262 startPreciseCoverage(Maybe<bool>(callCount), Maybe<bool>(detailed));
266 Response V8ProfilerAgentImpl::start() {
267 if (m_recordingCPUProfile)
return Response::OK();
268 if (!m_enabled)
return Response::Error(
"Profiler is not enabled");
269 m_recordingCPUProfile =
true;
270 m_frontendInitiatedProfileId = nextProfileId();
271 startProfiling(m_frontendInitiatedProfileId);
272 m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling,
true);
273 return Response::OK();
276 Response V8ProfilerAgentImpl::stop(
277 std::unique_ptr<protocol::Profiler::Profile>* profile) {
278 if (!m_recordingCPUProfile) {
279 return Response::Error(
"No recording profiles found");
281 m_recordingCPUProfile =
false;
282 std::unique_ptr<protocol::Profiler::Profile> cpuProfile =
283 stopProfiling(m_frontendInitiatedProfileId, !!profile);
285 *profile = std::move(cpuProfile);
286 if (!profile->get())
return Response::Error(
"Profile is not found");
288 m_frontendInitiatedProfileId = String16();
289 m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling,
false);
290 return Response::OK();
293 Response V8ProfilerAgentImpl::startPreciseCoverage(Maybe<bool> callCount,
294 Maybe<bool> detailed) {
295 if (!m_enabled)
return Response::Error(
"Profiler is not enabled");
296 bool callCountValue = callCount.fromMaybe(
false);
297 bool detailedValue = detailed.fromMaybe(
false);
298 m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted,
true);
299 m_state->setBoolean(ProfilerAgentState::preciseCoverageCallCount,
301 m_state->setBoolean(ProfilerAgentState::preciseCoverageDetailed,
308 C::Mode mode = callCountValue
309 ? (detailedValue ? C::kBlockCount : C::kPreciseCount)
310 : (detailedValue ? C::kBlockBinary : C::kPreciseBinary);
311 C::SelectMode(m_isolate, mode);
312 return Response::OK();
315 Response V8ProfilerAgentImpl::stopPreciseCoverage() {
316 if (!m_enabled)
return Response::Error(
"Profiler is not enabled");
317 m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted,
false);
318 m_state->setBoolean(ProfilerAgentState::preciseCoverageCallCount,
false);
319 m_state->setBoolean(ProfilerAgentState::preciseCoverageDetailed,
false);
320 v8::debug::Coverage::SelectMode(m_isolate, v8::debug::Coverage::kBestEffort);
321 return Response::OK();
325 std::unique_ptr<protocol::Profiler::CoverageRange> createCoverageRange(
326 int start,
int end,
int count) {
327 return protocol::Profiler::CoverageRange::create()
328 .setStartOffset(start)
334 Response coverageToProtocol(
336 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
338 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> result =
339 protocol::Array<protocol::Profiler::ScriptCoverage>::create();
340 v8::Isolate* isolate = inspector->isolate();
341 for (
size_t i = 0;
i < coverage.ScriptCount();
i++) {
344 std::unique_ptr<protocol::Array<protocol::Profiler::FunctionCoverage>>
346 protocol::Array<protocol::Profiler::FunctionCoverage>::create();
347 for (
size_t j = 0; j < script_data.FunctionCount(); j++) {
349 script_data.GetFunctionData(j);
350 std::unique_ptr<protocol::Array<protocol::Profiler::CoverageRange>>
351 ranges = protocol::Array<protocol::Profiler::CoverageRange>::create();
354 ranges->addItem(createCoverageRange(function_data.StartOffset(),
355 function_data.EndOffset(),
356 function_data.Count()));
359 for (
size_t k = 0; k < function_data.BlockCount(); k++) {
361 function_data.GetBlockData(k);
362 ranges->addItem(createCoverageRange(block_data.StartOffset(),
363 block_data.EndOffset(),
364 block_data.Count()));
368 protocol::Profiler::FunctionCoverage::create()
369 .setFunctionName(toProtocolString(
372 .setRanges(std::move(ranges))
373 .setIsBlockCoverage(function_data.HasBlockCoverage())
378 if (script->SourceURL().ToLocal(&name) && name->Length()) {
379 url = toProtocolString(isolate, name);
380 }
else if (script->Name().ToLocal(&name) && name->Length()) {
381 url = resourceNameToUrl(inspector, name);
383 result->addItem(protocol::Profiler::ScriptCoverage::create()
384 .setScriptId(String16::fromInteger(script->Id()))
386 .setFunctions(std::move(functions))
389 *out_result = std::move(result);
390 return Response::OK();
394 Response V8ProfilerAgentImpl::takePreciseCoverage(
395 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
397 if (!m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
399 return Response::Error(
"Precise coverage has not been started.");
403 return coverageToProtocol(m_session->inspector(), coverage, out_result);
406 Response V8ProfilerAgentImpl::getBestEffortCoverage(
407 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
411 v8::debug::Coverage::CollectBestEffort(m_isolate);
412 return coverageToProtocol(m_session->inspector(), coverage, out_result);
416 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>>
417 typeProfileToProtocol(V8InspectorImpl* inspector,
419 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>>
420 result = protocol::Array<protocol::Profiler::ScriptTypeProfile>::create();
421 v8::Isolate* isolate = inspector->isolate();
422 for (
size_t i = 0;
i < type_profile.ScriptCount();
i++) {
424 type_profile.GetScriptData(
i);
426 std::unique_ptr<protocol::Array<protocol::Profiler::TypeProfileEntry>>
428 protocol::Array<protocol::Profiler::TypeProfileEntry>::create();
430 for (
const auto& entry : script_data.Entries()) {
431 std::unique_ptr<protocol::Array<protocol::Profiler::TypeObject>> types =
432 protocol::Array<protocol::Profiler::TypeObject>::create();
433 for (
const auto& type : entry.Types()) {
435 protocol::Profiler::TypeObject::create()
436 .setName(toProtocolString(
440 entries->addItem(protocol::Profiler::TypeProfileEntry::create()
441 .setOffset(entry.SourcePosition())
442 .setTypes(std::move(types))
447 if (script->SourceURL().ToLocal(&name) && name->Length()) {
448 url = toProtocolString(isolate, name);
449 }
else if (script->Name().ToLocal(&name) && name->Length()) {
450 url = resourceNameToUrl(inspector, name);
452 result->addItem(protocol::Profiler::ScriptTypeProfile::create()
453 .setScriptId(String16::fromInteger(script->Id()))
455 .setEntries(std::move(entries))
462 Response V8ProfilerAgentImpl::startTypeProfile() {
463 m_state->setBoolean(ProfilerAgentState::typeProfileStarted,
true);
464 v8::debug::TypeProfile::SelectMode(m_isolate,
465 v8::debug::TypeProfile::kCollect);
466 return Response::OK();
469 Response V8ProfilerAgentImpl::stopTypeProfile() {
470 m_state->setBoolean(ProfilerAgentState::typeProfileStarted,
false);
471 v8::debug::TypeProfile::SelectMode(m_isolate, v8::debug::TypeProfile::kNone);
472 return Response::OK();
475 Response V8ProfilerAgentImpl::takeTypeProfile(
476 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>>*
478 if (!m_state->booleanProperty(ProfilerAgentState::typeProfileStarted,
480 return Response::Error(
"Type profile has not been started.");
484 v8::debug::TypeProfile::Collect(m_isolate);
485 *out_result = typeProfileToProtocol(m_session->inspector(), type_profile);
486 return Response::OK();
489 String16 V8ProfilerAgentImpl::nextProfileId() {
490 return String16::fromInteger(
491 v8::base::Relaxed_AtomicIncrement(&s_lastProfileId, 1));
494 void V8ProfilerAgentImpl::startProfiling(
const String16& title) {
496 if (!m_startedProfilesCount) {
500 m_state->integerProperty(ProfilerAgentState::samplingInterval, 0);
503 ++m_startedProfilesCount;
507 std::unique_ptr<protocol::Profiler::Profile> V8ProfilerAgentImpl::stopProfiling(
508 const String16& title,
bool serialize) {
512 std::unique_ptr<protocol::Profiler::Profile> result;
514 if (serialize) result = createCPUProfile(m_session->inspector(), profile);
517 --m_startedProfilesCount;
518 if (!m_startedProfilesCount) {
520 m_profiler =
nullptr;
int GetSamplesCount() const
int64_t GetStartTime() const
int64_t GetEndTime() const
unsigned GetNodeId() const
const CpuProfileNode * GetSample(int index) const
void SetSamplingInterval(int us)
CpuProfile * StopProfiling(Local< String > title)
static CpuProfiler * New(Isolate *isolate)
void StartProfiling(Local< String > title, CpuProfilingMode mode, bool record_samples=false)
const CpuProfileNode * GetTopDownRoot() const
int64_t GetSampleTimestamp(int index) const