V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
v8-heap-profiler-agent-impl.cc
1 // Copyright 2016 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-heap-profiler-agent-impl.h"
6 
7 #include "src/inspector/injected-script.h"
8 #include "src/inspector/inspected-context.h"
9 #include "src/inspector/protocol/Protocol.h"
10 #include "src/inspector/string-util.h"
11 #include "src/inspector/v8-debugger.h"
12 #include "src/inspector/v8-inspector-impl.h"
13 #include "src/inspector/v8-inspector-session-impl.h"
14 
15 #include "include/v8-inspector.h"
16 #include "include/v8-profiler.h"
17 #include "include/v8-version.h"
18 
19 namespace v8_inspector {
20 
21 namespace {
22 
23 namespace HeapProfilerAgentState {
24 static const char heapProfilerEnabled[] = "heapProfilerEnabled";
25 static const char heapObjectsTrackingEnabled[] = "heapObjectsTrackingEnabled";
26 static const char allocationTrackingEnabled[] = "allocationTrackingEnabled";
27 static const char samplingHeapProfilerEnabled[] = "samplingHeapProfilerEnabled";
28 static const char samplingHeapProfilerInterval[] =
29  "samplingHeapProfilerInterval";
30 }
31 
32 class HeapSnapshotProgress final : public v8::ActivityControl {
33  public:
34  explicit HeapSnapshotProgress(protocol::HeapProfiler::Frontend* frontend)
35  : m_frontend(frontend) {}
36  ControlOption ReportProgressValue(int done, int total) override {
37  m_frontend->reportHeapSnapshotProgress(done, total,
38  protocol::Maybe<bool>());
39  if (done >= total) {
40  m_frontend->reportHeapSnapshotProgress(total, total, true);
41  }
42  m_frontend->flush();
43  return kContinue;
44  }
45 
46  private:
47  protocol::HeapProfiler::Frontend* m_frontend;
48 };
49 
50 class GlobalObjectNameResolver final
52  public:
53  explicit GlobalObjectNameResolver(V8InspectorSessionImpl* session)
54  : m_offset(0), m_strings(10000), m_session(session) {}
55 
56  const char* GetName(v8::Local<v8::Object> object) override {
57  InspectedContext* context = m_session->inspector()->getContext(
58  m_session->contextGroupId(),
59  InspectedContext::contextId(object->CreationContext()));
60  if (!context) return "";
61  String16 name = context->origin();
62  size_t length = name.length();
63  if (m_offset + length + 1 >= m_strings.size()) return "";
64  for (size_t i = 0; i < length; ++i) {
65  UChar ch = name[i];
66  m_strings[m_offset + i] = ch > 0xFF ? '?' : static_cast<char>(ch);
67  }
68  m_strings[m_offset + length] = '\0';
69  char* result = &*m_strings.begin() + m_offset;
70  m_offset += length + 1;
71  return result;
72  }
73 
74  private:
75  size_t m_offset;
76  std::vector<char> m_strings;
77  V8InspectorSessionImpl* m_session;
78 };
79 
80 class HeapSnapshotOutputStream final : public v8::OutputStream {
81  public:
82  explicit HeapSnapshotOutputStream(protocol::HeapProfiler::Frontend* frontend)
83  : m_frontend(frontend) {}
84  void EndOfStream() override {}
85  int GetChunkSize() override { return 102400; }
86  WriteResult WriteAsciiChunk(char* data, int size) override {
87  m_frontend->addHeapSnapshotChunk(String16(data, size));
88  m_frontend->flush();
89  return kContinue;
90  }
91 
92  private:
93  protocol::HeapProfiler::Frontend* m_frontend;
94 };
95 
96 v8::Local<v8::Object> objectByHeapObjectId(v8::Isolate* isolate, int id) {
97  v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
98  v8::Local<v8::Value> value = profiler->FindObjectById(id);
99  if (value.IsEmpty() || !value->IsObject()) return v8::Local<v8::Object>();
100  return value.As<v8::Object>();
101 }
102 
103 class InspectableHeapObject final : public V8InspectorSession::Inspectable {
104  public:
105  explicit InspectableHeapObject(int heapObjectId)
106  : m_heapObjectId(heapObjectId) {}
107  v8::Local<v8::Value> get(v8::Local<v8::Context> context) override {
108  return objectByHeapObjectId(context->GetIsolate(), m_heapObjectId);
109  }
110 
111  private:
112  int m_heapObjectId;
113 };
114 
115 class HeapStatsStream final : public v8::OutputStream {
116  public:
117  explicit HeapStatsStream(protocol::HeapProfiler::Frontend* frontend)
118  : m_frontend(frontend) {}
119 
120  void EndOfStream() override {}
121 
122  WriteResult WriteAsciiChunk(char* data, int size) override {
123  DCHECK(false);
124  return kAbort;
125  }
126 
127  WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* updateData,
128  int count) override {
129  DCHECK_GT(count, 0);
130  std::unique_ptr<protocol::Array<int>> statsDiff =
131  protocol::Array<int>::create();
132  for (int i = 0; i < count; ++i) {
133  statsDiff->addItem(updateData[i].index);
134  statsDiff->addItem(updateData[i].count);
135  statsDiff->addItem(updateData[i].size);
136  }
137  m_frontend->heapStatsUpdate(std::move(statsDiff));
138  return kContinue;
139  }
140 
141  private:
142  protocol::HeapProfiler::Frontend* m_frontend;
143 };
144 
145 } // namespace
146 
147 V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl(
148  V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
149  protocol::DictionaryValue* state)
150  : m_session(session),
151  m_isolate(session->inspector()->isolate()),
152  m_frontend(frontendChannel),
153  m_state(state),
154  m_hasTimer(false) {}
155 
156 V8HeapProfilerAgentImpl::~V8HeapProfilerAgentImpl() = default;
157 
158 void V8HeapProfilerAgentImpl::restore() {
159  if (m_state->booleanProperty(HeapProfilerAgentState::heapProfilerEnabled,
160  false))
161  m_frontend.resetProfiles();
162  if (m_state->booleanProperty(
163  HeapProfilerAgentState::heapObjectsTrackingEnabled, false))
164  startTrackingHeapObjectsInternal(m_state->booleanProperty(
165  HeapProfilerAgentState::allocationTrackingEnabled, false));
166  if (m_state->booleanProperty(
167  HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) {
168  double samplingInterval = m_state->doubleProperty(
169  HeapProfilerAgentState::samplingHeapProfilerInterval, -1);
170  DCHECK_GE(samplingInterval, 0);
171  startSampling(Maybe<double>(samplingInterval));
172  }
173 }
174 
175 Response V8HeapProfilerAgentImpl::collectGarbage() {
176  m_isolate->LowMemoryNotification();
177  return Response::OK();
178 }
179 
180 Response V8HeapProfilerAgentImpl::startTrackingHeapObjects(
181  Maybe<bool> trackAllocations) {
182  m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, true);
183  bool allocationTrackingEnabled = trackAllocations.fromMaybe(false);
184  m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled,
185  allocationTrackingEnabled);
186  startTrackingHeapObjectsInternal(allocationTrackingEnabled);
187  return Response::OK();
188 }
189 
190 Response V8HeapProfilerAgentImpl::stopTrackingHeapObjects(
191  Maybe<bool> reportProgress) {
192  requestHeapStatsUpdate();
193  takeHeapSnapshot(std::move(reportProgress));
194  stopTrackingHeapObjectsInternal();
195  return Response::OK();
196 }
197 
198 Response V8HeapProfilerAgentImpl::enable() {
199  m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true);
200  return Response::OK();
201 }
202 
203 Response V8HeapProfilerAgentImpl::disable() {
204  stopTrackingHeapObjectsInternal();
205  if (m_state->booleanProperty(
206  HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) {
207  v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
208  if (profiler) profiler->StopSamplingHeapProfiler();
209  }
210  m_isolate->GetHeapProfiler()->ClearObjectIds();
211  m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false);
212  return Response::OK();
213 }
214 
215 Response V8HeapProfilerAgentImpl::takeHeapSnapshot(Maybe<bool> reportProgress) {
216  v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
217  if (!profiler) return Response::Error("Cannot access v8 heap profiler");
218  std::unique_ptr<HeapSnapshotProgress> progress;
219  if (reportProgress.fromMaybe(false))
220  progress.reset(new HeapSnapshotProgress(&m_frontend));
221 
222  GlobalObjectNameResolver resolver(m_session);
223  const v8::HeapSnapshot* snapshot =
224  profiler->TakeHeapSnapshot(progress.get(), &resolver);
225  if (!snapshot) return Response::Error("Failed to take heap snapshot");
226  HeapSnapshotOutputStream stream(&m_frontend);
227  snapshot->Serialize(&stream);
228  const_cast<v8::HeapSnapshot*>(snapshot)->Delete();
229  return Response::OK();
230 }
231 
232 Response V8HeapProfilerAgentImpl::getObjectByHeapObjectId(
233  const String16& heapSnapshotObjectId, Maybe<String16> objectGroup,
234  std::unique_ptr<protocol::Runtime::RemoteObject>* result) {
235  bool ok;
236  int id = heapSnapshotObjectId.toInteger(&ok);
237  if (!ok) return Response::Error("Invalid heap snapshot object id");
238 
239  v8::HandleScope handles(m_isolate);
240  v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id);
241  if (heapObject.IsEmpty()) return Response::Error("Object is not available");
242 
243  if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject))
244  return Response::Error("Object is not available");
245 
246  *result = m_session->wrapObject(heapObject->CreationContext(), heapObject,
247  objectGroup.fromMaybe(""), false);
248  if (!*result) return Response::Error("Object is not available");
249  return Response::OK();
250 }
251 
252 Response V8HeapProfilerAgentImpl::addInspectedHeapObject(
253  const String16& inspectedHeapObjectId) {
254  bool ok;
255  int id = inspectedHeapObjectId.toInteger(&ok);
256  if (!ok) return Response::Error("Invalid heap snapshot object id");
257 
258  v8::HandleScope handles(m_isolate);
259  v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id);
260  if (heapObject.IsEmpty()) return Response::Error("Object is not available");
261 
262  if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject))
263  return Response::Error("Object is not available");
264  m_session->addInspectedObject(
265  std::unique_ptr<InspectableHeapObject>(new InspectableHeapObject(id)));
266  return Response::OK();
267 }
268 
269 Response V8HeapProfilerAgentImpl::getHeapObjectId(
270  const String16& objectId, String16* heapSnapshotObjectId) {
271  v8::HandleScope handles(m_isolate);
272  v8::Local<v8::Value> value;
273  v8::Local<v8::Context> context;
274  Response response =
275  m_session->unwrapObject(objectId, &value, &context, nullptr);
276  if (!response.isSuccess()) return response;
277  if (value->IsUndefined()) return Response::InternalError();
278 
279  v8::SnapshotObjectId id = m_isolate->GetHeapProfiler()->GetObjectId(value);
280  *heapSnapshotObjectId = String16::fromInteger(static_cast<size_t>(id));
281  return Response::OK();
282 }
283 
284 void V8HeapProfilerAgentImpl::requestHeapStatsUpdate() {
285  HeapStatsStream stream(&m_frontend);
286  v8::SnapshotObjectId lastSeenObjectId =
287  m_isolate->GetHeapProfiler()->GetHeapStats(&stream);
288  m_frontend.lastSeenObjectId(
289  lastSeenObjectId, m_session->inspector()->client()->currentTimeMS());
290 }
291 
292 // static
293 void V8HeapProfilerAgentImpl::onTimer(void* data) {
294  reinterpret_cast<V8HeapProfilerAgentImpl*>(data)->requestHeapStatsUpdate();
295 }
296 
297 void V8HeapProfilerAgentImpl::startTrackingHeapObjectsInternal(
298  bool trackAllocations) {
299  m_isolate->GetHeapProfiler()->StartTrackingHeapObjects(trackAllocations);
300  if (!m_hasTimer) {
301  m_hasTimer = true;
302  m_session->inspector()->client()->startRepeatingTimer(
303  0.05, &V8HeapProfilerAgentImpl::onTimer, reinterpret_cast<void*>(this));
304  }
305 }
306 
307 void V8HeapProfilerAgentImpl::stopTrackingHeapObjectsInternal() {
308  if (m_hasTimer) {
309  m_session->inspector()->client()->cancelTimer(
310  reinterpret_cast<void*>(this));
311  m_hasTimer = false;
312  }
313  m_isolate->GetHeapProfiler()->StopTrackingHeapObjects();
314  m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled,
315  false);
316  m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, false);
317 }
318 
319 Response V8HeapProfilerAgentImpl::startSampling(
320  Maybe<double> samplingInterval) {
321  v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
322  if (!profiler) return Response::Error("Cannot access v8 heap profiler");
323  const unsigned defaultSamplingInterval = 1 << 15;
324  double samplingIntervalValue =
325  samplingInterval.fromMaybe(defaultSamplingInterval);
326  m_state->setDouble(HeapProfilerAgentState::samplingHeapProfilerInterval,
327  samplingIntervalValue);
328  m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled,
329  true);
330  profiler->StartSamplingHeapProfiler(
331  static_cast<uint64_t>(samplingIntervalValue), 128,
332  v8::HeapProfiler::kSamplingForceGC);
333  return Response::OK();
334 }
335 
336 namespace {
337 std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode>
338 buildSampingHeapProfileNode(v8::Isolate* isolate,
339  const v8::AllocationProfile::Node* node) {
340  auto children = protocol::Array<
341  protocol::HeapProfiler::SamplingHeapProfileNode>::create();
342  for (const auto* child : node->children)
343  children->addItem(buildSampingHeapProfileNode(isolate, child));
344  size_t selfSize = 0;
345  for (const auto& allocation : node->allocations)
346  selfSize += allocation.size * allocation.count;
347  std::unique_ptr<protocol::Runtime::CallFrame> callFrame =
348  protocol::Runtime::CallFrame::create()
349  .setFunctionName(toProtocolString(isolate, node->name))
350  .setScriptId(String16::fromInteger(node->script_id))
351  .setUrl(toProtocolString(isolate, node->script_name))
352  .setLineNumber(node->line_number - 1)
353  .setColumnNumber(node->column_number - 1)
354  .build();
355  std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> result =
356  protocol::HeapProfiler::SamplingHeapProfileNode::create()
357  .setCallFrame(std::move(callFrame))
358  .setSelfSize(selfSize)
359  .setChildren(std::move(children))
360  .setId(node->node_id)
361  .build();
362  return result;
363 }
364 } // namespace
365 
366 Response V8HeapProfilerAgentImpl::stopSampling(
367  std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) {
368  Response result = getSamplingProfile(profile);
369  if (result.isSuccess()) {
370  m_isolate->GetHeapProfiler()->StopSamplingHeapProfiler();
371  m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled,
372  false);
373  }
374  return result;
375 }
376 
377 Response V8HeapProfilerAgentImpl::getSamplingProfile(
378  std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) {
379  v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
380  // Need a scope as v8::AllocationProfile contains Local handles.
381  v8::HandleScope scope(m_isolate);
382  std::unique_ptr<v8::AllocationProfile> v8Profile(
383  profiler->GetAllocationProfile());
384  if (!v8Profile)
385  return Response::Error("V8 sampling heap profiler was not started.");
386  v8::AllocationProfile::Node* root = v8Profile->GetRootNode();
387  auto samples = protocol::Array<
388  protocol::HeapProfiler::SamplingHeapProfileSample>::create();
389  for (const auto& sample : v8Profile->GetSamples()) {
390  samples->addItem(protocol::HeapProfiler::SamplingHeapProfileSample::create()
391  .setSize(sample.size * sample.count)
392  .setNodeId(sample.node_id)
393  .setOrdinal(static_cast<double>(sample.sample_id))
394  .build());
395  }
396  *profile = protocol::HeapProfiler::SamplingHeapProfile::create()
397  .setHead(buildSampingHeapProfileNode(m_isolate, root))
398  .setSamples(std::move(samples))
399  .build();
400  return Response::OK();
401 }
402 
403 } // namespace v8_inspector
V8_INLINE bool IsEmpty() const
Definition: v8.h:195
Local< Context > CreationContext()
Definition: api.cc:4857
void Serialize(OutputStream *stream, SerializationFormat format=kJSON) const
Definition: api.cc:10364