v8-profiler-agent-impl.cc 21.6 KB
Newer Older
1 2 3 4
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/inspector/v8-profiler-agent-impl.h"
6

7 8
#include <vector>

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 15 16 17 18
#include "src/inspector/string-util.h"
#include "src/inspector/v8-debugger.h"
#include "src/inspector/v8-inspector-impl.h"
#include "src/inspector/v8-inspector-session-impl.h"
#include "src/inspector/v8-stack-trace-impl.h"
19

20 21 22 23 24 25
namespace v8_inspector {

namespace ProfilerAgentState {
static const char samplingInterval[] = "samplingInterval";
static const char userInitiatedProfiling[] = "userInitiatedProfiling";
static const char profilerEnabled[] = "profilerEnabled";
26
static const char preciseCoverageStarted[] = "preciseCoverageStarted";
27
static const char preciseCoverageCallCount[] = "preciseCoverageCallCount";
28
static const char preciseCoverageDetailed[] = "preciseCoverageDetailed";
29 30
static const char preciseCoverageAllowTriggeredUpdates[] =
    "preciseCoverageAllowTriggeredUpdates";
31
static const char typeProfileStarted[] = "typeProfileStarted";
32
}  // namespace ProfilerAgentState
33 34 35

namespace {

36 37 38 39 40 41 42 43 44
String16 resourceNameToUrl(V8InspectorImpl* inspector,
                           v8::Local<v8::String> v8Name) {
  String16 name = toProtocolString(inspector->isolate(), v8Name);
  if (!inspector) return name;
  std::unique_ptr<StringBuffer> url =
      inspector->client()->resourceNameToUrl(toStringView(name));
  return url ? toString16(url->string()) : name;
}

45 46 47 48
std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>>
buildInspectorObjectForPositionTicks(const v8::CpuProfileNode* node) {
  unsigned lineCount = node->GetHitLineCount();
  if (!lineCount) return nullptr;
49 50
  auto array =
      std::make_unique<protocol::Array<protocol::Profiler::PositionTickInfo>>();
51 52 53 54 55 56 57 58
  std::vector<v8::CpuProfileNode::LineTick> entries(lineCount);
  if (node->GetLineTicks(&entries[0], lineCount)) {
    for (unsigned i = 0; i < lineCount; i++) {
      std::unique_ptr<protocol::Profiler::PositionTickInfo> line =
          protocol::Profiler::PositionTickInfo::create()
              .setLine(entries[i].line)
              .setTicks(entries[i].hit_count)
              .build();
59
      array->emplace_back(std::move(line));
60 61 62 63 64 65
    }
  }
  return array;
}

std::unique_ptr<protocol::Profiler::ProfileNode> buildInspectorObjectFor(
66 67
    V8InspectorImpl* inspector, const v8::CpuProfileNode* node) {
  v8::Isolate* isolate = inspector->isolate();
68 69 70
  v8::HandleScope handleScope(isolate);
  auto callFrame =
      protocol::Runtime::CallFrame::create()
71
          .setFunctionName(toProtocolString(isolate, node->GetFunctionName()))
72
          .setScriptId(String16::fromInteger(node->GetScriptId()))
73
          .setUrl(resourceNameToUrl(inspector, node->GetScriptResourceName()))
74 75 76 77 78 79 80 81 82 83 84
          .setLineNumber(node->GetLineNumber() - 1)
          .setColumnNumber(node->GetColumnNumber() - 1)
          .build();
  auto result = protocol::Profiler::ProfileNode::create()
                    .setCallFrame(std::move(callFrame))
                    .setHitCount(node->GetHitCount())
                    .setId(node->GetNodeId())
                    .build();

  const int childrenCount = node->GetChildrenCount();
  if (childrenCount) {
85
    auto children = std::make_unique<protocol::Array<int>>();
86
    for (int i = 0; i < childrenCount; i++)
87
      children->emplace_back(node->GetChild(i)->GetNodeId());
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
    result->setChildren(std::move(children));
  }

  const char* deoptReason = node->GetBailoutReason();
  if (deoptReason && deoptReason[0] && strcmp(deoptReason, "no reason"))
    result->setDeoptReason(deoptReason);

  auto positionTicks = buildInspectorObjectForPositionTicks(node);
  if (positionTicks) result->setPositionTicks(std::move(positionTicks));

  return result;
}

std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples(
    v8::CpuProfile* v8profile) {
103
  auto array = std::make_unique<protocol::Array<int>>();
104 105
  int count = v8profile->GetSamplesCount();
  for (int i = 0; i < count; i++)
106
    array->emplace_back(v8profile->GetSample(i)->GetNodeId());
107 108 109 110 111
  return array;
}

std::unique_ptr<protocol::Array<int>> buildInspectorObjectForTimestamps(
    v8::CpuProfile* v8profile) {
112
  auto array = std::make_unique<protocol::Array<int>>();
113 114 115 116
  int count = v8profile->GetSamplesCount();
  uint64_t lastTime = v8profile->GetStartTime();
  for (int i = 0; i < count; i++) {
    uint64_t ts = v8profile->GetSampleTimestamp(i);
117
    array->emplace_back(static_cast<int>(ts - lastTime));
118 119 120 121 122
    lastTime = ts;
  }
  return array;
}

123 124
void flattenNodesTree(V8InspectorImpl* inspector,
                      const v8::CpuProfileNode* node,
125
                      protocol::Array<protocol::Profiler::ProfileNode>* list) {
126
  list->emplace_back(buildInspectorObjectFor(inspector, node));
127 128
  const int childrenCount = node->GetChildrenCount();
  for (int i = 0; i < childrenCount; i++)
129
    flattenNodesTree(inspector, node->GetChild(i), list);
130 131 132
}

std::unique_ptr<protocol::Profiler::Profile> createCPUProfile(
133
    V8InspectorImpl* inspector, v8::CpuProfile* v8profile) {
134
  auto nodes =
135
      std::make_unique<protocol::Array<protocol::Profiler::ProfileNode>>();
136
  flattenNodesTree(inspector, v8profile->GetTopDownRoot(), nodes.get());
137 138 139 140 141 142 143 144 145 146 147
  return protocol::Profiler::Profile::create()
      .setNodes(std::move(nodes))
      .setStartTime(static_cast<double>(v8profile->GetStartTime()))
      .setEndTime(static_cast<double>(v8profile->GetEndTime()))
      .setSamples(buildInspectorObjectForSamples(v8profile))
      .setTimeDeltas(buildInspectorObjectForTimestamps(v8profile))
      .build();
}

std::unique_ptr<protocol::Debugger::Location> currentDebugLocation(
    V8InspectorImpl* inspector) {
148 149 150 151 152 153 154 155
  auto stackTrace = V8StackTraceImpl::capture(inspector->debugger(), 1);
  CHECK(stackTrace);
  CHECK(!stackTrace->isEmpty());
  return protocol::Debugger::Location::create()
      .setScriptId(String16::fromInteger(stackTrace->topScriptId()))
      .setLineNumber(stackTrace->topLineNumber())
      .setColumnNumber(stackTrace->topColumnNumber())
      .build();
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
}

volatile int s_lastProfileId = 0;

}  // namespace

class V8ProfilerAgentImpl::ProfileDescriptor {
 public:
  ProfileDescriptor(const String16& id, const String16& title)
      : m_id(id), m_title(title) {}
  String16 m_id;
  String16 m_title;
};

V8ProfilerAgentImpl::V8ProfilerAgentImpl(
    V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
    protocol::DictionaryValue* state)
    : m_session(session),
      m_isolate(m_session->inspector()->isolate()),
      m_state(state),
176
      m_frontend(frontendChannel) {}
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

V8ProfilerAgentImpl::~V8ProfilerAgentImpl() {
  if (m_profiler) m_profiler->Dispose();
}

void V8ProfilerAgentImpl::consoleProfile(const String16& title) {
  if (!m_enabled) return;
  String16 id = nextProfileId();
  m_startedProfiles.push_back(ProfileDescriptor(id, title));
  startProfiling(id);
  m_frontend.consoleProfileStarted(
      id, currentDebugLocation(m_session->inspector()), title);
}

void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) {
  if (!m_enabled) return;
  String16 id;
  String16 resolvedTitle;
  // Take last started profile if no title was passed.
  if (title.isEmpty()) {
    if (m_startedProfiles.empty()) return;
    id = m_startedProfiles.back().m_id;
    resolvedTitle = m_startedProfiles.back().m_title;
    m_startedProfiles.pop_back();
  } else {
    for (size_t i = 0; i < m_startedProfiles.size(); i++) {
      if (m_startedProfiles[i].m_title == title) {
        resolvedTitle = title;
        id = m_startedProfiles[i].m_id;
        m_startedProfiles.erase(m_startedProfiles.begin() + i);
        break;
      }
    }
    if (id.isEmpty()) return;
  }
  std::unique_ptr<protocol::Profiler::Profile> profile =
      stopProfiling(id, true);
  if (!profile) return;
215 216 217
  m_frontend.consoleProfileFinished(
      id, currentDebugLocation(m_session->inspector()), std::move(profile),
      resolvedTitle);
218 219
}

