Memory Tooling in Firefox Developer Tools in 2014

March 4th, 2014

A big push for the Firefox Developer Tools team this year is performance tools. Jim Blandy and I are collaborating on the memory half of performance tooling. What follows is a broad overview of our plans.

Definitions

A Recurring Theme

As we build the infrastructure and lay the foundation for the memory panel, we will expose utility and testing functions developers can use now. Generally, the console object will expose these functions.

The benefit of this approach is two-fold. First, it enables developers to cash in on our work quickly. Second, it gives us a larger testing population; helping us catch and fix bugs as soon as possible.

Graph Algorithms on the Heap

Depth First Search and Dominator Trees

If x dominates y, then any path from the global window to y must pass through x. We can use this information in two practical ways:

  1. If you nullify all references to x, every y such that x dominates y will also become unreachable and will eventually be garbage collected.

  2. We can calculate the retained size of x. That is, the amount of memory that will be reclaimed if x (and therefore also every y such that x dominates y) were to be garbage collected.

We can expose this information to developers with console.retainedSize(obj).

Breadth First Search

By doing a BFS in the heap graph from the global window to an object, we find the shortest retaining path for that object. We can use this path to construct a developer-friendly label for that object. Often the label we provide will be a snippet of JavaScript that can be evaluated in the console. For example: "window.MyApp.WidgetView.element". Other times, we will be forced to display labels that cannot be evaluated in the console: "window.[[requestAnimationFrame renderLoop]].[[ closure environment ]].player.sprite".

This can be exposed to developers as a useful little pair of methods on console. If you expect an object to be reclaimed by GC, you will be able to tag it with console.expectGarbageCollected(obj). Next, you would perform whatever actions are supposed to trigger the clean up of that object. Finally, you could call console.logRetained() to log the retaining path of any objects that you tagged via console.expectGarbageCollected that have not been garbage collected. I realize these aren't the greatest method names; please tweet me your suggestions!

Tracking Allocation Sites

We will track the allocation site of every object in the heap. Allocation sites come into play in a few ways.

First, if you interact with one component of your app, and notice that an unrelated component is allocating or retaining objects, you most likely have an opportunity to reduce memory consumption. Perhaps that unrelated component can lazily delay any allocations it needs, thereby lowering your app's memory usage when that component isn't active.

Second, once developers know which objects are using their precious memory, the next info they need is where the objects were allocated. That leads to why they were allocated, and finally how to reduce those allocations. We can hack this workflow and group objects by allocation site then sort them for developers to effectively make the first step (which objects) redundant.

I'm not sure what the best way to expose this information to developers before the complete memory panel is ready. Tracking allocations isn't lightweight; we can't do it all the time, you have to turn the mode on. We could expose console.startTrackingAllocationSites() and console.stopTrackingAllocationSites(), and then allow calls to console.allocationSite(obj) if obj was allocated while we were tracking allocation sites. Or, we could expose console.startLoggingAllocationSites() and console.stopLoggingAllocationSites(), which could just dump every allocation site to the console as it occurs. Tweet at me if you have an opinion about the best API from which to expose this data.

Putting it all together

The memory panel will feature a live-updating graph. To construct this graph we will frequently poll the recent categorized allocations, and the total, non-granular heap size. This gives us a fuzzy, slightly inaccurate picture of the heap over time, but it should be efficient enough for us to do at a high frequency. At a less frequent interval, we will take a census. This will be a reality check of sorts that gives us precise numbers for each category of objects in the heap.

You will be able to click on the graph to get a shallow view into the heap at that past moment in time. Alternatively, you will be able to select a region of the graph to view the difference in memory consumption between the start and end points of your selection.

If you need to deep dive into the full heap state, you'll be able to take snapshots, which are too heavy for us to automatically collect on an interval. These can be compared with other snapshots down to each individual object, so you will be able to see exactly what has been allocated and reclaimed in the time between when each snapshot was taken. They will also be exportable and importable as core dumps, so you could attach them to bug tickets, send to other developers, etc.

