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