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#pragma once
27
28#include "SlotVisitor.h"
29#include <functional>
30#include <wtf/Lock.h>
31#include <wtf/Vector.h>
32#include <wtf/text/UniquedStringImpl.h>
33#include <wtf/text/WTFString.h>
34
35namespace JSC {
36
37class ConservativeRoots;
38class HeapProfiler;
39class HeapSnapshot;
40class JSCell;
41
42typedef unsigned NodeIdentifier;
43
44struct HeapSnapshotNode {
45 HeapSnapshotNode(JSCell* cell, unsigned identifier)
46 : cell(cell)
47 , identifier(identifier)
48 { }
49
50 JSCell* cell;
51 NodeIdentifier identifier;
52};
53
54enum class EdgeType : uint8_t {
55 Internal, // Normal strong reference. No name.
56 Property, // Named property. In `object.property` the name is "property"
57 Index, // Indexed property. In `array[0]` name is index "0".
58 Variable, // Variable held by a scope. In `let x, f=() => x++` name is "x" in f's captured scope.
59 // FIXME: <https://webkit.org/b/154934> Heap Snapshot should include "Weak" edges
60};
61
62struct HeapSnapshotEdge {
63 HeapSnapshotEdge(JSCell* fromCell, JSCell* toCell)
64 : type(EdgeType::Internal)
65 {
66 from.cell = fromCell;
67 to.cell = toCell;
68 }
69
70 HeapSnapshotEdge(JSCell* fromCell, JSCell* toCell, EdgeType type, UniquedStringImpl* name)
71 : type(type)
72 {
73 ASSERT(type == EdgeType::Property || type == EdgeType::Variable);
74 from.cell = fromCell;
75 to.cell = toCell;
76 u.name = name;
77 }
78
79 HeapSnapshotEdge(JSCell* fromCell, JSCell* toCell, uint32_t index)
80 : type(EdgeType::Index)
81 {
82 from.cell = fromCell;
83 to.cell = toCell;
84 u.index = index;
85 }
86
87 union {
88 JSCell *cell;
89 NodeIdentifier identifier;
90 } from;
91
92 union {
93 JSCell *cell;
94 NodeIdentifier identifier;
95 } to;
96
97 union {
98 UniquedStringImpl* name;
99 uint32_t index;
100 } u;
101
102 EdgeType type;
103};
104
105class JS_EXPORT_PRIVATE HeapSnapshotBuilder {
106 WTF_MAKE_FAST_ALLOCATED;
107public:
108 enum SnapshotType { InspectorSnapshot, GCDebuggingSnapshot };
109
110 HeapSnapshotBuilder(HeapProfiler&, SnapshotType = SnapshotType::InspectorSnapshot);
111 ~HeapSnapshotBuilder();
112
113 static void resetNextAvailableObjectIdentifier();
114
115 // Performs a garbage collection that builds a snapshot of all live cells.
116 void buildSnapshot();
117
118 // A root or marked cell.
119 void appendNode(JSCell*);
120
121 // A reference from one cell to another.
122 void appendEdge(JSCell* from, JSCell* to, SlotVisitor::RootMarkReason);
123 void appendPropertyNameEdge(JSCell* from, JSCell* to, UniquedStringImpl* propertyName);
124 void appendVariableNameEdge(JSCell* from, JSCell* to, UniquedStringImpl* variableName);
125 void appendIndexEdge(JSCell* from, JSCell* to, uint32_t index);
126
127 void setOpaqueRootReachabilityReasonForCell(JSCell*, const char*);
128 void setWrappedObjectForCell(JSCell*, void*);
129 void setLabelForCell(JSCell*, const String&);
130
131 String json();
132 String json(Function<bool (const HeapSnapshotNode&)> allowNodeCallback);
133
134private:
135 static NodeIdentifier nextAvailableObjectIdentifier;
136 static NodeIdentifier getNextObjectIdentifier();
137
138 // Finalized snapshots are not modified during building. So searching them
139 // for an existing node can be done concurrently without a lock.
140 bool previousSnapshotHasNodeForCell(JSCell*, NodeIdentifier&);
141
142 String descriptionForCell(JSCell*) const;
143
144 struct RootData {
145 const char* reachabilityFromOpaqueRootReasons { nullptr };
146 SlotVisitor::RootMarkReason markReason { SlotVisitor::RootMarkReason::None };
147 };
148
149 HeapProfiler& m_profiler;
150
151 // SlotVisitors run in parallel.
152 Lock m_buildingNodeMutex;
153 std::unique_ptr<HeapSnapshot> m_snapshot;
154 Lock m_buildingEdgeMutex;
155 Vector<HeapSnapshotEdge> m_edges;
156 HashMap<JSCell*, RootData> m_rootData;
157 HashMap<JSCell*, void*> m_wrappedObjectPointers;
158 HashMap<JSCell*, String> m_cellLabels;
159 SnapshotType m_snapshotType;
160};
161
162} // namespace JSC
163