[ Home ]
SBCL Internals

The pages on this CLiki-driven site can be edited by anybody at any time. No warranty of any kind can therefore be made; any implied warranties of merchantability or fitness for a particular purpose are expressly disclaimed
[ Home ] [ Recent Changes ] [ About CLiki ] [ Text Formatting ]

It's possible to use bare gdb, instead of ldb, to debug SBCL's cold init. (You could also in principle use gdb to debug a running SBCL system after cold init is complete, but there's very seldom cause to do this.)

Generally you'll want to do things with the SBCL cold-sbcl.core file, which means you need a gdb prompt after it's been loaded. It can be useful to put gdb breakpoints on call_into_lisp? and/or call_into_c before you do the gdb 'run' command.

Once you're at a gdb prompt with your SBCL cold-sbcl.core file loaded, other tricks may be useful. I (Bill Newman) don't know how much interest there is in this, so I'll just list the stuff that I've found useful. If anyone's curious, ask me and I'll fill in the appropriate section(s).

Using gdb to debug internal errors in cold init

So you have something like

fatal error encountered in SBCL runtime system:
internal error too early in init, can't recover
There's no LDB in this build; exiting.
internal error #23
    SC: 14, Offset: 0   lispobj 0x505a84a7
Using gdb to debug this is likely to be straightforward (though tedious). See gdb for internal errors.

Using gdb to put a breakpoint on a Lisp function whose address is known from cold-sbcl.map

As long as you want to do this early enough in cold init that the GC hasn't started moving code around, this is easy. If, for example, you want to stop when SB!EXT:QUIT is called, you locate that line in the map:

0x08337D64: SB!EXT:QUIT   #X08337D4D
and place a breakpoint at that address:
(gdb) break *0x08337D64

Note that you can only have enabled breakpoints in mapped memory, so if you try to restart sbcl after doing this, gdb will complain about "Cannot access memory at address". Use "info break" to find out which breakpoint it was, then "disable n" to disable it. And don't forget to put another breakpoint somewhere else (call_into_lisp? is a good place) that will be hit before the function you're debugging, so that you can re-enable the breakpoint next time.

Using gdb to find the containing function for an address

(only tested on x86)

If you get a random stop in Lisp code after the first GC, the addresses in cold-sbcl.map are unreliable. So what do you do then?

Program received signal SIGSEGV, Segmentation fault.
0x092ecf51 in ?? ()
(gdb) call search_dynamic_space(0x092ecf51)
$1 = (lispobj *) 0x92eb0b0
this is probably a 'code' object
(gdb) print /x * ((struct code *)0x92eb0b0) 
$7 = {header = 0x3a7a, code_size = 0x223c, entry_points = 0x92eb19d, 
  debug_info = 0x92eb099, trace_table_offset = 0x88e4, constants = {0xb0d4237}}
entry_points is a chain of some kind of functions
(gdb) print /x * ((struct simple_fun *)0x92eb198)
$8 = {header = 0x3a7e, self = 0x92eb1b0, next = 0x92eb9d5, name = 0x92ed3ff, 
  arglist = 0x92ed4ab, type = 0x92ed4fb, code = {0x8f}}
name is probably a string
(gdb) x/8s 0x92ed3ff
0x92ed3ff:       ""
0x92ed400:       "top level local call SB!KERNEL::SUB-GC"
0x92ed427:       ""
0x92ed428:       "\226\006"
0x92ed42b:       ""
0x92ed42c:       "¢"
0x92ed42e:       ""
0x92ed42f:       ""

This should work if the function is still in dynamic space (i.e. PURIFY has not run, or it was created since purification). The similar search_read_only_space may be more useful if these contions do not apply.

The most useful function for finding a component from a given program counter position is probably component_ptr_from_pc(lispobj* obj). It searches read-only, static and dynamic space and returns the object when it finds a code object. -- Andreas Fuchs?

If you get unlikely-looking garbage instead of sensible stuff like this, first check that you have masked out the tag bits on the addresses at each stage. It may be possible that sometimes you get closures instead of simple_funs, or a function named by a symbol instead of a string. Look around, use your initiative, update this page.

Understanding the Lisp arguments to a Lisp function you put a gdb breakpoint on

(unwritten: see above)

Using gdb to do Lisp backtrace by hand

(gdb) call backtrace_from_fp($ebp, number-of-frames) (or $rbp if on x86-64)

It should print you a backtrace on the standard output (inferior lisp buffer when using Slime). If $ebp is trashed at that point, you can try $esp. If neither gives anything sensible, try using $esp + something to make it word aligned, plus a word or two.

Inspecting objects in memory

There are two ways to do this: it's easier to call ldb if you have it, though if you go this route you should check quite carefully that the object definitions in print.c actually match up with what objdef.lisp says, because they're "maintained" (sigh) independently.

(gdb) call ldb_monitor()
LDB monitor
ldb> print 0x080ab307
$17=      0x080ab307: other pointer
            header: 0x000003b6: instance header
$38=        name: 0x070226ff: !RANDOM-COLD-INIT
$39=        function: 0x080ab1d5: # 
            raw_addr: 0x080ab1ec: 33729659
ldb> exit
(gdb) 

Otherwise you can do it by hand. In this case, the low tag bits of 0x080ab307 say that it's an other-pointer (check sbcl.h or early-objdef.lisp). Now mask out the tag bits to get 0x080ab300 and dump the word at that address:

(gdb) x/20x 0x80ab300
0x80ab300:      0x000003b6      0x070226ff      0x080ab1d5      0x080ab1ec

From sbcl.h, b6 (the highlighted bit) is type_Fdefn. So we cast the masked address to a pointer to struct fdefn and find out what the slots are:

(gdb) print (struct fdefn *)0x80ab300?
$1 = {header = 950, name = 117581567, function = 134918613, 
  raw_addr = 0x80ab1ec ":s??:\017"}

SIGTRAP handling

Some ports use the same signal for gdb-placed breakpoints as for the system trap calls that sigtrap_handler? does. It can get complicated debugging them. This section needs writing.

Using gdb user commands to poke around in SBCL internals

There is a (tested on x86 only) .gdbinit tailored for sbcl at http://boinkor.net/lisp/sbcl/gdbinit. The commands defined use some of the heuristic approaches described on this page. You can use them .gdbinit to:


This page is linked from: gdb for internal errors   ldb   Registers  

CLiki pages can be edited by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively