/work/obj-fuzz/dist/include/mozilla/devtools/DeserializedNode.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #ifndef mozilla_devtools_DeserializedNode__ |
7 | | #define mozilla_devtools_DeserializedNode__ |
8 | | |
9 | | #include "js/UbiNode.h" |
10 | | #include "js/UniquePtr.h" |
11 | | #include "mozilla/devtools/CoreDump.pb.h" |
12 | | #include "mozilla/HashFunctions.h" |
13 | | #include "mozilla/Maybe.h" |
14 | | #include "mozilla/Move.h" |
15 | | #include "mozilla/Vector.h" |
16 | | |
17 | | // `Deserialized{Node,Edge}` translate protobuf messages from our core dump |
18 | | // format into structures we can rely upon for implementing `JS::ubi::Node` |
19 | | // specializations on top of. All of the properties of the protobuf messages are |
20 | | // optional for future compatibility, and this is the layer where we validate |
21 | | // that the properties that do actually exist in any given message fulfill our |
22 | | // semantic requirements. |
23 | | // |
24 | | // Both `DeserializedNode` and `DeserializedEdge` are always owned by a |
25 | | // `HeapSnapshot` instance, and their lifetimes must not extend after that of |
26 | | // their owning `HeapSnapshot`. |
27 | | |
28 | | namespace mozilla { |
29 | | namespace devtools { |
30 | | |
31 | | class HeapSnapshot; |
32 | | |
33 | | using NodeId = uint64_t; |
34 | | using StackFrameId = uint64_t; |
35 | | |
36 | | // A `DeserializedEdge` represents an edge in the heap graph pointing to the |
37 | | // node with id equal to `DeserializedEdge::referent` that we deserialized from |
38 | | // a core dump. |
39 | | struct DeserializedEdge { |
40 | | NodeId referent; |
41 | | // A borrowed reference to a string owned by this node's owning HeapSnapshot. |
42 | | const char16_t* name; |
43 | | |
44 | | explicit DeserializedEdge(NodeId referent, const char16_t* edgeName = nullptr) |
45 | | : referent(referent) |
46 | | , name(edgeName) |
47 | 0 | { } |
48 | | DeserializedEdge(DeserializedEdge&& rhs); |
49 | | DeserializedEdge& operator=(DeserializedEdge&& rhs); |
50 | | |
51 | | private: |
52 | | DeserializedEdge(const DeserializedEdge&) = delete; |
53 | | DeserializedEdge& operator=(const DeserializedEdge&) = delete; |
54 | | }; |
55 | | |
56 | | // A `DeserializedNode` is a node in the heap graph that we deserialized from a |
57 | | // core dump. |
58 | | struct DeserializedNode { |
59 | | using EdgeVector = Vector<DeserializedEdge>; |
60 | | using UniqueStringPtr = UniquePtr<char16_t[]>; |
61 | | |
62 | | NodeId id; |
63 | | JS::ubi::CoarseType coarseType; |
64 | | // A borrowed reference to a string owned by this node's owning HeapSnapshot. |
65 | | const char16_t* typeName; |
66 | | uint64_t size; |
67 | | EdgeVector edges; |
68 | | Maybe<StackFrameId> allocationStack; |
69 | | // A borrowed reference to a string owned by this node's owning HeapSnapshot. |
70 | | const char* jsObjectClassName; |
71 | | // A borrowed reference to a string owned by this node's owning HeapSnapshot. |
72 | | const char* scriptFilename; |
73 | | // A borrowed reference to a string owned by this node's owning HeapSnapshot. |
74 | | const char16_t* descriptiveTypeName; |
75 | | // A weak pointer to this node's owning `HeapSnapshot`. Safe without |
76 | | // AddRef'ing because this node's lifetime is equal to that of its owner. |
77 | | HeapSnapshot* owner; |
78 | | |
79 | | DeserializedNode(NodeId id, |
80 | | JS::ubi::CoarseType coarseType, |
81 | | const char16_t* typeName, |
82 | | uint64_t size, |
83 | | EdgeVector&& edges, |
84 | | const Maybe<StackFrameId>& allocationStack, |
85 | | const char* className, |
86 | | const char* filename, |
87 | | const char16_t* descriptiveName, |
88 | | HeapSnapshot& owner) |
89 | | : id(id) |
90 | | , coarseType(coarseType) |
91 | | , typeName(typeName) |
92 | | , size(size) |
93 | | , edges(std::move(edges)) |
94 | | , allocationStack(allocationStack) |
95 | | , jsObjectClassName(className) |
96 | | , scriptFilename(filename) |
97 | | , descriptiveTypeName(descriptiveName) |
98 | | , owner(&owner) |
99 | 0 | { } |
100 | 0 | virtual ~DeserializedNode() { } |
101 | | |
102 | | DeserializedNode(DeserializedNode&& rhs) |
103 | | : id(rhs.id) |
104 | | , coarseType(rhs.coarseType) |
105 | | , typeName(rhs.typeName) |
106 | | , size(rhs.size) |
107 | | , edges(std::move(rhs.edges)) |
108 | | , allocationStack(rhs.allocationStack) |
109 | | , jsObjectClassName(rhs.jsObjectClassName) |
110 | | , scriptFilename(rhs.scriptFilename) |
111 | | , descriptiveTypeName(rhs.descriptiveTypeName) |
112 | | , owner(rhs.owner) |
113 | 0 | { } |
114 | | |
115 | | DeserializedNode& operator=(DeserializedNode&& rhs) |
116 | 0 | { |
117 | 0 | MOZ_ASSERT(&rhs != this); |
118 | 0 | this->~DeserializedNode(); |
119 | 0 | new(this) DeserializedNode(std::move(rhs)); |
120 | 0 | return *this; |
121 | 0 | } |
122 | | |
123 | | // Get a borrowed reference to the given edge's referent. This method is |
124 | | // virtual to provide a hook for gmock and gtest. |
125 | | virtual JS::ubi::Node getEdgeReferent(const DeserializedEdge& edge); |
126 | | |
127 | | struct HashPolicy; |
128 | | |
129 | | protected: |
130 | | // This is only for use with `MockDeserializedNode` in testing. |
131 | | DeserializedNode(NodeId id, const char16_t* typeName, uint64_t size) |
132 | | : id(id) |
133 | | , coarseType(JS::ubi::CoarseType::Other) |
134 | | , typeName(typeName) |
135 | | , size(size) |
136 | | , edges() |
137 | | , allocationStack(Nothing()) |
138 | | , jsObjectClassName(nullptr) |
139 | | , scriptFilename(nullptr) |
140 | | , descriptiveTypeName(nullptr) |
141 | | , owner(nullptr) |
142 | 0 | { } |
143 | | |
144 | | private: |
145 | | DeserializedNode(const DeserializedNode&) = delete; |
146 | | DeserializedNode& operator=(const DeserializedNode&) = delete; |
147 | | }; |
148 | | |
149 | | static inline js::HashNumber |
150 | | hashIdDerivedFromPtr(uint64_t id) |
151 | 0 | { |
152 | 0 | return mozilla::HashGeneric(id); |
153 | 0 | } Unexecuted instantiation: UnifiedBindings2.cpp:mozilla::devtools::hashIdDerivedFromPtr(unsigned long) Unexecuted instantiation: UnifiedBindings4.cpp:mozilla::devtools::hashIdDerivedFromPtr(unsigned long) Unexecuted instantiation: UnifiedBindings8.cpp:mozilla::devtools::hashIdDerivedFromPtr(unsigned long) Unexecuted instantiation: DeserializedNode.cpp:mozilla::devtools::hashIdDerivedFromPtr(unsigned long) Unexecuted instantiation: DominatorTree.cpp:mozilla::devtools::hashIdDerivedFromPtr(unsigned long) Unexecuted instantiation: HeapSnapshot.cpp:mozilla::devtools::hashIdDerivedFromPtr(unsigned long) Unexecuted instantiation: HeapSnapshotTempFileHelperParent.cpp:mozilla::devtools::hashIdDerivedFromPtr(unsigned long) Unexecuted instantiation: Unified_cpp_tests_gtest0.cpp:mozilla::devtools::hashIdDerivedFromPtr(unsigned long) |
154 | | |
155 | | struct DeserializedNode::HashPolicy |
156 | | { |
157 | | using Lookup = NodeId; |
158 | | |
159 | 0 | static js::HashNumber hash(const Lookup& lookup) { |
160 | 0 | return hashIdDerivedFromPtr(lookup); |
161 | 0 | } |
162 | | |
163 | 0 | static bool match(const DeserializedNode& existing, const Lookup& lookup) { |
164 | 0 | return existing.id == lookup; |
165 | 0 | } |
166 | | }; |
167 | | |
168 | | // A `DeserializedStackFrame` is a stack frame referred to by a thing in the |
169 | | // heap graph that we deserialized from a core dump. |
170 | | struct DeserializedStackFrame { |
171 | | StackFrameId id; |
172 | | Maybe<StackFrameId> parent; |
173 | | uint32_t line; |
174 | | uint32_t column; |
175 | | // Borrowed references to strings owned by this DeserializedStackFrame's |
176 | | // owning HeapSnapshot. |
177 | | const char16_t* source; |
178 | | const char16_t* functionDisplayName; |
179 | | bool isSystem; |
180 | | bool isSelfHosted; |
181 | | // A weak pointer to this frame's owning `HeapSnapshot`. Safe without |
182 | | // AddRef'ing because this frame's lifetime is equal to that of its owner. |
183 | | HeapSnapshot* owner; |
184 | | |
185 | | explicit DeserializedStackFrame(StackFrameId id, |
186 | | const Maybe<StackFrameId>& parent, |
187 | | uint32_t line, |
188 | | uint32_t column, |
189 | | const char16_t* source, |
190 | | const char16_t* functionDisplayName, |
191 | | bool isSystem, |
192 | | bool isSelfHosted, |
193 | | HeapSnapshot& owner) |
194 | | : id(id) |
195 | | , parent(parent) |
196 | | , line(line) |
197 | | , column(column) |
198 | | , source(source) |
199 | | , functionDisplayName(functionDisplayName) |
200 | | , isSystem(isSystem) |
201 | | , isSelfHosted(isSelfHosted) |
202 | | , owner(&owner) |
203 | 0 | { |
204 | 0 | MOZ_ASSERT(source); |
205 | 0 | } |
206 | | |
207 | | JS::ubi::StackFrame getParentStackFrame() const; |
208 | | |
209 | | struct HashPolicy; |
210 | | |
211 | | protected: |
212 | | // This is exposed only for MockDeserializedStackFrame in the gtests. |
213 | | explicit DeserializedStackFrame() |
214 | | : id(0) |
215 | | , parent(Nothing()) |
216 | | , line(0) |
217 | | , column(0) |
218 | | , source(nullptr) |
219 | | , functionDisplayName(nullptr) |
220 | | , isSystem(false) |
221 | | , isSelfHosted(false) |
222 | | , owner(nullptr) |
223 | 0 | { }; |
224 | | }; |
225 | | |
226 | | struct DeserializedStackFrame::HashPolicy { |
227 | | using Lookup = StackFrameId; |
228 | | |
229 | 0 | static js::HashNumber hash(const Lookup& lookup) { |
230 | 0 | return hashIdDerivedFromPtr(lookup); |
231 | 0 | } |
232 | | |
233 | 0 | static bool match(const DeserializedStackFrame& existing, const Lookup& lookup) { |
234 | 0 | return existing.id == lookup; |
235 | 0 | } |
236 | | }; |
237 | | |
238 | | } // namespace devtools |
239 | | } // namespace mozilla |
240 | | |
241 | | namespace JS { |
242 | | namespace ubi { |
243 | | |
244 | | using mozilla::devtools::DeserializedNode; |
245 | | using mozilla::devtools::DeserializedStackFrame; |
246 | | |
247 | | template<> |
248 | | class Concrete<DeserializedNode> : public Base |
249 | | { |
250 | | protected: |
251 | 0 | explicit Concrete(DeserializedNode* ptr) : Base(ptr) { } |
252 | 0 | DeserializedNode& get() const { |
253 | 0 | return *static_cast<DeserializedNode*>(ptr); |
254 | 0 | } |
255 | | |
256 | | public: |
257 | 0 | static void construct(void* storage, DeserializedNode* ptr) { |
258 | 0 | new (storage) Concrete(ptr); |
259 | 0 | } |
260 | | |
261 | 0 | CoarseType coarseType() const final { return get().coarseType; } |
262 | 0 | Id identifier() const override { return get().id; } |
263 | 0 | bool isLive() const override { return false; } |
264 | | const char16_t* typeName() const override; |
265 | | Node::Size size(mozilla::MallocSizeOf mallocSizeof) const override; |
266 | 0 | const char* jsObjectClassName() const override { return get().jsObjectClassName; } |
267 | 0 | const char* scriptFilename() const final { return get().scriptFilename; } |
268 | 0 | const char16_t* descriptiveTypeName() const override { return get().descriptiveTypeName; } |
269 | | |
270 | 0 | bool hasAllocationStack() const override { return get().allocationStack.isSome(); } |
271 | | StackFrame allocationStack() const override; |
272 | | |
273 | | // We ignore the `bool wantNames` parameter because we can't control whether |
274 | | // the core dump was serialized with edge names or not. |
275 | | js::UniquePtr<EdgeRange> edges(JSContext* cx, bool) const override; |
276 | | |
277 | | static const char16_t concreteTypeName[]; |
278 | | }; |
279 | | |
280 | | template<> |
281 | | class ConcreteStackFrame<DeserializedStackFrame> : public BaseStackFrame |
282 | | { |
283 | | protected: |
284 | | explicit ConcreteStackFrame(DeserializedStackFrame* ptr) |
285 | | : BaseStackFrame(ptr) |
286 | 0 | { } |
287 | | |
288 | 0 | DeserializedStackFrame& get() const { |
289 | 0 | return *static_cast<DeserializedStackFrame*>(ptr); |
290 | 0 | } |
291 | | |
292 | | public: |
293 | 0 | static void construct(void* storage, DeserializedStackFrame* ptr) { |
294 | 0 | new (storage) ConcreteStackFrame(ptr); |
295 | 0 | } |
296 | | |
297 | 0 | uint64_t identifier() const override { return get().id; } |
298 | 0 | uint32_t line() const override { return get().line; } |
299 | 0 | uint32_t column() const override { return get().column; } |
300 | 0 | bool isSystem() const override { return get().isSystem; } |
301 | 0 | bool isSelfHosted(JSContext* cx) const override { return get().isSelfHosted; } |
302 | 0 | void trace(JSTracer* trc) override { } |
303 | 0 | AtomOrTwoByteChars source() const override { |
304 | 0 | return AtomOrTwoByteChars(get().source); |
305 | 0 | } |
306 | 0 | AtomOrTwoByteChars functionDisplayName() const override { |
307 | 0 | return AtomOrTwoByteChars(get().functionDisplayName); |
308 | 0 | } |
309 | | |
310 | | StackFrame parent() const override; |
311 | | bool constructSavedFrameStack(JSContext* cx, |
312 | | MutableHandleObject outSavedFrameStack) |
313 | | const override; |
314 | | }; |
315 | | |
316 | | } // namespace ubi |
317 | | } // namespace JS |
318 | | |
319 | | #endif // mozilla_devtools_DeserializedNode__ |