Web Inspector: Add Heap domain start/stop tracking commands
[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 "InspectorEnvironment.h"
31 #include "JSCInlines.h"
32 #include "VM.h"
33 #include <wtf/RunLoop.h>
34 #include <wtf/Stopwatch.h>
35
36 using namespace JSC;
37
38 namespace Inspector {
39
40 InspectorHeapAgent::InspectorHeapAgent(AgentContext& context)
41     : InspectorAgentBase(ASCIILiteral("Heap"))
42     , m_frontendDispatcher(std::make_unique<HeapFrontendDispatcher>(context.frontendRouter))
43     , m_backendDispatcher(HeapBackendDispatcher::create(context.backendDispatcher, this))
44     , m_environment(context.environment)
45 {
46 }
47
48 InspectorHeapAgent::~InspectorHeapAgent()
49 {
50 }
51
52 void InspectorHeapAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*)
53 {
54 }
55
56 void InspectorHeapAgent::willDestroyFrontendAndBackend(DisconnectReason)
57 {
58     ErrorString ignored;
59     stopTracking(ignored);
60     disable(ignored);
61 }
62
63 void InspectorHeapAgent::enable(ErrorString&)
64 {
65     if (m_enabled)
66         return;
67
68     m_enabled = true;
69
70     m_environment.vm().heap.addObserver(this);
71 }
72
73 void InspectorHeapAgent::disable(ErrorString&)
74 {
75     if (!m_enabled)
76         return;
77
78     m_enabled = false;
79
80     m_environment.vm().heap.removeObserver(this);
81
82     clearHeapSnapshots();
83 }
84
85 void InspectorHeapAgent::gc(ErrorString&)
86 {
87     VM& vm = m_environment.vm();
88     JSLockHolder lock(vm);
89     sanitizeStackForVM(&vm);
90     vm.heap.collectAllGarbage();
91 }
92
93 void InspectorHeapAgent::snapshot(ErrorString&, double* timestamp, String* snapshotData)
94 {
95     VM& vm = m_environment.vm();
96     JSLockHolder lock(vm);
97
98     HeapSnapshotBuilder snapshotBuilder(vm.ensureHeapProfiler());
99     snapshotBuilder.buildSnapshot();
100
101     *timestamp = m_environment.executionStopwatch()->elapsedTime();
102     *snapshotData = snapshotBuilder.json([&] (const HeapSnapshotNode& node) {
103         if (Structure* structure = node.cell->structure(vm)) {
104             if (JSGlobalObject* globalObject = structure->globalObject()) {
105                 if (!m_environment.canAccessInspectedScriptState(globalObject->globalExec()))
106                     return false;
107             }
108         }
109         return true;
110     });
111 }
112
113 void InspectorHeapAgent::startTracking(ErrorString& errorString)
114 {
115     if (m_tracking)
116         return;
117
118     m_tracking = true;
119
120     double timestamp;
121     String snapshotData;
122     snapshot(errorString, &timestamp, &snapshotData);
123
124     m_frontendDispatcher->trackingStart(timestamp, snapshotData);
125 }
126
127 void InspectorHeapAgent::stopTracking(ErrorString& errorString)
128 {
129     if (!m_tracking)
130         return;
131
132     m_tracking = false;
133
134     double timestamp;
135     String snapshotData;
136     snapshot(errorString, &timestamp, &snapshotData);
137
138     m_frontendDispatcher->trackingComplete(timestamp, snapshotData);
139 }
140
141 static Inspector::Protocol::Heap::GarbageCollection::Type protocolTypeForHeapOperation(HeapOperation operation)
142 {
143     switch (operation) {
144     case FullCollection:
145         return Inspector::Protocol::Heap::GarbageCollection::Type::Full;
146     case EdenCollection:
147         return Inspector::Protocol::Heap::GarbageCollection::Type::Partial;
148     default:
149         ASSERT_NOT_REACHED();
150         return Inspector::Protocol::Heap::GarbageCollection::Type::Full;
151     }
152 }
153
154 void InspectorHeapAgent::willGarbageCollect()
155 {
156     ASSERT(m_enabled);
157     ASSERT(std::isnan(m_gcStartTime));
158
159     m_gcStartTime = m_environment.executionStopwatch()->elapsedTime();
160 }
161
162 void InspectorHeapAgent::didGarbageCollect(HeapOperation operation)
163 {
164     ASSERT(m_enabled);
165     ASSERT(!std::isnan(m_gcStartTime));
166
167     // FIXME: Include number of bytes freed by collection.
168
169     double startTime = m_gcStartTime;
170     double endTime = m_environment.executionStopwatch()->elapsedTime();
171
172     // Dispatch the event asynchronously because this method may be
173     // called between collection and sweeping and we don't want to
174     // create unexpected JavaScript allocations that the Sweeper does
175     // not expect to encounter. JavaScript allocations could happen
176     // with WebKitLegacy's in process inspector which shares the same
177     // VM as the inspected page.
178
179     RunLoop::current().dispatch([this, startTime, endTime, operation]() {
180         auto collection = Inspector::Protocol::Heap::GarbageCollection::create()
181             .setType(protocolTypeForHeapOperation(operation))
182             .setStartTime(startTime)
183             .setEndTime(endTime)
184             .release();
185
186         m_frontendDispatcher->garbageCollected(WTFMove(collection));
187     });
188
189     m_gcStartTime = NAN;
190 }
191
192 void InspectorHeapAgent::clearHeapSnapshots()
193 {
194     if (HeapProfiler* heapProfiler = m_environment.vm().heapProfiler())
195         heapProfiler->clearSnapshots();
196 }
197
198 } // namespace Inspector