DOMEventFields: function (marker) {
    let fields = Object.create(null);

    if ("type" in marker) {
      fields[L10N.getStr("marker.field.DOMEventType")] = marker.type;
    }

    if ("eventPhase" in marker) {
      let label;
      switch (marker.eventPhase) {
        case Ci.nsIDOMEvent.AT_TARGET:
          label = L10N.getStr("marker.value.DOMEventTargetPhase");
          break;
        case Ci.nsIDOMEvent.CAPTURING_PHASE:
          label = L10N.getStr("marker.value.DOMEventCapturingPhase");
          break;
        case Ci.nsIDOMEvent.BUBBLING_PHASE:
          label = L10N.getStr("marker.value.DOMEventBubblingPhase");
          break;
      }
      fields[L10N.getStr("marker.field.DOMEventPhase")] = label;
    }

    return fields;
  },
 WorkerFields: function (marker) {
   if ("workerOperation" in marker) {
     let label = L10N.getStr(`marker.worker.${marker.workerOperation}`);
     return {
       [L10N.getStr("marker.field.type")]: label
     };
   }
 },
 MessagePortFields: function (marker) {
   if ("messagePortOperation" in marker) {
     let label = L10N.getStr(`marker.messagePort.${marker.messagePortOperation}`);
     return {
       [L10N.getStr("marker.field.type")]: label
     };
   }
 },
Example #4
0
 buildDuration: function (doc, marker) {
   let label = L10N.getStr("marker.field.duration");
   let start = L10N.getFormatStrWithNumbers("timeline.tick", marker.start);
   let end = L10N.getFormatStrWithNumbers("timeline.tick", marker.end);
   let duration = L10N.getFormatStrWithNumbers("timeline.tick", marker.end - marker.start);
   let el = DOM.buildNameValueLabel(doc, label, duration);
   el.classList.add("marker-details-duration");
   el.setAttribute("tooltiptext", `${start} → ${end}`);
   return el;
 },
Example #5
0
 GCLabel: function (marker) {
   if (!marker) {
     return L10N.getStr("marker.label.garbageCollection2");
   }
   // Only if a `nonincrementalReason` exists, do we want to label
   // this as a non incremental GC event.
   if ("nonincrementalReason" in marker) {
     return L10N.getStr("marker.label.garbageCollection.nonIncremental");
   }
   return L10N.getStr("marker.label.garbageCollection.incremental");
 },
Example #6
0
  GCFields: function (marker) {
    let fields = Object.create(null);
    let cause = marker.causeName;
    let label = L10N.getStr(`marker.gcreason.label.${cause}`) || cause;

    fields[L10N.getStr("marker.field.causeName")] = label;

    if ("nonincrementalReason" in marker) {
      fields[L10N.getStr("marker.field.nonIncrementalCause")] = marker.nonincrementalReason;
    }

    return fields;
  },
  MinorGCFields: function (marker) {
    let fields = Object.create(null);

    if ("causeName" in marker) {
      let cause = marker.causeName;
      let label = L10N.getStr(`marker.gcreason.label.${cause}`) || cause;
      fields[L10N.getStr("marker.field.causeName")] = label;
    }

    fields[L10N.getStr("marker.field.type")] = L10N.getStr("marker.nurseryCollection");

    return fields;
  },
Example #8
0
 StylesFields: function (marker) {
   if ("restyleHint" in marker) {
     return {
       [L10N.getStr("marker.field.restyleHint")]: marker.restyleHint.replace(/eRestyle_/g, "")
     };
   }
 },
 JSLabel: function (marker = {}) {
   let generic = L10N.getStr("marker.label.javascript");
   if ("causeName" in marker) {
     return JS_MARKER_MAP[marker.causeName] || generic;
   }
   return generic;
 },
Example #10
0
/**
 * Calculates the relative costs of this frame compared to a root,
 * and generates allocations information if specified. Uses caching
 * if possible.
 *
 * @param {ThreadNode|FrameNode} node
 *                               The node we are calculating.
 * @param {ThreadNode} options.root
 *                     The root thread node to calculate relative costs.
 *                     Generates [self|total] [duration|percentage] values.
 * @param {boolean} options.allocations
 *                  Generates `totalAllocations` and `selfAllocations`.
 *
 * @return {object}
 */
