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 #include "src/inspector/public/V8InspectorClient.h" | |
14 | |
15 #include <v8-profiler.h> | |
16 #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 #if V8_MAJOR_VERSION >= 5 | |
27 static const char samplingHeapProfilerEnabled[] = "samplingHeapProfilerEnabled"; | |
28 static const char samplingHeapProfilerInterval[] = "samplingHeapProfilerInterval
"; | |
29 #endif | |
30 } | |
31 | |
32 class HeapSnapshotProgress final : public v8::ActivityControl { | |
33 public: | |
34 HeapSnapshotProgress(protocol::HeapProfiler::Frontend* frontend) | |
35 : m_frontend(frontend) { } | |
36 ControlOption ReportProgressValue(int done, int total) override | |
37 { | |
38 m_frontend->reportHeapSnapshotProgress(done, total, protocol::Maybe<bool
>()); | |
39 if (done >= total) { | |
40 m_frontend->reportHeapSnapshotProgress(total, total, true); | |
41 } | |
42 m_frontend->flush(); | |
43 return kContinue; | |
44 } | |
45 private: | |
46 protocol::HeapProfiler::Frontend* m_frontend; | |
47 }; | |
48 | |
49 class GlobalObjectNameResolver final : public v8::HeapProfiler::ObjectNameResolv
er { | |
50 public: | |
51 explicit GlobalObjectNameResolver(V8InspectorSessionImpl* session) | |
52 : m_offset(0), m_strings(10000), m_session(session) {} | |
53 | |
54 const char* GetName(v8::Local<v8::Object> object) override | |
55 { | |
56 InspectedContext* context = m_session->inspector()->getContext(m_session
->contextGroupId(), V8Debugger::contextId(object->CreationContext())); | |
57 if (!context) | |
58 return ""; | |
59 String16 name = context->origin(); | |
60 size_t length = name.length(); | |
61 if (m_offset + length + 1 >= m_strings.size()) | |
62 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 { | |
87 m_frontend->addHeapSnapshotChunk(String16(data, size)); | |
88 m_frontend->flush(); | |
89 return kContinue; | |
90 } | |
91 private: | |
92 protocol::HeapProfiler::Frontend* m_frontend; | |
93 }; | |
94 | |
95 v8::Local<v8::Object> objectByHeapObjectId(v8::Isolate* isolate, int id) | |
96 { | |
97 v8::HeapProfiler* profiler = isolate->GetHeapProfiler(); | |
98 v8::Local<v8::Value> value = profiler->FindObjectById(id); | |
99 if (value.IsEmpty() || !value->IsObject()) | |
100 return v8::Local<v8::Object>(); | |
101 return value.As<v8::Object>(); | |
102 } | |
103 | |
104 class InspectableHeapObject final : public V8InspectorSession::Inspectable { | |
105 public: | |
106 explicit InspectableHeapObject(int heapObjectId) : m_heapObjectId(heapObject
Id) { } | |
107 v8::Local<v8::Value> get(v8::Local<v8::Context> context) override | |
108 { | |
109 return objectByHeapObjectId(context->GetIsolate(), m_heapObjectId); | |
110 } | |
111 private: | |
112 int m_heapObjectId; | |
113 }; | |
114 | |
115 class HeapStatsStream final : public v8::OutputStream { | |
116 public: | |
117 HeapStatsStream(protocol::HeapProfiler::Frontend* frontend) | |
118 : m_frontend(frontend) | |
119 { | |
120 } | |
121 | |
122 void EndOfStream() override { } | |
123 | |
124 WriteResult WriteAsciiChunk(char* data, int size) override | |
125 { | |
126 DCHECK(false); | |
127 return kAbort; | |
128 } | |
129 | |
130 WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* updateData, int count)
override | |
131 { | |
132 DCHECK_GT(count, 0); | |
133 std::unique_ptr<protocol::Array<int>> statsDiff = protocol::Array<int>::
create(); | |
134 for (int i = 0; i < count; ++i) { | |
135 statsDiff->addItem(updateData[i].index); | |
136 statsDiff->addItem(updateData[i].count); | |
137 statsDiff->addItem(updateData[i].size); | |
138 } | |
139 m_frontend->heapStatsUpdate(std::move(statsDiff)); | |
140 return kContinue; | |
141 } | |
142 | |
143 private: | |
144 protocol::HeapProfiler::Frontend* m_frontend; | |
145 }; | |
146 | |
147 } // namespace | |
148 | |
149 V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl(V8InspectorSessionImpl* session
, protocol::FrontendChannel* frontendChannel, 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 } | |
157 | |
158 V8HeapProfilerAgentImpl::~V8HeapProfilerAgentImpl() | |
159 { | |
160 } | |
161 | |
162 void V8HeapProfilerAgentImpl::restore() | |
163 { | |
164 if (m_state->booleanProperty(HeapProfilerAgentState::heapProfilerEnabled, fa
lse)) | |
165 m_frontend.resetProfiles(); | |
166 if (m_state->booleanProperty(HeapProfilerAgentState::heapObjectsTrackingEnab
led, false)) | |
167 startTrackingHeapObjectsInternal(m_state->booleanProperty(HeapProfilerAg
entState::allocationTrackingEnabled, false)); | |
168 #if V8_MAJOR_VERSION >= 5 | |
169 if (m_state->booleanProperty(HeapProfilerAgentState::samplingHeapProfilerEna
bled, false)) { | |
170 ErrorString error; | |
171 double samplingInterval = m_state->doubleProperty(HeapProfilerAgentState
::samplingHeapProfilerInterval, -1); | |
172 DCHECK_GE(samplingInterval, 0); | |
173 startSampling(&error, Maybe<double>(samplingInterval)); | |
174 } | |
175 #endif | |
176 } | |
177 | |
178 void V8HeapProfilerAgentImpl::collectGarbage(ErrorString*) | |
179 { | |
180 m_isolate->LowMemoryNotification(); | |
181 } | |
182 | |
183 void V8HeapProfilerAgentImpl::startTrackingHeapObjects(ErrorString*, const proto
col::Maybe<bool>& trackAllocations) | |
184 { | |
185 m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, true
); | |
186 bool allocationTrackingEnabled = trackAllocations.fromMaybe(false); | |
187 m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, alloc
ationTrackingEnabled); | |
188 startTrackingHeapObjectsInternal(allocationTrackingEnabled); | |
189 } | |
190 | |
191 void V8HeapProfilerAgentImpl::stopTrackingHeapObjects(ErrorString* error, const
protocol::Maybe<bool>& reportProgress) | |
192 { | |
193 requestHeapStatsUpdate(); | |
194 takeHeapSnapshot(error, reportProgress); | |
195 stopTrackingHeapObjectsInternal(); | |
196 } | |
197 | |
198 void V8HeapProfilerAgentImpl::enable(ErrorString*) | |
199 { | |
200 m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true); | |
201 } | |
202 | |
203 void V8HeapProfilerAgentImpl::disable(ErrorString* error) | |
204 { | |
205 stopTrackingHeapObjectsInternal(); | |
206 #if V8_MAJOR_VERSION >= 5 | |
207 if (m_state->booleanProperty(HeapProfilerAgentState::samplingHeapProfilerEna
bled, false)) { | |
208 v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); | |
209 if (profiler) | |
210 profiler->StopSamplingHeapProfiler(); | |
211 } | |
212 #endif | |
213 m_isolate->GetHeapProfiler()->ClearObjectIds(); | |
214 m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false); | |
215 } | |
216 | |
217 void V8HeapProfilerAgentImpl::takeHeapSnapshot(ErrorString* errorString, const p
rotocol::Maybe<bool>& reportProgress) | |
218 { | |
219 v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); | |
220 if (!profiler) { | |
221 *errorString = "Cannot access v8 heap profiler"; | |
222 return; | |
223 } | |
224 std::unique_ptr<HeapSnapshotProgress> progress; | |
225 if (reportProgress.fromMaybe(false)) | |
226 progress = wrapUnique(new HeapSnapshotProgress(&m_frontend)); | |
227 | |
228 GlobalObjectNameResolver resolver(m_session); | |
229 const v8::HeapSnapshot* snapshot = profiler->TakeHeapSnapshot(progress.get()
, &resolver); | |
230 if (!snapshot) { | |
231 *errorString = "Failed to take heap snapshot"; | |
232 return; | |
233 } | |
234 HeapSnapshotOutputStream stream(&m_frontend); | |
235 snapshot->Serialize(&stream); | |
236 const_cast<v8::HeapSnapshot*>(snapshot)->Delete(); | |
237 } | |
238 | |
239 void V8HeapProfilerAgentImpl::getObjectByHeapObjectId(ErrorString* error, const
String16& heapSnapshotObjectId, const protocol::Maybe<String16>& objectGroup, st
d::unique_ptr<protocol::Runtime::RemoteObject>* result) | |
240 { | |
241 bool ok; | |
242 int id = heapSnapshotObjectId.toInteger(&ok); | |
243 if (!ok) { | |
244 *error = "Invalid heap snapshot object id"; | |
245 return; | |
246 } | |
247 | |
248 v8::HandleScope handles(m_isolate); | |
249 v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id); | |
250 if (heapObject.IsEmpty()) { | |
251 *error = "Object is not available"; | |
252 return; | |
253 } | |
254 | |
255 if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject))
{ | |
256 *error = "Object is not available"; | |
257 return; | |
258 } | |
259 | |
260 *result = m_session->wrapObject(heapObject->CreationContext(), heapObject, o
bjectGroup.fromMaybe(""), false); | |
261 if (!result) | |
262 *error = "Object is not available"; | |
263 } | |
264 | |
265 void V8HeapProfilerAgentImpl::addInspectedHeapObject(ErrorString* errorString, c
onst String16& inspectedHeapObjectId) | |
266 { | |
267 bool ok; | |
268 int id = inspectedHeapObjectId.toInteger(&ok); | |
269 if (!ok) { | |
270 *errorString = "Invalid heap snapshot object id"; | |
271 return; | |
272 } | |
273 | |
274 v8::HandleScope handles(m_isolate); | |
275 v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id); | |
276 if (heapObject.IsEmpty()) { | |
277 *errorString = "Object is not available"; | |
278 return; | |
279 } | |
280 | |
281 if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject))
{ | |
282 *errorString = "Object is not available"; | |
283 return; | |
284 } | |
285 | |
286 m_session->addInspectedObject(wrapUnique(new InspectableHeapObject(id))); | |
287 } | |
288 | |
289 void V8HeapProfilerAgentImpl::getHeapObjectId(ErrorString* errorString, const St
ring16& objectId, String16* heapSnapshotObjectId) | |
290 { | |
291 v8::HandleScope handles(m_isolate); | |
292 v8::Local<v8::Value> value; | |
293 v8::Local<v8::Context> context; | |
294 if (!m_session->unwrapObject(errorString, objectId, &value, &context, nullpt
r) || value->IsUndefined()) | |
295 return; | |
296 | |
297 v8::SnapshotObjectId id = m_isolate->GetHeapProfiler()->GetObjectId(value); | |
298 *heapSnapshotObjectId = String16::fromInteger(id); | |
299 } | |
300 | |
301 void V8HeapProfilerAgentImpl::requestHeapStatsUpdate() | |
302 { | |
303 HeapStatsStream stream(&m_frontend); | |
304 v8::SnapshotObjectId lastSeenObjectId = m_isolate->GetHeapProfiler()->GetHea
pStats(&stream); | |
305 m_frontend.lastSeenObjectId(lastSeenObjectId, m_session->inspector()->client
()->currentTimeMS()); | |
306 } | |
307 | |
308 // static | |
309 void V8HeapProfilerAgentImpl::onTimer(void* data) | |
310 { | |
311 reinterpret_cast<V8HeapProfilerAgentImpl*>(data)->requestHeapStatsUpdate(); | |
312 } | |
313 | |
314 void V8HeapProfilerAgentImpl::startTrackingHeapObjectsInternal(bool trackAllocat
ions) | |
315 { | |
316 m_isolate->GetHeapProfiler()->StartTrackingHeapObjects(trackAllocations); | |
317 if (!m_hasTimer) { | |
318 m_hasTimer = true; | |
319 m_session->inspector()->client()->startRepeatingTimer(0.05, &V8HeapProfi
lerAgentImpl::onTimer, reinterpret_cast<void*>(this)); | |
320 } | |
321 } | |
322 | |
323 void V8HeapProfilerAgentImpl::stopTrackingHeapObjectsInternal() | |
324 { | |
325 if (m_hasTimer) { | |
326 m_session->inspector()->client()->cancelTimer(reinterpret_cast<void*>(th
is)); | |
327 m_hasTimer = false; | |
328 } | |
329 m_isolate->GetHeapProfiler()->StopTrackingHeapObjects(); | |
330 m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, fals
e); | |
331 m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, false
); | |
332 } | |
333 | |
334 void V8HeapProfilerAgentImpl::startSampling(ErrorString* errorString, const Mayb
e<double>& samplingInterval) | |
335 { | |
336 #if V8_MAJOR_VERSION >= 5 | |
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 = samplingInterval.fromMaybe(defaultSamplingInt
erval); | |
344 m_state->setDouble(HeapProfilerAgentState::samplingHeapProfilerInterval, sam
plingIntervalValue); | |
345 m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled, tru
e); | |
346 #if V8_MAJOR_VERSION * 1000 + V8_MINOR_VERSION >= 5002 | |
347 profiler->StartSamplingHeapProfiler(static_cast<uint64_t>(samplingIntervalVa
lue), 128, v8::HeapProfiler::kSamplingForceGC); | |
348 #else | |
349 profiler->StartSamplingHeapProfiler(static_cast<uint64_t>(samplingIntervalVa
lue), 128); | |
350 #endif | |
351 #endif | |
352 } | |
353 | |
354 #if V8_MAJOR_VERSION >= 5 | |
355 namespace { | |
356 std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> buildSampingHea
pProfileNode(const v8::AllocationProfile::Node* node) | |
357 { | |
358 auto children = protocol::Array<protocol::HeapProfiler::SamplingHeapProfileN
ode>::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 = protocol::Runtime:
:CallFrame::create() | |
365 .setFunctionName(toProtocolString(node->name)) | |
366 .setScriptId(String16::fromInteger(node->script_id)) | |
367 .setUrl(toProtocolString(node->script_name)) | |
368 .setLineNumber(node->line_number - 1) | |
369 .setColumnNumber(node->column_number - 1) | |
370 .build(); | |
371 std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> result = pr
otocol::HeapProfiler::SamplingHeapProfileNode::create() | |
372 .setCallFrame(std::move(callFrame)) | |
373 .setSelfSize(selfSize) | |
374 .setChildren(std::move(children)).build(); | |
375 return result; | |
376 } | |
377 } // namespace | |
378 #endif | |
379 | |
380 void V8HeapProfilerAgentImpl::stopSampling(ErrorString* errorString, std::unique
_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) | |
381 { | |
382 #if V8_MAJOR_VERSION >= 5 | |
383 v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); | |
384 if (!profiler) { | |
385 *errorString = "Cannot access v8 heap profiler"; | |
386 return; | |
387 } | |
388 v8::HandleScope scope(m_isolate); // Allocation profile contains Local handl
es. | |
389 std::unique_ptr<v8::AllocationProfile> v8Profile(profiler->GetAllocationProf
ile()); | |
390 profiler->StopSamplingHeapProfiler(); | |
391 m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled, fal
se); | |
392 if (!v8Profile) { | |
393 *errorString = "Cannot access v8 sampled heap profile."; | |
394 return; | |
395 } | |
396 v8::AllocationProfile::Node* root = v8Profile->GetRootNode(); | |
397 *profile = protocol::HeapProfiler::SamplingHeapProfile::create() | |
398 .setHead(buildSampingHeapProfileNode(root)).build(); | |
399 #endif | |
400 } | |
401 | |
402 } // namespace v8_inspector | |
OLD | NEW |