Bug 943070 - Add a "depth" trace type to the tracer; r=past
authorNick Fitzgerald <fitzgen@gmail.com>
Thu, 05 Dec 2013 10:58:33 -0800
changeset 174658 012395358640ffd7ba8333697a41281c2883ad64
parent 174657 9915d93be7b6b2527a2c9922fbf7dc8d0b2f8fea
child 174659 bd5b4ba543ec10091b3ba06c9f424c726c9bf6c9
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspast
bugs943070
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 943070 - Add a "depth" trace type to the tracer; r=past
toolkit/devtools/server/actors/tracer.js
toolkit/devtools/server/tests/unit/test_trace_actor-08.js
toolkit/devtools/server/tests/unit/xpcshell.ini
--- a/toolkit/devtools/server/actors/tracer.js
+++ b/toolkit/devtools/server/actors/tracer.js
@@ -9,16 +9,35 @@ const { Cu } = require("chrome");
 const { reportException } =
   Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {}).DevToolsUtils;
 
 const { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 
 Cu.import("resource://gre/modules/jsdebugger.jsm");
 addDebuggerToGlobal(this);
 
+// TODO bug 943125: remove this polyfill and use Debugger.Frame.prototype.depth
+// once it is implemented.
+if (!Object.getOwnPropertyDescriptor(Debugger.Frame.prototype, "depth")) {
+  Debugger.Frame.prototype._depth = null;
+  Object.defineProperty(Debugger.Frame.prototype, "depth", {
+    get: function () {
+      if (this._depth === null) {
+        if (!this.older) {
+          this._depth = 0;
+        } else {
+          this._depth = 1 + this.older.depth;
+        }
+      }
+
+      return this._depth;
+    }
+  });
+}
+
 const { setTimeout } = require("sdk/timers");
 
 /**
  * The number of milliseconds we should buffer frame enter/exit packets before
  * sending.
  */
 const BUFFER_SEND_DELAY = 50;
 
@@ -39,17 +58,18 @@ const TRACE_TYPES = new Set([
   "time",
   "return",
   "throw",
   "yield",
   "name",
   "location",
   "callsite",
   "parameterNames",
-  "arguments"
+  "arguments",
+  "depth"
 ]);
 
 /**
  * Creates a TraceActor. TraceActor provides a stream of function
  * call/return packets to a remote client gathering a full trace.
  */
 function TraceActor(aConn, aParentActor)
 {
@@ -64,16 +84,17 @@ function TraceActor(aConn, aParentActor)
   this._requestsForTraceType = Object.create(null);
   for (let type of TRACE_TYPES) {
     this._requestsForTraceType[type] = 0;
   }
 
   this._sequence = 0;
   this._bufferSendTimer = null;
   this._buffer = [];
+  this.onExitFrame = this.onExitFrame.bind(this);
 
   this.global = aParentActor.window.wrappedJSObject;
 }
 
 TraceActor.prototype = {
   actorPrefix: "trace",
 
   get attached() { return this._attached; },
@@ -343,29 +364,38 @@ TraceActor.prototype = {
       for (let arg of aFrame.arguments) {
         if (i++ > MAX_ARGUMENTS) {
           break;
         }
         packet.arguments.push(createValueGrip(arg, true));
       }
     }
 
-    aFrame.onPop = this.onExitFrame.bind(this);
+    if (this._requestsForTraceType.depth) {
+      packet.depth = aFrame.depth;
+    }
+
+    const onExitFrame = this.onExitFrame;
+    aFrame.onPop = function (aCompletion) {
+      onExitFrame(this, aCompletion);
+    };
 
     this._send(packet);
   },
 
   /**
    * Called by the engine when a frame is exited. Sends an unsolicited packet to
    * the client carrying requested trace information.
    *
+   * @param Debugger.Frame aFrame
+   *        The Debugger.Frame that was just exited.
    * @param aCompletion object
    *        The debugger completion value for the frame.
    */
-  onExitFrame: function(aCompletion) {
+  onExitFrame: function(aFrame, aCompletion) {
     let packet = {
       type: "exitedFrame",
       sequence: this._sequence++,
     };
 
     if (!aCompletion) {
       packet.why = "terminated";
     } else if (aCompletion.hasOwnProperty("return")) {
@@ -375,16 +405,20 @@ TraceActor.prototype = {
     } else {
       packet.why = "throw";
     }
 
     if (this._requestsForTraceType.time) {
       packet.time = Date.now() - this._startTime;
     }
 
+    if (this._requestsForTraceType.depth) {
+      packet.depth = aFrame.depth;
+    }
+
     if (aCompletion) {
       if (this._requestsForTraceType.return) {
         packet.return = createValueGrip(aCompletion.return, true);
       }
 
       if (this._requestsForTraceType.throw) {
         packet.throw = createValueGrip(aCompletion.throw, true);
       }
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/unit/test_trace_actor-08.js
@@ -0,0 +1,125 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test the "depth" trace type.
+ */
+
+let { defer } = devtools.require("sdk/core/promise");
+
+var gDebuggee;
+var gClient;
+var gTraceClient;
+
+function run_test()
+{
+  initTestTracerServer();
+  gDebuggee = addTestGlobal("test-tracer-actor");
+  gClient = new DebuggerClient(DebuggerServer.connectPipe());
+  gClient.connect(function() {
+    attachTestTab(gClient, "test-tracer-actor", function(aResponse, aTabClient) {
+      gClient.attachTracer(aResponse.traceActor, function(aResponse, aTraceClient) {
+        gTraceClient = aTraceClient;
+        test_frame_depths();
+      });
+    });
+  });
+  do_test_pending();
+}
+
+function test_frame_depths()
+{
+  const tracesStopped = defer();
+  gClient.addListener("traces", (aEvent, { traces }) => {
+    for (let t of traces) {
+      check_trace(t);
+    }
+    tracesStopped.resolve();
+  });
+
+  start_trace()
+    .then(eval_code)
+    .then(() => tracesStopped.promise)
+    .then(stop_trace)
+    .then(function() {
+      finishClient(gClient);
+    }).then(null, error => {
+      do_check_true(false, "Should not get an error, got: " + error);
+    });
+}
+
+function start_trace()
+{
+  let deferred = defer();
+  gTraceClient.startTrace(["depth", "name"], null, function() { deferred.resolve(); });
+  return deferred.promise;
+}
+
+function eval_code()
+{
+  gDebuggee.eval("(" + function iife() {
+    function first() {
+      second();
+    }
+
+    function second() {
+      third();
+    }
+
+    function third() {
+    }
+
+    first();
+  } + ")()");
+}
+
+function stop_trace()
+{
+  let deferred = defer();
+  gTraceClient.stopTrace(null, function() { deferred.resolve(); });
+  return deferred.promise;
+}
+
+function check_trace({ sequence, depth, name })
+{
+  switch(sequence) {
+  case 0:
+    do_check_eq(name, "(eval)");
+    // Fall through
+  case 9:
+    do_check_eq(depth, 0);
+    break;
+
+  case 1:
+    do_check_eq(name, "iife");
+    // Fall through
+  case 8:
+    do_check_eq(depth, 1);
+    break;
+
+  case 2:
+    do_check_eq(name, "first");
+    // Fall through
+  case 7:
+    do_check_eq(depth, 2);
+    break;
+
+  case 3:
+    do_check_eq(name, "second");
+    // Fall through
+  case 6:
+    do_check_eq(depth, 3);
+    break;
+
+  case 4:
+    do_check_eq(name, "third");
+    // Fall through
+  case 5:
+    do_check_eq(depth, 4);
+    break;
+
+  default:
+    // Should have covered all sequences.
+    do_check_true(false);
+  }
+}
--- a/toolkit/devtools/server/tests/unit/xpcshell.ini
+++ b/toolkit/devtools/server/tests/unit/xpcshell.ini
@@ -184,9 +184,10 @@ reason = bug 820380
 [test_add_actors.js]
 [test_trace_actor-01.js]
 [test_trace_actor-02.js]
 [test_trace_actor-03.js]
 [test_trace_actor-04.js]
 [test_trace_actor-05.js]
 [test_trace_actor-06.js]
 [test_trace_actor-07.js]
+[test_trace_actor-08.js]
 [test_ignore_caught_exceptions.js]