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