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