• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/inspector/v8-profiler-agent-impl.h"
6 
7 #include <vector>
8 
9 #include "src/base/atomicops.h"
10 #include "src/debug/debug-interface.h"
11 #include "src/flags.h"  // TODO(jgruber): Remove include and DEPS entry.
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"
18 
19 #include "include/v8-profiler.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 typeProfileStarted[] = "typeProfileStarted";
31 }
32 
33 namespace {
34 
resourceNameToUrl(V8InspectorImpl * inspector,v8::Local<v8::String> v8Name)35 String16 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 
44 std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>>
buildInspectorObjectForPositionTicks(const v8::CpuProfileNode * node)45 buildInspectorObjectForPositionTicks(const v8::CpuProfileNode* node) {
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)
56               .build();
57       array->addItem(std::move(line));
58     }
59   }
60   return array;
61 }
62 
buildInspectorObjectFor(V8InspectorImpl * inspector,const v8::CpuProfileNode * node)63 std::unique_ptr<protocol::Profiler::ProfileNode> buildInspectorObjectFor(
64     V8InspectorImpl* inspector, const v8::CpuProfileNode* node) {
65   v8::Isolate* isolate = inspector->isolate();
66   v8::HandleScope handleScope(isolate);
67   auto callFrame =
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)
74           .build();
75   auto result = protocol::Profiler::ProfileNode::create()
76                     .setCallFrame(std::move(callFrame))
77                     .setHitCount(node->GetHitCount())
78                     .setId(node->GetNodeId())
79                     .build();
80 
81   const int childrenCount = node->GetChildrenCount();
82   if (childrenCount) {
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));
87   }
88 
89   const char* deoptReason = node->GetBailoutReason();
90   if (deoptReason && deoptReason[0] && strcmp(deoptReason, "no reason"))
91     result->setDeoptReason(deoptReason);
92 
93   auto positionTicks = buildInspectorObjectForPositionTicks(node);
94   if (positionTicks) result->setPositionTicks(std::move(positionTicks));
95 
96   return result;
97 }
98 
buildInspectorObjectForSamples(v8::CpuProfile * v8profile)99 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples(
100     v8::CpuProfile* v8profile) {
101   auto array = protocol::Array<int>::create();
102   int count = v8profile->GetSamplesCount();
103   for (int i = 0; i < count; i++)
104     array->addItem(v8profile->GetSample(i)->GetNodeId());
105   return array;
106 }
107 
buildInspectorObjectForTimestamps(v8::CpuProfile * v8profile)108 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForTimestamps(
109     v8::CpuProfile* v8profile) {
110   auto array = protocol::Array<int>::create();
111   int count = v8profile->GetSamplesCount();
112   uint64_t lastTime = v8profile->GetStartTime();
113   for (int i = 0; i < count; i++) {
114     uint64_t ts = v8profile->GetSampleTimestamp(i);
115     array->addItem(static_cast<int>(ts - lastTime));
116     lastTime = ts;
117   }
118   return array;
119 }
120 
flattenNodesTree(V8InspectorImpl * inspector,const v8::CpuProfileNode * node,protocol::Array<protocol::Profiler::ProfileNode> * list)121 void flattenNodesTree(V8InspectorImpl* inspector,
122                       const v8::CpuProfileNode* node,
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);
128 }
129 
createCPUProfile(V8InspectorImpl * inspector,v8::CpuProfile * v8profile)130 std::unique_ptr<protocol::Profiler::Profile> createCPUProfile(
131     V8InspectorImpl* inspector, v8::CpuProfile* v8profile) {
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))
140       .build();
141 }
142 
currentDebugLocation(V8InspectorImpl * inspector)143 std::unique_ptr<protocol::Debugger::Location> currentDebugLocation(
144     V8InspectorImpl* inspector) {
145   std::unique_ptr<V8StackTraceImpl> callStack =
146       inspector->debugger()->captureStackTrace(false /* fullStack */);
147   auto location = protocol::Debugger::Location::create()
148                       .setScriptId(toString16(callStack->topScriptId()))
149                       .setLineNumber(callStack->topLineNumber())
150                       .build();
151   location->setColumnNumber(callStack->topColumnNumber());
152   return location;
153 }
154 
155 volatile int s_lastProfileId = 0;
156 
157 }  // namespace
158 
159 class V8ProfilerAgentImpl::ProfileDescriptor {
160  public:
ProfileDescriptor(const String16 & id,const String16 & title)161   ProfileDescriptor(const String16& id, const String16& title)
162       : m_id(id), m_title(title) {}
163   String16 m_id;
164   String16 m_title;
165 };
166 
V8ProfilerAgentImpl(V8InspectorSessionImpl * session,protocol::FrontendChannel * frontendChannel,protocol::DictionaryValue * state)167 V8ProfilerAgentImpl::V8ProfilerAgentImpl(
168     V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
169     protocol::DictionaryValue* state)
170     : m_session(session),
171       m_isolate(m_session->inspector()->isolate()),
172       m_state(state),
173       m_frontend(frontendChannel) {}
174 
~V8ProfilerAgentImpl()175 V8ProfilerAgentImpl::~V8ProfilerAgentImpl() {
176   if (m_profiler) m_profiler->Dispose();
177 }
178 
consoleProfile(const String16 & title)179 void V8ProfilerAgentImpl::consoleProfile(const String16& title) {
180   if (!m_enabled) return;
181   String16 id = nextProfileId();
182   m_startedProfiles.push_back(ProfileDescriptor(id, title));
183   startProfiling(id);
184   m_frontend.consoleProfileStarted(
185       id, currentDebugLocation(m_session->inspector()), title);
186 }
187 
consoleProfileEnd(const String16 & title)188 void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) {
189   if (!m_enabled) return;
190   String16 id;
191   String16 resolvedTitle;
192   // Take last started profile if no title was passed.
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();
198   } else {
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);
204         break;
205       }
206     }
207     if (id.isEmpty()) return;
208   }
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),
215                                     resolvedTitle);
216 }
217 
enable()218 Response V8ProfilerAgentImpl::enable() {
219   if (m_enabled) return Response::OK();
220   m_enabled = true;
221   m_state->setBoolean(ProfilerAgentState::profilerEnabled, true);
222   return Response::OK();
223 }
224 
disable()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();
230   stop(nullptr);
231   stopPreciseCoverage();
232   DCHECK(!m_profiler);
233   m_enabled = false;
234   m_state->setBoolean(ProfilerAgentState::profilerEnabled, false);
235   return Response::OK();
236 }
237 
setSamplingInterval(int interval)238 Response V8ProfilerAgentImpl::setSamplingInterval(int interval) {
239   if (m_profiler) {
240     return Response::Error("Cannot change sampling interval when profiling.");
241   }
242   m_state->setInteger(ProfilerAgentState::samplingInterval, interval);
243   return Response::OK();
244 }
245 
restore()246 void V8ProfilerAgentImpl::restore() {
247   DCHECK(!m_enabled);
248   if (!m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false))
249     return;
250   m_enabled = true;
251   DCHECK(!m_profiler);
252   if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling,
253                                false)) {
254     start();
255   }
256   if (m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
257                                false)) {
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));
263   }
264 }
265 
start()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();
274 }
275 
stop(std::unique_ptr<protocol::Profiler::Profile> * profile)276 Response V8ProfilerAgentImpl::stop(
277     std::unique_ptr<protocol::Profiler::Profile>* profile) {
278   if (!m_recordingCPUProfile) {
279     return Response::Error("No recording profiles found");
280   }
281   m_recordingCPUProfile = false;
282   std::unique_ptr<protocol::Profiler::Profile> cpuProfile =
283       stopProfiling(m_frontendInitiatedProfileId, !!profile);
284   if (profile) {
285     *profile = std::move(cpuProfile);
286     if (!profile->get()) return Response::Error("Profile is not found");
287   }
288   m_frontendInitiatedProfileId = String16();
289   m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false);
290   return Response::OK();
291 }
292 
startPreciseCoverage(Maybe<bool> callCount,Maybe<bool> detailed)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,
300                       callCountValue);
301   m_state->setBoolean(ProfilerAgentState::preciseCoverageDetailed,
302                       detailedValue);
303   // BlockCount is a superset of PreciseCount. It includes block-granularity
304   // coverage data if it exists (at the time of writing, that's the case for
305   // each function recompiled after the BlockCount mode has been set); and
306   // function-granularity coverage data otherwise.
307   typedef v8::debug::Coverage C;
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();
313 }
314 
stopPreciseCoverage()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();
322 }
323 
324 namespace {
createCoverageRange(int start,int end,int count)325 std::unique_ptr<protocol::Profiler::CoverageRange> createCoverageRange(
326     int start, int end, int count) {
327   return protocol::Profiler::CoverageRange::create()
328       .setStartOffset(start)
329       .setEndOffset(end)
330       .setCount(count)
331       .build();
332 }
333 
coverageToProtocol(V8InspectorImpl * inspector,const v8::debug::Coverage & coverage,std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> * out_result)334 Response coverageToProtocol(
335     V8InspectorImpl* inspector, const v8::debug::Coverage& coverage,
336     std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
337         out_result) {
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++) {
342     v8::debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
343     v8::Local<v8::debug::Script> script = script_data.GetScript();
344     std::unique_ptr<protocol::Array<protocol::Profiler::FunctionCoverage>>
345         functions =
346             protocol::Array<protocol::Profiler::FunctionCoverage>::create();
347     for (size_t j = 0; j < script_data.FunctionCount(); j++) {
348       v8::debug::Coverage::FunctionData function_data =
349           script_data.GetFunctionData(j);
350       std::unique_ptr<protocol::Array<protocol::Profiler::CoverageRange>>
351           ranges = protocol::Array<protocol::Profiler::CoverageRange>::create();
352 
353       // Add function range.
354       ranges->addItem(createCoverageRange(function_data.StartOffset(),
355                                           function_data.EndOffset(),
356                                           function_data.Count()));
357 
358       // Process inner blocks.
359       for (size_t k = 0; k < function_data.BlockCount(); k++) {
360         v8::debug::Coverage::BlockData block_data =
361             function_data.GetBlockData(k);
362         ranges->addItem(createCoverageRange(block_data.StartOffset(),
363                                             block_data.EndOffset(),
364                                             block_data.Count()));
365       }
366 
367       functions->addItem(
368           protocol::Profiler::FunctionCoverage::create()
369               .setFunctionName(toProtocolString(
370                   isolate,
371                   function_data.Name().FromMaybe(v8::Local<v8::String>())))
372               .setRanges(std::move(ranges))
373               .setIsBlockCoverage(function_data.HasBlockCoverage())
374               .build());
375     }
376     String16 url;
377     v8::Local<v8::String> name;
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);
382     }
383     result->addItem(protocol::Profiler::ScriptCoverage::create()
384                         .setScriptId(String16::fromInteger(script->Id()))
385                         .setUrl(url)
386                         .setFunctions(std::move(functions))
387                         .build());
388   }
389   *out_result = std::move(result);
390   return Response::OK();
391 }
392 }  // anonymous namespace
393 
takePreciseCoverage(std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> * out_result)394 Response V8ProfilerAgentImpl::takePreciseCoverage(
395     std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
396         out_result) {
397   if (!m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
398                                 false)) {
399     return Response::Error("Precise coverage has not been started.");
400   }
401   v8::HandleScope handle_scope(m_isolate);
402   v8::debug::Coverage coverage = v8::debug::Coverage::CollectPrecise(m_isolate);
403   return coverageToProtocol(m_session->inspector(), coverage, out_result);
404 }
405 
getBestEffortCoverage(std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> * out_result)406 Response V8ProfilerAgentImpl::getBestEffortCoverage(
407     std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
408         out_result) {
409   v8::HandleScope handle_scope(m_isolate);
410   v8::debug::Coverage coverage =
411       v8::debug::Coverage::CollectBestEffort(m_isolate);
412   return coverageToProtocol(m_session->inspector(), coverage, out_result);
413 }
414 
415 namespace {
416 std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>>
typeProfileToProtocol(V8InspectorImpl * inspector,const v8::debug::TypeProfile & type_profile)417 typeProfileToProtocol(V8InspectorImpl* inspector,
418                       const v8::debug::TypeProfile& type_profile) {
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++) {
423     v8::debug::TypeProfile::ScriptData script_data =
424         type_profile.GetScriptData(i);
425     v8::Local<v8::debug::Script> script = script_data.GetScript();
426     std::unique_ptr<protocol::Array<protocol::Profiler::TypeProfileEntry>>
427         entries =
428             protocol::Array<protocol::Profiler::TypeProfileEntry>::create();
429 
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()) {
434         types->addItem(
435             protocol::Profiler::TypeObject::create()
436                 .setName(toProtocolString(
437                     isolate, type.FromMaybe(v8::Local<v8::String>())))
438                 .build());
439       }
440       entries->addItem(protocol::Profiler::TypeProfileEntry::create()
441                            .setOffset(entry.SourcePosition())
442                            .setTypes(std::move(types))
443                            .build());
444     }
445     String16 url;
446     v8::Local<v8::String> name;
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);
451     }
452     result->addItem(protocol::Profiler::ScriptTypeProfile::create()
453                         .setScriptId(String16::fromInteger(script->Id()))
454                         .setUrl(url)
455                         .setEntries(std::move(entries))
456                         .build());
457   }
458   return result;
459 }
460 }  // anonymous namespace
461 
startTypeProfile()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();
467 }
468 
stopTypeProfile()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();
473 }
474 
takeTypeProfile(std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>> * out_result)475 Response V8ProfilerAgentImpl::takeTypeProfile(
476     std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>>*
477         out_result) {
478   if (!m_state->booleanProperty(ProfilerAgentState::typeProfileStarted,
479                                 false)) {
480     return Response::Error("Type profile has not been started.");
481   }
482   v8::HandleScope handle_scope(m_isolate);
483   v8::debug::TypeProfile type_profile =
484       v8::debug::TypeProfile::Collect(m_isolate);
485   *out_result = typeProfileToProtocol(m_session->inspector(), type_profile);
486   return Response::OK();
487 }
488 
nextProfileId()489 String16 V8ProfilerAgentImpl::nextProfileId() {
490   return String16::fromInteger(
491       v8::base::Relaxed_AtomicIncrement(&s_lastProfileId, 1));
492 }
493 
startProfiling(const String16 & title)494 void V8ProfilerAgentImpl::startProfiling(const String16& title) {
495   v8::HandleScope handleScope(m_isolate);
496   if (!m_startedProfilesCount) {
497     DCHECK(!m_profiler);
498     m_profiler = v8::CpuProfiler::New(m_isolate);
499     int interval =
500         m_state->integerProperty(ProfilerAgentState::samplingInterval, 0);
501     if (interval) m_profiler->SetSamplingInterval(interval);
502   }
503   ++m_startedProfilesCount;
504   m_profiler->StartProfiling(toV8String(m_isolate, title), true);
505 }
506 
stopProfiling(const String16 & title,bool serialize)507 std::unique_ptr<protocol::Profiler::Profile> V8ProfilerAgentImpl::stopProfiling(
508     const String16& title, bool serialize) {
509   v8::HandleScope handleScope(m_isolate);
510   v8::CpuProfile* profile =
511       m_profiler->StopProfiling(toV8String(m_isolate, title));
512   std::unique_ptr<protocol::Profiler::Profile> result;
513   if (profile) {
514     if (serialize) result = createCPUProfile(m_session->inspector(), profile);
515     profile->Delete();
516   }
517   --m_startedProfilesCount;
518   if (!m_startedProfilesCount) {
519     m_profiler->Dispose();
520     m_profiler = nullptr;
521   }
522   return result;
523 }
524 
525 }  // namespace v8_inspector
526