Hello! As a gift for Guile 2.0’s third birthday [0], here’s a quick hack to enhance the debugging experience for Guile hackers in GDB! The attached code is a GDB extension, written in Guile, using the nice Guile API that landed into GDB master last week (thanks, Doug!). Once you have GDB master (7.8) built with Guile support, just type this at the GDB prompt: (gdb) guile (load "scmpp.scm") From there on, life in GDB is different. :-) The main feature is printing of ‘SCM’ values. As you know, ‘SCM’ values are bit patterns, sometimes with pointers in disguise and so on–to the experienced Guile hacker, “404” is synonymous with #t, not “page not found”. So, before: --8<---------------cut here---------------start------------->8--- Breakpoint 1, scm_display (obj=0xf04310, port=0x6f9f30) at print.c:1437 1437 { (gdb) bt #0 scm_display (obj=0xf04310, port=0x6f9f30) at print.c:1437 #1 0x00007ffff7b28ef1 in vm_debug_engine (vm=, program=0x6eb240, argv=, nargs=2) at vm-i-system.c:855 #2 0x00007ffff7aaafe3 in scm_primitive_eval (exp=exp@entry=0x8e1440) at eval.c:685 #3 0x00007ffff7aab043 in scm_eval (exp=0x8e1440, module_or_state=module_or_state@entry=0x8a8c60) at eval.c:719 #4 0x00007ffff7afa26d in scm_shell (argc=1, argv=0x7fffffffd118) at script.c:441 #5 0x00007ffff7ac753d in invoke_main_func (body_data=0x7fffffffcfe0) at init.c:337 #6 0x00007ffff7aa14ca in c_body (d=0x7fffffffcf20) at continuations.c:511 #7 0x00007ffff7b33ac8 in vm_regular_engine (vm=, program=0x6f57e0, argv=, nargs=2) at vm-i-system.c:855 #8 0x00007ffff7aaaaa3 in scm_call_4 (proc=0x7d2570, arg1=arg1@entry=0x404, arg2=, arg3=, arg4=) at eval.c:507 --8<---------------cut here---------------end--------------->8--- After: --8<---------------cut here---------------start------------->8--- (gdb) gu (load "scmpp.scm") (gdb) bt #0 scm_display (obj=("happy" birthday Guile (2 . 0)), port=#) at print.c:1437 #1 0x00007ffff7b28ef1 in vm_debug_engine (vm=, program=#, argv=, nargs=2) at vm-i-system.c:855 #2 0x00007ffff7aaafe3 in scm_primitive_eval ( exp=exp@entry=((@ (ice-9 control) %) (begin (load-user-init) ((@ (ice-9 top-repl) top-repl))))) at eval.c:685 #3 0x00007ffff7aab043 in scm_eval (exp=((@ (ice-9 control) %) (begin (load-user-init) ((@ (ice-9 top-repl) top-repl)))), module_or_state=module_or_state@entry=# (# (# () #f #f # (ice-9 deprecated) interface #f # () # #f # #f #f #f300b840> # () #f #f # (srfi srfi-4) interface #f # () # #f # #f #f #f300b0e0>) #f #f # (guile) interface #f # () # #f # #f # #f3055dc0> # () #f #f # (system base compile) interface #f # () # #f # #f #f #f30554a0> # () #f #f # (ice-9 readline) interface #f # () # #f # #f #f #f30626c0> # () #f #f # (ice-9 history) interface #f # () # #f # #f #f #f3063540> # () #f #f # (srfi srfi-1) interface #f # () # #f # #f #f #f3066500> # () #f #f # (srfi srfi-26) interface #f # () # #f # #f #f #f3075b00> # () #f #f # (texinfo reflection) interface #f # () # #f # #f #f #f3075360> # (# (# () #f #f # (ice-9 null) interface #f # () # #f # #f #f #f3083560>) #f #f # (ice-9 safe-r5rs) interface #f # () # #f # #f #f #f30830e0>) #f #f # (ice-9 r5rs) interface #f # () # #f # #f #f #f3088120> # () #f #f # (ice-9 session) interface #f # () # #f # #f #f #f3094160> # () #f #f # (ice-9 regex) interface #f # () # #f # #f #f #f30987c0> # () #f #f # (ice-9 threads) interface #f # () # #f # #f #f #f309bd20> # () #f #f # (value-history) interface #f # () # #f # #f #f #f309b680>) #f #f # (guile-user) directory #f # () # #f # #f # () #f #f # (guile-user) interface #f # () # #f # #f #f #f30b3d20> #f30b3d00>) at eval.c:719 #4 0x00007ffff7afa26d in scm_shell (argc=1, argv=0x7fffffffd118) at script.c:441 #5 0x00007ffff7ac753d in invoke_main_func (body_data=0x7fffffffcfe0) at init.c:337 #6 0x00007ffff7aa14ca in c_body (d=0x7fffffffcf20) at continuations.c:511 #7 0x00007ffff7b33ac8 in vm_regular_engine (vm=, program=#, argv=, nargs=2) at vm-i-system.c:855 #8 0x00007ffff7aaaaa3 in scm_call_4 (proc=#, arg1=arg1@entry=#t, arg2=, arg3=, arg4=) at eval.c:507 --8<---------------cut here---------------end--------------->8--- (I hear some say: “is this huge dump of ‘module_or_state’ really an improvement?” Well, granted, this one is a bit annoying, we’ll have to think of a way to truncate it, maybe. But it shows that many data types are pretty-printed, including all the structure fields. :-)) Traditionally, people would typically type ‘call scm_write(x, 0x204)’ to print the value of ‘x’. But in addition to being tedious, this won’t work on a core file, and can otherwise destabilize the Guile process being debugged. So scmpp.scm teaches GDB about Guile’s type tagging so that it can print ‘SCM’ values. A decade ago or so, an SCM value printer was available in GDB itself (with ‘set language scheme’). But that was tricky C code, and since it was maintained outside of Guile, it inevitably went out of sync. The good thing is that scmpp.scm can be maintained within Guile itself. This one is for Guile 2.0, but it shouldn’t be difficult to adjust it to 2.2. The printing-value code in scmpp.scm uses a tailored pattern matcher that makes the bit-fiddling code easier to read. Furthermore, it can use one of two back-ends: GDB, or the FFI. The GDB back-end fiddles with values from an inferior process, while the FFI back-end touches values of the running process. The whole point of the FFI back-end is to allow for testing: we can run a test suite for the SCM-decoding code without having to run GDB itself. There’s also a simple VM stack walker at the end of the file, which is quite handy. When GDB stack filters are supported, we might be able to arrange so that ‘bt’ shows both stacks interleaved. Happy hacking, and happy birthday Guile 2.0! Thanks, Ludo’. [0] http://lists.gnu.org/archive/html/guile-user/2014-02/msg00008.html