figure 2: generating stacks.
function myStack()
{
var caller, depth;
var stack = new Array();
for (caller = arguments.callee, depth = 0;
caller && depth < 12;
caller = caller.caller, depth++) {
var args = new Array();
for (var i = 0; i < caller.arguments.length; i++)
args.push( caller.arguments[i]);
stack.push({
caller: caller,
args: args
});
}
this.stack = stack;
}
figure 3: Wrapping asynchronous requests.
(a)
function dosomething(a, b)
{
service.dosomething(a + b, function (ret, err) {
if (err)
throw (err);
process(ret);
});
}
(b)
function dispatch(func, args, callback)
{
var stack = new myStack();
dodispatch(func, args, function (ret, err) {
try {
callback(ret, err);
} catch (e) {
e.linkedStack = stack;
myHandleException(e);
}
});
}
expensive to assemble, we can achieve
this by recording the stack trace before
dispatching every asynchronous call
and then chaining it to any caught exception. The global exception handler
will print all members of the exception,
displaying both stack traces in the process. Our core dispatch routine would
look something like Figure 3b. This
allows transparent handling of server-side failures using the same exception
handler. If an asynchronous closure
generates an unanticipated exception,
we can include the context in which the
original XMLHTTPRequest was made.
By carefully following these design
principles, we can construct an environment that dramatically improves
our ability to debug issues by enabling
users to provide developers with richer
information that will allow for further
analysis. Unfortunately, this environment is required to overcome the inad-equacies of current JavaScript runtime
environments. Without a single point
to handle all uncaught exceptions, we
are forced to wrap all callbacks in a try/
catch block; and without reliable stack
traces, we are forced to generate our
own debugging infrastructure. It seems
clear that a browser that implements
these two features would soon become
the preferred development environment for AJAX applications. Until that
happens, careful design of the AJAX environment can still yield dramatic improvements in debuggability and serviceability for users of an application.
lar object. To get a function’s name, we
simply need to search the members of
the window object, all of their children,
and all children of their prototype objects. If we find a match, then we can
construct the name using this lineage.
With the function name and the arguments, we can display a reasonable
facsimile of a stack trace, even on browsers without native support for stack
traces. One caveat, however, is that getting function names doesn’t work with
Internet Explorer 7. For reasons that are
not well understood, global functions
are not included when iterating over
members of the window object.
Careful construction of the global
exception handler allows us to handle
both native browser and dynamically
generated exceptions. Although having stack traces attached to our cus-
tom exceptions is useful, the true power of this mechanism is evident when
dealing with asynchronous closures
in a complex environment, particularly asynchronous XMLHT TPRequest
objects. In a complicated AJAX application, all server activity must happen asynchronously; otherwise, the
browser will hang while waiting for a
response. A typical service model will
look something like Figure 3a.
If an exception occurs in the process() function, then a wrapper embedded in the service implementation
will catch the result and hand it off to
our exception handler. But the stack
trace will end at process(), when
what we really want is the stack trace
at the point when dosomething()
was called. Because our stack traces
are generated on demand and are in-
Related articles
on queue.acm.org
Making the Move to AJAX (Case Study)
Jeff Norwalk
http://queue.acm.org/detail.cfm?id=1515744
Debugging in an Asynchronous World
Michael Donat
http://queue.acm.org/detail.cfm?id=945134
Debugging Devices
(Kode Vicious column)
http://queue.acm.org/detail.cfm?id=1483103
Eric Schrock has been a staff engineer at sun
Microsystems since 2003. after starting in the solaris
kernel group—where he worked on zFs, among other
things—schrock spent the past few years helping to
develop the sun storage 7000 series of appliances as
part of the company’s Fishworks engineering team.
The complete source code for the examples included
here, as well as the latest version of the browser support
table, can be found at http://blogs.sun.com/eschrock/
resource/ajax/ index.html.
© 2009 aCM 0001-0782/09/0500 $5.00