Darrin Henein has created a beautiful mockup of the memory panel. Caveat: despite said beauty, the mockup is still very much a work in progress, it is far from complete, and what we ship might look very different!

You can follow along with our work by watching the bugs in this bugzilla dependency graph.

2014 will be an exciting year for memory tooling in Firefox Developer Tools!

Hiding Implementation Details with ECMAScript 6 WeakMaps

January 13th, 2014

WeakMaps are a new feature in ECMAScript 6 that, among many other things, gives us a new technique to hide private implementation data and methods from consumers of the public API we choose to expose.

Overview

Here is what the basics look like:

const privates = new WeakMap();

function Public() {
  const me = {
    // Private data goes here
  };
  privates.set(this, me);
}

Public.prototype.method = function () {
  const me = privates.get(this);
  // Do stuff with private data in `me`...
};

module.exports = Public;

Two things to take note of:

  1. Private data and methods belong inside the object stored in the privates WeakMap.

  2. Everything exposed on the instance and prototype is public; everything else is inaccessible from the outside world because privates isn't exported from the module.

In the Firefox Developer Tools, Anton Kovalyov used this pattern in our editor module. We use CodeMirror as the underlying implementation for our editor, but do not want to expose it directly to consumers of the editor API. Not exposing CodeMirror allows us to upgrade it when there are backwards incompatible releases or even replace CodeMirror with a different editor without the fear of breaking third party addons that have come to depend on older CodeMirror versions.

const editors = new WeakMap();

// ...

Editor.prototype = {

  // ...

  /**
   * Mark a range of text inside the two {line, ch} bounds. Since the range may
   * be modified, for example, when typing text, this method returns a function
   * that can be used to remove the mark.
   */
  markText: function(from, to, className = "marked-text") {
    let cm = editors.get(this);
    let text = cm.getRange(from, to);
    let span = cm.getWrapperElement().ownerDocument.createElement("span");
    span.className = className;
    span.textContent = text;

    let mark = cm.markText(from, to, { replacedWith: span });
    return {
      anchor: span,
      clear: () => mark.clear()
    };
  },

  // ...

};

module.exports = Editor;

In the editor module, editors is the WeakMap mapping public Editor instances to private CodeMirror instances.

Why WeakMaps?

WeakMaps are used instead of normal Maps or the combination of instance IDs and a plain object so that we neither hold onto references and leak memory nor need to introduce manual object lifetime management. For more information, see the "Why WeakMaps?" section of the MDN documentation for WeakMaps.

Compared to Other Approaches

Prefixing Private Members with an Underscore

This habit comes from the world of Python, but is pretty well spread through JS land.

function Public() {
  this._private = "foo";
}

Public.prototype.method = function () {
  // Do stuff with `this._private`...
};

It works just fine when you can trust that the consumers of your API will respect your wishes and ignore the "private" methods that are prefixed by an underscore. For example, this works peachy when the only people consuming your API are also on your team, hacking on a different part of the same app.

It completely breaks down when third parties are consuming your API and you want to move quickly and refactor without fear.

Closing Over Private Data in the Constructor

Alternatively, you can close over private data in your constructor or just define functions which return objects with function members that close over private variables.

function Public() {
  const closedOverPrivate = "foo";
  this.method = function () {
    // Do stuff with `closedOverPrivate`...
  };
}

// Or

function makePublic() {
  const closedOverPrivate = "foo";
  return {
    method: function () {
      // Do stuff with `closedOverPrivate`...
    }
  };
}

This works perfectly as far as information hiding goes: the private data is inaccessible to API consumers.

However, you are creating new copies of every method for each instance that you create. This can balloon your memory footprint if you are instantiating many instances, which can lead to noticeable GC pauses or even your app's process getting killed on mobile platforms.

ES6 Symbols

