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