Deminifying JavaScript call stacks

Christian Gonzalez
Web Dev @ Microsoft
2 min readMar 30, 2017

When deploying a new version of Office Online, we look at several pieces of telemetry to help determine the overall health of an individual application. One of these signals is the rate at which our users are experiencing unhanded JavaScript exceptions. We attempt to group these exceptions based on the value of the stack property on the error object to determine how often a particular exception is occurring.

Since our JavaScript is aggressively minified with the closure compiler, it can be a bit difficult to track down the code that caused the exception since the method names have all been changed in an effort to reduce the number of bytes sent over the wire.

As an example, consider this JavaScript snippet which will cause a crash when the user clicks a button on the page.

After running it through a minifier, the output becomes something like the snippet below. This is pretty printed for clarity. In production you’d likely see all the line breaks and spaces stripped out.

I created a jsfiddle hosting this script and some basic HTML to allow the user to force a crash on the page by calling the ‘causeCrash’ function. When the user clicks the “crash button”, the stack property on the error object passed to the window.onerror handler will be something like this:

TypeError: Cannot read property 'length' of undefined
at https://fiddle.jshell.net/_display/:62:26
at t (https://fiddle.jshell.net/_display/:63:10)
at e (https://fiddle.jshell.net/_display/:56:17)
at n (https://fiddle.jshell.net/_display/:52:17)
at causeCrash (https://fiddle.jshell.net/_display/:67:8)
at HTMLButtonElement.<anonymous> (https://fiddle.jshell.net/_display/:70:9)

It’s not very easy to determine what happened from this stack trace alone. In an effort to improve our app’s reliability, we developed an open source .NET library that parses the stack string and determines the original function names for the code that appears in the stack trace. This tool works with any code generated by a JavaScript minifier that produces source maps, including Closure Compiler, UglifyJS2 and AjaxMin.

For each function call that appears in the minfied stack trace, the library looks up the relevant mapping entry in the source map that corresponds to the location information for that function call. In the previous example, the call to function i in the minified stack frame has a corresponding deminified stack frame with the following information:

FilePath: "crashcauser.js"
MethodName: "level1"
SourcePosition.ZeroBasedColumnNumber: 8
SourcePosition.ZeroBasedLineNumber: 5

We bucket our exceptions by these deminified stack frame entries. This means that the same exception occurring on multiple browsers would be be in the same bucket. Bucketing by deminified stack frames also allows for us to track exceptions across multiple deployments, even though the minified method names might have changed between the two different versions of the application. This analysis has provided us with the necessary information to assign bugs to the right developers for our top unexpected JavaScript exceptions in Office Online.

--

--

Christian Gonzalez
Web Dev @ Microsoft

Software Engineer working on Fluid Framework at Microsoft