DeserializedNode.h 10 KB

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