Bug 981183 - Part 1 - Gathering timestamps at the call watcher actor. r=vporof
authorDaosheng Mu <daoshengmu@gmail.com>
Sun, 13 Sep 2015 22:49:00 +0200
changeset 295096 4ba1955735ce4be6f0b4f1a972cc5938670970d5
parent 295095 6be981250868dea41ff57eedf038e33b96de8844
child 295097 993ae08a8759a1fa1a9a66a5d4ddceb9ec3b783d
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvporof
bugs981183
milestone43.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 981183 - Part 1 - Gathering timestamps at the call watcher actor. r=vporof
browser/devtools/canvasdebugger/snapshotslist.js
toolkit/devtools/server/actors/call-watcher.js
toolkit/devtools/server/actors/canvas.js
--- a/browser/devtools/canvasdebugger/snapshotslist.js
+++ b/browser/devtools/canvasdebugger/snapshotslist.js
@@ -415,24 +415,25 @@ let SnapshotsListView = Heritage.extend(
         screenshot: null
       };
       let functionCalls = snapshotItem.attachment.calls;
       let thumbnails = snapshotItem.attachment.thumbnails;
       let screenshot = snapshotItem.attachment.screenshot;
 
       // Prepare all the function calls for serialization.
       yield DevToolsUtils.yieldingEach(functionCalls, (call, i) => {
-        let { type, name, file, line, argsPreview, callerPreview } = call;
+        let { type, name, file, line, timestamp, argsPreview, callerPreview } = call;
         return call.getDetails().then(({ stack }) => {
           data.calls[i] = {
             type: type,
             name: name,
             file: file,
             line: line,
             stack: stack,
+            timestamp: timestamp,
             argsPreview: argsPreview,
             callerPreview: callerPreview
           };
         });
       });
 
       // Prepare all the thumbnails for serialization.
       yield DevToolsUtils.yieldingEach(thumbnails, (thumbnail, i) => {
--- a/toolkit/devtools/server/actors/call-watcher.js
+++ b/toolkit/devtools/server/actors/call-watcher.js
@@ -50,31 +50,34 @@ let FunctionCallActor = protocol.ActorCl
    *        The object owning the function when it was called.
    *        For example, in `foo.bar()`, the caller is `foo`.
    * @param number type
    *        Either METHOD_FUNCTION, METHOD_GETTER or METHOD_SETTER.
    * @param string name
    *        The called function's name.
    * @param array stack
    *        The called function's stack, as a list of { name, file, line } objects.
+   * @param number timestamp
+   *        The timestamp of draw-related functions
    * @param array args
    *        The called function's arguments.
    * @param any result
    *        The value returned by the function call.
    * @param boolean holdWeak
    *        Determines whether or not FunctionCallActor stores a weak reference
    *        to the underlying objects.
    */
-  initialize: function(conn, [window, global, caller, type, name, stack, args, result], holdWeak) {
+  initialize: function(conn, [window, global, caller, type, name, stack, timestamp, args, result], holdWeak) {
     protocol.Actor.prototype.initialize.call(this, conn);
 
     this.details = {
       type: type,
       name: name,
       stack: stack,
+      timestamp: timestamp
     };
 
     // Store a weak reference to all objects so we don't
     // prevent natural GC if `holdWeak` was passed into
     // setup as truthy. Used in the Web Audio Editor.
     if (holdWeak) {
       let weakRefs = {
         window: Cu.getWeakReference(window),
@@ -82,25 +85,27 @@ let FunctionCallActor = protocol.ActorCl
         result: Cu.getWeakReference(result),
         args: Cu.getWeakReference(args)
       };
 
       Object.defineProperties(this.details, {
         window: { get: () => weakRefs.window.get() },
         caller: { get: () => weakRefs.caller.get() },
         result: { get: () => weakRefs.result.get() },
-        args: { get: () => weakRefs.args.get() }
+        args: { get: () => weakRefs.args.get() },
+        timestamp: { get: () => weakRefs.timestamp.get() },
       });
     }
     // Otherwise, hold strong references to the objects.
     else {
       this.details.window = window;
       this.details.caller = caller;
       this.details.result = result;
       this.details.args = args;
+      this.details.timestamp = timestamp;
     }
 
     this.meta = {
       global: -1,
       previews: { caller: "", args: "" }
     };
 
     if (global == "WebGLRenderingContext") {
@@ -123,27 +128,28 @@ let FunctionCallActor = protocol.ActorCl
    */
   form: function() {
     return {
       actor: this.actorID,
       type: this.details.type,
       name: this.details.name,
       file: this.details.stack[0].file,
       line: this.details.stack[0].line,
+      timestamp: this.details.timestamp,
       callerPreview: this.meta.previews.caller,
       argsPreview: this.meta.previews.args
     };
   },
 
   /**
    * Gets more information about this function call, which is not necessarily
    * available on the Front instance.
    */
   getDetails: method(function() {
-    let { type, name, stack } = this.details;
+    let { type, name, stack, timestamp } = this.details;
 
     // Since not all calls on the stack have corresponding owner files (e.g.
     // callbacks of a requestAnimationFrame etc.), there's no benefit in
     // returning them, as the user can't jump to the Debugger from them.
     for (let i = stack.length - 1;;) {
       if (stack[i].file) {
         break;
       }
@@ -151,17 +157,18 @@ let FunctionCallActor = protocol.ActorCl
       i--;
     }
 
     // XXX: Use grips for objects and serialize them properly, in order
     // to add the function's caller, arguments and return value. Bug 978957.
     return {
       type: type,
       name: name,
-      stack: stack
+      stack: stack,
+      timestamp: timestamp
     };
   }, {
     response: { info: RetVal("call-details") }
   }),
 
   /**
    * Serializes the caller's name so that it can be easily be transferred
    * as a string, but still be useful when displayed in a potential UI.
@@ -238,16 +245,17 @@ let FunctionCallFront = protocol.FrontCl
    * to avoid extra roundtrips.
    */
   form: function(form) {
     this.actorID = form.actor;
     this.type = form.type;
     this.name = form.name;
     this.file = form.file;
     this.line = form.line;
+    this.timestamp = form.timestamp;
     this.callerPreview = form.callerPreview;
     this.argsPreview = form.argsPreview;
   }
 });
 
 /**
  * This actor observes function calls on certain objects or globals.
  */
@@ -329,16 +337,23 @@ let CallWatcherActor = exports.CallWatch
    */
   isRecording: method(function() {
     return this._recording;
   }, {
     response: RetVal("boolean")
   }),
 
   /**
+   * Initialize frame start timestamp for measuring
+   */
+  initFrameStartTimestamp: method(function() {
+    this._frameStartTimestamp = this.tabActor.window.performance.now();
+  }),
+
+  /**
    * Starts recording function calls.
    */
   resumeRecording: method(function() {
     this._recording = true;
   }),
 
   /**
    * Stops recording function calls.
@@ -419,19 +434,20 @@ let CallWatcherActor = exports.CallWatch
         let result;
         try {
           result = Cu.waiveXrays(originalFunc.apply(this, args));
         } catch (e) {
           throw createContentError(e, unwrappedWindow);
         }
 
         if (self._recording) {
+          let timestamp = self.tabActor.window.performance.now() - self._frameStartTimestamp;
           let stack = getStack(name);
           let type = CallWatcherFront.METHOD_FUNCTION;
-          callback(unwrappedWindow, global, this, type, name, stack, args, result);
+          callback(unwrappedWindow, global, this, type, name, stack, timestamp, args, result);
         }
         return result;
       }, target, { defineAs: name });
 
       Object.defineProperty(target, name, {
         configurable: descriptor.configurable,
         enumerable: descriptor.enumerable,
         writable: true
@@ -448,30 +464,32 @@ let CallWatcherActor = exports.CallWatch
       let originalSetter = Cu.unwaiveXrays(target.__lookupSetter__(name));
 
       Object.defineProperty(target, name, {
         get: function(...args) {
           if (!originalGetter) return undefined;
           let result = Cu.waiveXrays(originalGetter.apply(this, args));
 
           if (self._recording) {
+            let timestamp = self.tabActor.window.performance.now() - self._frameStartTimestamp;
             let stack = getStack(name);
             let type = CallWatcherFront.GETTER_FUNCTION;
-            callback(unwrappedWindow, global, this, type, name, stack, args, result);
+            callback(unwrappedWindow, global, this, type, name, stack, timestamp, args, result);
           }
           return result;
         },
         set: function(...args) {
           if (!originalSetter) return;
           originalSetter.apply(this, args);
 
           if (self._recording) {
+            let timestamp = self.tabActor.window.performance.now() - self._frameStartTimestamp;
             let stack = getStack(name);
             let type = CallWatcherFront.SETTER_FUNCTION;
-            callback(unwrappedWindow, global, this, type, name, stack, args, undefined);
+            callback(unwrappedWindow, global, this, type, name, stack, timestamp, args, undefined);
           }
         },
         configurable: descriptor.configurable,
         enumerable: descriptor.enumerable
       });
     }
 
     /**
--- a/toolkit/devtools/server/actors/canvas.js
+++ b/toolkit/devtools/server/actors/canvas.js
@@ -311,16 +311,17 @@ let CanvasActor = exports.CanvasActor = 
    */
   recordAnimationFrame: method(function() {
     if (this._callWatcher.isRecording()) {
       return this._currentAnimationFrameSnapshot.promise;
     }
 
     this._recordingContainsDrawCall = false;
     this._callWatcher.eraseRecording();
+    this._callWatcher.initFrameStartTimestamp();
     this._callWatcher.resumeRecording();
 
     let deferred = this._currentAnimationFrameSnapshot = promise.defer();
     return deferred.promise;
   }, {
     response: { snapshot: RetVal("nullable:frame-snapshot") }
   }),