[WTF] Import std::optional reference implementation as WTF::Optional
[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 "InjectedScript.h"
31 #include "InjectedScriptManager.h"
32 #include "InspectorEnvironment.h"
33 #include "JSCInlines.h"
34 #include "VM.h"
35 #include <wtf/RunLoop.h>
36 #include <wtf/Stopwatch.h>
37
38 using namespace JSC;
39
40 namespace Inspector {
41
42 struct GarbageCollectionData {
43     Inspector::Protocol::Heap::GarbageCollection::Type type;
44     double startTime;
45     double endTime;
46 };
47
48 class SendGarbageCollectionEventsTask {
49 public:
50     SendGarbageCollectionEventsTask(HeapFrontendDispatcher&);
51     void addGarbageCollection(GarbageCollectionData&);
52     void reset();
53 private:
54     void timerFired();
55
56     HeapFrontendDispatcher& m_frontendDispatcher;
57     Vector<GarbageCollectionData> m_collections;
58     RunLoop::Timer<SendGarbageCollectionEventsTask> m_timer;
59     Lock m_mutex;
60 };
61
62 SendGarbageCollectionEventsTask::SendGarbageCollectionEventsTask(HeapFrontendDispatcher& frontendDispatcher)
63     : m_frontendDispatcher(frontendDispatcher)
64     , m_timer(RunLoop::current(), this, &SendGarbageCollectionEventsTask::timerFired)
65 {
66 }
67
68 void SendGarbageCollectionEventsTask::addGarbageCollection(GarbageCollectionData& collection)
69 {
70     {
71         std::lock_guard<Lock> lock(m_mutex);
72         m_collections.append(collection);
73     }
74
75     if (!m_timer.isActive())
76         m_timer.startOneShot(0);
77 }
78
79 void SendGarbageCollectionEventsTask::reset()
80 {
81     {
82         std::lock_guard<Lock> lock(m_mutex);
83         m_collections.clear();
84     }
85
86     m_timer.stop();
87 }
88
89 void SendGarbageCollectionEventsTask::timerFired()
90 {
91     Vector<GarbageCollectionData> collectionsToSend;
92
93     {
94         std::lock_guard<Lock> lock(m_mutex);
95         m_collections.swap(collectionsToSend);
96     }
97
98     // The timer is stopped on agent destruction, so this method will never be called after agent has been destroyed.
99     for (auto& collection : collectionsToSend) {
100         auto protocolObject = Inspector::Protocol::Heap::GarbageCollection::create()
101             .setType(collection.type)
102             .setStartTime(collection.startTime)
103             .setEndTime(collection.endTime)
104             .release();
105         m_frontendDispatcher.garbageCollected(WTFMove(protocolObject));
106     }
107 }
108
109 InspectorHeapAgent::InspectorHeapAgent(AgentContext& context)
110     : InspectorAgentBase(ASCIILiteral("Heap"))
111     , m_injectedScriptManager(context.injectedScriptManager)
112     , m_frontendDispatcher(std::make_unique<HeapFrontendDispatcher>(context.frontendRouter))
113     , m_backendDispatcher(HeapBackendDispatcher::create(context.backendDispatcher, this))
114     , m_environment(context.environment)
115     , m_sendGarbageCollectionEventsTask(std::make_unique<SendGarbageCollectionEventsTask>(*m_frontendDispatcher))
116 {
117 }
118
119 InspectorHeapAgent::~InspectorHeapAgent()
120 {
121     m_sendGarbageCollectionEventsTask->reset();
122 }
123
124 void InspectorHeapAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*)
125 {
126 }
127
128 void InspectorHeapAgent::willDestroyFrontendAndBackend(DisconnectReason)
129 {
130     // Stop tracking without taking a snapshot.
131     m_tracking = false;
132
133     ErrorString ignored;
134     disable(ignored);
135 }
136
137 void InspectorHeapAgent::enable(ErrorString&)
138 {
139     if (m_enabled)
140         return;
141
142     m_enabled = true;
143
144     m_environment.vm().heap.addObserver(this);
145 }
146
147 void InspectorHeapAgent::disable(ErrorString&)
148 {
149     if (!m_enabled)
150         return;
151
152     m_enabled = false;
153
154     m_environment.vm().heap.removeObserver(this);
155     m_sendGarbageCollectionEventsTask->reset();
156
157     clearHeapSnapshots();
158 }
159
160 void InspectorHeapAgent::gc(ErrorString&)
161 {
162     VM& vm = m_environment.vm();
163     JSLockHolder lock(vm);
164     sanitizeStackForVM(&vm);
165     vm.heap.collectAllGarbage();
166 }
167
168 void InspectorHeapAgent::snapshot(ErrorString&, double* timestamp, String* snapshotData)
169 {
170     VM& vm = m_environment.vm();
171     JSLockHolder lock(vm);
172
173     HeapSnapshotBuilder snapshotBuilder(vm.ensureHeapProfiler());
174     snapshotBuilder.buildSnapshot();
175
176     *timestamp = m_environment.executionStopwatch()->elapsedTime();
177     *snapshotData = snapshotBuilder.json([&] (const HeapSnapshotNode& node) {
178         if (Structure* structure = node.cell->structure(vm)) {
179             if (JSGlobalObject* globalObject = structure->globalObject()) {
180                 if (!m_environment.canAccessInspectedScriptState(globalObject->globalExec()))
181                     return false;
182             }
183         }
184         return true;
185     });
186 }
187
188 void InspectorHeapAgent::startTracking(ErrorString& errorString)
189 {
190     if (m_tracking)
191         return;
192
193     m_tracking = true;
194
195     double timestamp;
196     String snapshotData;
197     snapshot(errorString, &timestamp, &snapshotData);
198
199     m_frontendDispatcher->trackingStart(timestamp, snapshotData);
200 }
201
202 void InspectorHeapAgent::stopTracking(ErrorString& errorString)
203 {
204     if (!m_tracking)
205         return;
206
207     m_tracking = false;
208
209     double timestamp;
210     String snapshotData;
211     snapshot(errorString, &timestamp, &snapshotData);
212
213     m_frontendDispatcher->trackingComplete(timestamp, snapshotData);
214 }
215
216 std::optional<HeapSnapshotNode> InspectorHeapAgent::nodeForHeapObjectIdentifier(ErrorString& errorString, unsigned heapObjectIdentifier)
217 {
218     HeapProfiler* heapProfiler = m_environment.vm().heapProfiler();
219     if (!heapProfiler) {
220         errorString = ASCIILiteral("No heap snapshot");
221         return std::nullopt;
222     }
223
224     HeapSnapshot* snapshot = heapProfiler->mostRecentSnapshot();
225     if (!snapshot) {
226         errorString = ASCIILiteral("No heap snapshot");
227         return std::nullopt;
228     }
229
230     const std::optional<HeapSnapshotNode> optionalNode = snapshot->nodeForObjectIdentifier(heapObjectIdentifier);
231     if (!optionalNode) {
232         errorString = ASCIILiteral("No object for identifier, it may have been collected");
233         return std::nullopt;
234     }
235
236     return optionalNode;
237 }
238
239 void InspectorHeapAgent::getPreview(ErrorString& errorString, int heapObjectId, Inspector::Protocol::OptOutput<String>* resultString, RefPtr<Inspector::Protocol::Debugger::FunctionDetails>& functionDetails, RefPtr<Inspector::Protocol::Runtime::ObjectPreview>& objectPreview)
240 {
241     // Prevent the cell from getting collected as we look it up.
242     VM& vm = m_environment.vm();
243     JSLockHolder lock(vm);
244     DeferGC deferGC(vm.heap);
245
246     unsigned heapObjectIdentifier = static_cast<unsigned>(heapObjectId);
247     const std::optional<HeapSnapshotNode> optionalNode = nodeForHeapObjectIdentifier(errorString, heapObjectIdentifier);
248     if (!optionalNode)
249         return;
250
251     // String preview.
252     JSCell* cell = optionalNode->cell;
253     if (cell->isString()) {
254         *resultString = cell->getString(nullptr);
255         return;
256     }
257
258     // FIXME: Provide preview information for Internal Objects? CodeBlock, Executable, etc.
259
260     Structure* structure = cell->structure(vm);
261     if (!structure) {
262         errorString = ASCIILiteral("Unable to get object details - Structure");
263         return;
264     }
265
266     JSGlobalObject* globalObject = structure->globalObject();
267     if (!globalObject) {
268         errorString = ASCIILiteral("Unable to get object details - GlobalObject");
269         return;
270     }
271
272     InjectedScript injectedScript = m_injectedScriptManager.injectedScriptFor(globalObject->globalExec());
273     if (injectedScript.hasNoValue()) {
274         errorString = ASCIILiteral("Unable to get object details - InjectedScript");
275         return;
276     }
277
278     // Function preview.
279     if (cell->inherits(JSFunction::info())) {
280         injectedScript.functionDetails(errorString, cell, &functionDetails);
281         return;
282     }
283
284     // Object preview.
285     objectPreview = injectedScript.previewValue(cell);
286 }
287
288 void InspectorHeapAgent::getRemoteObject(ErrorString& errorString, int heapObjectId, const String* optionalObjectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result)
289 {
290     // Prevent the cell from getting collected as we look it up.
291     VM& vm = m_environment.vm();
292     JSLockHolder lock(vm);
293     DeferGC deferGC(vm.heap);
294
295     unsigned heapObjectIdentifier = static_cast<unsigned>(heapObjectId);
296     const std::optional<HeapSnapshotNode> optionalNode = nodeForHeapObjectIdentifier(errorString, heapObjectIdentifier);
297     if (!optionalNode)
298         return;
299
300     JSCell* cell = optionalNode->cell;
301     Structure* structure = cell->structure(vm);
302     if (!structure) {
303         errorString = ASCIILiteral("Unable to get object details");
304         return;
305     }
306
307     JSGlobalObject* globalObject = structure->globalObject();
308     if (!globalObject) {
309         errorString = ASCIILiteral("Unable to get object details");
310         return;
311     }
312
313     InjectedScript injectedScript = m_injectedScriptManager.injectedScriptFor(globalObject->globalExec());
314     if (injectedScript.hasNoValue()) {
315         errorString = ASCIILiteral("Unable to get object details - InjectedScript");
316         return;
317     }
318
319     String objectGroup = optionalObjectGroup ? *optionalObjectGroup : String();
320     result = injectedScript.wrapObject(cell, objectGroup, true);
321 }
322
323 static Inspector::Protocol::Heap::GarbageCollection::Type protocolTypeForHeapOperation(CollectionScope scope)
324 {
325     switch (scope) {
326     case CollectionScope::Full:
327         return Inspector::Protocol::Heap::GarbageCollection::Type::Full;
328     case CollectionScope::Eden:
329         return Inspector::Protocol::Heap::GarbageCollection::Type::Partial;
330     }
331     ASSERT_NOT_REACHED();
332     return Inspector::Protocol::Heap::GarbageCollection::Type::Full;
333 }
334
335 void InspectorHeapAgent::willGarbageCollect()
336 {
337     ASSERT(m_enabled);
338     ASSERT(std::isnan(m_gcStartTime));
339
340     m_gcStartTime = m_environment.executionStopwatch()->elapsedTime();
341 }
342
343 void InspectorHeapAgent::didGarbageCollect(CollectionScope scope)
344 {
345     ASSERT(m_enabled);
346     ASSERT(!std::isnan(m_gcStartTime));
347
348     // FIXME: Include number of bytes freed by collection.
349
350     // Dispatch the event asynchronously because this method may be
351     // called between collection and sweeping and we don't want to
352     // create unexpected JavaScript allocations that the Sweeper does
353     // not expect to encounter. JavaScript allocations could happen
354     // with WebKitLegacy's in process inspector which shares the same
355     // VM as the inspected page.
356
357     GarbageCollectionData data;
358     data.type = protocolTypeForHeapOperation(scope);
359     data.startTime = m_gcStartTime;
360     data.endTime = m_environment.executionStopwatch()->elapsedTime();
361
362     m_sendGarbageCollectionEventsTask->addGarbageCollection(data);
363
364     m_gcStartTime = NAN;
365 }
366
367 void InspectorHeapAgent::clearHeapSnapshots()
368 {
369     VM& vm = m_environment.vm();
370     JSLockHolder lock(vm);
371
372     if (HeapProfiler* heapProfiler = vm.heapProfiler()) {
373         heapProfiler->clearSnapshots();
374         HeapSnapshotBuilder::resetNextAvailableObjectIdentifier();
375     }
376 }
377
378 } // namespace Inspector