Blending Guile and GDB together
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 0x77b28ef1 in vm_debug_engine (vm=, program=0x6eb240, argv=, nargs=2) at vm-i-system.c:855 #2 0x77aaafe3 in scm_primitive_eval (exp=exp@entry=0x8e1440) at eval.c:685 #3 0x77aab043 in scm_eval (exp=0x8e1440, module_or_state=module_or_state@entry=0x8a8c60) at eval.c:719 #4 0x77afa26d in scm_shell (argc=1, argv=0x7fffd118) at script.c:441 #5 0x77ac753d in invoke_main_func (body_data=0x7fffcfe0) at init.c:337 #6 0x77aa14ca in c_body (d=0x7fffcf20) at continuations.c:511 #7 0x77b33ac8 in vm_regular_engine (vm=, program=0x6f57e0, argv=, nargs=2) at vm-i-system.c:855 #8 0x77a3 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 0x77b28ef1 in vm_debug_engine (vm=, program=#, argv=, nargs=2) at vm-i-system.c:855 #2 0x77aaafe3 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 0x77aab043 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 0x77afa26d in scm_shell (argc=1, argv=0x7fffd118) at script.c:441 #5 0x77ac753d in invoke_main_func (body_data=0x7fffcfe0) at init.c:337 #6 0x77aa14ca in c_body (d=0x7fffcf20) at continuations.c:511 #7 0x77b33ac8 in vm_regular_engine (vm=, program=#, argv=, nargs=2) at vm-i-system.c:855 #8 0x77a3 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
Potluck dish - Simple functional reactive programming
Hello Guilers, I didn't have time to put together a proper potluck dish, but I wanted to find something to share anyway. Lately I've been playing around with functional reactive programming (FRP) applied to video games. This style of programming allows for a declarative, functional way of describing time-varying values. Contrast this method of programming with more traditional hooks and callbacks. My FRP module can be used on top of hooks to escape callback hell. And now for a simple example. This morning I was writing a program using my game engine, guile-2d, and I wanted to display the number of times the GC has been run in the game window. Without FRP I could have done something like: (define gc-label-position (vector2 0 40)) (define gc-counter 0) (define (make-gc-label) (let ((text (format #f "GCs: ~d" counter))) (make-label font text gc-label-position))) (define gc-label (make-gc-label)) (add-hook! after-gc-hook (lambda () (set! gc-counter (1+ gc-counter)) (set! gc-label (make-gc-label This code isn't terrible, but wouldn't it be nice to declare that 'gc-label' will always contain a string with the number of GC runs in it instead? Enter FRP: (define gc-label-position (vector2 0 40)) (define gc-counter (make-root-signal 0)) (define gc-label (signal-map (lambda (counter) (let ((text (format #f "GCs: ~d" counter))) (make-label font text gc-label-position))) gc-counter)) (add-hook! after-gc-hook (lambda () (signal-set! gc-counter (1+ (signal-ref gc-counter) 'gc-counter' and 'gc-label' both become 'signals', or time-varying values. Now, when the GC runs, the 'gc-counter' signal is incremented by 1. The act of setting 'gc-counter' triggers propagation of the counter to the 'gc-label' signal which maps the counter to a new label that prints the current number of GC runs. Magic! Note that 'after-gc-hook' is still needed to bootstrap the signal graph, but once that is out of the way it's signals all the way down. This example was fairly trivial, but what if the desired chain reaction was more complicated? Writing the logic using regular callback procedures would become a nightmare. The nightmare that JavaScript programmers constantly find themselves in. And that's my potluck dish! I'm currently working on a new version of this API that will allow the signal graph to handle the dynamic environment of the REPL, but it's not ready yet. Thanks to all of the Guile maintainers and contributors for the great work these past 3 years! - David Thompson signals.scm Description: Binary data
Re: Potluck dish - Simple functional reactive programming
David Thompson skribis: > This code isn't terrible, but wouldn't it be nice to declare that > 'gc-label' will always contain a string with the number of GC runs in > it instead? Enter FRP: > > (define gc-label-position (vector2 0 40)) > (define gc-counter (make-root-signal 0)) > (define gc-label > (signal-map (lambda (counter) > (let ((text (format #f "GCs: ~d" counter))) > (make-label font text gc-label-position))) > gc-counter)) > > (add-hook! after-gc-hook >(lambda () > (signal-set! gc-counter (1+ (signal-ref gc-counter) Neat! I’m a big fan. I think we should consider adding an FRP module to Guile eventually. Cheers, Ludo’.
Re: Blending Guile and GDB together
Cheers! On Sun, 2014-02-16 at 17:22 +0100, Ludovic Courtès wrote: > 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 0x77b28ef1 in vm_debug_engine (vm=, > program=0x6eb240, argv=, nargs=2) > at vm-i-system.c:855 > #2 0x77aaafe3 in scm_primitive_eval (exp=exp@entry=0x8e1440) at > eval.c:685 > #3 0x77aab043 in scm_eval (exp=0x8e1440, > module_or_state=module_or_state@entry=0x8a8c60) at eval.c:719 > #4 0x77afa26d in scm_shell (argc=1, argv=0x7fffd118) at > script.c:441 > #5 0x77ac753d in invoke_main_func (body_data=0x7fffcfe0) at > init.c:337 > #6 0x77aa14ca in c_body (d=0x7fffcf20) at continuations.c:511 > #7 0x77b33ac8 in vm_regular_engine (vm=, > program=0x6f57e0, argv=, nargs=2) > at vm-i-system.c:855 > #8 0x77a3 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=# 6f9f30>) at print.c:1437 > #1 0x77b28ef1 in vm_debug_engine (vm=, > program=#, argv=, nargs=2) > at vm-i-system.c:855 > #2 0x77aaafe3 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 0x77aab043 in scm_eval (exp=((@ (ice-9 control) %) (begin > (load-user-init) ((@ (ice-9 top-repl) top-repl, > module_or_state=module_or_state@entry=# 8b5240> (# (# 871ac0> () #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 # 8466e0> #f # #f3055dc0> # () > #f #f # (system base compile) interface #f # 883640> () # #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 # bdd420> () # #f # #f #f #f3075360> > # (# > (# () #f #f # (ice-9 null) > interface #f # () # #f # dbec40> #f #f #f3083560>) #f #f # (ice-9 safe-r5rs) interface > #f # () # #f # #f #f > #f30830e0>) #f #f # (ice-9 r5rs) interface #f # d99ae0> () # #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 # 8b51c0> #f # () #f #f # > (guile-user) interface #f # () # #f > # #f #f #f30b3d20> #f30b3d00>) at eval.c:719 > #4 0x77afa26d in scm_shell (argc=1, argv=0x7fffd118) at > script.c:441 > #5 0x77ac753d in invoke_main_func (body_data=0x7fffcfe0) at > init.c:337 > #6 0x77aa14ca in c_body (d=0x7fffcf20) at continuations.c:511 > #7 0x77b33ac8 in vm_regular_engine (vm=, > program=#, argv=, nargs=2) > at vm-i-system.c:855 > #8 0x77a3 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 fi
Potluck, midi -> chiptune
Hi, For this year's potluck, I wrote prog that converts a MIDI file into an 8-bit-era chiptune, such as might have been rendered by a Game Boy or SNES. It is here: https://github.com/spk121/furry-nemesis Specifically it converts a file named tmp.midi (that must be in the current directory) to a file named tmp.wav. I'll be honest: this code is just a couple of days old, and it is really raw. Incomplete and painfully slow. But, it is good for a laugh. I'll fix it up over the next couple of weeks, if I can find some spare time. -Mike Gran
[PATCH] Improved ^c support for gdb/guile
Hi. Here's my modest contribution to the Guile anniversary potluck. The patch to selftest-support.exp could be done differently, I've tried to keep it simple. The problem is that gdb with guile will get SIGPWR from time to time when Guile's GC kicks in, and we need this to not alter test behaviour. The patch just tells the parent gdb to ignore SIGPWR, which is simple enough without loss of coverage. A good question is what other signals Guile GC might use. Regression tested on amd64-linux with guile 2.0.9. 2014-02-17 Doug Evans * Makefile.in (SUBDIR_GUILE_OBS): Add scm-sigint.o. (SUBDIR_GUILE_SRCS): Add scm-sigint.c. (scm-sigint.o): New rule. * guile/guile-internal.h (gdbscm_make_sigint_exception): Declare. (gdbscm_install_sigint_handler): Declare. (gdbscm_enable_sigint, gdbscm_disable_sigint): Declare. (gdbscm_initialize_sigint): Declare. * guile/guile.c (initialize_gdb_module): Call gdbscm_initialize_sigint. * guile/scm-exception.c (gdbscm_make_sigint_exception): New function. (gdbscm_scm_from_gdb_exception): Call it. * guile/scm-safe-call.c: #include "guile.h". (gdbscm_enter_guile_mode, gdbscm_exit_guile_mode): New functions. (gdbscm_with_guile, gdbscm_call_guile): Call them. * guile/scm-sigint.c: New file. testsuite/ * gdb.gdb/guile-interrupts.exp: New file. * gdb.gdb/guile-interrupts.gdb: New file. * lib/selftest-support.exp (selftest_setup): Don't stop for SIGPWR. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 2884725..2871e47 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -299,6 +299,7 @@ SUBDIR_GUILE_OBS = \ scm-ports.o \ scm-pretty-print.o \ scm-safe-call.o \ + scm-sigint.o \ scm-string.o \ scm-symbol.o \ scm-symtab.o \ @@ -322,6 +323,7 @@ SUBDIR_GUILE_SRCS = \ guile/scm-ports.c \ guile/scm-pretty-print.c \ guile/scm-safe-call.c \ + guile/scm-sigint.c \ guile/scm-string.c \ guile/scm-symbol.c \ guile/scm-symtab.c \ @@ -2280,6 +2282,10 @@ scm-frame.o: $(srcdir)/guile/scm-frame.c $(COMPILE) $(srcdir)/guile/scm-frame.c $(POSTCOMPILE) +scm-sigint.o: $(srcdir)/guile/scm-sigint.c + $(COMPILE) $(srcdir)/guile/scm-sigint.c + $(POSTCOMPILE) + scm-gsmob.o: $(srcdir)/guile/scm-gsmob.c $(COMPILE) $(srcdir)/guile/scm-gsmob.c $(POSTCOMPILE) diff --git a/gdb/guile/guile-internal.h b/gdb/guile/guile-internal.h index dcdd422..d4d718d 100644 --- a/gdb/guile/guile-internal.h +++ b/gdb/guile/guile-internal.h @@ -288,6 +288,8 @@ extern SCM gdbscm_out_of_range_error (const char *subr, int arg_pos, extern SCM gdbscm_make_misc_error (const char *subr, int arg_pos, SCM bad_value, const char *error); +extern SCM gdbscm_make_sigint_exception (void); + extern void gdbscm_throw (SCM exception) ATTRIBUTE_NORETURN; extern SCM gdbscm_scm_from_gdb_exception (struct gdb_exception exception); @@ -341,6 +343,14 @@ extern char *gdbscm_safe_eval_string (const char *string, int display_result); extern char *gdbscm_safe_source_script (const char *filename); extern void gdbscm_enter_repl (void); + +/* scm-sigint.c */ + +extern void gdbscm_install_sigint_handler (struct signal_handler *previous); + +extern void gdbscm_enable_sigint (void); + +extern void gdbscm_disable_sigint (void); /* Interface to various GDB objects, in alphabetical order. */ @@ -533,6 +543,7 @@ extern void gdbscm_initialize_math (void); extern void gdbscm_initialize_objfiles (void); extern void gdbscm_initialize_pretty_printers (void); extern void gdbscm_initialize_ports (void); +extern void gdbscm_initialize_sigint (void); extern void gdbscm_initialize_smobs (void); extern void gdbscm_initialize_strings (void); extern void gdbscm_initialize_symbols (void); diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c index b7134f7..8f71c0a 100644 --- a/gdb/guile/guile.c +++ b/gdb/guile/guile.c @@ -545,6 +545,7 @@ initialize_gdb_module (void *data) gdbscm_initialize_objfiles (); gdbscm_initialize_ports (); gdbscm_initialize_pretty_printers (); + gdbscm_initialize_sigint (); gdbscm_initialize_strings (); gdbscm_initialize_symbols (); gdbscm_initialize_symtabs (); diff --git a/gdb/guile/scm-exception.c b/gdb/guile/scm-exception.c index a96a350..f752da8 100644 --- a/gdb/guile/scm-exception.c +++ b/gdb/guile/scm-exception.c @@ -404,6 +404,16 @@ gdbscm_memory_error_p (SCM key) return scm_is_eq (key, memory_error_symbol); } +/* Create a SIGINT . */ + +SCM +gdbscm_make_sigint_exception (void) +{ + /* This is copied from top-repl.scm. */ + return gdbscm_make_error (signal_symbol, NULL, _("User interrupt"), + SCM_EOL, scm_list_1 (scm_from_int (SIGINT))); +} + /* Wrapper around scm_throw to throw a gdb:exception. This function does not return. Thi