Come work with me on Firefox Developer Tools

July 8th, 2014

My team at Mozilla, the half of the larger devtools team that works on JavaScript and performance tools, is looking to hire another software engineer.

We have members of the devtools team in our San Francisco, London, Vancouver, Paris, Toronto, and Portland offices, but many also work remotely.

We are responsible for writing the full stack of the tools we create, from the C++ platform APIs exposing SpiderMonkey and Gecko internals, to the JavaScript/CSS/HTML based frontend that you see when you open the Firefox Developer Tools.

Some of the things we're working on:

One of the most important things for me is that every line of code we write at Mozilla is Free and Open Source from day one, and we're dedicated to keeping the web open.

Apply here!

Debugging Web Performance with Firefox DevTools - Velocity 2014

June 26th, 2014

On Tuesday, June 3rd, 2014, I gave a presentation on debugging web performance with Firefox DevTools to the Velocity Conf 2014, Santa Clara. I'm not sure how useful the slides are without me talking, but here they are:

Debugging Web Performance with Firefox DevTools - Velocity 2014

Beyond Source Maps

March 12th, 2014

There's been some recent talk on es-discuss about standardizing source maps in ECMAScript 7. Before that happens, we should take a moment to reflect on what source maps have done well, where they are lacking, and meditate on what a more perfect debug format for compilers targeting JavaScript might be. My response quickly outgrew an email reply, and so I am collecting my thoughts and posting them here.

Before diving in, it makes sense to tell you where I am coming from. I implemented the mozilla/source-map library and the source map support in the Firefox Developer Tools' debugger. I'm a coauthor of the document specifying source maps; although the actual design of the format was done by John Lenz. I mostly documented and polished the parts that weren't about the format itself: like how to find a script's source map. On top of that, I've debugged scripts with source maps compiled from browserify, the r.js AMD module optimizer, CoffeeScript, ClojureScript, Emscripten, UglifyJS, Closure Compiler, and more.

When do source maps work well?

Source maps work pretty well for:

When do source maps fall short?

Whenever you start doing "real compilation" instead of "transpiling" and your source language's semantics don't match JavaScript's semantics, source maps break down:

A stepping debugger isn't useful without the ability to inspect bindings and values in the environment, and the above issues create humungous hurdles for doing that!

Moreover, using the console as a REPL, adding a watch expression, or setting a new conditional breakpoint must be done with JavaScript, not the source language.

SourceMap.next?

Requirements

An ideal SourceMap.next format should support the existing use of source maps:

Additionally, any SourceMap.next format should support debugging JavaScript object code that the existing source map format fails to satisfy:

A Kernel of a Solution

The ideas I present here aren't fully baked yet, but I have confidence in the approach.

Because a SourceMap.next must do more than translate file, line, and column locations, it no longer makes sense to build the format on location mappings. Instead it should annotate the abstract syntax tree of the JavaScript object code with debugging information. Instead of adding debugging information to a line and column location in the JavaScript object code which in turn can be approximately translated into a specific JavaScript statement or expression, annotating the AST adds debugging information directly on a specific JavaScript statement or expression. It cuts out the middle man while simultaneously simplifying the architecture. Many existing tools are already using this approach: they create a JavaScript AST, annotate it with source language locations, and use escodegen to generate both the JavaScript object code and a source map!

Annotating the AST enables us to take advantage of the hierarchy present in the tree. If you are searching for a specific annotation and the current AST node does not have it, recurse on the node's parent. This makes it easy to provide very detailed information or a less fine-grained summary. For example, in the following AST, rather than requiring source location annotations for each AssignmentExpression, Identifier, BinaryExpression, and Literal AST nodes, one could simply add the annotation to the AssignmentExpression node. Then, any queries for source location information on any of the other nodes would bubble up to the AssignmentExpression and yield its annotated source location information.

AssignmentExpression
  operator: "="
  left: Identifier
    name: "answer"
  right: BinaryExpression
    operator: "*"
    left: Literal
      value: 6
    right: literal
      value: 7

Here are a few debugging annotations that should cover the requirements listed above:

"Solving the SourceMap.next problem once and for all" reduces to defining a compact binary encoding of JavaScript's AST that supports an arbitrary number of arbitrary debugging annotations on each node. The beauty of this approach is that as long as we take care that this format is future-extensible with new types of debugging annotations, we can resolve any future issues without needing to update the binary format. We just add new debugging annotations and keep the old annotations for legacy consumers until they die off. The only time the binary format changes is when a new version of the ECMAScript standard is released and defines new syntactic forms that the AST must incorporate.

The final affordance of having an extensible debugging format is that compilers which target JavaScript can progressively add more debugging information over time. Since specific annotations are optional, the compiler can simply skip those for which it can't provide information. When a later version of the compiler can provide that information, it can start adding those annotations.

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

« Older Entries


Recent Entries

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

My Talk from Front Trends 2013 on June 21st, 2013

"Compiling to JavaScript, and Debugging with Source Maps" on Mozilla Hacks on May 22nd, 2013

Creative Commons License

Fork me on GitHub