I wanted to show a new feature in an upcoming version of RemedyBG which allows the presentation of data from arrays of structures (AoS) in an interesting fashion. As far as I know, there isn't an interactive debugger out there that has this feature.
I'd like to show this using a concrete example from Handmade Hero. Let's suppose you are debugging GridBuildSpatialPartition and would like to investigate a problem with the given lighting_box
s. Maybe there is an issue where one of these lighting_box
s has an unexpected dimension, or something similar, and we'd like to find out which one it is.
A lighting_box is defined as follows:
struct lighting_box { v3 BoxMin, BoxMax; v3 RefC; u32 IsLight; };
In the function we are debugging, we are given InputBoxCount
and InputBoxes
-- the number of light_boxes and a pointer to an array of lighting_box
s, respectively.
We can visualize the data in RemedyBG today by using the expression, InputBoxes, InputBoxCount
, and we get something that looks like:
This eye sore makes it rather difficult to spot a problem with any particular item especially given that the dimensions (width/height) aren't explicitly stored in the array.
We could walk through the array, expanding out each element as necessary, doing the math in our heads to compute the width and height, but this is tedious, error-prone and probably not what we want.
We could also consider stopping the program, adding an assertion to detect the invalid constraint in one of the dimensions and re-run, hoping that we get back to the same condition and reproduce the problem.
Instead, I'd like to show you a better way using a new type of format specifier that can help us track down this problem more efficiently.
The new formatter can be used to construct a table of values, with one or more columns, where the values within each column of the table are described by an arbitrary expression typically ranging over the elements of the array. The formatter can be used after any array expression in a watch window with a list of one or more expressions, wrapped in braces and semicolon separated, one expression per column.
To help with this there are two, new pseudo-variables that can be used within this context: $
, which evaluates to whatever the current array element is for a particular row and __index
, which evaluates to the current row in the table. So in our example, on row 5, say, the pseudo variable $
will evaluate to InputBoxes[5]
and __index
evaluates to, simply, 5
.
To get things started, let's say we want to display each array element's BoxMin.x in this table. To do this we would use InputBoxes, InputBoxCount { $.BoxMin.x }
. With this, we get:
The value is a now a scrollable, nested table showing the BoxMin.x value for all elements of the array. Notice, by default, that the column header shown is expression itself. We can fix this and give the column a more readable name by prefixing the expression with @column-name
e.g., InputBoxes, InputBoxCount { @minx $.BoxMin.x }
resulting in:
Now, since we can use any arbitrary expression, we can extend this to show us each lighting_box's width and height using the following format specifier InputBoxes, InputBoxCount { @width $.BoxMax.x - $.BoxMin.x; @height $.BoxMax.y - $.BoxMin.y }
And finally, since we probably want to know the index of any problematic 'lighting_box' we can add an additional column to display the index, with column name "idx", as follows: InputBoxes, InputBoxCount { @idx __index; @width $.BoxMax.x - $.BoxMin.x; @height $.BoxMax.y - $.BoxMin.y }
In any case, I hope this upcoming feature for visualizing the state of your program turns out to be useful to you as well.
Looks interesting.
In the future it would be nice to be able to save format specifier (or more generally expressions) to be able to reuse them.
For example save { @idx __index; @width $.BoxMax.x - $.BoxMin.x; @height $.BoxMax.y - $.BoxMin.y }
with the name light_box
And then be able to type InputBoxes, InputBoxCount #light_box
Probably simple text substitution would be enough ?
Can we use this syntax to format non array elements ? For example:
some_struct_or_union { @name_1 $.member_1; @name_2 $.member_10 }
Since the names aren't generally necessary for a single element, maybe discarding the column names in that case would be useful, or having the name inlined name_1: value; name_2: value
.
Can we use other format specifier inside, for example str
:
some_string { $.bytes, str $.used }
Thanks for your work.
Simon,
Having macro-like substitution in the watch window is a good idea. Will definitely implement this at some point.
In this iteration, the feature will apply to arrays only. However, as a work around you could do &some_struct_or_union, 1 { ... }
.
A subset of the format specifiers will be usable within the expressions for the first pass (e.g., you might want to see values in both hex and decimal in two separate columns). Certain things like str
won't be supported just yet.
Thanks for this. I can really see myself using it more often than the normal array view.
(Brought to my attention by @thebigfox)
Looks like this already exists in WinDBG: https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/dx--display-visualizer-variables-
Per gives some examples of this here: https://twitter.com/pervognsen/status/1514472371138097152?cxt=HHwWgMCj_Z6mvoQqAAAA