Wednesday, March 15th, 2006 | Author: Kalle

[updated March 20th, to improve the code a bit]
In most lower-level programming languages, debugging functionality exists to allow the programmer to more easily track down bugs that appear in code. One of these functionalities is called “a stack trace” and allows a programmer to pull up the list of functions which were called which lead to some crash or exception in the code. An example of this in Java might look like:

Exception in thread "Thread-2" org.w3c.dom.DOMException: INVALID_CHARACTER_ERR: An invalid or illegal XML character is specified.
at com.sun.org.apache.xerces.internal.dom.CoreDocumentImpl.createAttribute(CoreDocumentImpl.java:572)
at com.sun.org.apache.xerces.internal.dom.ElementImpl.setAttribute(ElementImpl.java:523)
at sserve.DomSynchronizedProtocol.prot_ELA(DomSynchronizedProtocol.java:297)
at sserve.DomSynchronizedProtocol.examine(DomSynchronizedProtocol.java:438)
at sserve.Waiter.handleData(Waiter.java:323)
at sserve.ServerPool.processSocket(ServerPool.java:406)
at sserve.CommService.run(CommService.java:43)
at java.lang.Thread.run(Thread.java:595)

The above may look like soup, but is immensely useful to someone who knows their way around the various classes mentioned in the code. I can for instance tell you that the processSocket function called the handleData function at line 406 in the ServerPool.java file. The handleData function called the examine function, called the prot_ELA function, which in turn called some functions inherent in the Java API used in the code where the exception happened. So I can look at these functions in turn, and hopefully figure out why the exception mentioned happened.

This functionality, the ability to refer to a series of function-calls leading up to erroneous behavior, does not exist in JavaScript, for various reasons. One reason is that JavaScript has not until very recently been considered a language in which such advanced functionality is needed.

I have written a JS class which allows a user to set a “trace_stack” boolean in the code, which semi-automatically injects code into JavaScript functions, enabling a stack trace functionality. This is not useful unless you have several .js files with several classes which interact with each other to some extent.

Personally, whenever I see some (cool) package which enhances my JavaScripts somehow, I am always concerned first and foremost about performance. No matter how immensely helpful a script is to me; if it slows my code down, I don’t want it. I choose effort and efficiency over laziness and inefficiency any day of the week (see now why I do not consider Basic a programming language, as it is built on principles opposite of mine). That said, I have ran some fairly extensive testing on this implementation of mine, and as you might have guessed, the performance (with the trace_stack flag disabled) loss is 0%. That is, your code will run at 100% of its regular speed unless you toggle on the trace_stack flag on, which you will only ever do if you run into bugs anyway. (Which is what it’s there for.)

The script is contained in a single class, called stacktrace, in a single file called stacktrace.js. Admittedly, you will need to do some modifications to your existing code to make it compatible with this class, but the changes are fairly minor. Another matter is that the word “that” has become a reserved keyword in this implementation for reasons that will be obvious if you read on.

Take a look at the class itself here [stacktrace.js].

Presuming you know a bit about how classes are created in JavaScript, let’s see about putting this to use in an example script. How about this [testClass.js]?

A few things of note, before we actually test that script.

  1. that versus this. that is an internal keyword used by the stacktrace to generate code appropriate to the situation, where the two possible situations are a) you want stack trace data, and b) you do not want stack trace data. In the latter case, the stacktrace _reg() function will remove all stacktrace functionality from the base script. In the former case, _reg() will in fact add to the script a bit, by wrapping the function in another function. Thus you can see why there is a performance loss when tracing the stack, so you shouldn’t do this unless you are hunting bugs or unless your code is unstable. Thus, in any function registered with the stacktrace, you need to use that instead of this, in all cases.
  2. Every class constructor (var class = function() { [constructor code] }) must include a line identifying itself to the stack. E.g. var KalleClass = function() { stack.id(this, “KalleClass”); } is enough. Failing to do so will cause your code to crumple and croak whenever you attempt to enable the trace stack, with a bunch of “this._sti has no properties”
  3. The property “_sti” in a class using the stacktrace is reserved. For the record, it stands for “stack trace instance”.
  4. You can choose to exclude functions from the stacktrace registry. It is generally a good idea to exclude functions which do not call other (self-written) functions, as these functions may still present a stack trace and refer the user to “themself” as the final location where an error occured, and it is generally a good idea to include functions which do call other (self-written) functions. Regardless which, there should be no difference in performance when the trace_stack flag is disabled.
  5. By default, stack tracing is enabled via the URL of the web page. For example, if your code is triggered via http://example.com/foo.html, then you would enable stack tracing by going to http://example.com/foo.html?tstack=1.