220
Response V8ProfilerAgentImpl::enable() {
221 222 223 224 225
  if (!m_enabled) {
    m_enabled = true;
    m_state->setBoolean(ProfilerAgentState::profilerEnabled, true);
  }

226
  return Response::Success();
227 228
}

229
Response V8ProfilerAgentImpl::disable() {
230 231 232 233 234 235 236 237 238 239 240
  if (m_enabled) {
    for (size_t i = m_startedProfiles.size(); i > 0; --i)
      stopProfiling(m_startedProfiles[i - 1].m_id, false);
    m_startedProfiles.clear();
    stop(nullptr);
    stopPreciseCoverage();
    DCHECK(!m_profiler);
    m_enabled = false;
    m_state->setBoolean(ProfilerAgentState::profilerEnabled, false);
  }

241
  return Response::Success();
242 243
}

244
Response V8ProfilerAgentImpl::setSamplingInterval(int interval) {
245
  if (m_profiler) {
246 247
    return Response::ServerError(
        "Cannot change sampling interval when profiling.");
248
  }
249
  m_state->setInteger(ProfilerAgentState::samplingInterval, interval);
250
  return Response::Success();
251 252 253 254
}

void V8ProfilerAgentImpl::restore() {
  DCHECK(!m_enabled);
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
  if (m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false)) {
    m_enabled = true;
    DCHECK(!m_profiler);
    if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling,
                                 false)) {
      start();
    }
    if (m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
                                 false)) {
      bool callCount = m_state->booleanProperty(
          ProfilerAgentState::preciseCoverageCallCount, false);
      bool detailed = m_state->booleanProperty(
          ProfilerAgentState::preciseCoverageDetailed, false);
      bool updatesAllowed = m_state->booleanProperty(
          ProfilerAgentState::preciseCoverageAllowTriggeredUpdates, false);
      double timestamp;
      startPreciseCoverage(Maybe<bool>(callCount), Maybe<bool>(detailed),
                           Maybe<bool>(updatesAllowed), &timestamp);
    }
  }
