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