So, finally, to see this in action, go here [test.html]. View source to see what the HTML looks like if you wish, but you should have a fairly good guess already.

With the trace_flag enabled, the output should look something like this:

--- debug pane ---
debugger initialized...
constructing testClass
stacktrace now knows who we are
created test, instance of testClass
firstFunction called with This is a test!; now gonna call secondFunction.
secondFunction; this is what the stack trace output looks like:
trace:	testClass: firstFunction
trace:		testClass: secondFunction

now I'm calling thirdFunction
thirdFunction; this is the stack trace:
trace:	testClass: firstFunction
trace:		testClass: secondFunction
trace:			testClass: thirdFunction

left thirdFunction; trace =
trace:	testClass: firstFunction
trace:		testClass: secondFunction

left secondFunction; trace =
trace:	testClass: firstFunction

called test.firstFunction('This is a test!')

With the trace_flag disabled, it should look something like this:

--- debug pane ---
debugger initialized...
constructing testClass
stacktrace now knows who we are
created test, instance of testClass
firstFunction called with This is a test!; now gonna call secondFunction.
secondFunction; this is what the stack trace output looks like:
[stack trace not available]
now I'm calling thirdFunction
thirdFunction; this is the stack trace:
[stack trace not available]
left thirdFunction; trace =
[stack trace not available]
left secondFunction; trace =
[stack trace not available]
called test.firstFunction('This is a test!')

To further explain what happens behind the scenes, the source code for the function test.firstFunction will look like this when the trace_stack flag is turned off,

function (somearg) {
dbgprint("firstFunction called with " + somearg + "; now gonna call secondFunction.");
this.secondFunction();
dbgprint("left secondFunction; trace = n" + stack.trace());
}

… and it will look like this, when trace_stack is turned on,

function (a1, a2, a3, a4, a5, a6, a7) {
this._sti.push(this, name);
return this._sti.pop(func(this, a1, a2, a3, a4, a5, a6, a7));
}

As you see, there are some limitations. There can only be 7 arguments to a function registered in the stacktrace, for instance (but this is a simple matter of my own limiting). Future revisions may examine the source for the function and limit the arguments accordingly. The values of this, name, and func are all internal here. func in this case has been modified to include a new initial argument, that, which is handed to it via the this reference in this._sti.pop(func(this, …)).

The only guaranteed performance loss, when the trace_stack flag is disabled, is a slightly longer load time, as the client has to reconstruct all the functions via the stacktrace class. But this is majorily minor and nothing I consider important — I care about runtime performance way more than load time.
Feel free to use this class wherever you want. It’s released under the MPL 1.1/LGPL 2.1.

Category: Code, Software
You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

3 Responses

  1. *smiles* What a marvelously technical post (and I will add that your code is beautifully commented). Seems like it would be a rather helpful function for people working on creating apps.

  2. I wish there was something to back trace line numbers!! that would be totally awesome!!, but well, its sucks that javascript can’t do that…

    php its really nice about this tho.. debug_backtrace(); will return all the stuff in one array..

  3. The whole way javascript handles errors is retarded, in my opinion, but it’s understandable. The language has grown from being a “way to hack in minor tweaks into a web site” into things like AJAX, etc., so it’s sort of outgrown itself a bit.

    Sorry about the belated approval of your comment, by the way, I was busy moving to another continent. :)

Leave a Reply