275 276
}

277
Response V8ProfilerAgentImpl::start() {
278 279
  if (m_recordingCPUProfile) return Response::Success();
  if (!m_enabled) return Response::ServerError("Profiler is not enabled");
280 281 282 283
  m_recordingCPUProfile = true;
  m_frontendInitiatedProfileId = nextProfileId();
  startProfiling(m_frontendInitiatedProfileId);
  m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, true);
284
  return Response::Success();
285 286
}

287
Response V8ProfilerAgentImpl::stop(
288
    std::unique_ptr<protocol::Profiler::Profile>* profile) {
289
  if (!m_recordingCPUProfile) {
290
    return Response::ServerError("No recording profiles found");
291
  }
292 293 294 295 296
  m_recordingCPUProfile = false;
  std::unique_ptr<protocol::Profiler::Profile> cpuProfile =
      stopProfiling(m_frontendInitiatedProfileId, !!profile);
  if (profile) {
    *profile = std::move(cpuProfile);
297
    if (!profile->get()) return Response::ServerError("Profile is not found");
298 299 300
  }
  m_frontendInitiatedProfileId = String16();
  m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false);
301
  return Response::Success();
302 303
}

304 305 306
Response V8ProfilerAgentImpl::startPreciseCoverage(
    Maybe<bool> callCount, Maybe<bool> detailed,
    Maybe<bool> allowTriggeredUpdates, double* out_timestamp) {
307
  if (!m_enabled) return Response::ServerError("Profiler is not enabled");
308
  *out_timestamp = v8::base::TimeTicks::Now().since_origin().InSecondsF();
309
  bool callCountValue = callCount.fromMaybe(false);
310
  bool detailedValue = detailed.fromMaybe(false);
311
  bool allowTriggeredUpdatesValue = allowTriggeredUpdates.fromMaybe(false);
312
  m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted, true);
313 314
  m_state->setBoolean(ProfilerAgentState::preciseCoverageCallCount,
                      callCountValue);
315 316
  m_state->setBoolean(ProfilerAgentState::preciseCoverageDetailed,
                      detailedValue);
317 318
  m_state->setBoolean(ProfilerAgentState::preciseCoverageAllowTriggeredUpdates,
                      allowTriggeredUpdatesValue);
319 320 321 322
  // BlockCount is a superset of PreciseCount. It includes block-granularity
  // coverage data if it exists (at the time of writing, that's the case for
  // each function recompiled after the BlockCount mode has been set); and
  // function-granularity coverage data otherwise.
323 324
  using C = v8::debug::Coverage;
  using Mode = v8::debug::CoverageMode;
325 326 327
  Mode mode = callCountValue
                  ? (detailedValue ? Mode::kBlockCount : Mode::kPreciseCount)
                  : (detailedValue ? Mode::kBlockBinary : Mode::kPreciseBinary);
328
  C::SelectMode(m_isolate, mode);
329
  return Response::Success();
330 331 332
}

Response V8ProfilerAgentImpl::stopPreciseCoverage() {
333
  if (!m_enabled) return Response::ServerError("Profiler is not enabled");
334
  m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted, false);
335
  m_state->setBoolean(ProfilerAgentState::preciseCoverageCallCount, false);
336
  m_state->setBoolean(ProfilerAgentState::preciseCoverageDetailed, false);
337 338
  v8::debug::Coverage::SelectMode(m_isolate,
                                  v8::debug::CoverageMode::kBestEffort);
339
  return Response::Success();
340 341 342
}

namespace {
343 344 345 346 347 348 349 350 351
std::unique_ptr<protocol::Profiler::CoverageRange> createCoverageRange(
    int start, int end, int count) {
  return protocol::Profiler::CoverageRange::create()
      .setStartOffset(start)
      .setEndOffset(end)
      .setCount(count)
      .build();
}

352
Response coverageToProtocol(
353
    V8InspectorImpl* inspector, const v8::debug::Coverage& coverage,
354 355
    std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
        out_result) {
356 357
  auto result =
      std::make_unique<protocol::Array<protocol::Profiler::ScriptCoverage>>();
358
  v8::Isolate* isolate = inspector->isolate();
359 360 361
  for (size_t i = 0; i < coverage.ScriptCount(); i++) {
    v8::debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
    v8::Local<v8::debug::Script> script = script_data.GetScript();
362
    auto functions = std::make_unique<
363
        protocol::Array<protocol::Profiler::FunctionCoverage>>();
364 365 366
    for (size_t j = 0; j < script_data.FunctionCount(); j++) {
      v8::debug::Coverage::FunctionData function_data =
          script_data.GetFunctionData(j);
367
      auto ranges = std::make_unique<
368
          protocol::Array<protocol::Profiler::CoverageRange>>();
369 370

      // Add function range.
371 372 373
      ranges->emplace_back(createCoverageRange(function_data.StartOffset(),
                                               function_data.EndOffset(),
                                               function_data.Count()));
374 375 376 377 378

      // Process inner blocks.
      for (size_t k = 0; k < function_data.BlockCount(); k++) {
        v8::debug::Coverage::BlockData block_data =
            function_data.GetBlockData(k);
379 380 381
        ranges->emplace_back(createCoverageRange(block_data.StartOffset(),
                                                 block_data.EndOffset(),
                                                 block_data.Count()));
382 383
      }

384
      functions->emplace_back(
385 386
          protocol::Profiler::FunctionCoverage::create()
              .setFunctionName(toProtocolString(
387
                  isolate,
388 389
                  function_data.Name().FromMaybe(v8::Local<v8::String>())))
              .setRanges(std::move(ranges))
390
              .setIsBlockCoverage(function_data.HasBlockCoverage())
391 392 393 394
              .build());
    }
    String16 url;
    v8::Local<v8::String> name;
395
    if (script->SourceURL().ToLocal(&name) && name->Length()) {
396
      url = toProtocolString(isolate, name);
397 398
    } else if (script->Name().ToLocal(&name) && name->Length()) {
      url = resourceNameToUrl(inspector, name);
399
    }
400 401 402 403 404
    result->emplace_back(protocol::Profiler::ScriptCoverage::create()
                             .setScriptId(String16::fromInteger(script->Id()))
                             .setUrl(url)
                             .setFunctions(std::move(functions))
                             .build());
405 406
  }
  *out_result = std::move(result);
407
  return Response::Success();
408 409 410 411 412
}
}  // anonymous namespace

Response V8ProfilerAgentImpl::takePreciseCoverage(
    std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
413 414
        out_result,
    double* out_timestamp) {
415 416
  if (!m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
                                false)) {
417
    return Response::ServerError("Precise coverage has not been started.");
418
  }
419 420
  v8::HandleScope handle_scope(m_isolate);
  v8::debug::Coverage coverage = v8::debug::Coverage::CollectPrecise(m_isolate);
421
  *out_timestamp = v8::base::TimeTicks::Now().since_origin().InSecondsF();
422
  return coverageToProtocol(m_session->inspector(), coverage, out_result);
423 424
}

425
void V8ProfilerAgentImpl::triggerPreciseCoverageDeltaUpdate(
426
    const String16& occasion) {
427 428 429 430
  if (!m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
                                false)) {
    return;
  }
431 432 433 434
  if (!m_state->booleanProperty(
          ProfilerAgentState::preciseCoverageAllowTriggeredUpdates, false)) {
    return;
  }
435 436 437 438 439
  v8::HandleScope handle_scope(m_isolate);
  v8::debug::Coverage coverage = v8::debug::Coverage::CollectPrecise(m_isolate);
  std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>
      out_result;
  coverageToProtocol(m_session->inspector(), coverage, &out_result);
440
  double now = v8::base::TimeTicks::Now().since_origin().InSecondsF();
441
  m_frontend.preciseCoverageDeltaUpdate(now, occasion, std::move(out_result));
442 443
}

444 445 446
Response V8ProfilerAgentImpl::getBestEffortCoverage(
    std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
        out_result) {
447 448 449
  v8::HandleScope handle_scope(m_isolate);
  v8::debug::Coverage coverage =
      v8::debug::Coverage::CollectBestEffort(m_isolate);
450
  return coverageToProtocol(m_session->inspector(), coverage, out_result);
451 452
}

453 454
namespace {
std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>>
455
typeProfileToProtocol(V8InspectorImpl* inspector,
456
                      const v8::debug::TypeProfile& type_profile) {
457
  auto result = std::make_unique<
458
      protocol::Array<protocol::Profiler::ScriptTypeProfile>>();
459
  v8::Isolate* isolate = inspector->isolate();
460 461 462 463
  for (size_t i = 0; i < type_profile.ScriptCount(); i++) {
    v8::debug::TypeProfile::ScriptData script_data =
        type_profile.GetScriptData(i);
    v8::Local<v8::debug::Script> script = script_data.GetScript();
464
    auto entries = std::make_unique<
465
        protocol::Array<protocol::Profiler::TypeProfileEntry>>();
466 467

    for (const auto& entry : script_data.Entries()) {
468 469
      auto types =
          std::make_unique<protocol::Array<protocol::Profiler::TypeObject>>();
470
      for (const auto& type : entry.Types()) {
471
        types->emplace_back(
472 473 474 475
            protocol::Profiler::TypeObject::create()
                .setName(toProtocolString(
                    isolate, type.FromMaybe(v8::Local<v8::String>())))
                .build());
476
      }
477 478 479 480
      entries->emplace_back(protocol::Profiler::TypeProfileEntry::create()
                                .setOffset(entry.SourcePosition())
                                .setTypes(std::move(types))
                                .build());
481 482 483
    }
    String16 url;
    v8::Local<v8::String> name;
484
    if (script->SourceURL().ToLocal(&name) && name->Length()) {
485
      url = toProtocolString(isolate, name);
486 487
    } else if (script->Name().ToLocal(&name) && name->Length()) {
      url = resourceNameToUrl(inspector, name);
488
    }
489 490 491 492 493
    result->emplace_back(protocol::Profiler::ScriptTypeProfile::create()
                             .setScriptId(String16::fromInteger(script->Id()))
                             .setUrl(url)
                             .setEntries(std::move(entries))
                             .build());
494 495 496 497 498 499 500 501
  }
  return result;
}
}  // anonymous namespace

Response V8ProfilerAgentImpl::startTypeProfile() {
  m_state->setBoolean(ProfilerAgentState::typeProfileStarted, true);
  v8::debug::TypeProfile::SelectMode(m_isolate,
502
                                     v8::debug::TypeProfileMode::kCollect);
503
  return Response::Success();
504 505 506 507
}

Response V8ProfilerAgentImpl::stopTypeProfile() {
  m_state->setBoolean(ProfilerAgentState::typeProfileStarted, false);
508 509
  v8::debug::TypeProfile::SelectMode(m_isolate,
                                     v8::debug::TypeProfileMode::kNone);
510
  return Response::Success();
511 512 513 514 515 516 517
}

Response V8ProfilerAgentImpl::takeTypeProfile(
    std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>>*
        out_result) {
  if (!m_state->booleanProperty(ProfilerAgentState::typeProfileStarted,
                                false)) {
518
    return Response::ServerError("Type profile has not been started.");
519 520 521 522
  }
  v8::HandleScope handle_scope(m_isolate);
  v8::debug::TypeProfile type_profile =
      v8::debug::TypeProfile::Collect(m_isolate);
523
  *out_result = typeProfileToProtocol(m_session->inspector(), type_profile);
524
  return Response::Success();
525 526
}

527
String16 V8ProfilerAgentImpl::nextProfileId() {
528
  return String16::fromInteger(
529
      v8::base::Relaxed_AtomicIncrement(&s_lastProfileId, 1));
530 531 532 533
}

void V8ProfilerAgentImpl::startProfiling(const String16& title) {
  v8::HandleScope handleScope(m_isolate);
534 535 536 537 538 539 540 541
  if (!m_startedProfilesCount) {
    DCHECK(!m_profiler);
    m_profiler = v8::CpuProfiler::New(m_isolate);
    int interval =
        m_state->integerProperty(ProfilerAgentState::samplingInterval, 0);
    if (interval) m_profiler->SetSamplingInterval(interval);
  }
  ++m_startedProfilesCount;
542
  m_profiler->StartProfiling(toV8String(m_isolate, title), true);
543 544 545 546 547 548
}

std::unique_ptr<protocol::Profiler::Profile> V8ProfilerAgentImpl::stopProfiling(
    const String16& title, bool serialize) {
  v8::HandleScope handleScope(m_isolate);
  v8::CpuProfile* profile =
549
      m_profiler->StopProfiling(toV8String(m_isolate, title));
550
  std::unique_ptr<protocol::Profiler::Profile> result;
551
  if (profile) {
552
    if (serialize) result = createCPUProfile(m_session->inspector(), profile);
553 554 555 556 557 558 559
    profile->Delete();
  }
  --m_startedProfilesCount;
  if (!m_startedProfilesCount) {
    m_profiler->Dispose();
    m_profiler = nullptr;
  }
560 561 562 563
  return result;
}

}  // namespace v8_inspector