mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-24 03:19:06 +00:00
Bug 1225588 - Expose DominatorTree to JavaScript; r=sfink,bz
This commit adds the DominatorTree.webidl interface, which is only exposed to chrome JS. The interface is implemented by mozilla::devtools::DominatorTree, which is a thin wrapper around JS::ubi::DominatorTree. This does not expose any methods on the DominatorTree interface, those will come as follow up changesets.
This commit is contained in:
parent
23ad260321
commit
1bcd4d95d4
30
devtools/shared/heapsnapshot/DominatorTree.cpp
Normal file
30
devtools/shared/heapsnapshot/DominatorTree.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/devtools/DominatorTree.h"
|
||||
#include "mozilla/dom/DominatorTreeBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace devtools {
|
||||
|
||||
/*** Cycle Collection Boilerplate *****************************************************************/
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DominatorTree, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(DominatorTree)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(DominatorTree)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DominatorTree)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
/* virtual */ JSObject*
|
||||
DominatorTree::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
|
||||
{
|
||||
return dom::DominatorTreeBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
} // namespace devtools
|
||||
} // namespace mozilla
|
50
devtools/shared/heapsnapshot/DominatorTree.h
Normal file
50
devtools/shared/heapsnapshot/DominatorTree.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_devtools_DominatorTree__
|
||||
#define mozilla_devtools_DominatorTree__
|
||||
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/RefCounted.h"
|
||||
#include "js/UbiNodeDominatorTree.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace devtools {
|
||||
|
||||
class DominatorTree final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
protected:
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
|
||||
virtual ~DominatorTree() { }
|
||||
|
||||
private:
|
||||
JS::ubi::DominatorTree mDominatorTree;
|
||||
|
||||
public:
|
||||
explicit DominatorTree(JS::ubi::DominatorTree&& aDominatorTree, nsISupports* aParent)
|
||||
: mParent(aParent)
|
||||
, mDominatorTree(Move(aDominatorTree))
|
||||
{
|
||||
MOZ_ASSERT(aParent);
|
||||
};
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS;
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DominatorTree);
|
||||
|
||||
nsISupports* GetParentObject() const { return mParent; }
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
};
|
||||
|
||||
} // namespace devtools
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_devtools_DominatorTree__
|
@ -52,6 +52,8 @@ using ::google::protobuf::io::ZeroCopyInputStream;
|
||||
|
||||
using JS::ubi::AtomOrTwoByteChars;
|
||||
|
||||
/*** Cycle Collection Boilerplate *****************************************************************/
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(HeapSnapshot)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HeapSnapshot)
|
||||
@ -381,6 +383,9 @@ HeapSnapshot::saveStackFrame(const protobuf::StackFrame& frame,
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef GET_STRING_OR_REF_WITH_PROP_NAMES
|
||||
#undef GET_STRING_OR_REF
|
||||
|
||||
static inline bool
|
||||
StreamHasData(GzipInputStream& stream)
|
||||
{
|
||||
@ -515,8 +520,26 @@ HeapSnapshot::TakeCensus(JSContext* cx, JS::HandleObject options,
|
||||
}
|
||||
}
|
||||
|
||||
#undef GET_STRING_OR_REF_WITH_PROP_NAMES
|
||||
#undef GET_STRING_OR_REF
|
||||
already_AddRefed<DominatorTree>
|
||||
HeapSnapshot::ComputeDominatorTree(ErrorResult& rv)
|
||||
{
|
||||
Maybe<JS::ubi::DominatorTree> maybeTree;
|
||||
{
|
||||
auto ccrt = CycleCollectedJSRuntime::Get();
|
||||
MOZ_ASSERT(ccrt);
|
||||
auto rt = ccrt->Runtime();
|
||||
MOZ_ASSERT(rt);
|
||||
JS::AutoCheckCannotGC nogc(rt);
|
||||
maybeTree = JS::ubi::DominatorTree::Create(rt, nogc, getRoot());
|
||||
}
|
||||
|
||||
if (maybeTree.isNothing()) {
|
||||
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return MakeAndAddRef<DominatorTree>(Move(*maybeTree), mParent);
|
||||
}
|
||||
|
||||
|
||||
/*** Saving Heap Snapshots ************************************************************************/
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "js/HashTable.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/devtools/DeserializedNode.h"
|
||||
#include "mozilla/devtools/DominatorTree.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
@ -150,6 +151,8 @@ public:
|
||||
void TakeCensus(JSContext* cx, JS::HandleObject options,
|
||||
JS::MutableHandleValue rval, ErrorResult& rv);
|
||||
|
||||
already_AddRefed<DominatorTree> ComputeDominatorTree(ErrorResult& rv);
|
||||
|
||||
dom::Nullable<uint64_t> GetCreationTime() {
|
||||
static const uint64_t maxTime = uint64_t(1) << 53;
|
||||
if (timestamp.isSome() && timestamp.ref() <= maxTime) {
|
||||
|
@ -15,6 +15,7 @@ EXPORTS.mozilla.devtools += [
|
||||
'AutoMemMap.h',
|
||||
'CoreDump.pb.h',
|
||||
'DeserializedNode.h',
|
||||
'DominatorTree.h',
|
||||
'FileDescriptorOutputStream.h',
|
||||
'HeapSnapshot.h',
|
||||
'HeapSnapshotTempFileHelperChild.h',
|
||||
@ -32,6 +33,7 @@ SOURCES += [
|
||||
'AutoMemMap.cpp',
|
||||
'CoreDump.pb.cc',
|
||||
'DeserializedNode.cpp',
|
||||
'DominatorTree.cpp',
|
||||
'FileDescriptorOutputStream.cpp',
|
||||
'HeapSnapshot.cpp',
|
||||
'HeapSnapshotTempFileHelperParent.cpp',
|
||||
|
@ -3,5 +3,6 @@ tags = devtools devtools-memory
|
||||
skip-if = buildapp == 'b2g' || os == 'android'
|
||||
support-files =
|
||||
|
||||
[test_DominatorTree_01.html]
|
||||
[test_SaveHeapSnapshot.html]
|
||||
|
||||
|
@ -0,0 +1,37 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Sanity test that we can compute dominator trees from a heap snapshot in a web window.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>ChromeUtils.saveHeapSnapshot test</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.onload = function() {
|
||||
const path = ChromeUtils.saveHeapSnapshot({ runtime: true });
|
||||
const snapshot = ChromeUtils.readHeapSnapshot(path);
|
||||
|
||||
const dominatorTree = snapshot.computeDominatorTree();
|
||||
ok(dominatorTree);
|
||||
ok(dominatorTree instanceof DominatorTree);
|
||||
|
||||
let threw = false;
|
||||
try {
|
||||
new DominatorTree();
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
}
|
||||
ok(threw, "Constructor shouldn't be usable");
|
||||
|
||||
SimpleTest.finish();
|
||||
};
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,47 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
console.log("Initializing worker.");
|
||||
|
||||
self.onmessage = e => {
|
||||
console.log("Starting test.");
|
||||
try {
|
||||
const path = ThreadSafeChromeUtils.saveHeapSnapshot({ runtime: true });
|
||||
const snapshot = ThreadSafeChromeUtils.readHeapSnapshot(path);
|
||||
|
||||
const dominatorTree = snapshot.computeDominatorTree();
|
||||
ok(dominatorTree);
|
||||
ok(dominatorTree instanceof DominatorTree);
|
||||
|
||||
let threw = false;
|
||||
try {
|
||||
new DominatorTree();
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
}
|
||||
ok(threw, "Constructor shouldn't be usable");
|
||||
} catch (e) {
|
||||
ok(false, "Unexpected error inside worker:\n" + e.toString() + "\n" + e.stack);
|
||||
} finally {
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
// Proxy assertions to the main thread.
|
||||
function ok(val, msg) {
|
||||
console.log("ok(" + !!val + ", \"" + msg + "\")");
|
||||
self.postMessage({
|
||||
type: "assertion",
|
||||
passed: !!val,
|
||||
msg,
|
||||
stack: Error().stack
|
||||
});
|
||||
}
|
||||
|
||||
// Tell the main thread we are done with the tests.
|
||||
function done() {
|
||||
console.log("done()");
|
||||
self.postMessage({
|
||||
type: "done"
|
||||
});
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Sanity test that we can compute dominator trees.
|
||||
|
||||
function run_test() {
|
||||
const path = ChromeUtils.saveHeapSnapshot({ runtime: true });
|
||||
const snapshot = ChromeUtils.readHeapSnapshot(path);
|
||||
|
||||
const dominatorTree = snapshot.computeDominatorTree();
|
||||
ok(dominatorTree);
|
||||
ok(dominatorTree instanceof DominatorTree);
|
||||
|
||||
let threw = false;
|
||||
try {
|
||||
new DominatorTree();
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
}
|
||||
ok(threw, "Constructor shouldn't be usable");
|
||||
|
||||
do_test_finished();
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that we can compute dominator trees from a snapshot in a worker.
|
||||
|
||||
add_task(function* () {
|
||||
const worker = new ChromeWorker("resource://test/dominator-tree-worker.js");
|
||||
worker.postMessage({});
|
||||
|
||||
let assertionCount = 0;
|
||||
worker.onmessage = e => {
|
||||
if (e.data.type !== "assertion") {
|
||||
return;
|
||||
}
|
||||
|
||||
ok(e.data.passed, e.data.msg + "\n" + e.data.stack);
|
||||
assertionCount++;
|
||||
};
|
||||
|
||||
yield waitForDone(worker);
|
||||
|
||||
ok(assertionCount > 0);
|
||||
worker.terminate();
|
||||
});
|
||||
|
||||
function waitForDone(w) {
|
||||
return new Promise((resolve, reject) => {
|
||||
w.onerror = e => {
|
||||
reject();
|
||||
ok(false, "Error in worker: " + e);
|
||||
};
|
||||
|
||||
w.addEventListener("message", function listener(e) {
|
||||
if (e.data.type === "done") {
|
||||
w.removeEventListener("message", listener, false);
|
||||
resolve();
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
}
|
@ -7,6 +7,7 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||
|
||||
support-files =
|
||||
Census.jsm
|
||||
dominator-tree-worker.js
|
||||
heap-snapshot-worker.js
|
||||
Match.jsm
|
||||
|
||||
@ -27,6 +28,8 @@ support-files =
|
||||
[test_census-tree-node-06.js]
|
||||
[test_census-tree-node-07.js]
|
||||
[test_census-tree-node-08.js]
|
||||
[test_DominatorTree_01.js]
|
||||
[test_DominatorTree_02.js]
|
||||
[test_HeapAnalyses_getCreationTime_01.js]
|
||||
[test_HeapAnalyses_readHeapSnapshot_01.js]
|
||||
[test_HeapAnalyses_takeCensusDiff_01.js]
|
||||
|
@ -409,6 +409,10 @@ DOMInterfaces = {
|
||||
}
|
||||
},
|
||||
|
||||
'DominatorTree': {
|
||||
'nativeType': 'mozilla::devtools::DominatorTree'
|
||||
},
|
||||
|
||||
'DOMException': {
|
||||
'binaryNames': {
|
||||
'message': 'messageMoz',
|
||||
|
41
dom/webidl/DominatorTree.webidl
Normal file
41
dom/webidl/DominatorTree.webidl
Normal file
@ -0,0 +1,41 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/**
|
||||
* In a directed graph with a root node `R`, a node `A` is said to "dominate" a
|
||||
* node `B` iff every path from `R` to `B` contains `A`. A node `A` is said to
|
||||
* be the "immediate dominator" of a node `B` iff it dominates `B`, is not `B`
|
||||
* itself, and does not dominate any other nodes which also dominate `B` in
|
||||
* turn.
|
||||
*
|
||||
* If we take every node from a graph `G` and create a new graph `T` with edges
|
||||
* to each node from its immediate dominator, then `T` is a tree (each node has
|
||||
* only one immediate dominator, or none if it is the root). This tree is called
|
||||
* a "dominator tree".
|
||||
*
|
||||
* This interface represents a dominator tree constructed from a HeapSnapshot's
|
||||
* heap graph. The domination relationship and dominator trees are useful tools
|
||||
* for analyzing heap graphs because they tell you:
|
||||
*
|
||||
* - Exactly what could be reclaimed by the GC if some node `A` became
|
||||
* unreachable: those nodes which are dominated by `A`,
|
||||
*
|
||||
* - The "retained size" of a node in the heap graph, in contrast to its
|
||||
* "shallow size". The "shallow size" is the space taken by a node itself,
|
||||
* not counting anything it references. The "retained size" of a node is its
|
||||
* shallow size plus the size of all the things that would be collected if
|
||||
* the original node wasn't (directly or indirectly) referencing them. In
|
||||
* other words, the retained size is the shallow size of a node plus the
|
||||
* shallow sizes of every other node it dominates. For example, the root
|
||||
* node in a binary tree might have a small shallow size that does not take
|
||||
* up much space itself, but it dominates the rest of the binary tree and
|
||||
* its retained size is therefore significant (assuming no external
|
||||
* references into the tree).
|
||||
*/
|
||||
[ChromeOnly, Exposed=(Window,System,Worker)]
|
||||
interface DominatorTree {
|
||||
|
||||
};
|
@ -56,4 +56,12 @@ interface HeapSnapshot {
|
||||
*/
|
||||
[Throws]
|
||||
any takeCensus(object? options);
|
||||
|
||||
/**
|
||||
* Compute the dominator tree for this heap snapshot.
|
||||
*
|
||||
* @see DominatorTree.webidl
|
||||
*/
|
||||
[Throws]
|
||||
DominatorTree computeDominatorTree();
|
||||
};
|
||||
|
@ -123,6 +123,7 @@ WEBIDL_FILES = [
|
||||
'DOMError.webidl',
|
||||
'DOMException.webidl',
|
||||
'DOMImplementation.webidl',
|
||||
'DominatorTree.webidl',
|
||||
'DOMMatrix.webidl',
|
||||
'DOMMobileMessageError.webidl',
|
||||
'DOMParser.webidl',
|
||||
|
@ -121,7 +121,7 @@ class JS_PUBLIC_API(DominatorTree)
|
||||
auto onEdge = [&](const Node& origin, const Edge& edge) {
|
||||
auto p = predecessorSets.lookupForAdd(edge.referent);
|
||||
if (!p) {
|
||||
auto set = rt->make_unique<NodeSet>();
|
||||
mozilla::UniquePtr<NodeSet, DeletePolicy<NodeSet>> set(js_new<NodeSet>());
|
||||
if (!set ||
|
||||
!set->init() ||
|
||||
!predecessorSets.add(p, edge.referent, mozilla::Move(set)))
|
||||
|
Loading…
x
Reference in New Issue
Block a user