• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
HeapSnapshotProgress(protocol::HeapProfiler::Frontend * frontend)34   explicit HeapSnapshotProgress(protocol::HeapProfiler::Frontend* frontend)
35       : m_frontend(frontend) {}
ReportProgressValue(int done,int total)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
51     : public v8::HeapProfiler::ObjectNameResolver {
52  public:
GlobalObjectNameResolver(V8InspectorSessionImpl * session)53   explicit GlobalObjectNameResolver(V8InspectorSessionImpl* session)
54       : m_offset(0), m_strings(10000), m_session(session) {}
55 
GetName(v8::Local<v8::Object> object)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:
HeapSnapshotOutputStream(protocol::HeapProfiler::Frontend * frontend)82   explicit HeapSnapshotOutputStream(protocol::HeapProfiler::Frontend* frontend)
83       : m_frontend(frontend) {}
EndOfStream()84   void EndOfStream() override {}
GetChunkSize()85   int GetChunkSize() override { return 102400; }
WriteAsciiChunk(char * data,int size)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 
objectByHeapObjectId(v8::Isolate * isolate,int id)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:
InspectableHeapObject(int heapObjectId)105   explicit InspectableHeapObject(int heapObjectId)
106       : m_heapObjectId(heapObjectId) {}
get(v8::Local<v8::Context> context)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:
HeapStatsStream(protocol::HeapProfiler::Frontend * frontend)117   explicit HeapStatsStream(protocol::HeapProfiler::Frontend* frontend)
118       : m_frontend(frontend) {}
119 
EndOfStream()120   void EndOfStream() override {}
121 
WriteAsciiChunk(char * data,int size)122   WriteResult WriteAsciiChunk(char* data, int size) override {
123     DCHECK(false);
124     return kAbort;
125   }
126 
WriteHeapStatsChunk(v8::HeapStatsUpdate * updateData,int count)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 
V8HeapProfilerAgentImpl(V8InspectorSessionImpl * session,protocol::FrontendChannel * frontendChannel,protocol::DictionaryValue * state)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 
~V8HeapProfilerAgentImpl()156 V8HeapProfilerAgentImpl::~V8HeapProfilerAgentImpl() {}
157 
restore()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 
collectGarbage()175 Response V8HeapProfilerAgentImpl::collectGarbage() {
176   m_isolate->LowMemoryNotification();
177   return Response::OK();
178 }
179 
startTrackingHeapObjects(Maybe<bool> trackAllocations)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 
stopTrackingHeapObjects(Maybe<bool> reportProgress)190 Response V8HeapProfilerAgentImpl::stopTrackingHeapObjects(
191     Maybe<bool> reportProgress) {
192   requestHeapStatsUpdate();
193   takeHeapSnapshot(std::move(reportProgress));
194   stopTrackingHeapObjectsInternal();
195   return Response::OK();
196 }
197 
enable()198 Response V8HeapProfilerAgentImpl::enable() {
199   m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true);
200   return Response::OK();
201 }
202 
disable()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 
takeHeapSnapshot(Maybe<bool> reportProgress)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 
getObjectByHeapObjectId(const String16 & heapSnapshotObjectId,Maybe<String16> objectGroup,std::unique_ptr<protocol::Runtime::RemoteObject> * result)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 
addInspectedHeapObject(const String16 & inspectedHeapObjectId)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 
getHeapObjectId(const String16 & objectId,String16 * heapSnapshotObjectId)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 
requestHeapStatsUpdate()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
onTimer(void * data)293 void V8HeapProfilerAgentImpl::onTimer(void* data) {
294   reinterpret_cast<V8HeapProfilerAgentImpl*>(data)->requestHeapStatsUpdate();
295 }
296 
startTrackingHeapObjectsInternal(bool trackAllocations)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 
stopTrackingHeapObjectsInternal()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 
startSampling(Maybe<double> samplingInterval)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>
buildSampingHeapProfileNode(v8::Isolate * isolate,const v8::AllocationProfile::Node * node)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           .build();
361   return result;
362 }
363 }  // namespace
364 
stopSampling(std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile> * profile)365 Response V8HeapProfilerAgentImpl::stopSampling(
366     std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) {
367   Response result = getSamplingProfile(profile);
368   if (result.isSuccess()) {
369     m_isolate->GetHeapProfiler()->StopSamplingHeapProfiler();
370     m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled,
371                         false);
372   }
373   return result;
374 }
375 
getSamplingProfile(std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile> * profile)376 Response V8HeapProfilerAgentImpl::getSamplingProfile(
377     std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) {
378   v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
379   v8::HandleScope scope(
380       m_isolate);  // v8::AllocationProfile contains Local handles.
381   std::unique_ptr<v8::AllocationProfile> v8Profile(
382       profiler->GetAllocationProfile());
383   if (!v8Profile)
384     return Response::Error("V8 sampling heap profiler was not started.");
385   v8::AllocationProfile::Node* root = v8Profile->GetRootNode();
386   *profile = protocol::HeapProfiler::SamplingHeapProfile::create()
387                  .setHead(buildSampingHeapProfileNode(m_isolate, root))
388                  .build();
389   return Response::OK();
390 }
391 
392 }  // namespace v8_inspector
393