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