Script Debugging Walkthrough

As I mentioned in my last post, the latest preview SDK has integrated support for debugging JavaScript scripts which were loaded from console through the .scriptload command. For this walkthrough, I'm going to use this formatting to indicate commands you should run, and this formatting to indicate the output you should see. The output will have slightly different formatting than what you see here.

As an example, I've pasted a script at the bottom of this post, copy and save it locally then load it into your debugger session with .scriptload DebuggableSample.js. Next, let's start actively debugging the script: .scriptdebug DebuggableSample.js For general information on getting setup on JavaScript, see JavaScript Debugger Scripting.

Once you see the prompt >>> Debug [DebuggableSample ] > and a request for input, you are inside the script debugger. First, let's see the list of events we can trap with the sx command.

>>> Debug [DebuggableSample ] >sx sx ab [ inactive] .... Break on script abort eh [ inactive] .... Break on any thrown exception en [ inactive] .... Break on entry to the script uh [ active] .... Break on unhandled exception

We're going to turn on break on entry so that the script will trap into the script debugger as soon as any code within it executes by running sxe en >>> Debug [DebuggableSample ] >sxe en sxe en Event filter 'en' is now active

Now let's exit the script debugger and we'll make a function call into the script which will trap into the debugger, run q to exit the script debugger. At this point, you are back in the normal debugger. Execute dx @$scriptContents.outermost() to call into our script. Now, we are back in the script debugger and broken in on the first line of the outermost JavaScript function. In addition to seeing the break into the debugger, you get information on the line (73) and the column (5) where the break took place as well as the relevant snippet of source code: var x = 99

0:000> dx @$scriptContents.outermost() >>> ****** SCRIPT BREAK DebuggableSample [BreakIn] ****** Location: line = 73, column = 5 Text: var x = 99 >>> Debug [DebuggableSample 73:5] >

Let's step a few times and get to another place in the script, run p, t, p, t, p, p. At this point, you should be broken into the throwAndCatch method on line 34. You can verify this by executing a stack trace with k >>> ****** SCRIPT BREAK DebuggableSample [Step Complete] ****** Location: line = 34, column = 5 Text: var curProc = host.currentProcess >>> Debug [DebuggableSample 34:5] >k k ## Function Pos Source Snippet -> [00] throwAndCatch 034:05 (var curProc = host.currentProcess) [01] outer 066:05 (var foo = throwAndCatch()) [02] outermost 074:05 (var result = outer())

From here, you can investigate the value of variables, try running ??someObj and ??someObj.b.

>>> Debug [DebuggableSample 34:5] >??someObj ??someObj someObj : {...} __proto__ : {...} a : 0x63 b : {...} >>> Debug [DebuggableSample 34:5] >??someObj.b ??someObj.b someObj.b : {...} __proto__ : {...} c : 0x20 d : Hello World

Let's set a breakpoint on the current line of code and see what breakpoints are now set using bpc and bl >>> Debug [DebuggableSample 34:5] >bpc bpc Breakpoint 1 set at 34:5 >>> Debug [DebuggableSample 34:5] >bl bl Id State Pos 1 enabled 34:5

From here, we'll disable the entry event and then just go and let the script continue to the end sxd en followed by a g >>> Debug [DebuggableSample 34:5] >sxd en sxd en Event filter 'en' is now inactive >>> Debug [DebuggableSample 34:5] >g g This is a fun test Of the script debugger foo.a = 99 Caught and returned! Test @$scriptContents.outermost() : TypeError: Unable to get property '42' of undefined or null reference

Execute the script method again and watch our breakpoint be hit, after which, we'll get a call stack dx @$scriptContents.outermost() then k 0:000> dx @$scriptContents.outermost() inside outer! >>> ****** SCRIPT BREAK DebuggableSample [Breakpoint 1] ****** Location: line = 34, column = 5 Text: var curProc = host.currentProcess >>> Debug [DebuggableSample 34:5] >k k ## Function Pos Source Snippet -> [00] throwAndCatch 034:05 (var curProc = host.currentProcess) [01] outer 066:05 (var foo = throwAndCatch()) [02] outermost 074:05 (var result = outer())

At this point, we want to stop debugging this script, so we detach from it. You can see that executing the function again will no longer break into the debugger .detach and q >>> Debug [DebuggableSample 34:5] >.detach .detach Debugger has been detached from script! >>> Debug [ ] >q q This is a fun test Of the script debugger foo.a = 99 Caught and returned! Test

There you have it! You can now use basic debugger commands when you hit issues when writing scripts. We'll be updating the official documentation shortly to include the basic usage. As always, feel free to leave comments and feedback below or reach out to me @aluhrs13 on Twitter.

-Andy

DebuggableSample.js:

 

"use strict";

class myObj
{
    toString()
    {
        var x = undefined[42];
        host.diagnostics.debugLog("ToString!\n");
    }
}

class iterObj
{
    *[Symbol.iterator]()
    {
        throw new Error("Error!");
    }
}

function foo()
{
    return new myObj();
}

function iter()
{
    return new iterObj();
}

function throwAndCatch()
{
    var outer = undefined;
    var someObj = {a : 99, b : {c : 32, d: "Hello World"} };
    var curProc = host.currentProcess;
    var curThread = host.currentThread;

    try
    {
        var x = undefined[42];
    } catch(e) 
    {
        outer = e;
    }

    host.diagnostics.debugLog("This is a fun test\n");
    host.diagnostics.debugLog("Of the script debugger\n");
    var foo = {a : 99, b : 72};
    host.diagnostics.debugLog("foo.a = ", foo.a, "\n");

    return outer;
}

function throwUnhandled()
{
    var proc = host.currentProcess;
    var thread = host.currentThread;
    host.diagnostics.debugLog("About to throw an exception!\n");
    throw new Error("This is an unhandled exception!\n");
    host.diagnostics.debugLog("This will never be hit!\n");
    return proc;
}

function outer()
{
    host.diagnostics.debugLog("inside outer!\n");
    var foo = throwAndCatch();
    host.diagnostics.debugLog("Caught and returned!\n");
    return foo;
}

function outermost()
{
    var x = 99;
    var result = outer();
    var y = 32;
    host.diagnostics.debugLog("Test\n");
    return result;
}

function initializeScript()
{

}