V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
v8-profiler-agent-impl.cc
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 
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>>
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 
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 
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 
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 
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 
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 
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 
160  public:
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 
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 
175 V8ProfilerAgentImpl::~V8ProfilerAgentImpl() {
176  if (m_profiler) m_profiler->Dispose();
177 }
178 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 {
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 
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;
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 
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 
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>>
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++) {
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;
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 
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 
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 
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 
489 String16 V8ProfilerAgentImpl::nextProfileId() {
490  return String16::fromInteger(
491  v8::base::Relaxed_AtomicIncrement(&s_lastProfileId, 1));
492 }
493 
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 
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
int GetSamplesCount() const
Definition: api.cc:10114
int64_t GetStartTime() const
Definition: api.cc:10102
int64_t GetEndTime() const
Definition: api.cc:10108
unsigned GetNodeId() const
Definition: api.cc:10043
const CpuProfileNode * GetSample(int index) const
Definition: api.cc:10089
void SetSamplingInterval(int us)
Definition: api.cc:10130
CpuProfile * StopProfiling(Local< String > title)
Definition: api.cc:10151
static CpuProfiler * New(Isolate *isolate)
Definition: api.cc:10118
void StartProfiling(Local< String > title, CpuProfilingMode mode, bool record_samples=false)
Definition: api.cc:10145
const CpuProfileNode * GetTopDownRoot() const
Definition: api.cc:10083
void Dispose()
Definition: api.cc:10123
int64_t GetSampleTimestamp(int index) const
Definition: api.cc:10095
void Delete()
Definition: api.cc:10067