Bug 1498190 - implement the 'Memory' column in about:performance, r=felipe,flod.
authorFlorian Quèze <florian@queze.net>
Thu, 22 Nov 2018 23:51:24 +0100
changeset 504316 287134f9e8048ec115591f22982817e1e0a54eec
parent 504315 bbe0fe93abc15eb745d7870dc57875d9773f9359
child 504317 a1cd697c733cd1fb28e585acee93d6cd8d3048bc
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfelipe, flod
bugs1498190
milestone65.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 1498190 - implement the 'Memory' column in about:performance, r=felipe,flod.
toolkit/components/aboutperformance/content/aboutPerformance.js
toolkit/components/aboutperformance/content/aboutPerformance.xhtml
toolkit/locales/en-US/toolkit/about/aboutPerformance.ftl
--- a/toolkit/components/aboutperformance/content/aboutPerformance.js
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.js
@@ -421,41 +421,53 @@ var State = {
     let addonHosts = new Map();
     for (let addon of addons)
       addonHosts.set(addon.mozExtensionHostname, addon.id);
 
     let counters = await ChromeUtils.requestPerformanceMetrics();
     let tabs = {};
     for (let counter of counters) {
       let {items, host, pid, counterId, windowId, duration, isWorker,
-           isTopLevel} = counter;
+           memoryInfo, isTopLevel} = counter;
       // If a worker has a windowId of 0 or max uint64, attach it to the
       // browser UI (doc group with id 1).
       if (isWorker && (windowId == 18446744073709552000 || !windowId))
         windowId = 1;
       let dispatchCount = 0;
       for (let {count} of items) {
         dispatchCount += count;
       }
 
+      let memory = 0;
+      for (let field in memoryInfo) {
+        if (field == "media") {
+          for (let mediaField of ["audioSize", "videoSize", "resourcesSize"]) {
+            memory += memoryInfo.media[mediaField];
+          }
+          continue;
+        }
+        memory += memoryInfo[field];
+      }
+
       let tab;
       let id = windowId;
       if (addonHosts.has(host)) {
         id = addonHosts.get(host);
       }
       if (id in tabs) {
         tab = tabs[id];
       } else {
-        tab = {windowId, host, dispatchCount: 0, duration: 0, children: []};
+        tab = {windowId, host, dispatchCount: 0, duration: 0, memory: 0, children: []};
         tabs[id] = tab;
       }
       tab.dispatchCount += dispatchCount;
       tab.duration += duration;
+      tab.memory += memory;
       if (!isTopLevel || isWorker) {
-        tab.children.push({host, isWorker, dispatchCount, duration,
+        tab.children.push({host, isWorker, dispatchCount, duration, memory,
                            counterId: pid + ":" + counterId});
       }
     }
 
     if (extensionCountersEnabled()) {
       let extCounters = await ExtensionParent.ParentAPIManager.retrievePerformanceCounters();
       for (let [id, apiMap] of extCounters) {
         let dispatchCount = 0, duration = 0;
@@ -463,17 +475,17 @@ var State = {
           dispatchCount += counter.calls;
           duration += counter.duration;
         }
 
         let tab;
         if (id in tabs) {
           tab = tabs[id];
         } else {
-          tab = {windowId: 0, host: id, dispatchCount: 0, duration: 0, children: []};
+          tab = {windowId: 0, host: id, dispatchCount: 0, duration: 0, memory: 0, children: []};
           tabs[id] = tab;
         }
         tab.dispatchCount += dispatchCount;
         tab.duration += duration;
       }
     }
 
     return {tabs, date: Cu.now()};
@@ -664,28 +676,27 @@ var State = {
       let prevChildren = new Map();
       if (prev) {
         for (let child of prev.children) {
           prevChildren.set(child.counterId, child);
         }
       }
       // For each subitem, create a new object including the deltas since the previous time.
       let children = tab.children.map(child => {
-        let {host, dispatchCount, duration, isWorker, counterId} = child;
-
+        let {host, dispatchCount, duration, memory, isWorker, counterId} = child;
         let dispatchesSincePrevious = dispatchCount;
         let durationSincePrevious = duration;
         if (prevChildren.has(counterId)) {
           let prevCounter = prevChildren.get(counterId);
           dispatchesSincePrevious -= prevCounter.dispatchCount;
           durationSincePrevious -= prevCounter.duration;
           prevChildren.delete(counterId);
         }
 
-        return {host, dispatchCount, duration, isWorker,
+        return {host, dispatchCount, duration, isWorker, memory,
                 dispatchesSincePrevious, durationSincePrevious};
       });
 
       // Any item that remains in prevChildren is a subitem that no longer
       // exists in the current sample; remember the values of its counters
       // so that the values don't go down for the parent item.
       tab.dispatchesFromFormerChildren = prev && prev.dispatchesFromFormerChildren || 0;
       tab.durationFromFormerChildren = prev && prev.durationFromFormerChildren || 0;
@@ -709,17 +720,17 @@ var State = {
           dispatches - prev.dispatchCount - (prev.dispatchesFromFormerChildren || 0);
       }
       if (oldest) {
         dispatchesSinceStartOfBuffer =
           dispatches - oldest.dispatchCount - (oldest.dispatchesFromFormerChildren || 0);
         durationSinceStartOfBuffer =
           duration - oldest.duration - (oldest.durationFromFormerChildren || 0);
       }
-      counters.push({id, name, image, type,
+      counters.push({id, name, image, type, memory: tab.memory,
                      totalDispatches: dispatches, totalDuration: duration,
                      durationSincePrevious, dispatchesSincePrevious,
                      durationSinceStartOfBuffer, dispatchesSinceStartOfBuffer,
                      children});
     }
     return counters;
   },
 };
@@ -1032,17 +1043,17 @@ var View = {
       if (energyImpact < 1)
         impact = "low";
       else if (energyImpact < 25)
         impact = "medium";
       document.l10n.setAttributes(elt, "energy-impact-" + impact,
                                   {value: energyImpact});
     }
   },
-  appendRow(name, energyImpact, tooltip, type, image = "") {
+  appendRow(name, energyImpact, memory, tooltip, type, image = "") {
     let row = document.createElement("tr");
 
     let elt = document.createElement("td");
     if (typeof name == "string") {
       elt.textContent = name;
     } else if (name.title) {
       document.l10n.setAttributes(elt, name.id, {title: name.title});
     } else {
@@ -1064,16 +1075,34 @@ var View = {
       type = "addon";
     document.l10n.setAttributes(elt, "type-" + type);
     row.appendChild(elt);
 
     elt = document.createElement("td");
     this.displayEnergyImpact(elt, energyImpact);
     row.appendChild(elt);
 
+    elt = document.createElement("td");
+    if (!memory) {
+      elt.textContent = "–";
+    } else {
+      let unit = "KB";
+      memory = Math.ceil(memory / 1024);
+      if (memory > 1024) {
+        memory = Math.ceil(memory / 1024 * 10) / 10;
+        unit = "MB";
+        if (memory > 1024) {
+          memory = Math.ceil(memory / 1024 * 100) / 100;
+          unit = "GB";
+        }
+      }
+      document.l10n.setAttributes(elt, "size-" + unit, {value: memory});
+    }
+    row.appendChild(elt);
+
     if (tooltip)
       document.l10n.setAttributes(row, "item", tooltip);
 
     elt = document.createElement("td");
     if (type == "tab") {
       let img = document.createElement("span");
       img.className = "action-icon close-icon";
       document.l10n.setAttributes(img, "close-tab");
@@ -1199,16 +1228,18 @@ var Control = {
       await wait(0);
 
       // Make sure that we do not keep obsolete stuff around.
       View.DOMCache.trimTo(state.deltas);
     } else {
       // If the mouse has been moved recently, update the data displayed
       // without moving any item to avoid the risk of users clicking an action
       // button for the wrong item.
+      // Memory use is unlikely to change dramatically within a few seconds, so
+      // it's probably fine to not update the Memory column in this case.
       if (Date.now() - this._lastMouseEvent < TIME_BEFORE_SORTING_AGAIN) {
         let energyImpactPerId = new Map();
         for (let {id, dispatchesSincePrevious,
                   durationSincePrevious} of State.getCounters()) {
           let energyImpact = this._computeEnergyImpact(dispatchesSincePrevious,
                                                        durationSincePrevious);
           energyImpactPerId.set(id, energyImpact);
         }
@@ -1235,21 +1266,22 @@ var Control = {
         selectedId = this.selectedRow.windowId;
         this.selectedRow = null;
       }
       let openItems = this._openItems;
       this._openItems = new Set();
 
       let counters = this._sortCounters(State.getCounters());
       for (let {id, name, image, type, totalDispatches, dispatchesSincePrevious,
-                totalDuration, durationSincePrevious, children} of counters) {
+                memory, totalDuration, durationSincePrevious, children} of counters) {
         let row =
           View.appendRow(name,
                          this._computeEnergyImpact(dispatchesSincePrevious,
                                                    durationSincePrevious),
+                         memory,
                          {totalDispatches, totalDuration: Math.ceil(totalDuration / 1000),
                           dispatchesSincePrevious,
                           durationSincePrevious: Math.ceil(durationSincePrevious / 1000)},
                          type, image);
         row.windowId = id;
         if (id == selectedId) {
           row.setAttribute("selected", "true");
           this.selectedRow = row;
@@ -1302,16 +1334,17 @@ var Control = {
       let type = "subframe";
       if (State.isTracker(host))
         type = "tracker";
       if (row.isWorker)
         type = "worker";
       View.appendRow(row.host,
                      this._computeEnergyImpact(row.dispatchesSincePrevious,
                                                row.durationSincePrevious),
+                     row.memory,
                      {totalDispatches: row.dispatchCount,
                       totalDuration: Math.ceil(row.duration / 1000),
                       dispatchesSincePrevious: row.dispatchesSincePrevious,
                       durationSincePrevious: Math.ceil(row.durationSincePrevious / 1000)},
                      type);
     }
   },
   _computeEnergyImpact(dispatches, duration) {
--- a/toolkit/components/aboutperformance/content/aboutPerformance.xhtml
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.xhtml
@@ -139,19 +139,25 @@
         display: table;
         table-layout: fixed;
         width: 100%;
       }
       #dispatch-table td:nth-child(2) {
         width: 8em;
       }
       #dispatch-table td:nth-child(3) {
-        width: 12em;
+        width: 9em;
       }
       #dispatch-table td:nth-child(4) {
+        width: 5em;
+      }
+      #dispatch-tbody td:nth-child(4) {
+        text-align: end;
+      }
+      #dispatch-table td:nth-child(5) {
         width: 20px;
       }
 
       /* Show action icons on selected or hovered rows */
       tr:-moz-any([selected], :hover) > td > .action-icon {
         padding: 1px 10px;
         opacity: 1;
       }
@@ -328,16 +334,17 @@
     </div>
     <div>
       <table id="dispatch-table">
         <thead id="dispatch-thead">
           <tr>
             <td data-l10n-id="column-name"/>
             <td data-l10n-id="column-type"/>
             <td data-l10n-id="column-energy-impact"/>
+            <td data-l10n-id="column-memory"/>
             <td></td><!-- actions -->
           </tr>
         </thead>
         <tbody id="dispatch-tbody"></tbody>
       </table>
     </div>
   </body>
 </html>
--- a/toolkit/locales/en-US/toolkit/about/aboutPerformance.ftl
+++ b/toolkit/locales/en-US/toolkit/about/aboutPerformance.ftl
@@ -4,16 +4,17 @@
 
 # Page title
 about-performance-title = Task Manager
 
 ## Column headers
 column-name = Name
 column-type = Type
 column-energy-impact = Energy Impact
+column-memory = Memory
 
 ## Special values for the Name column
 ghost-windows = Recently closed tabs
 # Variables:
 #   $title (String) - the title of the preloaded page, typically 'New Tab'
 preloaded-tab = Preloaded: { $title }
 
 ## Values for the Type column
@@ -29,16 +30,24 @@ type-other = Other
 ##
 ## Variables:
 ##   $value (Number) - Value of the energy impact, eg. 0.25 (low),
 ##                     5.38 (medium), 105.38 (high)
 energy-impact-high = High ({ $value })
 energy-impact-medium = Medium ({ $value })
 energy-impact-low = Low ({ $value })
 
+## Values for the Memory column
+##
+## Variables:
+##   $value (Number) - How much memory is used
+size-KB = { $value } KB
+size-MB = { $value } MB
+size-GB = { $value } GB
+
 ## Tooltips for the action buttons
 close-tab =
     .title = Close tab
 show-addon =
     .title = Show in Add-ons Manager
 
 # Tooltip when hovering an item of the about:performance table
 # Variables: