Bug 795917 - Get executable lines through the remote debugging protocol;r=ejpbruel
authorSeris <seerriss@gmail.com>
Wed, 01 Oct 2014 17:57:49 +0100
changeset 208180 81f24d931d8926cf2aab90499c66958c60558962
parent 208179 a1d7b16101c718acb913416bd36608c098c29b87
child 208181 e8b947548ce64607f1dbfc952a4c9d61d619485e
push id27579
push userkwierso@gmail.com
push dateWed, 01 Oct 2014 23:02:13 +0000
treeherderautoland@f771fd927304 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersejpbruel
bugs795917
milestone35.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 795917 - Get executable lines through the remote debugging protocol;r=ejpbruel
toolkit/components/telemetry/Histograms.json
toolkit/devtools/client/dbg-client.jsm
toolkit/devtools/server/actors/script.js
toolkit/devtools/server/tests/unit/test_get-executable-lines-source-map.js
toolkit/devtools/server/tests/unit/test_get-executable-lines.js
toolkit/devtools/server/tests/unit/xpcshell.ini
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -5724,16 +5724,30 @@
   "COOKIES_3RDPARTY_NUM_ATTEMPTS_BLOCKED": {
     "expires_in_version": "default",
     "kind": "linear",
     "low": "10",
     "high": "500",
     "n_buckets": "50",
     "description": "The total number of distinct attempts by third-party sites to place cookies which have been rejected.  Measures are normalized per 24h."
   },
