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