HeapSnapshot.h (7972B)
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_HeapSnapshot__ 7 #define mozilla_devtools_HeapSnapshot__ 8 9 #include "js/HashTable.h" 10 #include "mozilla/devtools/DeserializedNode.h" 11 #include "mozilla/dom/BindingDeclarations.h" 12 #include "mozilla/dom/Nullable.h" 13 #include "mozilla/Maybe.h" 14 #include "mozilla/TimeStamp.h" 15 #include "mozilla/UniquePtrExtensions.h" 16 17 #include "CoreDump.pb.h" 18 #include "nsCOMPtr.h" 19 #include "nsCRTGlue.h" 20 #include "nsCycleCollectionParticipant.h" 21 #include "nsISupports.h" 22 #include "nsWrapperCache.h" 23 #include "nsXPCOM.h" 24 25 namespace mozilla { 26 class ErrorResult; 27 28 namespace devtools { 29 30 class DominatorTree; 31 32 using UniqueTwoByteString = UniqueFreePtr<char16_t[]>; 33 using UniqueOneByteString = UniqueFreePtr<char[]>; 34 35 class HeapSnapshot final : public nsISupports, public nsWrapperCache { 36 friend struct DeserializedNode; 37 friend struct DeserializedEdge; 38 friend struct DeserializedStackFrame; 39 friend class JS::ubi::Concrete<JS::ubi::DeserializedNode>; 40 41 explicit HeapSnapshot(JSContext* cx, nsISupports* aParent) 42 : timestamp(Nothing()), 43 rootId(0), 44 nodes(cx), 45 frames(cx), 46 mParent(aParent) { 47 MOZ_ASSERT(aParent); 48 }; 49 50 // Initialize this HeapSnapshot from the given buffer that contains a 51 // serialized core dump. Do NOT take ownership of the buffer, only borrow it 52 // for the duration of the call. Return false on failure. 53 bool init(JSContext* cx, const uint8_t* buffer, uint32_t size); 54 55 using NodeIdSet = js::HashSet<NodeId>; 56 57 // Save the given `protobuf::Node` message in this `HeapSnapshot` as a 58 // `DeserializedNode`. 59 bool saveNode(const protobuf::Node& node, NodeIdSet& edgeReferents); 60 61 // Save the given `protobuf::StackFrame` message in this `HeapSnapshot` as a 62 // `DeserializedStackFrame`. The saved stack frame's id is returned via the 63 // out parameter. 64 bool saveStackFrame(const protobuf::StackFrame& frame, 65 StackFrameId& outFrameId); 66 67 public: 68 // The maximum number of stack frames that we will serialize into a core 69 // dump. This helps prevent over-recursion in the protobuf library when 70 // deserializing stacks. 71 static const size_t MAX_STACK_DEPTH = 60; 72 73 private: 74 // If present, a timestamp in the same units that `PR_Now` gives. 75 Maybe<uint64_t> timestamp; 76 77 // The id of the root node for this deserialized heap graph. 78 NodeId rootId; 79 80 // The set of nodes in this deserialized heap graph, keyed by id. 81 using NodeSet = js::HashSet<DeserializedNode, DeserializedNode::HashPolicy>; 82 NodeSet nodes; 83 84 // The set of stack frames in this deserialized heap graph, keyed by id. 85 using FrameSet = 86 js::HashSet<DeserializedStackFrame, DeserializedStackFrame::HashPolicy>; 87 FrameSet frames; 88 89 Vector<UniqueTwoByteString> internedTwoByteStrings; 90 Vector<UniqueOneByteString> internedOneByteStrings; 91 92 using StringOrRef = Variant<const std::string*, uint64_t>; 93 94 template <typename CharT, typename InternedStringSet> 95 const CharT* getOrInternString(InternedStringSet& internedStrings, 96 Maybe<StringOrRef>& maybeStrOrRef); 97 98 protected: 99 nsCOMPtr<nsISupports> mParent; 100 101 virtual ~HeapSnapshot() {} 102 103 public: 104 // Create a `HeapSnapshot` from the given buffer that contains a serialized 105 // core dump. Do NOT take ownership of the buffer, only borrow it for the 106 // duration of the call. 107 static already_AddRefed<HeapSnapshot> Create(JSContext* cx, 108 dom::GlobalObject& global, 109 const uint8_t* buffer, 110 uint32_t size, ErrorResult& rv); 111 112 // Creates the `$TEMP_DIR/XXXXXX-XXX.fxsnapshot` core dump file that heap 113 // snapshots are serialized into. 114 static already_AddRefed<nsIFile> CreateUniqueCoreDumpFile( 115 ErrorResult& rv, const TimeStamp& now, nsAString& outFilePath, 116 nsAString& outSnapshotId); 117 118 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 119 NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(HeapSnapshot) 120 MOZ_DECLARE_REFCOUNTED_TYPENAME(HeapSnapshot) 121 122 nsISupports* GetParentObject() const { return mParent; } 123 124 virtual JSObject* WrapObject(JSContext* aCx, 125 JS::Handle<JSObject*> aGivenProto) override; 126 127 const char16_t* borrowUniqueString(const char16_t* duplicateString, 128 size_t length); 129 130 // Get the root node of this heap snapshot's graph. 131 JS::ubi::Node getRoot() { 132 auto p = nodes.lookup(rootId); 133 MOZ_ASSERT(p); 134 const DeserializedNode& node = *p; 135 return JS::ubi::Node(const_cast<DeserializedNode*>(&node)); 136 } 137 138 Maybe<JS::ubi::Node> getNodeById(JS::ubi::Node::Id nodeId) { 139 auto p = nodes.lookup(nodeId); 140 if (!p) return Nothing(); 141 return Some(JS::ubi::Node(const_cast<DeserializedNode*>(&*p))); 142 } 143 144 void TakeCensus(JSContext* cx, JS::Handle<JSObject*> options, 145 JS::MutableHandle<JS::Value> rval, ErrorResult& rv); 146 147 void DescribeNode(JSContext* cx, JS::Handle<JSObject*> breakdown, 148 uint64_t nodeId, JS::MutableHandle<JS::Value> rval, 149 ErrorResult& rv); 150 151 already_AddRefed<DominatorTree> ComputeDominatorTree(ErrorResult& rv); 152 153 void ComputeShortestPaths(JSContext* cx, uint64_t start, 154 const dom::Sequence<uint64_t>& targets, 155 uint64_t maxNumPaths, 156 JS::MutableHandle<JSObject*> results, 157 ErrorResult& rv); 158 159 dom::Nullable<uint64_t> GetCreationTime() { 160 static const uint64_t maxTime = uint64_t(1) << 53; 161 if (timestamp.isSome() && timestamp.ref() <= maxTime) { 162 return dom::Nullable<uint64_t>(timestamp.ref()); 163 } 164 165 return dom::Nullable<uint64_t>(); 166 } 167 }; 168 169 // A `CoreDumpWriter` is given the data we wish to save in a core dump and 170 // serializes it to disk, or memory, or a socket, etc. 171 class CoreDumpWriter { 172 public: 173 virtual ~CoreDumpWriter() {}; 174 175 // Write the given bits of metadata we would like to associate with this core 176 // dump. 177 virtual bool writeMetadata(uint64_t timestamp) = 0; 178 179 enum EdgePolicy : bool { INCLUDE_EDGES = true, EXCLUDE_EDGES = false }; 180 181 // Write the given `JS::ubi::Node` to the core dump. The given `EdgePolicy` 182 // dictates whether its outgoing edges should also be written to the core 183 // dump, or excluded. 184 virtual bool writeNode(const JS::ubi::Node& node, 185 EdgePolicy includeEdges) = 0; 186 }; 187 188 // Serialize the heap graph as seen from `node` with the given `CoreDumpWriter`. 189 // If `wantNames` is true, capture edge names. If `zones` is non-null, only 190 // capture the sub-graph within the zone set, otherwise capture the whole heap 191 // graph. Returns false on failure. 192 bool WriteHeapGraph(JSContext* cx, const JS::ubi::Node& node, 193 CoreDumpWriter& writer, bool wantNames, 194 JS::CompartmentSet* compartments, 195 JS::AutoCheckCannotGC& noGC, uint32_t& outNodeCount, 196 uint32_t& outEdgeCount); 197 inline bool WriteHeapGraph(JSContext* cx, const JS::ubi::Node& node, 198 CoreDumpWriter& writer, bool wantNames, 199 JS::CompartmentSet* compartments, 200 JS::AutoCheckCannotGC& noGC) { 201 uint32_t ignoreNodeCount; 202 uint32_t ignoreEdgeCount; 203 return WriteHeapGraph(cx, node, writer, wantNames, compartments, noGC, 204 ignoreNodeCount, ignoreEdgeCount); 205 } 206 207 // Get the mozilla::MallocSizeOf for the current thread's JSRuntime. 208 MallocSizeOf GetCurrentThreadDebuggerMallocSizeOf(); 209 210 } // namespace devtools 211 } // namespace mozilla 212 213 #endif // mozilla_devtools_HeapSnapshot__