Another language feature coming in ECMAScript 6 is the Symbol primitive type and it is designed for the kind of information hiding we have been discussing.

const privateFoo = Symbol("foo");

function Public() {
  this[privateFoo] = "bar";
}

Public.prototype.method = function () {
  // Do stuff with `this[privateFoo]`...
};

module.exports = Public;

Unfortunately, Symbols are only implemented in V8 (behind the --harmony or --harmony_symbols flags) at the time of writing, but this is temporary.

More problematic is the fact that you can enumerate the Symbols in an object with the Object.getOwnPropertySymbols and Object.getOwnPropertyKeys functions. Because you can enumerate the Symbols in an object, a determined third party could still access your private implementation.

Conclusion

The WeakMap privates pattern is the best choice when you really need to hide private implementation details from public API consumers.

References

Re-evaluate Individual Functions in Firefox Developer Tools' Scratchpad

November 22nd, 2013

My friend and co-worker Panos Astithas was in town the other day, and so we decided to hack together on some features that we wouldn't normally be working on in our regular day-to-day. Among them was a new evaluation feature for the Firefox Developer Tools' Scratchpad that allows you to evaluate individual top level functions at a time. The command is bound to the Cmd + E keyboard shortcut and was inspired by SLIME's slime-compile-defun (C-c C-c) command.

Being able to re-evaluate individual functions at a time enables faster prototyping. The previous workflow whenever you wanted to edit functions was to refresh the app, repeat interactions that put the app back into the previous state, and then finally observe how your changes affected the app. In many cases, you can avoid this lengthy process by editing your code in the Scratchpad and using Cmd + E to re-evaluate individual functions. Re-evaluating individual functions doesn't refresh the app, and lets it maintain its existing state. No refreshing or repeated interactions required.

When you re-evaluate a function with Cmd + E, we flash the function's source text briefly to provide some visual feedback.

This feature is available in Firefox 28, which is currently in the Nightly release channel.

Testing Source Maps

October 2nd, 2013

"How do I test the source maps my compiler/transpiler/build tool creates?"

I have seen this question come up a few times, and unfortunately, people usually don't find an answer, give up, and don't test their source maps. However, testing source maps isn't too hard if you are using Node.js and the mozilla/source-map library. If you aren't using Node.js and the mozilla/source-map library, you can still use the following techniques, but you will need to implement a source map consumer.

Your tool most likely has a function that generates both the JavaScript that will be executed by the browser's JavaScript engine/Node/whatever, and a source map. Something like this:

var result = compile(input, { url: inputUrl, sourceMap: true });
result.code;      // The JavaScript code
result.sourceMap; // The source map

It is easiest to base your tests around this function.

To test generated source maps:

  1. Create some test input to compile.

  2. Select some different bits of code in the test input and record their { url, line, column } locations. Generally, a "bit of code" should be a specific token, function invocation, etc.

  3. Compile and generate a source map for the test input.

  4. Create a SourceMapConsumer for the generated source map.

    var smc = new SourceMapConsumer(result.sourceMap);
    
  5. Inspect the compiled code and find the generated code that corresponds to the bits of code you selected in step (2). For each of the { line, column } locations in the compiled code, add an assertion to your test that smc.originalPositionFor(location) is equal to the expected location in your original source.

There is another approach, that is easier for compilers targeting JavaScript than for build tools, which can be used when you don't want to inspect the generated JavaScript at all.

  1. You can add a location debugging statement to your language that compiles to JavaScript code similar to:

    // SpiderMonkey (Firefox)
    (function (e) { console.log(e.lineNumber, e.columnNumber); }(new Error()));
    
    
    // In other engines, you need to parse the first frame from the error's
    // stack property yourself
    (function (e) { console.log(parseLocation(e.stack)); }(new Error()));
    
  2. Create test input which has one or more of these location debugging statements.

  3. Compile the test input and create a SourceMapConsumer for your generated source map.

  4. Run the compiled input and read its output.

  5. Assert that querying the SourceMapConsumer for the original location(s) of that output points to the expected location(s) in your original test input source.

