[WTF] Use m_suspendCount instead of m_suspended flag in Thread
[WebKit-https.git] / Source / JavaScriptCore / heap / HeapSnapshotBuilder.cpp
1 /*
2  * Copyright (C) 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 "HeapSnapshotBuilder.h"
28
29 #include "DeferGC.h"
30 #include "Heap.h"
31 #include "HeapProfiler.h"
32 #include "HeapSnapshot.h"
33 #include "JSCInlines.h"
34 #include "JSCell.h"
35 #include "PreventCollectionScope.h"
36 #include "VM.h"
37 #include <wtf/text/StringBuilder.h>
38
39 namespace JSC {
40     
41 unsigned HeapSnapshotBuilder::nextAvailableObjectIdentifier = 1;
42 unsigned HeapSnapshotBuilder::getNextObjectIdentifier() { return nextAvailableObjectIdentifier++; }
43 void HeapSnapshotBuilder::resetNextAvailableObjectIdentifier() { HeapSnapshotBuilder::nextAvailableObjectIdentifier = 1; }
44
45 HeapSnapshotBuilder::HeapSnapshotBuilder(HeapProfiler& profiler)
46     : m_profiler(profiler)
47 {
48 }
49
50 HeapSnapshotBuilder::~HeapSnapshotBuilder()
51 {
52 }
53
54 void HeapSnapshotBuilder::buildSnapshot()
55 {
56     PreventCollectionScope preventCollectionScope(m_profiler.vm().heap);
57     
58     m_snapshot = std::make_unique<HeapSnapshot>(m_profiler.mostRecentSnapshot());
59     {
60         m_profiler.setActiveSnapshotBuilder(this);
61         m_profiler.vm().heap.collectNow(Sync, CollectionScope::Full);
62         m_profiler.setActiveSnapshotBuilder(nullptr);
63     }
64     m_snapshot->finalize();
65
66     m_profiler.appendSnapshot(WTFMove(m_snapshot));
67 }
68
69 void HeapSnapshotBuilder::appendNode(JSCell* cell)
70 {
71     ASSERT(m_profiler.activeSnapshotBuilder() == this);
72     ASSERT(Heap::isMarkedConcurrently(cell));
73
74     if (hasExistingNodeForCell(cell))
75         return;
76
77     std::lock_guard<Lock> lock(m_buildingNodeMutex);
78
79     m_snapshot->appendNode(HeapSnapshotNode(cell, getNextObjectIdentifier()));
80 }
81
82 void HeapSnapshotBuilder::appendEdge(JSCell* from, JSCell* to)
83 {
84     ASSERT(m_profiler.activeSnapshotBuilder() == this);
85     ASSERT(to);
86
87     // Avoid trivial edges.
88     if (from == to)
89         return;
90
91     std::lock_guard<Lock> lock(m_buildingEdgeMutex);
92
93     m_edges.append(HeapSnapshotEdge(from, to));
94 }
95
96 void HeapSnapshotBuilder::appendPropertyNameEdge(JSCell* from, JSCell* to, UniquedStringImpl* propertyName)
97 {
98     ASSERT(m_profiler.activeSnapshotBuilder() == this);
99     ASSERT(to);
100
101     std::lock_guard<Lock> lock(m_buildingEdgeMutex);
102
103     m_edges.append(HeapSnapshotEdge(from, to, EdgeType::Property, propertyName));
104 }
105
106 void HeapSnapshotBuilder::appendVariableNameEdge(JSCell* from, JSCell* to, UniquedStringImpl* variableName)
107 {
108     ASSERT(m_profiler.activeSnapshotBuilder() == this);
109     ASSERT(to);
110
111     std::lock_guard<Lock> lock(m_buildingEdgeMutex);
112
113     m_edges.append(HeapSnapshotEdge(from, to, EdgeType::Variable, variableName));
114 }
115
116 void HeapSnapshotBuilder::appendIndexEdge(JSCell* from, JSCell* to, uint32_t index)
117 {
118     ASSERT(m_profiler.activeSnapshotBuilder() == this);
119     ASSERT(to);
120
121     std::lock_guard<Lock> lock(m_buildingEdgeMutex);
122
123     m_edges.append(HeapSnapshotEdge(from, to, index));
124 }
125
126 bool HeapSnapshotBuilder::hasExistingNodeForCell(JSCell* cell)
127 {
128     if (!m_snapshot->previous())
129         return false;
130
131     return !!m_snapshot->previous()->nodeForCell(cell);
132 }
133
134
135 // Heap Snapshot JSON Format:
136 //
137 //   {
138 //      "version": 1.0,
139 //      "nodes": [
140 //          <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal>,
141 //          <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal>,
142 //          ...
143 //      ],
144 //      "nodeClassNames": [
145 //          "string", "Structure", "Object", ...
146 //      ],
147 //      "edges": [
148 //          <fromNodeId>, <toNodeId>, <edgeTypeIndex>, <edgeExtraData>,
149 //          <fromNodeId>, <toNodeId>, <edgeTypeIndex>, <edgeExtraData>,
150 //          ...
151 //      ],
152 //      "edgeTypes": [
153 //          "Internal", "Property", "Index", "Variable"
154 //      ],
155 //      "edgeNames": [
156 //          "propertyName", "variableName", ...
157 //      ]
158 //   }
159 //
160 // Notes:
161 //
162 //     <nodeClassNameIndex>
163 //       - index into the "nodeClassNames" list.
164 //
165 //     <internal>
166 //       - 0 = false, 1 = true.
167 //
168 //     <edgeTypeIndex>
169 //       - index into the "edgeTypes" list.
170 //
171 //     <edgeExtraData>
172 //       - for Internal edges this should be ignored (0).
173 //       - for Index edges this is the index value.
174 //       - for Property or Variable edges this is an index into the "edgeNames" list.
175
176 static uint8_t edgeTypeToNumber(EdgeType type)
177 {
178     return static_cast<uint8_t>(type);
179 }
180
181 static const char* edgeTypeToString(EdgeType type)
182 {
183     switch (type) {
184     case EdgeType::Internal:
185         return "Internal";
186     case EdgeType::Property:
187         return "Property";
188     case EdgeType::Index:
189         return "Index";
190     case EdgeType::Variable:
191         return "Variable";
192     }
193     ASSERT_NOT_REACHED();
194     return "Internal";
195 }
196
197 String HeapSnapshotBuilder::json()
198 {
199     return json([] (const HeapSnapshotNode&) { return true; });
200 }
201
202 String HeapSnapshotBuilder::json(std::function<bool (const HeapSnapshotNode&)> allowNodeCallback)
203 {
204     VM& vm = m_profiler.vm();
205     DeferGCForAWhile deferGC(vm.heap);
206
207     // Build a node to identifier map of allowed nodes to use when serializing edges.
208     HashMap<JSCell*, unsigned> allowedNodeIdentifiers;
209
210     // Build a list of used class names.
211     HashMap<const char*, unsigned> classNameIndexes;
212     classNameIndexes.set("<root>", 0);
213     unsigned nextClassNameIndex = 1;
214
215     // Build a list of used edge names.
216     HashMap<UniquedStringImpl*, unsigned> edgeNameIndexes;
217     unsigned nextEdgeNameIndex = 0;
218
219     StringBuilder json;
220
221     auto appendNodeJSON = [&] (const HeapSnapshotNode& node) {
222         // Let the client decide if they want to allow or disallow certain nodes.
223         if (!allowNodeCallback(node))
224             return;
225
226         allowedNodeIdentifiers.set(node.cell, node.identifier);
227
228         auto result = classNameIndexes.add(node.cell->classInfo(vm)->className, nextClassNameIndex);
229         if (result.isNewEntry)
230             nextClassNameIndex++;
231         unsigned classNameIndex = result.iterator->value;
232
233         bool isInternal = false;
234         if (!node.cell->isString()) {
235             Structure* structure = node.cell->structure(vm);
236             isInternal = !structure || !structure->globalObject();
237         }
238
239         // <nodeId>, <sizeInBytes>, <className>, <optionalInternalBoolean>
240         json.append(',');
241         json.appendNumber(node.identifier);
242         json.append(',');
243         json.appendNumber(node.cell->estimatedSizeInBytes());
244         json.append(',');
245         json.appendNumber(classNameIndex);
246         json.append(',');
247         json.append(isInternal ? '1' : '0');
248     };
249
250     bool firstEdge = true;
251     auto appendEdgeJSON = [&] (const HeapSnapshotEdge& edge) {
252         if (!firstEdge)
253             json.append(',');
254         firstEdge = false;
255
256         // <fromNodeId>, <toNodeId>, <edgeTypeIndex>, <edgeExtraData>
257         json.appendNumber(edge.from.identifier);
258         json.append(',');
259         json.appendNumber(edge.to.identifier);
260         json.append(',');
261         json.appendNumber(edgeTypeToNumber(edge.type));
262         json.append(',');
263         switch (edge.type) {
264         case EdgeType::Property:
265         case EdgeType::Variable: {
266             auto result = edgeNameIndexes.add(edge.u.name, nextEdgeNameIndex);
267             if (result.isNewEntry)
268                 nextEdgeNameIndex++;
269             unsigned edgeNameIndex = result.iterator->value;
270             json.appendNumber(edgeNameIndex);
271             break;
272         }
273         case EdgeType::Index:
274             json.appendNumber(edge.u.index);
275             break;
276         default:
277             // No data for this edge type.
278             json.append('0');
279             break;
280         }
281     };
282
283     json.append('{');
284
285     // version
286     json.appendLiteral("\"version\":1");
287
288     // nodes
289     json.append(',');
290     json.appendLiteral("\"nodes\":");
291     json.append('[');
292     json.appendLiteral("0,0,0,0"); // <root>
293     for (HeapSnapshot* snapshot = m_profiler.mostRecentSnapshot(); snapshot; snapshot = snapshot->previous()) {
294         for (auto& node : snapshot->m_nodes)
295             appendNodeJSON(node);
296     }
297     json.append(']');
298
299     // node class names
300     json.append(',');
301     json.appendLiteral("\"nodeClassNames\":");
302     json.append('[');
303     Vector<const char *> orderedClassNames(classNameIndexes.size());
304     for (auto& entry : classNameIndexes)
305         orderedClassNames[entry.value] = entry.key;
306     classNameIndexes.clear();
307     bool firstClassName = true;
308     for (auto& className : orderedClassNames) {
309         if (!firstClassName)
310             json.append(',');
311         firstClassName = false;
312         json.appendQuotedJSONString(className);
313     }
314     orderedClassNames.clear();
315     json.append(']');
316
317     // Process edges.
318     // Replace pointers with identifiers.
319     // Remove any edges that we won't need.
320     m_edges.removeAllMatching([&] (HeapSnapshotEdge& edge) {
321         // If the from cell is null, this means a <root> edge.
322         if (!edge.from.cell)
323             edge.from.identifier = 0;
324         else {
325             auto fromLookup = allowedNodeIdentifiers.find(edge.from.cell);
326             if (fromLookup == allowedNodeIdentifiers.end())
327                 return true;
328             edge.from.identifier = fromLookup->value;
329         }
330
331         if (!edge.to.cell)
332             edge.to.identifier = 0;
333         else {
334             auto toLookup = allowedNodeIdentifiers.find(edge.to.cell);
335             if (toLookup == allowedNodeIdentifiers.end())
336                 return true;
337             edge.to.identifier = toLookup->value;
338         }
339
340         return false;
341     });
342     allowedNodeIdentifiers.clear();
343     m_edges.shrinkToFit();
344
345     // Sort edges based on from identifier.
346     std::sort(m_edges.begin(), m_edges.end(), [&] (const HeapSnapshotEdge& a, const HeapSnapshotEdge& b) {
347         return a.from.identifier < b.from.identifier;
348     });
349
350     // edges
351     json.append(',');
352     json.appendLiteral("\"edges\":");
353     json.append('[');
354     for (auto& edge : m_edges)
355         appendEdgeJSON(edge);
356     json.append(']');
357
358     // edge types
359     json.append(',');
360     json.appendLiteral("\"edgeTypes\":");
361     json.append('[');
362     json.appendQuotedJSONString(edgeTypeToString(EdgeType::Internal));
363     json.append(',');
364     json.appendQuotedJSONString(edgeTypeToString(EdgeType::Property));
365     json.append(',');
366     json.appendQuotedJSONString(edgeTypeToString(EdgeType::Index));
367     json.append(',');
368     json.appendQuotedJSONString(edgeTypeToString(EdgeType::Variable));
369     json.append(']');
370
371     // edge names
372     json.append(',');
373     json.appendLiteral("\"edgeNames\":");
374     json.append('[');
375     Vector<UniquedStringImpl*> orderedEdgeNames(edgeNameIndexes.size());
376     for (auto& entry : edgeNameIndexes)
377         orderedEdgeNames[entry.value] = entry.key;
378     edgeNameIndexes.clear();
379     bool firstEdgeName = true;
380     for (auto& edgeName : orderedEdgeNames) {
381         if (!firstEdgeName)
382             json.append(',');
383         firstEdgeName = false;
384         json.appendQuotedJSONString(edgeName);
385     }
386     orderedEdgeNames.clear();
387     json.append(']');
388
389     json.append('}');
390     return json.toString();
391 }
392
393 } // namespace JSC