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