The track
command allows you to track the data of gdb expressions on hitting breakpoints. While this module is active,
whenever a breakpoint is hit an internal callback will be called, this may be a performance issue for some. All
breakpoints that have a trackpoint attached will automatically continue when hit, making data collection an automated
task.
The (classic) quick way to use track is by using single breakpoints and/or attaching tracks to existing breakpoints. For more complex scenarios see the track set section later.
Shows the currently known breakpoints (similar to info break
) along with the information about registered tracking
information.
This will use gdbs existing breakpoint no num
and will execute expression
each time, recording the resulting string
along with a timestamp.
Instead of giving a number in num
you can also provide an expression that will then be given to gdb to create a new
breakpoint. This breakpoint however will remain active even after the trackpoint has been deleted. Be careful though
that you may end up with multiple breakpoints for the same address which may incur a performance hit. We try to filter
out by the location string you passed, but what gdb gives us may not always be the same you passed, thus we can not
distinguish.
See track expressions section later.
This shows a table with all the collected data. In (default) relative mode, all timestamps are relative to the first
recording. You can set vdb-track-time-relative
to disable this and use local timestamps instead (useful for long
running programs where the breakpoint is only hit occasionally). Note that this will display the string from the
expression per table entry without any further formatting, as such it is most wise to use expressions only that have
small outputs.
Setting vdb-track-clear-at-start
to off will disable the automated clearing of tracking data when (re)starting a
process.
If at a specific breakpoint an expression did not yield any output (or caused an exception) this field will remain empty.
This deletes a track entry by the number shown in track show
, just like del
does for breakpoints. You can specify
multiple trackpoints.
Clears the data cache displayed by track data
.
A track set is a bunch of more detailed track specifications that work together. There are a few predefined ones and then you can add your own. For most detail have a look at the ssl_set definition in track.py
ssl_set
ssl_set = {
"SSL_read" : # A function/location to set the breakpoint
[ # a list of "actions" with their parameters, a non matching "filter" will stop evaluation
( "filter", [ "map", "ssl_fd_filter", "s->rbio->num" ] ),
( "hex", [
( "buf", "$ret" ), # hex expects address and length
( "$rdi", "$ret" ) # only if the above doesn't work, try to fall back to these
]
),
],
"SSL_write" : [ ( "hex", [ ( "buf", "num" ), ( "$rdi", "$rsi" ) ] ) ],
"connect" : [
( "filter", [ "cmp", "{port}", "ntohs( ((uint16_t*)addr)[1] )/p" ] ),
( "filter", [ "cmp", "{ip}", "( ((sockaddr_in*)addr)->sin_addr)" ] ),
( "store", [ "fd", "ssl_fd_filter" ] )
],
"close" : [
( "delete", [ "fd", "ssl_fd_filter" ] )
]
}
A Set is a dict of breakpoint locations associated with a list of actions that may or may not work together. These
actions can have as parameters expressions that need to be evaluated. To define how to evaluate them, you can append a
/<epxression-specifieer
at the end. See track expressions section for details.
Each breakpoint location key has a list of tuples that each contain an action item. The first of the tuple is the name, the rest is depending on the action item type.
At certain points there is the possibility to use {name}
to request parameters. These must be specify in the form of
name=value
after the set enabling command.
Certain actions do support $ret
as the value. This is a special value that means the return value of the function the
breakpoint currently is in should be used.
Filters can abort the evaluation of the list of actions. The second parameter is a list of filter types. There are multiple types of filters:
cmp, <value>, <expression>
Compare the given value. Instead of a value you usually want to use{name}
to be able to specify that as a parameter when enabling the set.map, <mapname>, <expression>
Compare if the value of the expression is in a set previously filled by store actions.
To allow for multiple alternative syntaxes to access certain values, whenever a value of an expression is not available, the filter behaves as if the comparison yields true.
A store takes an expression and the name of a map. This name can be used in a map filter command. This is useful for when at a certain point your program maps something to an id but at the interesting points only the id is available for a filter. Multiple stores will store multiple values in a set.
The delete will delete the value from the set that is given by the mapname.
This action will output the given data as a hexdump. Instead of just a pair/tuple of buffer/size, this will take a list
of tuples. The first one that yields a result will be used to hexdump. Supports $ret
as an expression.
Takes a list of expressions (supports $ret
too ) that will then be filled into the internal track data store and are
available via track data
The following sets and their purpose are predefined
ssl
Hooks intoSSL_read
andSSL_write
and can filter by port and IP, and will display buffers as hexdumps.
Normally the expression is one that is evaluated by gdb to lead a gdb value of a variable by the given name.
There are various ways where you can append a single letter to a specification (Either track/x
for single tracks, or
at the end of an expression in track sets).
The meaning of the letters is as follows:
v
Query the currently selected frame (in the breakpoint) for a variable with that name. This is usually the default.x
Usegdb.execute()
on the expression and use the result (ususally a string).X
Usegdb.execute()
on the expression, but then usegdb.parse_and_eval("$")
to get the gdb.Value of that result.E
Use pythonseval()
to run the expression as python code. For convenience$()
is synonymous withgdb.parse_and_eval()
.p
Callgdb.parse_and_eval()
and use the result (usually gdb.Value).
Depending on the context where these values are being used, they can be subject to conversion to other types.
Using the /i <interval>[,<count>]
flag you can enable an interval mode (instead of breakpoint mode). In this mode the
program will be interrupted every <interval>
seconds and the expression is executed as usual. This means you would
most likely want to use global variables for this. An optional <count>
parameter can be added, stopping data
acquisition after that many counts.
Using vdb-track-interval-sleep
you can specify a default value to sleep before recalculating if the program should be
interrupted. Using 0 here causes the module to calculate a time on its own, depending on the interval.
With vdb-track-interval-sync-to-second
you can tell that the first interval should start approximately at a whole
second, this way you might be better able to synchronized with existing program timings.
You can use vdb-track-skip-long-intervals
to skip an interrupt, if during data gathering and processing the next
should have been there. Normally it would be executed right after the previous one in that case, but sometimes being
synced is more important than getting data on every step.
Currently the breaking and data acquisition takes at least approximately 10-15ms, depending on the amount of data being read and the speed of the interface being used. You should not chose a significantly smaller interval, otherwise this interferes too much with your programs timing.
At various places you can notify the track module that the data is an array instead of a single variable by using the
syntax [@###]
( where ###
is the number of array elements ). This can be combined efficiently with the struct mode
to directly get multiple values at once.
Using /s
we can instead of an expression to execute use a special enhanced version of python structure specifiers. It
is a comma seperated list of <name>:<spec>
where <spec>
is a specifier of the python struct module. The name is
optional, if left out, the data will not be acquired by the track module.
Using /u
we can gather data in a special mode for arrays. For that one of the fields needs to be named ID
. In this
mode the whole array is being read, but only entries for which an ID
has not yet been seen will be used. The result
will be an array instead of a single value per track data entry. Modules that work on the data need explicit support for
them.
TODO: all combinations of useful flags
track/sui 1,5000 0xfb44530[@256] ID:H,:6x,VALUE:Q
Tracks every 1 second ( for 5000 seconds ), taking the address 0xfb44530 and interpreting it as an array of 256 elements
of an 16bit integer (named ID), followed by 6 bytes padding data, followed by an 64bit integer ( named VALUE ). The
values will be unified by ID
. In this case this also means that after 64k you will get no more updates. This is
probably the most efficient way to produce data for the histogram module.