Web Inspector: Heap: lazily create the agent
[WebKit-https.git] / Source / JavaScriptCore / inspector / agents / InspectorHeapAgent.cpp
1 /*
2  * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "InspectorHeapAgent.h"
28
29 #include "HeapProfiler.h"
30 #include "HeapSnapshot.h"
31 #include "InjectedScript.h"
32 #include "InjectedScriptManager.h"
33 #include "InspectorEnvironment.h"
34 #include "JSCInlines.h"
35 #include "VM.h"
36 #include <wtf/Stopwatch.h>
37
38 using namespace JSC;
39
40 namespace Inspector {
41
42 InspectorHeapAgent::InspectorHeapAgent(AgentContext& context)
43     : InspectorAgentBase("Heap"_s)
44     , m_injectedScriptManager(context.injectedScriptManager)
45     , m_frontendDispatcher(std::make_unique<HeapFrontendDispatcher>(context.frontendRouter))
46     , m_backendDispatcher(HeapBackendDispatcher::create(context.backendDispatcher, this))
47     , m_environment(context.environment)
48 {
49 }
50
51 void InspectorHeapAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*)
52 {
53 }
54
55 void InspectorHeapAgent::willDestroyFrontendAndBackend(DisconnectReason)
56 {
57     // Stop tracking without taking a snapshot.
58     m_tracking = false;
59
60     ErrorString ignored;
61     disable(ignored);
62 }
63
64 void InspectorHeapAgent::enable(ErrorString&)
65 {
66     if (m_enabled)
67         return;
68
69     m_enabled = true;
70
71     m_environment.vm().heap.addObserver(this);
72 }
73
74 void InspectorHeapAgent::disable(ErrorString&)
75 {
76     if (!m_enabled)
77         return;
78
79     m_enabled = false;
80
81     m_environment.vm().heap.removeObserver(this);
82
83     clearHeapSnapshots();
84 }
85
86 void InspectorHeapAgent::gc(ErrorString&)
87 {
88     VM& vm = m_environment.vm();
89     JSLockHolder lock(vm);
90     sanitizeStackForVM(&vm);
91     vm.heap.collectNow(Sync, CollectionScope::Full);
92 }
93
94 void InspectorHeapAgent::snapshot(ErrorString&, double* timestamp, String* snapshotData)
95 {
96     VM& vm = m_environment.vm();
97     JSLockHolder lock(vm);
98
99     HeapSnapshotBuilder snapshotBuilder(vm.ensureHeapProfiler());
100     snapshotBuilder.buildSnapshot();
101
102     *timestamp = m_environment.executionStopwatch()->elapsedTime().seconds();
103     *snapshotData = snapshotBuilder.json([&] (const HeapSnapshotNode& node) {
104         if (Structure* structure = node.cell->structure(vm)) {
105             if (JSGlobalObject* globalObject = structure->globalObject()) {
106                 if (!m_environment.canAccessInspectedScriptState(globalObject->globalExec()))
107                     return false;
108             }
109         }
110         return true;
111     });
112 }
113
114 void InspectorHeapAgent::startTracking(ErrorString& errorString)
115 {
116     if (m_tracking)
117         return;
118
119     m_tracking = true;
120
121     double timestamp;
122     String snapshotData;
123     snapshot(errorString, &timestamp, &snapshotData);
124
125     m_frontendDispatcher->trackingStart(timestamp, snapshotData);
126 }
127
128 void InspectorHeapAgent::stopTracking(ErrorString& errorString)
129 {
130     if (!m_tracking)
131         return;
132
133     m_tracking = false;
134
135     double timestamp;
136     String snapshotData;
137     snapshot(errorString, &timestamp, &snapshotData);
138
139     m_frontendDispatcher->trackingComplete(timestamp, snapshotData);
140 }
141
142 Optional<HeapSnapshotNode> InspectorHeapAgent::nodeForHeapObjectIdentifier(ErrorString& errorString, unsigned heapObjectIdentifier)
143 {
144     HeapProfiler* heapProfiler = m_environment.vm().heapProfiler();
145     if (!heapProfiler) {
146         errorString = "No heap snapshot"_s;
147         return WTF::nullopt;
148     }
149
150     HeapSnapshot* snapshot = heapProfiler->mostRecentSnapshot();
151     if (!snapshot) {
152         errorString = "No heap snapshot"_s;
153         return WTF::nullopt;
154     }
155
156     const Optional<HeapSnapshotNode> optionalNode = snapshot->nodeForObjectIdentifier(heapObjectIdentifier);
157     if (!optionalNode) {
158         errorString = "No object for identifier, it may have been collected"_s;
159         return WTF::nullopt;
160     }
161
162     return optionalNode;
163 }
164
165 void InspectorHeapAgent::getPreview(ErrorString& errorString, int heapObjectId, Optional<String>& resultString, RefPtr<Protocol::Debugger::FunctionDetails>& functionDetails, RefPtr<Protocol::Runtime::ObjectPreview>& objectPreview)
166 {
167     // Prevent the cell from getting collected as we look it up.
168     VM& vm = m_environment.vm();
169     JSLockHolder lock(vm);
170     DeferGC deferGC(vm.heap);
171
172     unsigned heapObjectIdentifier = static_cast<unsigned>(heapObjectId);
173     const Optional<HeapSnapshotNode> optionalNode = nodeForHeapObjectIdentifier(errorString, heapObjectIdentifier);
174     if (!optionalNode)
175         return;
176
177     // String preview.
178     JSCell* cell = optionalNode->cell;
179     if (cell->isString()) {
180         resultString = asString(cell)->tryGetValue();
181         return;
182     }
183
184     // FIXME: Provide preview information for Internal Objects? CodeBlock, Executable, etc.
185
186     Structure* structure = cell->structure(vm);
187     if (!structure) {
188         errorString = "Unable to get object details - Structure"_s;
189         return;
190     }
191
192     JSGlobalObject* globalObject = structure->globalObject();
193     if (!globalObject) {
194         errorString = "Unable to get object details - GlobalObject"_s;
195         return;
196     }
197
198     InjectedScript injectedScript = m_injectedScriptManager.injectedScriptFor(globalObject->globalExec());
199     if (injectedScript.hasNoValue()) {
200         errorString = "Unable to get object details - InjectedScript"_s;
201         return;
202     }
203
204     // Function preview.
205     if (cell->inherits<JSFunction>(vm)) {
206         injectedScript.functionDetails(errorString, cell, functionDetails);
207         return;
208     }
209
210     // Object preview.
211     objectPreview = injectedScript.previewValue(cell);
212 }
213
214 void InspectorHeapAgent::getRemoteObject(ErrorString& errorString, int heapObjectId, const String* optionalObjectGroup, RefPtr<Protocol::Runtime::RemoteObject>& result)
215 {
216     // Prevent the cell from getting collected as we look it up.
217     VM& vm = m_environment.vm();
218     JSLockHolder lock(vm);
219     DeferGC deferGC(vm.heap);
220
221     unsigned heapObjectIdentifier = static_cast<unsigned>(heapObjectId);
222     const Optional<HeapSnapshotNode> optionalNode = nodeForHeapObjectIdentifier(errorString, heapObjectIdentifier);
223     if (!optionalNode)
224         return;
225
226     JSCell* cell = optionalNode->cell;
227     Structure* structure = cell->structure(vm);
228     if (!structure) {
229         errorString = "Unable to get object details"_s;
230         return;
231     }
232
233     JSGlobalObject* globalObject = structure->globalObject();
234     if (!globalObject) {
235         errorString = "Unable to get object details"_s;
236         return;
237     }
238
239     InjectedScript injectedScript = m_injectedScriptManager.injectedScriptFor(globalObject->globalExec());
240     if (injectedScript.hasNoValue()) {
241         errorString = "Unable to get object details - InjectedScript"_s;
242         return;
243     }
244
245     String objectGroup = optionalObjectGroup ? *optionalObjectGroup : String();
246     result = injectedScript.wrapObject(cell, objectGroup, true);
247 }
248
249 static Protocol::Heap::GarbageCollection::Type protocolTypeForHeapOperation(CollectionScope scope)
250 {
251     switch (scope) {
252     case CollectionScope::Full:
253         return Protocol::Heap::GarbageCollection::Type::Full;
254     case CollectionScope::Eden:
255         return Protocol::Heap::GarbageCollection::Type::Partial;
256     }
257     ASSERT_NOT_REACHED();
258     return Protocol::Heap::GarbageCollection::Type::Full;
259 }
260
261 void InspectorHeapAgent::willGarbageCollect()
262 {
263     if (!m_enabled)
264         return;
265
266     m_gcStartTime = m_environment.executionStopwatch()->elapsedTime();
267 }
268
269 void InspectorHeapAgent::didGarbageCollect(CollectionScope scope)
270 {
271     if (!m_enabled) {
272         m_gcStartTime = Seconds::nan();
273         return;
274     }
275
276     if (std::isnan(m_gcStartTime)) {
277         // We were not enabled when the GC began.
278         return;
279     }
280
281     // FIXME: Include number of bytes freed by collection.
282
283     Seconds endTime = m_environment.executionStopwatch()->elapsedTime();
284     dispatchGarbageCollectedEvent(protocolTypeForHeapOperation(scope), m_gcStartTime, endTime);
285
286     m_gcStartTime = Seconds::nan();
287 }
288
289 void InspectorHeapAgent::clearHeapSnapshots()
290 {
291     VM& vm = m_environment.vm();
292     JSLockHolder lock(vm);
293
294     if (HeapProfiler* heapProfiler = vm.heapProfiler()) {
295         heapProfiler->clearSnapshots();
296         HeapSnapshotBuilder::resetNextAvailableObjectIdentifier();
297     }
298 }
299
300 void InspectorHeapAgent::dispatchGarbageCollectedEvent(Protocol::Heap::GarbageCollection::Type type, Seconds startTime, Seconds endTime)
301 {
302     auto protocolObject = Protocol::Heap::GarbageCollection::create()
303         .setType(type)
304         .setStartTime(startTime.seconds())
305         .setEndTime(endTime.seconds())
306         .release();
307
308     m_frontendDispatcher->garbageCollected(WTFMove(protocolObject));
309 }
310
311 } // namespace Inspector