Episode
Defrag Tools #170 - Debugger - JavaScript Scripting
In this episode of Defrag Tools, Andrew Richards talks to Andy Luhrs and Bill Messmer from the Debugging Tools for Windows team. We talk about the new JavaScript extensibility and scripting abilities in WinDbg available in the WDK and SDK build 14951 and newer.
- Blog - https://blogs.msdn.microsoft.com/windbg/
- Email - windbgfb@microsoft.com
Bill leveraged the debugger object model previously in these episodes:
- Defrag Tools #138 - Debugging - 'dx' Command Part 1
- Defrag Tools #139 - Debugging - 'dx' Command Part 2
- Defrag Tools #169 - Debugging Tools for Windows Team
Timeline:
[00:00] Welcome and introductions
[00:24] New SDK drop
[00:29] Why JavaScript
[02:07] New commands
[03:50] Visual Studio Code
[04:00] Example - Hello World
[07:15] Debugger default namespaces
[09:07] Example - Print all threads
[10:26] Example - Conditional breakpoint
[18:13] 'g' vs. 'gc' – Andrew was right! 'gc' resumes execution in the same way that it started. So if you hit 'p' and then hit a conditional breakpoint that has 'gc' in it, the 'gc' will just finish your initial 'p'.
[20:40] Example - Unique stacks
[34:40] Example - Addition
Questions/Comments? Email us at defragtools@microsoft.com
JavaScript MSDN Docs:
- JavaScript Debugger Scripting
- JavaScript Debugger Example Scripts
- Native Debugger Objects in JavaScript Extensions
Unique Stacks Example (the right one):
"use strict";
class __stackEntry { constructor(frameString) { this.threads = []; this.frameString = frameString; this.children = new Map(); }
display(indent)
{
for (var child of this.children.values())
{
host.diagnostics.debugLog(indent, child.frameString, " [Threads In Branch: ");
for (var thread of child.threads)
{
host.diagnostics.debugLog(thread.Id, " ");
}
host.diagnostics.debugLog("]\n");
child.display(indent + " ");
}
}
}
class __stackMap { constructor(process) { this.__process = process; this.__root = new __stackEntry(""); this.build(); }
build()
{
for (var thread of this.__process.Threads)
{
var current = this.__root;
var frameNum = 0;
var frameCount = thread.Stack.Frames.Count();
for (var frameNum = frameCount - 1; frameNum >= 0; --frameNum) {
var frame = thread.Stack.Frames[frameNum];
var frameString = frame.toString();
if (current.children.has(frameString)) {
current = current.children.get(frameString);
current.threads.push(thread);
}
else {
var newEntry = new __stackEntry(frameString);
current.children.set(frameString, newEntry);
current = newEntry;
current.threads.push(thread);
}
}
}
}
findEntry(thread)
{
var current = this.__root;
var frameCount = thread.Stack.Frames.Count();
for (var frameNum = frameCount - 1; frameNum >= 0; --frameNum)
{
var frame = thread.Stack.Frames[frameNum];
var frameString = frame.toString();
if (!current.children.has(frameString))
{
return null;
}
current = current.children.get(frameString);
}
return current;
}
display()
{
this.__root.display("");
}
}
class __threadSameStacks { constructor(thread) { this.__thread = thread; }
getDimensionality()
{
return 1;
}
getValueAt(idx)
{
for (var idxVal of this)
{
var tid = idxVal[Symbol.indicies][0];
if (idxVal[Symbol.indicies][0] == idx && tid != this.__thread.Id)
{
return idxVal.value;
}
}
return undefined;
}
*[Symbol.iterator]()
{
var context = this.__thread.hostContext;
var session = host.namespace.Debugger.Sessions.getValueAt(context);
var process = session.Processes.getValueAt(context);
var map = new __stackMap(process);
var entry = map.findEntry(this.__thread);
if (entry != null)
{
for (var sharingThread of entry.threads)
{
if (sharingThread.Id != this.__thread.Id)
{
yield new host.indexedValue(sharingThread, [sharingThread.Id]);
}
}
}
}
}
class __threadExtension { get IdenticalStacks() { return new __threadSameStacks(this); } }
function invokeScript() { var map = new __stackMap(host.currentProcess); map.display(); }
function initializeScript() { return [new host.namedModelParent(__threadExtension, "Debugger.Models.Thread")]; }
In this episode of Defrag Tools, Andrew Richards talks to Andy Luhrs and Bill Messmer from the Debugging Tools for Windows team. We talk about the new JavaScript extensibility and scripting abilities in WinDbg available in the WDK and SDK build 14951 and newer.
- Blog - https://blogs.msdn.microsoft.com/windbg/
- Email - windbgfb@microsoft.com
Bill leveraged the debugger object model previously in these episodes:
- Defrag Tools #138 - Debugging - 'dx' Command Part 1
- Defrag Tools #139 - Debugging - 'dx' Command Part 2
- Defrag Tools #169 - Debugging Tools for Windows Team
Timeline:
[00:00] Welcome and introductions
[00:24] New SDK drop
[00:29] Why JavaScript
[02:07] New commands
[03:50] Visual Studio Code
[04:00] Example - Hello World
[07:15] Debugger default namespaces
[09:07] Example - Print all threads
[10:26] Example - Conditional breakpoint
[18:13] 'g' vs. 'gc' – Andrew was right! 'gc' resumes execution in the same way that it started. So if you hit 'p' and then hit a conditional breakpoint that has 'gc' in it, the 'gc' will just finish your initial 'p'.
[20:40] Example - Unique stacks
[34:40] Example - Addition
Questions/Comments? Email us at defragtools@microsoft.com
JavaScript MSDN Docs:
- JavaScript Debugger Scripting
- JavaScript Debugger Example Scripts
- Native Debugger Objects in JavaScript Extensions
Unique Stacks Example (the right one):
"use strict";
class __stackEntry { constructor(frameString) { this.threads = []; this.frameString = frameString; this.children = new Map(); }
display(indent)
{
for (var child of this.children.values())
{
host.diagnostics.debugLog(indent, child.frameString, " [Threads In Branch: ");
for (var thread of child.threads)
{
host.diagnostics.debugLog(thread.Id, " ");
}
host.diagnostics.debugLog("]\n");
child.display(indent + " ");
}
}
}
class __stackMap { constructor(process) { this.__process = process; this.__root = new __stackEntry(""); this.build(); }
build()
{
for (var thread of this.__process.Threads)
{
var current = this.__root;
var frameNum = 0;
var frameCount = thread.Stack.Frames.Count();
for (var frameNum = frameCount - 1; frameNum >= 0; --frameNum) {
var frame = thread.Stack.Frames[frameNum];
var frameString = frame.toString();
if (current.children.has(frameString)) {
current = current.children.get(frameString);
current.threads.push(thread);
}
else {
var newEntry = new __stackEntry(frameString);
current.children.set(frameString, newEntry);
current = newEntry;
current.threads.push(thread);
}
}
}
}
findEntry(thread)
{
var current = this.__root;
var frameCount = thread.Stack.Frames.Count();
for (var frameNum = frameCount - 1; frameNum >= 0; --frameNum)
{
var frame = thread.Stack.Frames[frameNum];
var frameString = frame.toString();
if (!current.children.has(frameString))
{
return null;
}
current = current.children.get(frameString);
}
return current;
}
display()
{
this.__root.display("");
}
}
class __threadSameStacks { constructor(thread) { this.__thread = thread; }
getDimensionality()
{
return 1;
}
getValueAt(idx)
{
for (var idxVal of this)
{
var tid = idxVal[Symbol.indicies][0];
if (idxVal[Symbol.indicies][0] == idx && tid != this.__thread.Id)
{
return idxVal.value;
}
}
return undefined;
}
*[Symbol.iterator]()
{
var context = this.__thread.hostContext;
var session = host.namespace.Debugger.Sessions.getValueAt(context);
var process = session.Processes.getValueAt(context);
var map = new __stackMap(process);
var entry = map.findEntry(this.__thread);
if (entry != null)
{
for (var sharingThread of entry.threads)
{
if (sharingThread.Id != this.__thread.Id)
{
yield new host.indexedValue(sharingThread, [sharingThread.Id]);
}
}
}
}
}
class __threadExtension { get IdenticalStacks() { return new __threadSameStacks(this); } }
function invokeScript() { var map = new __stackMap(host.currentProcess); map.display(); }
function initializeScript() { return [new host.namedModelParent(__threadExtension, "Debugger.Models.Thread")]; }
Have feedback? Submit an issue here.