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 "include/v8-profiler.h"
10 #include "src/base/atomicops.h"
11 #include "src/base/platform/time.h"
12 #include "src/debug/debug-interface.h"
13 #include "src/inspector/protocol/Protocol.h"
14 #include "src/inspector/string-util.h"
15 #include "src/inspector/v8-debugger.h"
16 #include "src/inspector/v8-inspector-impl.h"
17 #include "src/inspector/v8-inspector-session-impl.h"
18 #include "src/inspector/v8-stack-trace-impl.h"
19 #include "src/logging/tracing-flags.h"
20
21 namespace v8_inspector {
22
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 preciseCoverageAllowTriggeredUpdates[] =
31 "preciseCoverageAllowTriggeredUpdates";
32 static const char typeProfileStarted[] = "typeProfileStarted";
33 static const char countersEnabled[] = "countersEnabled";
34 static const char runtimeCallStatsEnabled[] = "runtimeCallStatsEnabled";
35 } // namespace ProfilerAgentState
36
37 namespace {
38
resourceNameToUrl(V8InspectorImpl * inspector,v8::Local<v8::String> v8Name)39 String16 resourceNameToUrl(V8InspectorImpl* inspector,
40 v8::Local<v8::String> v8Name) {
41 String16 name = toProtocolString(inspector->isolate(), v8Name);
42 if (!inspector) return name;
43 std::unique_ptr<StringBuffer> url =
44 inspector->client()->resourceNameToUrl(toStringView(name));
45 return url ? toString16(url->string()) : name;
46 }
47
48 std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>>
buildInspectorObjectForPositionTicks(const v8::CpuProfileNode * node)49 buildInspectorObjectForPositionTicks(const v8::CpuProfileNode* node) {
50 unsigned lineCount = node->GetHitLineCount();
51 if (!lineCount) return nullptr;
52 auto array =
53 std::make_unique<protocol::Array<protocol::Profiler::PositionTickInfo>>();
54 std::vector<v8::CpuProfileNode::LineTick> entries(lineCount);
55 if (node->GetLineTicks(&entries[0], lineCount)) {
56 for (unsigned i = 0; i < lineCount; i++) {
57 std::unique_ptr<protocol::Profiler::PositionTickInfo> line =
58 protocol::Profiler::PositionTickInfo::create()
59 .setLine(entries[i].line)
60 .setTicks(entries[i].hit_count)
61 .build();
62 array->emplace_back(std::move(line));
63 }
64 }
65 return array;
66 }
67
buildInspectorObjectFor(V8InspectorImpl * inspector,const v8::CpuProfileNode * node)68 std::unique_ptr<protocol::Profiler::ProfileNode> buildInspectorObjectFor(
69 V8InspectorImpl* inspector, const v8::CpuProfileNode* node) {
70 v8::Isolate* isolate = inspector->isolate();
71 v8::HandleScope handleScope(isolate);
72 auto callFrame =
73 protocol::Runtime::CallFrame::create()
74 .setFunctionName(toProtocolString(isolate, node->GetFunctionName()))
75 .setScriptId(String16::fromInteger(node->GetScriptId()))
76 .setUrl(resourceNameToUrl(inspector, node->GetScriptResourceName()))
77 .setLineNumber(node->GetLineNumber() - 1)
78 .setColumnNumber(node->GetColumnNumber() - 1)
79 .build();
80 auto result = protocol::Profiler::ProfileNode::create()
81 .setCallFrame(std::move(callFrame))
82 .setHitCount(node->GetHitCount())
83 .setId(node->GetNodeId())
84 .build();
85
86 const int childrenCount = node->GetChildrenCount();
87 if (childrenCount) {
88 auto children = std::make_unique<protocol::Array<int>>();
89 for (int i = 0; i < childrenCount; i++)
90 children->emplace_back(node->GetChild(i)->GetNodeId());
91 result->setChildren(std::move(children));
92 }
93
94 const char* deoptReason = node->GetBailoutReason();
95 if (deoptReason && deoptReason[0] && strcmp(deoptReason, "no reason"))
96 result->setDeoptReason(deoptReason);
97
98 auto positionTicks = buildInspectorObjectForPositionTicks(node);
99 if (positionTicks) result->setPositionTicks(std::move(positionTicks));
100
101 return result;
102 }
103
buildInspectorObjectForSamples(v8::CpuProfile * v8profile)104 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples(
105 v8::CpuProfile* v8profile) {
106 auto array = std::make_unique<protocol::Array<int>>();
107 int count = v8profile->GetSamplesCount();
108 for (int i = 0; i < count; i++)
109 array->emplace_back(v8profile->GetSample(i)->GetNodeId());
110 return array;
111 }
112
buildInspectorObjectForTimestamps(v8::CpuProfile * v8profile)113 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForTimestamps(
114 v8::CpuProfile* v8profile) {
115 auto array = std::make_unique<protocol::Array<int>>();
116 int count = v8profile->GetSamplesCount();
117 uint64_t lastTime = v8profile->GetStartTime();
118 for (int i = 0; i < count; i++) {
119 uint64_t ts = v8profile->GetSampleTimestamp(i);
120 array->emplace_back(static_cast<int>(ts - lastTime));
121 lastTime = ts;
122 }
123 return array;
124 }
125
flattenNodesTree(V8InspectorImpl * inspector,const v8::CpuProfileNode * node,protocol::Array<protocol::Profiler::ProfileNode> * list)126 void flattenNodesTree(V8InspectorImpl* inspector,
127 const v8::CpuProfileNode* node,
128 protocol::Array<protocol::Profiler::ProfileNode>* list) {
129 list->emplace_back(buildInspectorObjectFor(inspector, node));
130 const int childrenCount = node->GetChildrenCount();
131 for (int i = 0; i < childrenCount; i++)
132 flattenNodesTree(inspector, node->GetChild(i), list);
133 }
134
createCPUProfile(V8InspectorImpl * inspector,v8::CpuProfile * v8profile)135 std::unique_ptr<protocol::Profiler::Profile> createCPUProfile(
136 V8InspectorImpl* inspector, v8::CpuProfile* v8profile) {
137 auto nodes =
138 std::make_unique<protocol::Array<protocol::Profiler::ProfileNode>>();
139 flattenNodesTree(inspector, v8profile->GetTopDownRoot(), nodes.get());
140 return protocol::Profiler::Profile::create()
141 .setNodes(std::move(nodes))
142 .setStartTime(static_cast<double>(v8profile->GetStartTime()))
143 .setEndTime(static_cast<double>(v8profile->GetEndTime()))
144 .setSamples(buildInspectorObjectForSamples(v8profile))
145 .setTimeDeltas(buildInspectorObjectForTimestamps(v8profile))
146 .build();
147 }
148
currentDebugLocation(V8InspectorImpl * inspector)149 std::unique_ptr<protocol::Debugger::Location> currentDebugLocation(
150 V8InspectorImpl* inspector) {
151 std::unique_ptr<V8StackTraceImpl> callStack =
152 inspector->debugger()->captureStackTrace(false /* fullStack */);
153 auto location = protocol::Debugger::Location::create()
154 .setScriptId(toString16(callStack->topScriptId()))
155 .setLineNumber(callStack->topLineNumber())
156 .build();
157 location->setColumnNumber(callStack->topColumnNumber());
158 return location;
159 }
160
161 volatile int s_lastProfileId = 0;
162
163 } // namespace
164
165 class V8ProfilerAgentImpl::ProfileDescriptor {
166 public:
ProfileDescriptor(const String16 & id,const String16 & title)167 ProfileDescriptor(const String16& id, const String16& title)
168 : m_id(id), m_title(title) {}
169 String16 m_id;
170 String16 m_title;
171 };
172
V8ProfilerAgentImpl(V8InspectorSessionImpl * session,protocol::FrontendChannel * frontendChannel,protocol::DictionaryValue * state)173 V8ProfilerAgentImpl::V8ProfilerAgentImpl(
174 V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
175 protocol::DictionaryValue* state)
176 : m_session(session),
177 m_isolate(m_session->inspector()->isolate()),
178 m_state(state),
179 m_frontend(frontendChannel) {}
180
~V8ProfilerAgentImpl()181 V8ProfilerAgentImpl::~V8ProfilerAgentImpl() {
182 if (m_profiler) m_profiler->Dispose();
183 }
184
consoleProfile(const String16 & title)185 void V8ProfilerAgentImpl::consoleProfile(const String16& title) {
186 if (!m_enabled) return;
187 String16 id = nextProfileId();
188 m_startedProfiles.push_back(ProfileDescriptor(id, title));
189 startProfiling(id);
190 m_frontend.consoleProfileStarted(
191 id, currentDebugLocation(m_session->inspector()), title);
192 }
193
consoleProfileEnd(const String16 & title)194 void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) {
195 if (!m_enabled) return;
196 String16 id;
197 String16 resolvedTitle;
198 // Take last started profile if no title was passed.
199 if (title.isEmpty()) {
200 if (m_startedProfiles.empty()) return;
201 id = m_startedProfiles.back().m_id;
202 resolvedTitle = m_startedProfiles.back().m_title;
203 m_startedProfiles.pop_back();
204 } else {
205 for (size_t i = 0; i < m_startedProfiles.size(); i++) {
206 if (m_startedProfiles[i].m_title == title) {
207 resolvedTitle = title;
208 id = m_startedProfiles[i].m_id;
209 m_startedProfiles.erase(m_startedProfiles.begin() + i);
210 break;
211 }
212 }
213 if (id.isEmpty()) return;
214 }
215 std::unique_ptr<protocol::Profiler::Profile> profile =
216 stopProfiling(id, true);
217 if (!profile) return;
218 std::unique_ptr<protocol::Debugger::Location> location =
219 currentDebugLocation(m_session->inspector());
220 m_frontend.consoleProfileFinished(id, std::move(location), std::move(profile),
221 resolvedTitle);
222 }
223
enable()224 Response V8ProfilerAgentImpl::enable() {
225 if (!m_enabled) {
226 m_enabled = true;
227 m_state->setBoolean(ProfilerAgentState::profilerEnabled, true);
228 }
229
230 return Response::Success();
231 }
232
disable()233 Response V8ProfilerAgentImpl::disable() {
234 if (m_enabled) {
235 for (size_t i = m_startedProfiles.size(); i > 0; --i)
236 stopProfiling(m_startedProfiles[i - 1].m_id, false);
237 m_startedProfiles.clear();
238 stop(nullptr);
239 stopPreciseCoverage();
240 DCHECK(!m_profiler);
241 m_enabled = false;
242 m_state->setBoolean(ProfilerAgentState::profilerEnabled, false);
243 }
244
245 if (m_counters) {
246 disableCounters();
247 m_state->setBoolean(ProfilerAgentState::countersEnabled, false);
248 }
249
250 if (m_runtime_call_stats_enabled) {
251 disableRuntimeCallStats();
252 m_state->setBoolean(ProfilerAgentState::runtimeCallStatsEnabled, false);
253 }
254
255 return Response::Success();
256 }
257
setSamplingInterval(int interval)258 Response V8ProfilerAgentImpl::setSamplingInterval(int interval) {
259 if (m_profiler) {
260 return Response::ServerError(
261 "Cannot change sampling interval when profiling.");
262 }
263 m_state->setInteger(ProfilerAgentState::samplingInterval, interval);
264 return Response::Success();
265 }
266
restore()267 void V8ProfilerAgentImpl::restore() {
268 DCHECK(!m_enabled);
269 if (m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false)) {
270 m_enabled = true;
271 DCHECK(!m_profiler);
272 if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling,
273 false)) {
274 start();
275 }
276 if (m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
277 false)) {
278 bool callCount = m_state->booleanProperty(
279 ProfilerAgentState::preciseCoverageCallCount, false);
280 bool detailed = m_state->booleanProperty(
281 ProfilerAgentState::preciseCoverageDetailed, false);
282 bool updatesAllowed = m_state->booleanProperty(
283 ProfilerAgentState::preciseCoverageAllowTriggeredUpdates, false);
284 double timestamp;
285 startPreciseCoverage(Maybe<bool>(callCount), Maybe<bool>(detailed),
286 Maybe<bool>(updatesAllowed), ×tamp);
287 }
288 }
289
290 if (m_state->booleanProperty(ProfilerAgentState::countersEnabled, false)) {
291 enableCounters();
292 }
293
294 if (m_state->booleanProperty(ProfilerAgentState::runtimeCallStatsEnabled,
295 false)) {
296 enableRuntimeCallStats();
297 }
298 }
299
start()300 Response V8ProfilerAgentImpl::start() {
301 if (m_recordingCPUProfile) return Response::Success();
302 if (!m_enabled) return Response::ServerError("Profiler is not enabled");
303 m_recordingCPUProfile = true;
304 m_frontendInitiatedProfileId = nextProfileId();
305 startProfiling(m_frontendInitiatedProfileId);
306 m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, true);
307 return Response::Success();
308 }
309
stop(std::unique_ptr<protocol::Profiler::Profile> * profile)310 Response V8ProfilerAgentImpl::stop(
311 std::unique_ptr<protocol::Profiler::Profile>* profile) {
312 if (!m_recordingCPUProfile) {
313 return Response::ServerError("No recording profiles found");
314 }
315 m_recordingCPUProfile = false;
316 std::unique_ptr<protocol::Profiler::Profile> cpuProfile =
317 stopProfiling(m_frontendInitiatedProfileId, !!profile);
318 if (profile) {
319 *profile = std::move(cpuProfile);
320 if (!profile->get()) return Response::ServerError("Profile is not found");
321 }
322 m_frontendInitiatedProfileId = String16();
323 m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false);
324 return Response::Success();
325 }
326
startPreciseCoverage(Maybe<bool> callCount,Maybe<bool> detailed,Maybe<bool> allowTriggeredUpdates,double * out_timestamp)327 Response V8ProfilerAgentImpl::startPreciseCoverage(
328 Maybe<bool> callCount, Maybe<bool> detailed,
329 Maybe<bool> allowTriggeredUpdates, double* out_timestamp) {
330 if (!m_enabled) return Response::ServerError("Profiler is not enabled");
331 *out_timestamp =
332 v8::base::TimeTicks::HighResolutionNow().since_origin().InSecondsF();
333 bool callCountValue = callCount.fromMaybe(false);
334 bool detailedValue = detailed.fromMaybe(false);
335 bool allowTriggeredUpdatesValue = allowTriggeredUpdates.fromMaybe(false);
336 m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted, true);
337 m_state->setBoolean(ProfilerAgentState::preciseCoverageCallCount,
338 callCountValue);
339 m_state->setBoolean(ProfilerAgentState::preciseCoverageDetailed,
340 detailedValue);
341 m_state->setBoolean(ProfilerAgentState::preciseCoverageAllowTriggeredUpdates,
342 allowTriggeredUpdatesValue);
343 // BlockCount is a superset of PreciseCount. It includes block-granularity
344 // coverage data if it exists (at the time of writing, that's the case for
345 // each function recompiled after the BlockCount mode has been set); and
346 // function-granularity coverage data otherwise.
347 using C = v8::debug::Coverage;
348 using Mode = v8::debug::CoverageMode;
349 Mode mode = callCountValue
350 ? (detailedValue ? Mode::kBlockCount : Mode::kPreciseCount)
351 : (detailedValue ? Mode::kBlockBinary : Mode::kPreciseBinary);
352 C::SelectMode(m_isolate, mode);
353 return Response::Success();
354 }
355
stopPreciseCoverage()356 Response V8ProfilerAgentImpl::stopPreciseCoverage() {
357 if (!m_enabled) return Response::ServerError("Profiler is not enabled");
358 m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted, false);
359 m_state->setBoolean(ProfilerAgentState::preciseCoverageCallCount, false);
360 m_state->setBoolean(ProfilerAgentState::preciseCoverageDetailed, false);
361 v8::debug::Coverage::SelectMode(m_isolate,
362 v8::debug::CoverageMode::kBestEffort);
363 return Response::Success();
364 }
365
366 namespace {
createCoverageRange(int start,int end,int count)367 std::unique_ptr<protocol::Profiler::CoverageRange> createCoverageRange(
368 int start, int end, int count) {
369 return protocol::Profiler::CoverageRange::create()
370 .setStartOffset(start)
371 .setEndOffset(end)
372 .setCount(count)
373 .build();
374 }
375
coverageToProtocol(V8InspectorImpl * inspector,const v8::debug::Coverage & coverage,std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> * out_result)376 Response coverageToProtocol(
377 V8InspectorImpl* inspector, const v8::debug::Coverage& coverage,
378 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
379 out_result) {
380 auto result =
381 std::make_unique<protocol::Array<protocol::Profiler::ScriptCoverage>>();
382 v8::Isolate* isolate = inspector->isolate();
383 for (size_t i = 0; i < coverage.ScriptCount(); i++) {
384 v8::debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
385 v8::Local<v8::debug::Script> script = script_data.GetScript();
386 auto functions = std::make_unique<
387 protocol::Array<protocol::Profiler::FunctionCoverage>>();
388 for (size_t j = 0; j < script_data.FunctionCount(); j++) {
389 v8::debug::Coverage::FunctionData function_data =
390 script_data.GetFunctionData(j);
391 auto ranges = std::make_unique<
392 protocol::Array<protocol::Profiler::CoverageRange>>();
393
394 // Add function range.
395 ranges->emplace_back(createCoverageRange(function_data.StartOffset(),
396 function_data.EndOffset(),
397 function_data.Count()));
398
399 // Process inner blocks.
400 for (size_t k = 0; k < function_data.BlockCount(); k++) {
401 v8::debug::Coverage::BlockData block_data =
402 function_data.GetBlockData(k);
403 ranges->emplace_back(createCoverageRange(block_data.StartOffset(),
404 block_data.EndOffset(),
405 block_data.Count()));
406 }
407
408 functions->emplace_back(
409 protocol::Profiler::FunctionCoverage::create()
410 .setFunctionName(toProtocolString(
411 isolate,
412 function_data.Name().FromMaybe(v8::Local<v8::String>())))
413 .setRanges(std::move(ranges))
414 .setIsBlockCoverage(function_data.HasBlockCoverage())
415 .build());
416 }
417 String16 url;
418 v8::Local<v8::String> name;
419 if (script->SourceURL().ToLocal(&name) && name->Length()) {
420 url = toProtocolString(isolate, name);
421 } else if (script->Name().ToLocal(&name) && name->Length()) {
422 url = resourceNameToUrl(inspector, name);
423 }
424 result->emplace_back(protocol::Profiler::ScriptCoverage::create()
425 .setScriptId(String16::fromInteger(script->Id()))
426 .setUrl(url)
427 .setFunctions(std::move(functions))
428 .build());
429 }
430 *out_result = std::move(result);
431 return Response::Success();
432 }
433 } // anonymous namespace
434
takePreciseCoverage(std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> * out_result,double * out_timestamp)435 Response V8ProfilerAgentImpl::takePreciseCoverage(
436 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
437 out_result,
438 double* out_timestamp) {
439 if (!m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
440 false)) {
441 return Response::ServerError("Precise coverage has not been started.");
442 }
443 v8::HandleScope handle_scope(m_isolate);
444 v8::debug::Coverage coverage = v8::debug::Coverage::CollectPrecise(m_isolate);
445 *out_timestamp =
446 v8::base::TimeTicks::HighResolutionNow().since_origin().InSecondsF();
447 return coverageToProtocol(m_session->inspector(), coverage, out_result);
448 }
449
triggerPreciseCoverageDeltaUpdate(const String16 & occassion)450 void V8ProfilerAgentImpl::triggerPreciseCoverageDeltaUpdate(
451 const String16& occassion) {
452 if (!m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
453 false)) {
454 return;
455 }
456 if (!m_state->booleanProperty(
457 ProfilerAgentState::preciseCoverageAllowTriggeredUpdates, false)) {
458 return;
459 }
460 v8::HandleScope handle_scope(m_isolate);
461 v8::debug::Coverage coverage = v8::debug::Coverage::CollectPrecise(m_isolate);
462 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>
463 out_result;
464 coverageToProtocol(m_session->inspector(), coverage, &out_result);
465 double now =
466 v8::base::TimeTicks::HighResolutionNow().since_origin().InSecondsF();
467 m_frontend.preciseCoverageDeltaUpdate(now, occassion, std::move(out_result));
468 }
469
getBestEffortCoverage(std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> * out_result)470 Response V8ProfilerAgentImpl::getBestEffortCoverage(
471 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
472 out_result) {
473 v8::HandleScope handle_scope(m_isolate);
474 v8::debug::Coverage coverage =
475 v8::debug::Coverage::CollectBestEffort(m_isolate);
476 return coverageToProtocol(m_session->inspector(), coverage, out_result);
477 }
478
479 namespace {
480 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>>
typeProfileToProtocol(V8InspectorImpl * inspector,const v8::debug::TypeProfile & type_profile)481 typeProfileToProtocol(V8InspectorImpl* inspector,
482 const v8::debug::TypeProfile& type_profile) {
483 auto result = std::make_unique<
484 protocol::Array<protocol::Profiler::ScriptTypeProfile>>();
485 v8::Isolate* isolate = inspector->isolate();
486 for (size_t i = 0; i < type_profile.ScriptCount(); i++) {
487 v8::debug::TypeProfile::ScriptData script_data =
488 type_profile.GetScriptData(i);
489 v8::Local<v8::debug::Script> script = script_data.GetScript();
490 auto entries = std::make_unique<
491 protocol::Array<protocol::Profiler::TypeProfileEntry>>();
492
493 for (const auto& entry : script_data.Entries()) {
494 auto types =
495 std::make_unique<protocol::Array<protocol::Profiler::TypeObject>>();
496 for (const auto& type : entry.Types()) {
497 types->emplace_back(
498 protocol::Profiler::TypeObject::create()
499 .setName(toProtocolString(
500 isolate, type.FromMaybe(v8::Local<v8::String>())))
501 .build());
502 }
503 entries->emplace_back(protocol::Profiler::TypeProfileEntry::create()
504 .setOffset(entry.SourcePosition())
505 .setTypes(std::move(types))
506 .build());
507 }
508 String16 url;
509 v8::Local<v8::String> name;
510 if (script->SourceURL().ToLocal(&name) && name->Length()) {
511 url = toProtocolString(isolate, name);
512 } else if (script->Name().ToLocal(&name) && name->Length()) {
513 url = resourceNameToUrl(inspector, name);
514 }
515 result->emplace_back(protocol::Profiler::ScriptTypeProfile::create()
516 .setScriptId(String16::fromInteger(script->Id()))
517 .setUrl(url)
518 .setEntries(std::move(entries))
519 .build());
520 }
521 return result;
522 }
523 } // anonymous namespace
524
startTypeProfile()525 Response V8ProfilerAgentImpl::startTypeProfile() {
526 m_state->setBoolean(ProfilerAgentState::typeProfileStarted, true);
527 v8::debug::TypeProfile::SelectMode(m_isolate,
528 v8::debug::TypeProfileMode::kCollect);
529 return Response::Success();
530 }
531
stopTypeProfile()532 Response V8ProfilerAgentImpl::stopTypeProfile() {
533 m_state->setBoolean(ProfilerAgentState::typeProfileStarted, false);
534 v8::debug::TypeProfile::SelectMode(m_isolate,
535 v8::debug::TypeProfileMode::kNone);
536 return Response::Success();
537 }
538
takeTypeProfile(std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>> * out_result)539 Response V8ProfilerAgentImpl::takeTypeProfile(
540 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>>*
541 out_result) {
542 if (!m_state->booleanProperty(ProfilerAgentState::typeProfileStarted,
543 false)) {
544 return Response::ServerError("Type profile has not been started.");
545 }
546 v8::HandleScope handle_scope(m_isolate);
547 v8::debug::TypeProfile type_profile =
548 v8::debug::TypeProfile::Collect(m_isolate);
549 *out_result = typeProfileToProtocol(m_session->inspector(), type_profile);
550 return Response::Success();
551 }
552
enableCounters()553 Response V8ProfilerAgentImpl::enableCounters() {
554 if (m_counters)
555 return Response::ServerError("Counters collection already enabled.");
556
557 if (V8Inspector* inspector = v8::debug::GetInspector(m_isolate))
558 m_counters = inspector->enableCounters();
559 else
560 return Response::ServerError("No inspector found.");
561
562 return Response::Success();
563 }
564
disableCounters()565 Response V8ProfilerAgentImpl::disableCounters() {
566 if (m_counters) m_counters.reset();
567 return Response::Success();
568 }
569
getCounters(std::unique_ptr<protocol::Array<protocol::Profiler::CounterInfo>> * out_result)570 Response V8ProfilerAgentImpl::getCounters(
571 std::unique_ptr<protocol::Array<protocol::Profiler::CounterInfo>>*
572 out_result) {
573 if (!m_counters)
574 return Response::ServerError("Counters collection is not enabled.");
575
576 *out_result =
577 std::make_unique<protocol::Array<protocol::Profiler::CounterInfo>>();
578
579 for (const auto& counter : m_counters->getCountersMap()) {
580 (*out_result)
581 ->emplace_back(
582 protocol::Profiler::CounterInfo::create()
583 .setName(String16(counter.first.data(), counter.first.length()))
584 .setValue(counter.second)
585 .build());
586 }
587
588 return Response::Success();
589 }
590
enableRuntimeCallStats()591 Response V8ProfilerAgentImpl::enableRuntimeCallStats() {
592 if (v8::internal::TracingFlags::runtime_stats.load()) {
593 return Response::ServerError(
594 "Runtime Call Stats collection is already enabled.");
595 }
596
597 v8::internal::TracingFlags::runtime_stats.store(true);
598 m_runtime_call_stats_enabled = true;
599
600 return Response::Success();
601 }
602
disableRuntimeCallStats()603 Response V8ProfilerAgentImpl::disableRuntimeCallStats() {
604 if (!v8::internal::TracingFlags::runtime_stats.load()) {
605 return Response::ServerError(
606 "Runtime Call Stats collection is not enabled.");
607 }
608
609 if (!m_runtime_call_stats_enabled) {
610 return Response::ServerError(
611 "Runtime Call Stats collection was not enabled by this session.");
612 }
613
614 v8::internal::TracingFlags::runtime_stats.store(false);
615 m_runtime_call_stats_enabled = false;
616
617 return Response::Success();
618 }
619
getRuntimeCallStats(std::unique_ptr<protocol::Array<protocol::Profiler::RuntimeCallCounterInfo>> * out_result)620 Response V8ProfilerAgentImpl::getRuntimeCallStats(
621 std::unique_ptr<
622 protocol::Array<protocol::Profiler::RuntimeCallCounterInfo>>*
623 out_result) {
624 if (!m_runtime_call_stats_enabled) {
625 return Response::ServerError(
626 "Runtime Call Stats collection is not enabled.");
627 }
628
629 if (!v8::internal::TracingFlags::runtime_stats.load()) {
630 return Response::ServerError(
631 "Runtime Call Stats collection was disabled outside of this session.");
632 }
633
634 *out_result = std::make_unique<
635 protocol::Array<protocol::Profiler::RuntimeCallCounterInfo>>();
636
637 v8::debug::EnumerateRuntimeCallCounters(
638 m_isolate,
639 [&](const char* name, int64_t count, v8::base::TimeDelta time) {
640 (*out_result)
641 ->emplace_back(protocol::Profiler::RuntimeCallCounterInfo::create()
642 .setName(String16(name))
643 .setValue(static_cast<double>(count))
644 .setTime(time.InSecondsF())
645 .build());
646 });
647
648 return Response::Success();
649 }
650
nextProfileId()651 String16 V8ProfilerAgentImpl::nextProfileId() {
652 return String16::fromInteger(
653 v8::base::Relaxed_AtomicIncrement(&s_lastProfileId, 1));
654 }
655
startProfiling(const String16 & title)656 void V8ProfilerAgentImpl::startProfiling(const String16& title) {
657 v8::HandleScope handleScope(m_isolate);
658 if (!m_startedProfilesCount) {
659 DCHECK(!m_profiler);
660 m_profiler = v8::CpuProfiler::New(m_isolate);
661 int interval =
662 m_state->integerProperty(ProfilerAgentState::samplingInterval, 0);
663 if (interval) m_profiler->SetSamplingInterval(interval);
664 }
665 ++m_startedProfilesCount;
666 m_profiler->StartProfiling(toV8String(m_isolate, title), true);
667 }
668
stopProfiling(const String16 & title,bool serialize)669 std::unique_ptr<protocol::Profiler::Profile> V8ProfilerAgentImpl::stopProfiling(
670 const String16& title, bool serialize) {
671 v8::HandleScope handleScope(m_isolate);
672 v8::CpuProfile* profile =
673 m_profiler->StopProfiling(toV8String(m_isolate, title));
674 std::unique_ptr<protocol::Profiler::Profile> result;
675 if (profile) {
676 if (serialize) result = createCPUProfile(m_session->inspector(), profile);
677 profile->Delete();
678 }
679 --m_startedProfilesCount;
680 if (!m_startedProfilesCount) {
681 m_profiler->Dispose();
682 m_profiler = nullptr;
683 }
684 return result;
685 }
686
687 } // namespace v8_inspector
688