Developer reference
Capturing arguments
Rebugger.stepin
— Function.stepin(s)
Given a buffer s
representing a string and "point" (the seek position) set at a call expression, replace the contents of the buffer with a let
expression that wraps the body of the callee.
For example, if s
has contents
<some code>
if x > 0.5
^fcomplex(x)
<more code>
where in the above ^
indicates position(s)
("point"), and if the definition of fcomplex
is
function fcomplex(x::A, y=1, z=""; kw1=3.2) where A<:AbstractArray{T} where T
<body>
end
rewrite s
so that its contents are
@eval ModuleOf_fcomplex let (x, y, z, kw1, A, T) = Main.Rebugger.getstored(id)
<body>
end
where Rebugger.getstored
returns has been pre-loaded with the values that would have been set when you called fcomplex(x)
in s
above. This line can be edited and eval
ed at the REPL to analyze or improve fcomplex
, or can be used for further stepin
calls.
Rebugger.prepare_caller_capture!
— Function.callexpr = prepare_caller_capture!(io)
Given a buffer io
representing a string and "point" (the seek position) set at a call expression, replace the call with one that stashes the function and arguments of the call.
For example, if io
has contents
<some code>
if x > 0.5
^fcomplex(x, 2; kw1=1.1)
<more code>
where in the above ^
indicates position(s)
("point"), rewrite this as
<some code>
if x > 0.5
Main.Rebugger.stashed[] = (fcomplex, (x, 2), (kw1=1.1,))
throw(Rebugger.StopException())
<more code>
(Keyword arguments do not affect dispatch and hence are not stashed.) Consequently, if this is eval
ed and execution reaches "^", it causes the arguments of the call to be placed in Rebugger.stashed
.
callexpr
is the original (unmodified) expression specifying the call, i.e., fcomplex(x, 2; kw1=1.1)
in this case.
This does the buffer-preparation for caller capture. For callee capture, see method_capture_from_callee
, and stepin
which puts these two together.
Rebugger.method_capture_from_callee
— Function.uuid = method_capture_from_callee(method; overwrite::Bool=false)
Create a version of method
that stores its inputs in Main.Rebugger.stored
. For a method
function fcomplex(x::A, y=1, z=""; kw1=3.2) where A<:AbstractArray{T} where T
<body>
end
if overwrite=false
, this generates a new method
function hidden_fcomplex(x::A, y=1, z=""; kw1=3.2) where A<:AbstractArray{T} where T
Main.Rebugger.stored[uuid] = Main.Rebugger.Stored(fcomplex, (:x, :y, :z, :kw1, :A, :T), deepcopy((x, y, z, kw1, A, T)))
throw(StopException())
end
(If a uuid
already exists for method
from a previous call to method_capture_from_callee
, it will simply be returned.)
With overwrite=true
, there are two differences:
- it replaces
fcomplex
rather than defininghidden_fcomplex
- rather than throwing
StopException
, it re-inserts<body>
after the line performing storage
The returned uuid
can be used for accessing the stored data.
Rebugger.signature_names!
— Function.fname, argnames, kwnames, parameternames = signature_names!(sigex::Expr)
Return the function name fname
and names given to its arguments, keyword arguments, and parameters, as specified by the method signature-expression sigex
.
sigex
will be modified if some of the arguments are unnamed.
Examples
julia> Rebugger.signature_names!(:(complexargs(w::Ref{A}, @nospecialize(x::Integer), y, z::String=""; kwarg::Bool=false, kw2::String="", kwargs...) where A <: AbstractArray{T,N} where {T,N}))
(:complexargs, (:w, :x, :y, :z), (:kwarg, :kw2, :kwargs), (:A, :T, :N))
julia> ex = :(myzero(::Float64)); # unnamed argument
julia> Rebugger.signature_names!(ex)
(:myzero, (:__Float64_1,), (), ())
julia> ex
:(myzero(__Float64_1::Float64))
Capturing stacktrace
Rebugger.capture_stacktrace
— Function.uuids = capture_stacktrace(mod, command)
Execute command
in module mod
. command
must throw an error. Then instrument the methods in the stacktrace so that their input variables are stored in Rebugger.stored
. After storing the inputs, restore the original methods.
Since this requires two eval
s of command
, usage should be limited to deterministic expressions that always result in the same call chain.
Rebugger.pregenerated_stacktrace
— Function.usrtrace, defs = pregenerated_stacktrace(trace, topname=:capture_stacktrace)
Generate a list of methods usrtrace
and their corresponding definition-expressions defs
from a stacktrace. Not all methods can be looked up, but this attempts to resolve, e.g., keyword-handling methods and so on.
Rebugger.linerange
— Function.r = linerange(expr, offset=0)
Compute the range of lines occupied by expr
. Returns nothing
if no line statements can be found.
Utilities
Rebugger.clear
— Function.Rebugger.clear()
Clear internal data. This deletes storage associated with stored variables, but also forces regeneration of capture methods, which can be handy while debugging Rebugger itself.
Rebugger.getstored
— Function.args_and_types = Rebugger.getstored(uuid)
Retrieve the values of stored arguments and type-parameters from the store specified uuid
. This makes a copy of values, so as to be safe for repeated execution of methods that modify their inputs.