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), &timestamp);
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