function getFrameInfo(node, options) {
  let data = gFrameData.get(node);

  if (!data) {
    if (node.nodeType === "Thread") {
      data = Object.create(null);
      data.functionName = global.L10N.getStr("table.root");
    } else {
      data = parseLocation(node.location, node.line, node.column);
      data.hasOptimizations = node.hasOptimizations();
      data.isContent = node.isContent;
      data.isMetaCategory = node.isMetaCategory;
    }
    data.samples = node.youngestFrameSamples;
    data.categoryData = CATEGORY_MAPPINGS[node.category] || {};
    data.nodeType = node.nodeType;

    // Frame name (function location or some meta information)
    data.name = data.isMetaCategory ? data.categoryData.label :
                shouldDemangle(data.functionName) ? demangle(data.functionName) : data.functionName;

    data.tooltiptext = data.isMetaCategory ? data.categoryData.label : node.location || "";

    gFrameData.set(node, data);
  }

  // If no options specified, we can't calculate relative values, abort here
  if (!options) {
    return data;
  }

  // If a root specified, calculate the relative costs in the context of
  // this call tree. The cached store may already have this, but generate
  // if it does not.
  let totalSamples = options.root.samples;
  let totalDuration = options.root.duration;
  if (options && options.root && !data.COSTS_CALCULATED) {
    data.selfDuration = node.youngestFrameSamples / totalSamples * totalDuration;
    data.selfPercentage = node.youngestFrameSamples / totalSamples * 100;
    data.totalDuration = node.samples / totalSamples * totalDuration;
    data.totalPercentage = node.samples / totalSamples * 100;
    data.COSTS_CALCULATED = true;
  }

  if (options && options.allocations && !data.ALLOCATION_DATA_CALCULATED) {
    let totalBytes = options.root.byteSize;
    data.selfCount = node.youngestFrameSamples;
    data.totalCount = node.samples;
    data.selfCountPercentage = node.youngestFrameSamples / totalSamples * 100;
    data.totalCountPercentage = node.samples / totalSamples * 100;
    data.selfSize = node.youngestFrameByteSize;
    data.totalSize = node.byteSize;
    data.selfSizePercentage = node.youngestFrameByteSize / totalBytes * 100;
    data.totalSizePercentage = node.byteSize / totalBytes * 100;
    data.ALLOCATION_DATA_CALCULATED = true;
  }

  return data;
}
 JSFields: function (marker) {
   if ("causeName" in marker && !JS_MARKER_MAP[marker.causeName]) {
     let label = PREFS["show-platform-data"] ? marker.causeName : GECKO_SYMBOL;
     return {
       [L10N.getStr("marker.field.causeName")]: label
     };
   }
 },
Example #12
0
 DOMEventFields: function (marker) {
   let fields = Object.create(null);
   if ("type" in marker) {
     fields[L10N.getStr("marker.field.DOMEventType")] = marker.type;
   }
   if ("eventPhase" in marker) {
     let phase;
     if (marker.eventPhase === Ci.nsIDOMEvent.AT_TARGET) {
       phase = L10N.getStr("marker.value.DOMEventTargetPhase");
     } else if (marker.eventPhase === Ci.nsIDOMEvent.CAPTURING_PHASE) {
       phase = L10N.getStr("marker.value.DOMEventCapturingPhase");
     } else if (marker.eventPhase === Ci.nsIDOMEvent.BUBBLING_PHASE) {
       phase = L10N.getStr("marker.value.DOMEventBubblingPhase");
     }
     fields[L10N.getStr("marker.field.DOMEventPhase")] = phase;
   }
   return fields;
 },
Example #13
0
  _buildNode: function (doc, startTime, dataScale, waterfallWidth) {
    let container = doc.createElement("hbox");
    container.className = "waterfall-header-container";
    container.setAttribute("flex", "1");

    let sidebar = doc.createElement("hbox");
    sidebar.className = "waterfall-sidebar theme-sidebar";
    sidebar.setAttribute("width", WATERFALL_MARKER_SIDEBAR_WIDTH);
    sidebar.setAttribute("align", "center");
    container.appendChild(sidebar);

    let name = doc.createElement("description");
    name.className = "plain waterfall-header-name";
    name.setAttribute("value", L10N.getStr("timeline.records"));
    sidebar.appendChild(name);

    let ticks = doc.createElement("hbox");
    ticks.className = "waterfall-header-ticks waterfall-background-ticks";
    ticks.setAttribute("align", "center");
    ticks.setAttribute("flex", "1");
    container.appendChild(ticks);

    let tickInterval = findOptimalTickInterval({
      ticksMultiple: WATERFALL_HEADER_TICKS_MULTIPLE,
      ticksSpacingMin: WATERFALL_HEADER_TICKS_SPACING_MIN,
      dataScale: dataScale
    });

    for (let x = 0; x < waterfallWidth; x += tickInterval) {
      let left = x + WATERFALL_HEADER_TEXT_PADDING;
      let time = Math.round(x / dataScale + startTime);
      let label = L10N.getFormatStr("timeline.tick", time);

      let node = doc.createElement("description");
      node.className = "plain waterfall-header-tick";
      node.style.transform = "translateX(" + left + "px)";
      node.setAttribute("value", label);
      ticks.appendChild(node);
    }

    return container;
  },
