Developer reference

Developer reference

Capturing arguments

Rebugger.stepinFunction.
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 evaled at the REPL to analyze or improve fcomplex, or can be used for further stepin calls.

source
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 evaled 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.

source
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 defining hidden_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.

source
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))
source

Capturing stacktrace

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 evals of command, usage should be limited to deterministic expressions that always result in the same call chain.

source
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.

source
Rebugger.linerangeFunction.
r = linerange(expr, offset=0)

Compute the range of lines occupied by expr. Returns nothing if no line statements can be found.

source

Utilities

Rebugger.clearFunction.
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.

source
Rebugger.getstoredFunction.
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.

source