tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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__