Bug 1231883 - Implement HeapSnapshot.describeNode; r=mrbkap
authorNick Fitzgerald <fitzgen@gmail.com>
Fri, 18 Dec 2015 12:05:14 -0800
changeset 277068 85f5b4e4c9a20e12091f3f8004c1f91efa204cb0
parent 277067 34b822f385678f55c39c4678432a6424a6713362
child 277069 b9f3786d8f60b8ebffaa8f17a0fc0d338276168c
push id16724
push usercbook@mozilla.com
push dateMon, 21 Dec 2015 11:00:52 +0000
treeherderfx-team@3f3f0361567c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs1231883
milestone46.0a1
Bug 1231883 - Implement HeapSnapshot.describeNode; r=mrbkap This commit implements the HeapSnapshot.describeNode method which allows chrome JS code to request a description of a given node as specified by our existing "breakdown" language. This description can be used to generate a human-readable label for the node.
devtools/shared/heapsnapshot/HeapSnapshot.cpp
devtools/shared/heapsnapshot/HeapSnapshot.h
devtools/shared/heapsnapshot/tests/unit/test_HeapSnapshot_describeNode_01.js
devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
dom/webidl/HeapSnapshot.webidl
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp
@@ -517,16 +517,53 @@ HeapSnapshot::TakeCensus(JSContext* cx, 
   }
 
   if (NS_WARN_IF(!handler.report(cx, rval))) {
     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 }
 
+void
+HeapSnapshot::DescribeNode(JSContext* cx, JS::HandleObject breakdown, uint64_t nodeId,
+                           JS::MutableHandleValue rval, ErrorResult& rv) {
+  MOZ_ASSERT(breakdown);
+  JS::RootedValue breakdownVal(cx, JS::ObjectValue(*breakdown));
+  JS::ubi::CountTypePtr rootType = JS::ubi::ParseBreakdown(cx, breakdownVal);
+  if (NS_WARN_IF(!rootType)) {
+    rv.Throw(NS_ERROR_UNEXPECTED);
+    return;
+  }
+
+  JS::ubi::RootedCount rootCount(cx, rootType->makeCount());
+  if (NS_WARN_IF(!rootCount)) {
+    rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
+
+  JS::ubi::Node::Id id(nodeId);
+  Maybe<JS::ubi::Node> node = getNodeById(id);
+  if (NS_WARN_IF(node.isNothing())) {
+    rv.Throw(NS_ERROR_INVALID_ARG);
+    return;
+  }
+
+  MallocSizeOf mallocSizeOf = GetCurrentThreadDebuggerMallocSizeOf();
+  if (NS_WARN_IF(!rootCount->count(mallocSizeOf, *node))) {
+    rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
+
+  if (NS_WARN_IF(!rootCount->report(cx, rval))) {
+    rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
+}
+
+
 already_AddRefed<DominatorTree>
 HeapSnapshot::ComputeDominatorTree(ErrorResult& rv)
 {
   Maybe<JS::ubi::DominatorTree> maybeTree;
   {
     auto ccrt = CycleCollectedJSRuntime::Get();
     MOZ_ASSERT(ccrt);
     auto rt = ccrt->Runtime();
--- a/devtools/shared/heapsnapshot/HeapSnapshot.h
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.h
@@ -154,16 +154,19 @@ public:
     if (!p)
       return Nothing();
     return Some(JS::ubi::Node(const_cast<DeserializedNode*>(&*p)));
   }
 
   void TakeCensus(JSContext* cx, JS::HandleObject options,
                   JS::MutableHandleValue rval, ErrorResult& rv);
 
+  void DescribeNode(JSContext* cx, JS::HandleObject breakdown, uint64_t nodeId,
+                    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) {
       return dom::Nullable<uint64_t>(timestamp.ref());
     }
 
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapSnapshot_describeNode_01.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that we can describe nodes with a breakdown.
+
+function run_test() {
+  const path = saveNewHeapSnapshot();
+  const snapshot = ChromeUtils.readHeapSnapshot(path);
+  ok(snapshot.describeNode);
+  equal(typeof snapshot.describeNode, "function");
+
+  const dt = snapshot.computeDominatorTree();
+
+  let threw = false;
+  try {
+    snapshot.describeNode(undefined, dt.root);
+  } catch (_) {
+    threw = true;
+  }
+  ok(threw, "Should require a breakdown");
+
+  const breakdown = {
+    by: "coarseType",
+    objects: { by: "objectClass" },
+    scripts: { by: "internalType" },
+    strings: { by: "internalType" },
+    other: { by: "internalType" }
+  };
+
+  threw = false;
+  try {
+    snapshot.describeNode(breakdown, 0);
+  } catch (_) {
+    threw = true;
+  }
+  ok(threw, "Should throw when given an invalid node id");
+
+  const description = snapshot.describeNode(breakdown, dt.root);
+  ok(description);
+  ok(description.other);
+  ok(description.other["JS::ubi::RootList"]);
+}
--- a/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
+++ b/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
@@ -46,16 +46,17 @@ support-files =
 [test_HeapAnalyses_takeCensus_02.js]
 [test_HeapAnalyses_takeCensus_03.js]
 [test_HeapAnalyses_takeCensus_04.js]
 [test_HeapAnalyses_takeCensus_05.js]
 [test_HeapAnalyses_takeCensus_06.js]
 [test_HeapAnalyses_takeCensus_07.js]
 [test_HeapSnapshot_creationTime_01.js]
 [test_HeapSnapshot_deepStack_01.js]
+[test_HeapSnapshot_describeNode_01.js]
 [test_HeapSnapshot_takeCensus_01.js]
 [test_HeapSnapshot_takeCensus_02.js]
 [test_HeapSnapshot_takeCensus_03.js]
 [test_HeapSnapshot_takeCensus_04.js]
 [test_HeapSnapshot_takeCensus_05.js]
 [test_HeapSnapshot_takeCensus_06.js]
 [test_HeapSnapshot_takeCensus_07.js]
 [test_HeapSnapshot_takeCensus_08.js]
--- a/dom/webidl/HeapSnapshot.webidl
+++ b/dom/webidl/HeapSnapshot.webidl
@@ -53,15 +53,26 @@ interface HeapSnapshot {
    *
    * See the `takeCensus` section of the `js/src/doc/Debugger/Debugger.Memory.md`
    * file for detailed documentation.
    */
   [Throws]
   any takeCensus(object? options);
 
   /**
+   * Describe `node` with the specified `breakdown`. See the comment above
+   * `takeCensus` or `js/src/doc/Debugger/Debugger.Memory.md` for detailed
+   * documentation on breakdowns.
+   *
+   * Throws an error when `node` is not the id of a node in the heap snapshot,
+   * or if the breakdown is invalid.
+   */
+  [Throws]
+  any describeNode(object breakdown, NodeId node);
+
+  /**
    * Compute the dominator tree for this heap snapshot.
    *
    * @see DominatorTree.webidl
    */
   [Throws]
   DominatorTree computeDominatorTree();
 };