For reference, here are some existing source map test suites from various projects (hopefully this post helps them expand their tests as well):

Destructuring Assignment in ECMAScript 6

August 15th, 2013

What is destructuring assignment?

Destructuring assignment allows you to assign the properties of an array or object to variables using syntax that looks similar to array or object literals. This syntax can be extremely terse, while still exhibiting more clarity than the traditional property access.

Without destructuring assignment, you might access the first three items in an array like this:

let first = someArray[0];
let second = someArray[1];
let third = someArray[2];

With destructuring assignment, the equivalent code becomes more concise and read-able:

let [first, second, third] = someArray;

TC39 (the governing committee of ECMAScript) has already reached consensus on destructuring assignment and it is part of the draft ES6 specification. Effectively what this means is that it is now up to the people writing JavaScript engines to start implementing it; SpiderMonkey (Firefox's JS engine) already has support for much of it. Track SpiderMonkey's destructuring (and general ES6) support in this bugzilla ticket.

Destructuring Arrays

We already saw one example of destructuring assignment on an array above. The general form of the syntax is:

[ variable1, variable2, ..., variableN ] = array;

This will just assign variable1 through variableN to the corresponding item in the array. If you want to declare your variables at the same time, you can add a var, let, or const in front of the assignment:

var [ variable1, variable2, ..., variableN ] = array;
let [ variable1, variable2, ..., variableN ] = array;
const [ variable1, variable2, ..., variableN ] = array;

Although variable is a misnomer since you can nest patterns as deep as you would like:

var [foo, [[bar], baz]] = [1, [[2], 3]];
console.log(foo);
// 1
console.log(bar);
// 2
console.log(baz);
// 3

Furthermore, you can skip over items in the array being destructured:

var [,,third] = ["foo", "bar", "baz"];
console.log(third);
// "baz"

And you can capture all trailing items in an array with a "rest" pattern:

var [head, ...tail] = [1, 2, 3, 4];
console.log(tail);
// [2, 3, 4]

When you access items in the array that are out of bounds or don't exist, you get the same result you would by indexing: undefined.

console.log([][0]);
// undefined

var [missing] = [];
console.log(missing);
// undefined

Destructuring Objects

Destructuring on objects lets you bind variables to different properties of an object. You specify the property being bound, followed by the variable you are binding its value to.

var robotA = { name: "Bender" };
var robotB = { name: "Flexo" };

var { name: nameA } = robotA;
var { name: nameB } = robotB;

console.log(nameA);
// "Bender"
console.log(nameB);
// "Flexo"

There is a helpful syntactical shortcut for when the property and variable names are the same:

var { foo, bar } = { foo: "lorem", bar: "ipsum" };
console.log(foo);
// "lorem"
console.log(bar);
// "ipsum"

And just like destructuring on arrays, you can nest and combine further destructuring:

var complicatedObj = {
  arrayProp: [
    "Zapp",
    { second: "Brannigan" }
  ]
};

var { arrayProp: [first, { second }] } = complicatedObj;

console.log(first);
// "Zapp"
console.log(second);
// "Brannigan"

When you destructure on properties that are not defined, you get undefined:

var { missing } = {};
console.log(missing);
// undefined

One potential gotcha you should be aware of is when you are using destructuring on an object to assign variables, but not to declare them (when there is no let, const, or var):

{ blowUp } = { blowUp: 10 };
// Syntax error

This happens because the engine attempts to parse the expression as a block statement (for example, { console } is a valid block statement). The solution is to either wrap the pattern or the whole expression in parenthesis:

({ safe }) = {};
({ andSound } = {});
// No errors

Destructuring Values that are not an Object or Array

When you try to use destructuring on null or undefined, you get a type error:

var [blowUp] = null;
// TypeError: null has no properties

However, you can destructure on other primitive types such as booleans, numbers, and strings, and get undefined:

var [wtf] = NaN;
console.log(wtf);
// undefined

This may come unexpected, but upon further examination it turns out to be simple. When a value is being destructured, it is first converted to an object using the abstract operation ToObject. Most types can be converted to an object, but null and undefined may not be.

Default Values

You can also provide default values for when the property you are destructuring is not defined:

var [missing = true] = [];
console.log(missing);
// true

var { x = 3 } = {};
console.log(x);
// 3

Practical Applications of Destructuring

Function Parameter Definitions

As developers, we can often expose nicer APIs by accepting a single object with multiple properties as a parameter instead of forcing our API consumers to remember the order of many individual parameters. We can use destructuring to avoid repeating this single parameter object whenever we want to reference one of its properties:

function removeBreakpoint({ url, line, column }) {
  // ...
}

This is a real world snippet of code from Firefox's debugger. We use this all over the place, it is crazy nice.

Configuration Object Parameters

Expanding on the previous example, we can also give default values to the properties of the objects we are destructuring. This is particularly helpful when we have an object that is meant to provide configuration and many of the object's properties already have sensible defaults. For example, jQuery's ajax function takes a configuration object as its second parameter, and could be re-written like this:

jQuery.ajax = function (url, {
  async = true,
  beforeSend = function () {},
  cache = true,
  complete = function () {},
  crossDomain = false,
  global = true,
  // ... more config
}) {
  // ... do stuff
};

This avoids repeating var foo = config.foo || 'default foo'; for each property of the configuration object.

With the ES6 Iteration Protocol

ECMAScript 6 also defines an iteration protocol somewhat similar to the one in Python. When you iterate over Maps (an ES6 addition to the standard library), you get a series of [key, value] pairs. We can destructure this pair to get easy access to both the key and the value:

var map = new Map();
map.set(window, "the global");
map.set(document, "the document");

for (let [key, value] of map) {
  console.log(key + " is " + value);
}
// "[object Window] is the global"
// "[object HTMLDocument] is the document"

Iterate over only the keys:

for (let [key] of map) {
  // ...
}

Or iterate over only the values:

for (let [,value] of map) {
  // ...
}

Multiple Return Values

I've written about this once before. Returning multiple values from a function is a lot more practical when you can destructure the result:

function returnMultipleValues() {
  return [1, 2];
}
var [foo, bar] = returnMultipleValues();

Alternatively, you can use an object as the container and name the returned values:

function returnMultipleValues() {
  return {
    foo: 1,
    bar: 2
  };
}
var { foo, bar } = returnMultipleValues();

These both end up much better than holding onto the temporary container:

function returnMultipleValues() {
  return {
    foo: 1,
    bar: 2
  };
}
var temp = returnMultipleValues();
var foo = temp.foo;
var bar = temp.bar;

Importing a Subset of a Module

When importing some module X into your own module Y, it is fairly common that module X exports more functions than you are using. With destructuring, you can be explicit about which parts of module X are being used in module Y:

const { SourceMapConsumer, SourceNode } = require("source-map");

« Older Entries

Newer Entries »


Recent Entries

Naming `eval` Scripts with the `//# sourceURL` Directive on December 5th, 2014

wu.js 2.0 on August 7th, 2014

Come work with me on Firefox Developer Tools on July 8th, 2014

Debugging Web Performance with Firefox DevTools - Velocity 2014 on June 26th, 2014

Beyond Source Maps on March 12th, 2014

Memory Tooling in Firefox Developer Tools in 2014 on March 4th, 2014

Hiding Implementation Details with ECMAScript 6 WeakMaps on January 13th, 2014

Re-evaluate Individual Functions in Firefox Developer Tools' Scratchpad on November 22nd, 2013

Testing Source Maps on October 2nd, 2013

Destructuring Assignment in ECMAScript 6 on August 15th, 2013

Creative Commons License

Fork me on GitHub