Example #14
0
  render() {
    const {
      onClearButtonClick,
      onRecordButtonClick,
      onImportButtonClick,
      isRecording,
      isLocked
    } = this.props;

    const recordButtonClassList = ["devtools-button", "record-button"];

    if (isRecording) {
      recordButtonClassList.push("checked");
    }

    return (
      div({ className: "devtools-toolbar" },
        div({ className: "toolbar-group" },
          button({
            id: "clear-button",
            className: "devtools-button",
            title: L10N.getStr("recordings.clear.tooltip"),
            onClick: onClearButtonClick
          }),
          button({
            id: "main-record-button",
            className: recordButtonClassList.join(" "),
            disabled: isLocked,
            title: L10N.getStr("recordings.start.tooltip"),
            onClick: onRecordButtonClick
          }),
          button({
            id: "import-button",
            className: "devtools-button",
            title: L10N.getStr("recordings.import.tooltip"),
            onClick: onImportButtonClick
          })
        )
      )
    );
  }
Example #15
0
  render() {
    const {
      onRecordButtonClick,
      isRecording,
      isLocked,
    } = this.props;

    const classList = ["devtools-button", "record-button"];

    if (isRecording) {
      classList.push("checked");
    }

    return button(
      {
        className: classList.join(" "),
        onClick: onRecordButtonClick,
        "data-standalone": "true",
        "data-text-only": "true",
        disabled: isLocked,
      },
      isRecording ? L10N.getStr("recordings.stop") : L10N.getStr("recordings.start")
    );
  }
Example #16
0
  render() {
    const {
      label,
      duration,
      onSelect,
      onSave,
      isLoading,
      isSelected,
      isRecording
    } = this.props;

    const className = `recording-list-item ${isSelected ? "selected" : ""}`;

    let durationText;
    if (isLoading) {
      durationText = L10N.getStr("recordingsList.loadingLabel");
    } else if (isRecording) {
      durationText = L10N.getStr("recordingsList.recordingLabel");
    } else {
      durationText = L10N.getFormatStr("recordingsList.durationLabel", duration);
    }

    return (
      li({ className, onClick: onSelect },
        div({ className: "recording-list-item-label" },
          label
        ),
        div({ className: "recording-list-item-footer" },
          span({ className: "recording-list-item-duration" }, durationText),
          button({ className: "recording-list-item-save", onClick: onSave },
            L10N.getStr("recordingsList.saveLabel")
          )
        )
      )
    );
  }
Example #17
0
/* 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/. */
"use strict";

/**
 * This file contains the tree view, displaying all the samples and frames
 * received from the proviler in a tree-like structure.
 */

const { L10N } = require("devtools/client/performance/modules/global");
const { Heritage } = require("devtools/client/shared/widgets/view-helpers");
const { AbstractTreeItem } = require("resource://devtools/client/shared/widgets/AbstractTreeItem.jsm");

const URL_LABEL_TOOLTIP = L10N.getStr("table.url.tooltiptext");
const VIEW_OPTIMIZATIONS_TOOLTIP = L10N.getStr("table.view-optimizations.tooltiptext2");

// px
const CALL_TREE_INDENTATION = 16;

// Used for rendering values in cells
const FORMATTERS = {
  TIME: (value) => L10N.getFormatStr("table.ms2", L10N.numberWithDecimals(value, 2)),
  PERCENT: (value) => L10N.getFormatStr("table.percentage3",
                                        L10N.numberWithDecimals(value, 2)),
  NUMBER: (value) => value || 0,
  BYTESIZE: (value) => L10N.getFormatStr("table.bytes", (value || 0))
};

