/src/mozilla-central/devtools/shared/heapsnapshot/DominatorTree.cpp
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 | | #include "mozilla/devtools/DominatorTree.h" |
7 | | #include "mozilla/dom/DominatorTreeBinding.h" |
8 | | |
9 | | namespace mozilla { |
10 | | namespace devtools { |
11 | | |
12 | | dom::Nullable<uint64_t> |
13 | | DominatorTree::GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv) |
14 | 0 | { |
15 | 0 | JS::ubi::Node::Id id(aNodeId); |
16 | 0 | auto node = mHeapSnapshot->getNodeById(id); |
17 | 0 | if (node.isNothing()) |
18 | 0 | return dom::Nullable<uint64_t>(); |
19 | 0 | |
20 | 0 | auto mallocSizeOf = GetCurrentThreadDebuggerMallocSizeOf(); |
21 | 0 | JS::ubi::Node::Size size = 0; |
22 | 0 | if (!mDominatorTree.getRetainedSize(*node, mallocSizeOf, size)) { |
23 | 0 | aRv.Throw(NS_ERROR_OUT_OF_MEMORY); |
24 | 0 | return dom::Nullable<uint64_t>(); |
25 | 0 | } |
26 | 0 | |
27 | 0 | MOZ_ASSERT(size != 0, |
28 | 0 | "The node should not have been unknown since we got it from the heap snapshot."); |
29 | 0 | return dom::Nullable<uint64_t>(size); |
30 | 0 | } |
31 | | |
32 | | struct NodeAndRetainedSize |
33 | | { |
34 | | JS::ubi::Node mNode; |
35 | | JS::ubi::Node::Size mSize; |
36 | | |
37 | | NodeAndRetainedSize(const JS::ubi::Node& aNode, JS::ubi::Node::Size aSize) |
38 | | : mNode(aNode) |
39 | | , mSize(aSize) |
40 | 0 | { } |
41 | | |
42 | | struct Comparator |
43 | | { |
44 | | static bool |
45 | | Equals(const NodeAndRetainedSize& aLhs, const NodeAndRetainedSize& aRhs) |
46 | 0 | { |
47 | 0 | return aLhs.mSize == aRhs.mSize; |
48 | 0 | } |
49 | | |
50 | | static bool |
51 | | LessThan(const NodeAndRetainedSize& aLhs, const NodeAndRetainedSize& aRhs) |
52 | 0 | { |
53 | 0 | // Use > because we want to sort from greatest to least retained size. |
54 | 0 | return aLhs.mSize > aRhs.mSize; |
55 | 0 | } |
56 | | }; |
57 | | }; |
58 | | |
59 | | void |
60 | | DominatorTree::GetImmediatelyDominated(uint64_t aNodeId, |
61 | | dom::Nullable<nsTArray<uint64_t>>& aOutResult, |
62 | | ErrorResult& aRv) |
63 | 0 | { |
64 | 0 | MOZ_ASSERT(aOutResult.IsNull()); |
65 | 0 |
|
66 | 0 | JS::ubi::Node::Id id(aNodeId); |
67 | 0 | Maybe<JS::ubi::Node> node = mHeapSnapshot->getNodeById(id); |
68 | 0 | if (node.isNothing()) |
69 | 0 | return; |
70 | 0 | |
71 | 0 | // Get all immediately dominated nodes and their retained sizes. |
72 | 0 | MallocSizeOf mallocSizeOf = GetCurrentThreadDebuggerMallocSizeOf(); |
73 | 0 | Maybe<JS::ubi::DominatorTree::DominatedSetRange> range = mDominatorTree.getDominatedSet(*node); |
74 | 0 | MOZ_ASSERT(range.isSome(), "The node should be known, since we got it from the heap snapshot."); |
75 | 0 | size_t length = range->length(); |
76 | 0 | nsTArray<NodeAndRetainedSize> dominatedNodes(length); |
77 | 0 | for (const JS::ubi::Node& dominatedNode : *range) { |
78 | 0 | JS::ubi::Node::Size retainedSize = 0; |
79 | 0 | if (NS_WARN_IF(!mDominatorTree.getRetainedSize(dominatedNode, mallocSizeOf, retainedSize))) { |
80 | 0 | aRv.Throw(NS_ERROR_OUT_OF_MEMORY); |
81 | 0 | return; |
82 | 0 | } |
83 | 0 | MOZ_ASSERT(retainedSize != 0, |
84 | 0 | "retainedSize should not be zero since we know the node is in the dominator tree."); |
85 | 0 |
|
86 | 0 | dominatedNodes.AppendElement(NodeAndRetainedSize(dominatedNode, retainedSize)); |
87 | 0 | } |
88 | 0 |
|
89 | 0 | // Sort them by retained size. |
90 | 0 | NodeAndRetainedSize::Comparator comparator; |
91 | 0 | dominatedNodes.Sort(comparator); |
92 | 0 |
|
93 | 0 | // Fill the result with the nodes' ids. |
94 | 0 | JS::ubi::Node root = mDominatorTree.root(); |
95 | 0 | aOutResult.SetValue(nsTArray<uint64_t>(length)); |
96 | 0 | for (const NodeAndRetainedSize& entry : dominatedNodes) { |
97 | 0 | // The root dominates itself, but we don't want to expose that to JS. |
98 | 0 | if (entry.mNode == root) |
99 | 0 | continue; |
100 | 0 | |
101 | 0 | aOutResult.Value().AppendElement(entry.mNode.identifier()); |
102 | 0 | } |
103 | 0 | } |
104 | | |
105 | | dom::Nullable<uint64_t> |
106 | | DominatorTree::GetImmediateDominator(uint64_t aNodeId) const |
107 | 0 | { |
108 | 0 | JS::ubi::Node::Id id(aNodeId); |
109 | 0 | Maybe<JS::ubi::Node> node = mHeapSnapshot->getNodeById(id); |
110 | 0 | if (node.isNothing()) |
111 | 0 | return dom::Nullable<uint64_t>(); |
112 | 0 | |
113 | 0 | JS::ubi::Node dominator = mDominatorTree.getImmediateDominator(*node); |
114 | 0 | if (!dominator || dominator == *node) |
115 | 0 | return dom::Nullable<uint64_t>(); |
116 | 0 | |
117 | 0 | return dom::Nullable<uint64_t>(dominator.identifier()); |
118 | 0 | } |
119 | | |
120 | | |
121 | | /*** Cycle Collection Boilerplate *****************************************************************/ |
122 | | |
123 | | NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DominatorTree, mParent, mHeapSnapshot) |
124 | | |
125 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(DominatorTree) |
126 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(DominatorTree) |
127 | | |
128 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DominatorTree) |
129 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
130 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
131 | 0 | NS_INTERFACE_MAP_END |
132 | | |
133 | | /* virtual */ JSObject* |
134 | | DominatorTree::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto) |
135 | 0 | { |
136 | 0 | return dom::DominatorTree_Binding::Wrap(aCx, this, aGivenProto); |
137 | 0 | } |
138 | | |
139 | | } // namespace devtools |
140 | | } // namespace mozilla |