DIY javascript stack trace

Javascript functions have a handy local variable called arguments that you can use to get information about the currently executing function. This can be used to build your own stack trace in any browser for situations where you can’t use a debugger to get information about your code.

That’s madness!

Yes it is. In most cases there are better tools like Firebug that have a full javascript debugger that you can use to get a stack trace whenever you want without having to do any tricksy javascript coding:
firebug's stack trace

I originally wrote this code because I had to debug a page that was running in the WinForms web browser component. The page worked fine when running in regular IE but totally locked up when it was running in the browser component. I was on my own debugging a huge tangle of javascript event code without any debugger to help me.

Other places this might help is if you want to log stack traces on mouse events where breaking into the debugger will stop the execution, when you’re automating browser testing or if you want to be able to silently log information a nasty to test error condition running on someone else’s browser.

The code

Here’s a stack trace implementation that adds a new debugging method of trace() to all functions. It uses the function.caller property to walk the call stack and the arguments collection to create a method signature for each function in the call stack.

It’s been tested in Firefox 3, IE7, Safari 3 and Opera 9.6.

  1. Function.prototype.trace = function()
  2. {
  3.     var trace = [];
  4.     var current = this;
  5.     while(current)
  6.     {
  7.         trace.push(current.signature());
  8.         current = current.caller;
  9.     }
  10.     return trace;
  11. }
  12. Function.prototype.signature = function()
  13. {
  14.     var signature = {
  15.         name: this.getName(),
  16.         params: [],
  17.         toString: function()
  18.         {
  19.             var params = this.params.length > 0 ?
  20.                 "'" + this.params.join("', '") + "'" : "";
  21.             return this.name + "(" + params + ")"
  22.         }
  23.     };
  24.     if(this.arguments)
  25.     {
  26.         for(var x=0; x<this .arguments.length; x++)
  27.             signature.params.push(this.arguments[x]);
  28.     }
  29.     return signature;
  30. }
  31. Function.prototype.getName = function()
  32. {
  33.     if(this.name)
  34.         return this.name;
  35.     var definition = this.toString().split("\n")[0];
  36.     var exp = /^function ([^\s(]+).+/|>;
  37.     if(exp.test(definition))
  38.         return definition.split("\n")[0].replace(exp, "$1") || "anonymous";
  39.     return "anonymous";
  40. }

Javascript file

To use the code, call the trace() method on any function. You will only get a meaningful stack trace on a function that is currently running so in most cases you’d want to call trace() on the arguments.callee method of the current function:

  1. var trace = arguments.callee.trace();
  2. document.getElementById("output").innerHTML = trace.join("<br />\n");

Complete example

How it works

The arguments collection is a local variable that is automatically created inside all javascript functions that contains a list of all parameters that are passed to the function. The arguments variable has a property called callee which is reference to the function itself. This is possible because functions are just a special type of object in javascript so they can be stored in a regular variable just like a number or a string can.

The arguments.callee property gives a reference to the currently running function. The function variable has a property called caller that gives a reference to the function that called the current function. This can be used to walk all the way up the function stack trace. We’ve reached the top of the stack when the caller property of a function returns null:

  1. var current = this;
  2. while(current)
  3. {
  4.     trace.push(current.signature());
  5.     current = current.caller;
  6. }

In most browsers, the function has a property called name that returns the name given to the function when it was created. IE doesn’t support this but calling the function’s toString() method will return the whole function definition and the name can be extracted from the definition. Anonymous functions don’t have to be given a name, so these functions will just return “anonymous” instead of a name:

  1. Function.prototype.getName = function()
  2. {
  3.     if(this.name)
  4.         return this.name;
  5.     var definition = this.toString().split("\n")[0];
  6.     var exp = /^function ([^\s(]+).+/|>;
  7.     if(exp.test(definition))
  8.         return definition.split("\n")[0].replace(exp, "$1") || "anonymous";
  9.     return "anonymous";
  10. }

Limitations

It won’t work with recursive code. The caller property gives a reference to the function not to the function’s activation object. The activation object is like an instance of the function that sits on the call stack. I’d be interested in hearing if anyone finds a way to work around this problem.

More information

For ASP.NET users, Joel Rumerman has created an version of the trace code and has included a neat trick for getting useful function names from functions created using prototypes.

Posted on 12 May 07 by Helen Emerson (last updated on 29 Aug 11).
Filed under Javascript