/**
 * Definitions for rendering cells. Triads of class name, property name from
 UnknownLabel: function (marker = {}) {
   return marker.name || L10N.getStr("marker.label.unknown");
 },
Example #19
0
 TIME: (value) => L10N.getFormatStr("table.ms2", L10N.numberWithDecimals(value, 2)),
Example #20
0
 PERCENT: (value) => L10N.getFormatStr("table.percentage3",
                                       L10N.numberWithDecimals(value, 2)),
 CycleCollectionFields: function (marker) {
   let label = marker.name.replace(/nsCycleCollector::/g, "");
   return {
     [L10N.getStr("marker.field.type")]: label
   };
 },
Example #22
0
 BYTESIZE: (value) => L10N.getFormatStr("table.bytes", (value || 0))
Example #23
0
/* 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/. */
"use strict";

const {DOM, createClass, PropTypes} = require("devtools/client/shared/vendor/react");
const {L10N} = require("devtools/client/performance/modules/global");
const {ul, div} = DOM;

module.exports = createClass({
  displayName: "Recording List",

  propTypes: {
    items: PropTypes.arrayOf(PropTypes.object).isRequired,
    itemComponent: PropTypes.func.isRequired
  },

  render() {
    const {
      items,
      itemComponent: Item,
    } = this.props;

    return items.length > 0
      ? ul({ className: "recording-list" }, ...items.map(Item))
      : div({ className: "recording-list-empty" }, L10N.getStr("noRecordingsText"));
  }
});
Example #24
0
const TIMELINE_BLUEPRINT = {
  /* Default definition used for markers that occur but are not defined here.
   * Should ultimately be defined, but this gives us room to work on the
   * front end separately from the platform. */
  "UNKNOWN": {
    group: 2,
    colorName: "graphs-grey",
    label: Formatters.UnknownLabel,
  },

  /* Group 0 - Reflow and Rendering pipeline */

  "Styles": {
    group: 0,
    colorName: "graphs-purple",
    label: L10N.getStr("marker.label.styles"),
    fields: Formatters.StylesFields,
  },
  "Reflow": {
    group: 0,
    colorName: "graphs-purple",
    label: L10N.getStr("marker.label.reflow"),
  },
  "Paint": {
    group: 0,
    colorName: "graphs-green",
    label: L10N.getStr("marker.label.paint"),
  },
  "Composite": {
    group: 0,
    colorName: "graphs-green",
 let toMs = ms => L10N.getFormatStrWithNumbers("timeline.tick", ms);
Example #26
0
  buildGraphImage: function() {
    const { markers, duration } = this._data;

    const { canvas, ctx } = this._getNamedCanvas("markers-overview-data");
    const canvasWidth = this._width;
    const canvasHeight = this._height;

    // Group markers into separate paint batches. This is necessary to
    // draw all markers sharing the same style at once.
    for (const marker of markers) {
      // Again skip over markers that we're filtering -- we don't want them
      // to be labeled as "Unknown"
      if (!MarkerBlueprintUtils.shouldDisplayMarker(marker, this._filter)) {
        continue;
      }

      const markerType = this._paintBatches.get(marker.name) ||
                                              this._paintBatches.get("UNKNOWN");
      markerType.batch.push(marker);
    }

    // Calculate each row's height, and the time-based scaling.

    const groupHeight = this.rowHeight * this._pixelRatio;
    const groupPadding = this.groupPadding * this._pixelRatio;
    const headerHeight = this.headerHeight * this._pixelRatio;
    const dataScale = this.dataScaleX = canvasWidth / duration;

    // Draw the header and overview background.

    ctx.fillStyle = this.headerBackgroundColor;
    ctx.fillRect(0, 0, canvasWidth, headerHeight);

    ctx.fillStyle = this.backgroundColor;
    ctx.fillRect(0, headerHeight, canvasWidth, canvasHeight);

    // Draw the alternating odd/even group backgrounds.

    ctx.fillStyle = this.alternatingBackgroundColor;
    ctx.beginPath();

    for (let i = 0; i < this._numberOfGroups; i += 2) {
      const top = headerHeight + i * groupHeight;
      ctx.rect(0, top, canvasWidth, groupHeight);
    }

    ctx.fill();

    // Draw the timeline header ticks.

    const fontSize = OVERVIEW_HEADER_TEXT_FONT_SIZE * this._pixelRatio;
    const fontFamily = OVERVIEW_HEADER_TEXT_FONT_FAMILY;
    const textPaddingLeft = OVERVIEW_HEADER_TEXT_PADDING_LEFT * this._pixelRatio;
    const textPaddingTop = OVERVIEW_HEADER_TEXT_PADDING_TOP * this._pixelRatio;

    const tickInterval = TickUtils.findOptimalTickInterval({
      ticksMultiple: OVERVIEW_HEADER_TICKS_MULTIPLE,
      ticksSpacingMin: OVERVIEW_HEADER_TICKS_SPACING_MIN,
      dataScale: dataScale
    });

    ctx.textBaseline = "middle";
    ctx.font = fontSize + "px " + fontFamily;
    ctx.fillStyle = this.headerTextColor;
    ctx.strokeStyle = this.headerTimelineStrokeColor;
    ctx.beginPath();

    for (let x = 0; x < canvasWidth; x += tickInterval) {
      const lineLeft = x;
      const textLeft = lineLeft + textPaddingLeft;
      const time = Math.round(x / dataScale);
      const label = ProfilerGlobal.L10N.getFormatStr("timeline.tick", time);
      ctx.fillText(label, textLeft, headerHeight / 2 + textPaddingTop);
      ctx.moveTo(lineLeft, 0);
      ctx.lineTo(lineLeft, canvasHeight);
    }

    ctx.stroke();

    // Draw the timeline markers.

    for (const [, { definition, batch }] of this._paintBatches) {
      const group = this._groupMap[definition.group];
      const top = headerHeight + group * groupHeight + groupPadding / 2;
      const height = groupHeight - groupPadding;

      const color = getColor(definition.colorName, this.theme);
      ctx.fillStyle = color;
      ctx.beginPath();

      for (const { start, end } of batch) {
        const left = start * dataScale;
        const width = Math.max((end - start) * dataScale, OVERVIEW_MARKER_WIDTH_MIN);
        ctx.rect(left, top, width, height);
      }

      ctx.fill();

      // Since all the markers in this batch (thus sharing the same style) have
      // been drawn, empty it. The next time new markers will be available,
      // they will be sorted and drawn again.
      batch.length = 0;
    }

    return canvas;
  },
Example #27
0
/**
 * Constructor for the memory graph. Inherits from PerformanceGraph.
 *
 * @param nsIDOMNode parent
 *        The parent node holding the overview.
 */
function MemoryGraph(parent) {
  PerformanceGraph.call(this, parent, ProfilerGlobal.L10N.getStr("graphs.memory"));
}
/**
 * This file contains utilities for creating elements for markers to be displayed,
 * and parsing out the blueprint to generate correct values for markers.
 */
const { Ci } = require("chrome");
const { L10N, PREFS } = require("devtools/client/performance/modules/global");

// String used to fill in platform data when it should be hidden.
const GECKO_SYMBOL = "(Gecko)";

/**
 * Mapping of JS marker causes to a friendlier form. Only
 * markers that are considered "from content" should be labeled here.
 */
const JS_MARKER_MAP = {
  "<script> element":          L10N.getStr("marker.label.javascript.scriptElement"),
  "promise callback":          L10N.getStr("marker.label.javascript.promiseCallback"),
  "promise initializer":       L10N.getStr("marker.label.javascript.promiseInit"),
  "Worker runnable":           L10N.getStr("marker.label.javascript.workerRunnable"),
  "javascript: URI":           L10N.getStr("marker.label.javascript.jsURI"),
  // The difference between these two event handler markers are differences
  // in their WebIDL implementation, so distinguishing them is not necessary.
  "EventHandlerNonNull":       L10N.getStr("marker.label.javascript.eventHandler"),
  "EventListener.handleEvent": L10N.getStr("marker.label.javascript.eventHandler"),
  // These markers do not get L10N'd because they're JS names.
  "setInterval handler":       "setInterval",
  "setTimeout handler":        "setTimeout",
  "FrameRequestCallback":      "requestAnimationFrame",
};

/**
Example #29
0
/**
 * Constructor for the framerate graph. Inherits from PerformanceGraph.
 *
 * @param nsIDOMNode parent
 *        The parent node holding the overview.
 */
function FramerateGraph(parent) {
  PerformanceGraph.call(this, parent, ProfilerGlobal.L10N.getStr("graphs.fps"));
}
 ok(CATEGORIES.every(e => e.label === L10N.getStr("category." + e.abbrev)),