+  "DEVTOOLS_DEBUGGER_RDP_LOCAL_GET_EXECUTABLE_LINES_MS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "10000",
+    "n_buckets": "1000",
+    "description": "The time (in milliseconds) that it took a 'getExecutableLines' request to go round trip."
+  },
+  "DEVTOOLS_DEBUGGER_RDP_REMOTE_GET_EXECUTABLE_LINES_MS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "10000",
+    "n_buckets": "1000",
+    "description": "The time (in milliseconds) that it took a 'getExecutableLines' request to go round trip."
+  },
   "DEVTOOLS_DEBUGGER_RDP_LOCAL_BLACKBOX_MS": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "10000",
     "n_buckets": "1000",
     "description": "The time (in milliseconds) that it took a 'blackbox' request to go round trip."
   },
   "DEVTOOLS_DEBUGGER_RDP_REMOTE_BLACKBOX_MS": {
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -2421,16 +2421,33 @@ SourceClient.prototype = {
           this._activeThread.emit("blackboxchange", this);
         }
       }
       return aResponse;
     }
   }),
 
   /**
+   * Get Executable Lines from a source
+   *
+   * @param aCallback Function
+   *        The callback function called when we receive the response from the server.
+   */
+  getExecutableLines: function(cb){
+    let packet = {
+      to: this._form.actor,
+      type: "getExecutableLines"
+    };
+
+    this._client.request(packet, res => {
+      cb(res.lines);
+    });
+  },
+
+  /**
    * Get a long string grip for this SourceClient's source.
    */
   source: function (aCallback) {
     let packet = {
       to: this._form.actor,
       type: "source"
     };
     this._client.request(packet, aResponse => {
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -2638,16 +2638,76 @@ SourceActor.prototype = {
     sourceFetched.then(({ contentType }) => {
       this._contentType = contentType;
     });
 
     return sourceFetched;
   },
 
   /**
+   * Get all executable lines from the current source
+   * @return Array - Executable lines of the current script
+   **/
+  getExecutableLines: function () {
+    // Check if the original source is source mapped
+    let packet = {
+      from: this.actorID
+    };
+
+    let lines;
+
+    if (this._sourceMap) {
+      lines = new Set();
+
+      // Position of executable lines in the generated source
+      let offsets = this.getExecutableOffsets(this._generatedSource, false);
+      for (let offset of offsets) {
+        let {line, source} = this._sourceMap.originalPositionFor({
+          line: offset.lineNumber,
+          column: offset.columnNumber
+        });
+
+        if (source === this._url) {
+          lines.add(line);
+        }
+      }
+    } else {
+      // Converting the set given by getExecutableOffsets to an array
+      lines = this.getExecutableOffsets(this._url, true);
+    }
+
+    // Converting the Set into an array
+    packet.lines = [line for (line of lines)];
+    packet.lines.sort((a, b) => {
+      return a - b;
+    });
+
+    return packet;
+  },
+
+  /**
+   * Extract all executable offsets from the given script
+   * @param String url - extract offsets of the script with this url
+   * @param Boolean onlyLine - will return only the line number
+   * @return Set - Executable offsets/lines of the script
+   **/
+  getExecutableOffsets: function (url, onlyLine) {
+    let offsets = new Set();
+    for (let s of this.threadActor.dbg.findScripts(this.threadActor.global)) {
+      if (s.url === url) {
+        for (let offset of s.getAllColumnOffsets()) {
+          offsets.add(onlyLine ? offset.lineNumber : offset);
+        }
+      }
+    }
+
+    return offsets;
+  },
+
+  /**
    * Handler for the "source" packet.
    */
   onSource: function () {
     return resolve(this._init)
       .then(this._getSourceText)
       .then(({ content, contentType }) => {
         return {
           from: this.actorID,
@@ -2853,17 +2913,18 @@ SourceActor.prototype = {
   }
 };
 
 SourceActor.prototype.requestTypes = {
   "source": SourceActor.prototype.onSource,
   "blackbox": SourceActor.prototype.onBlackBox,
   "unblackbox": SourceActor.prototype.onUnblackBox,
   "prettyPrint": SourceActor.prototype.onPrettyPrint,
-  "disablePrettyPrint": SourceActor.prototype.onDisablePrettyPrint
+  "disablePrettyPrint": SourceActor.prototype.onDisablePrettyPrint,
+  "getExecutableLines": SourceActor.prototype.getExecutableLines
 };
 
 
 /**
  * Determine if a given value is non-primitive.
  *
  * @param Any aValue
  *        The value to test.
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/unit/test_get-executable-lines-source-map.js
@@ -0,0 +1,56 @@
+/* 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/. */
+
+/**
+ * Test if getExecutableLines return correct information
+ */
+
+let gDebuggee;
+let gClient;
+let gThreadClient;
+
+const SOURCE_MAPPED_FILE = getFileUrl("sourcemapped.js");
+
+function run_test() {
+  initTestDebuggerServer();
+  gDebuggee = addTestGlobal("test-get-executable-lines");
+  gClient = new DebuggerClient(DebuggerServer.connectPipe());
+  gClient.connect(function _onConnect() {
+    attachTestTabAndResume(
+      gClient,
+      "test-get-executable-lines",
+      function (aResponse, aTabClient, aThreadClient) {
+        gThreadClient = aThreadClient;
+        test_executable_lines();
+      }
+    );
+  });
+
+  do_test_pending();
+}
+
+function test_executable_lines() {
+  gClient.addOneTimeListener("newSource", function _onNewSource(evt, packet) {
+    do_check_eq(evt, "newSource");
+
+    gThreadClient.getSources(function ({error, sources}) {
+      do_check_true(!error);
+      let source = gThreadClient.source(sources[0]);
+      source.getExecutableLines(function(lines){
+        do_check_true(arrays_equal([1, 2, 4, 6], lines));
+        finishClient(gClient);
+      });
+    });
+  });
+
+  let code = readFile("sourcemapped.js") + "\n//# sourceMappingURL=" +
+    getFileUrl("source-map-data/sourcemapped.map");
+
+  Components.utils.evalInSandbox(code, gDebuggee, "1.8",
+    SOURCE_MAPPED_FILE, 1);
+}
+
+function arrays_equal(a,b) {
+  return !(a<b || b<a);
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/unit/test_get-executable-lines.js
@@ -0,0 +1,55 @@
+/* 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/. */
+
+/**
+ * Test if getExecutableLines return correct information
+ */
+
+let gDebuggee;
+let gClient;
+let gThreadClient;
+
+const SOURCE_MAPPED_FILE = getFileUrl("sourcemapped.js");
+
+function run_test() {
+  initTestDebuggerServer();
+  gDebuggee = addTestGlobal("test-get-executable-lines");
+  gClient = new DebuggerClient(DebuggerServer.connectPipe());
+  gClient.connect(function _onConnect() {
+    attachTestTabAndResume(
+      gClient,
+      "test-get-executable-lines",
+      function (aResponse, aTabClient, aThreadClient) {
+        gThreadClient = aThreadClient;
+        test_executable_lines();
+      }
+    );
+  });
+
+  do_test_pending();
+}
+
+function test_executable_lines() {
+  gClient.addOneTimeListener("newSource", function _onNewSource(evt, packet) {
+    do_check_eq(evt, "newSource");
+
+    gThreadClient.getSources(function ({error, sources}) {
+      do_check_true(!error);
+      let source = gThreadClient.source(sources[0]);
+      source.getExecutableLines(function(lines){
+        do_check_true(arrays_equal([2, 3, 5, 6, 7, 8, 12, 14, 16], lines));
+        finishClient(gClient);
+      });
+    });
+  });
+
+  let code = readFile("sourcemapped.js");
+
+  Components.utils.evalInSandbox(code, gDebuggee, "1.8",
+    SOURCE_MAPPED_FILE, 1);
+}
+
+function arrays_equal(a,b) {
+  return !(a<b || b<a);
+}
\ No newline at end of file
--- a/toolkit/devtools/server/tests/unit/xpcshell.ini
+++ b/toolkit/devtools/server/tests/unit/xpcshell.ini
@@ -208,12 +208,10 @@ reason = bug 820380
 reason = bug 937197
 [test_layout-reflows-observer.js]
 [test_protocolSpec.js]
 [test_registerClient.js]
 [test_client_request.js]
 [test_monitor_actor.js]
 [test_symbols-01.js]
 [test_symbols-02.js]
-[test_memory_footprint.js]
-run-sequentially = measure memory, has to be run solo
-skip-if = os != 'linux' || debug || asan
-reason = bug 1014071
+[test_get-executable-lines.js]
+[test_get-executable-lines-source-map.js]