Bug 1392408 Part 5 - Listen for alternate stack traces in StackTraceCollector, r=ochameau.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 16 May 2019 08:09:07 -1000
changeset 533132 8a7327b91ed243944681e5a82c1da2db7023d297
parent 533131 b224c05dc123549420aa8747e1600c0bb68da0f6
child 533133 5168e91ed5b4da5b8c9c9a05cfc7f250a6de47af
push id11276
push userrgurzau@mozilla.com
push dateMon, 20 May 2019 13:11:24 +0000
treeherdermozilla-beta@847755a7c325 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersochameau
bugs1392408
milestone68.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 1392408 Part 5 - Listen for alternate stack traces in StackTraceCollector, r=ochameau.
devtools/server/actors/network-monitor/stack-trace-collector.js
--- a/devtools/server/actors/network-monitor/stack-trace-collector.js
+++ b/devtools/server/actors/network-monitor/stack-trace-collector.js
@@ -20,65 +20,110 @@ function StackTraceCollector(filters, ne
   this.filters = filters;
   this.stacktracesById = new Map();
   this.netmonitors = netmonitors;
 }
 
 StackTraceCollector.prototype = {
   init() {
     Services.obs.addObserver(this, "http-on-opening-request");
+    Services.obs.addObserver(this, "network-monitor-alternate-stack");
     ChannelEventSinkFactory.getService().registerCollector(this);
     this.onGetStack = this.onGetStack.bind(this);
     for (const { messageManager } of this.netmonitors) {
       messageManager.addMessageListener("debug:request-stack:request", this.onGetStack);
     }
   },
 
   destroy() {
     Services.obs.removeObserver(this, "http-on-opening-request");
+    Services.obs.removeObserver(this, "network-monitor-alternate-stack");
     ChannelEventSinkFactory.getService().unregisterCollector(this);
     for (const { messageManager } of this.netmonitors) {
       messageManager.removeMessageListener("debug:request-stack:request",
         this.onGetStack);
     }
   },
 
   _saveStackTrace(channel, stacktrace) {
+    if (this.stacktracesById.has(channel.channelId)) {
+      // We can get up to two stack traces for the same channel: one each from
+      // the two observer topics we are listening to. Use the first stack trace
+      // which is specified, and ignore any later one.
+      return;
+    }
     for (const { messageManager } of this.netmonitors) {
       messageManager.sendAsyncMessage("debug:request-stack-available", {
         channelId: channel.channelId,
         stacktrace: stacktrace && stacktrace.length > 0,
       });
     }
     this.stacktracesById.set(channel.channelId, stacktrace);
   },
 
-  observe(subject) {
+  observe(subject, topic, data) {
     const channel = subject.QueryInterface(Ci.nsIHttpChannel);
 
     if (!matchRequest(channel, this.filters)) {
       return;
     }
 
-    // Convert the nsIStackFrame XPCOM objects to a nice JSON that can be
-    // passed around through message managers etc.
-    let frame = components.stack;
     const stacktrace = [];
-    if (frame && frame.caller) {
-      frame = frame.caller;
-      while (frame) {
-        stacktrace.push({
-          filename: frame.filename,
-          lineNumber: frame.lineNumber,
-          columnNumber: frame.columnNumber,
-          functionName: frame.name,
-          asyncCause: frame.asyncCause,
-        });
-        frame = frame.caller || frame.asyncCaller;
+    switch (topic) {
+      case "http-on-opening-request": {
+        // The channel is being opened on the main thread, associate the current
+        // stack with it.
+        //
+        // Convert the nsIStackFrame XPCOM objects to a nice JSON that can be
+        // passed around through message managers etc.
+        let frame = components.stack;
+        if (frame && frame.caller) {
+          frame = frame.caller;
+          while (frame) {
+            stacktrace.push({
+              filename: frame.filename,
+              lineNumber: frame.lineNumber,
+              columnNumber: frame.columnNumber,
+              functionName: frame.name,
+              asyncCause: frame.asyncCause,
+            });
+            frame = frame.caller || frame.asyncCaller;
+          }
+        }
+        break;
       }
+      case "network-monitor-alternate-stack": {
+        // An alternate stack trace is being specified for this channel.
+        // The topic data is the JSON for the saved frame stack we should use,
+        // so convert this into the expected format.
+        //
+        // This topic is used in the following cases:
+        //
+        // - The HTTP channel is opened asynchronously or on a different thread
+        //   from the code which triggered its creation, in which case the stack
+        //   from components.stack will be empty. The alternate stack will be
+        //   for the point we want to associate with the channel.
+        //
+        // - The channel is not a nsIHttpChannel, and we will receive no
+        //   opening request notification for it.
+        let frame = JSON.parse(data);
+        while (frame) {
+          stacktrace.push({
+            filename: frame.source,
+            lineNumber: frame.line,
+            columnNumber: frame.column,
+            functionName: frame.functionDisplayName,
+            asyncCause: frame.asyncCause,
+          });
+          frame = frame.parent || frame.asyncParent;
+        }
+        break;
+      }
+      default:
+        throw new Error("Unexpected observe() topic");
     }
 
     this._saveStackTrace(channel, stacktrace);
   },
 
   // eslint-disable-next-line no-shadow
   onChannelRedirect(oldChannel, newChannel, flags) {
     // We can be called with any nsIChannel, but are interested only in HTTP channels