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