Here are some things I’ve learned about using the lldb
debugger.
Always compile Ruby with -g -O0
(see [building-ruby]). This disables
optimisations and compiles with debug information so that stacktraces are much
more easily readable.
When you’re in the lldb
shell. start the process with process launch
, or
run
, or r
. This will run until the end (or a crash or breakpoint).
You can tell lldb to stop at the beginning of the program by using
--stop-at-entry
as an argument to the run command
Add this to your ~/.lldbinit
type format add --format hex VALUE
command script import -r ~/src/ruby/ruby/misc/lldb_cruby.py
This will always display VALUE objects in hex, which is easier to read. especially as these are often memory locations.
It also pulls in the Ruby lldb helpers which gives you rp
which allows you to
print out detailed information about any VALUE
pointer’s by checking the type
of the data referenced by the pointer and then interpreting the struct.
Some of the functions I use most often defined in the lldb
ruby helpers are:
dump_page
- takes an address of a heap page as an argument and dumps a list
of the contents of every slot on that pagedump_page_rvalue
- same as the above but takes an RVALUE *
and dumps the
page containing that RVALUE
heap_page
- takes either an RVALUE *
or a heap_page *
as argument and
prints out information about the page. Basically finds and dereferences a
heap_page *
b == breakpoint
, f == frame
etcbt
prints the backtraceframe n
where n
is an integer, jumps to stack frame n
frame variable
prints a list of the locals in the current stack framesource list
(or just f
) shows you where you areThe recommended way is either:
breakpoint set --name some_function_name
orbreakpoint set --file io.c --line 1234
But these can be abbreviated to
b some_function_name
b io.c:1234
You can run expressions using expr
or p
. This is useful to look at
variables, or check truthiness of stuff.
Some examples:
(lldb) p objspace->flags
(rb_objspace::(anonymous struct)) $7 = {
mode = 0
immediate_sweep = 1
dont_gc = 0
dont_incremental = 0
during_gc = 0
during_compacting = 0
gc_stressful = 0
has_hook = 0
during_minor_gc = 0
during_incremental_marking = 0
}
(lldb) p objspace->flags.mode == 1
(bool) $8 = false
In both of these cases you can see lldb has saved the result of the expression
to a register as well as printing it out (the $7
in the first example and the
$8
in the second). These are sequentially incrementing for the life of your
lldb session starting at $0
.
You can refer to them again in other expressions.
(lldb) p $7.mode == $8
(bool) $10 = true
Here is an example of dealing with VALUE
objects and inspecting internal Ruby
structures in lldb
: [ruby-inspecting-structs-lldb]