From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 10915 invoked by alias); 27 Apr 2015 07:01:43 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 10848 invoked by uid 89); 27 Apr 2015 07:01:40 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.9 required=5.0 tests=AWL,BAYES_40,FREEMAIL_ENVFROM_END_DIGIT,FREEMAIL_FROM,RCVD_IN_DNSWL_LOW,SPF_PASS autolearn=ham version=3.3.2 X-HELO: mail-pd0-f175.google.com Received: from mail-pd0-f175.google.com (HELO mail-pd0-f175.google.com) (209.85.192.175) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-GCM-SHA256 encrypted) ESMTPS; Mon, 27 Apr 2015 07:01:33 +0000 Received: by pdbnk13 with SMTP id nk13so118853145pdb.0 for ; Mon, 27 Apr 2015 00:01:31 -0700 (PDT) X-Received: by 10.68.183.99 with SMTP id el3mr19777037pbc.24.1430118091374; Mon, 27 Apr 2015 00:01:31 -0700 (PDT) Received: from seba.sebabeach.org.gmail.com (173-13-178-53-sfba.hfc.comcastbusiness.net. [173.13.178.53]) by mx.google.com with ESMTPSA id b10sm18303489pdo.84.2015.04.27.00.01.29 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 27 Apr 2015 00:01:30 -0700 (PDT) From: Doug Evans To: Andy Wingo Cc: gdb-patches@sourceware.org Subject: Re: [PATCH v5] Add Guile frame unwinder interface References: <87zj6hdq6b.fsf@igalia.com> Date: Mon, 27 Apr 2015 07:01:00 -0000 In-Reply-To: <87zj6hdq6b.fsf@igalia.com> (Andy Wingo's message of "Thu, 09 Apr 2015 22:04:12 +0200") Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain X-IsSubscribed: yes X-SW-Source: 2015-04/txt/msg00983.txt.bz2 Andy Wingo writes: > Hi, When we last left our story ... Just a few more nits. > > Changes in this patch: > > * Remove enable-frame-unwinder!, disable-frame-unwinder! > > * set-frame-unwinder-enabled! takes frame unwinder object only, not a > name > > * Removed priority; instead, first the unwinders for the current > objfile are run, then the ones from the current progspace, then the > global ones, but in an unspecified order within each locus > > * Renamed "scope" to "locus" > > * Names required to be unique only within locus > > * s/ephemeral frame/pending frame/ > > * Documentation updates > > * make-unwind-info takes a frame-id object; added a make-frame-id > constructor. > > * There's no recursion check in scm-frame-unwinder.c; instead the > check in frame_unwind_is_unwinding is sufficient. > > * I kept the #:enabled init keyword to make-frame-unwinder, as it > matches what frame filters do, as well as python filters/unwinders. > Let me know if that was an error. > > * Removed dead "has-active-frame-unwinders?" > > * Fixed docstring on "all-frame-unwinders" > > * (prune-frame-unwinders!) doesn't cons if all unwinders are valid > (i.e. their loci haven't gone away), and fix prune-frame-unwinders! > docstring. > > * Renamed unregister-frame-unwinder! to remove-frame-unwinder!, as > requested. > > * Fixed test coding style > > * Fixed some trailing .* in pattern > > * Test unwind before and after enabling unwinder > > Still to do: > > * Make the test architecture-independent if possible > > There were a couple of questions on the review from v4 of this patch > that I address specifically in the thread for v4 and don't mention > above. > > WDYT? :) > > Andy Copying over from the other thread, if you want to keep priorities, ok. Otherwise, you'll need to remove them from the documentation below. > > From 898b8fd80dc5b533be9219472af7645e91d37985 Mon Sep 17 00:00:00 2001 > From: Andy Wingo > Date: Thu, 5 Mar 2015 16:40:20 +0100 > Subject: [PATCH] Add Guile frame unwinder interface > > gdb/doc/ChangeLog: > > * guile.texi (Guile Frame Unwinder API): New section. > > gdb/ChangeLog: > > * guile/scm-symbol.c (gdbscm_lookup_symbol): Don't error if there > is no selected frame and no block is selected; instead, fall back > to the current frame. > * guile/scm-frame-unwinder.c: New file. > * guile/lib/gdb/frame-unwinders.scm: New file. > * guile/guile.c (initialize_gdb_module): Call > gdbscm_initialize_frame_unwinders. > * guile/guile-internal.h (gdbscm_initialize_frame_unwinders): New > declaration. > * frame.c (get_prev_frame): Detect recursive unwinds, returning > NULL in that case. > * frame-unwind.h (frame_unwind_got_bytes): Make buf arg const. > (frame_unwind_is_unwinding): New declaration. > * frame-unwind.c (is_unwinding): New file-local variable. > (set_is_unwinding, unset_is_unwinding): New file-local helpers. > (frame_unwind_is_unwinding): New exported predicate. > (frame_unwind_try_handler): Arrange for > frame_unwind_is_unwinding to return true when unwinding the > innermost frame. > (frame_unwind_got_bytes): Make buf arg const. > * data-directory/Makefile.in (GUILE_SOURCE_FILES): Add > frame-unwinders.scm. > (GUILE_COMPILED_FILES): Add frame-unwinders.go. > * Makefile.in (SUBDIR_GUILE_OBS): Add scm-frame-unwinder.o. > (SUBDIR_GUILE_SRCS): Add scm-frame-unwinder.c > (scm-frame-unwinder.o): New target. > > gdb/testsuite/ChangeLog: > > * gdb.guile/scm-frame-unwinder.exp: > * gdb.guile/scm-frame-unwinder.c: > * gdb.guile/scm-frame-unwinder-gdb.scm.in: > * gdb.guile/scm-frame-unwinder.scm: Add unwinder tests. > --- > gdb/ChangeLog | 29 ++ > gdb/Makefile.in | 6 + > gdb/data-directory/Makefile.in | 2 + > gdb/doc/ChangeLog | 4 + > gdb/doc/guile.texi | 210 ++++++++ > gdb/frame-unwind.c | 38 +- > gdb/frame-unwind.h | 7 +- > gdb/frame.c | 16 + > gdb/guile/guile-internal.h | 1 + > gdb/guile/guile.c | 1 + > gdb/guile/lib/gdb/frame-unwinders.scm | 235 +++++++++ > gdb/guile/scm-frame-unwinder.c | 570 +++++++++++++++++++++ > gdb/guile/scm-symbol.c | 4 +- > gdb/testsuite/ChangeLog | 7 + > .../gdb.guile/scm-frame-unwinder-gdb.scm.in | 31 ++ > gdb/testsuite/gdb.guile/scm-frame-unwinder.c | 35 ++ > gdb/testsuite/gdb.guile/scm-frame-unwinder.exp | 90 ++++ > gdb/testsuite/gdb.guile/scm-frame-unwinder.scm | 41 ++ > 18 files changed, 1324 insertions(+), 3 deletions(-) > create mode 100644 gdb/guile/lib/gdb/frame-unwinders.scm > create mode 100644 gdb/guile/scm-frame-unwinder.c > create mode 100644 gdb/testsuite/gdb.guile/scm-frame-unwinder-gdb.scm.in > create mode 100644 gdb/testsuite/gdb.guile/scm-frame-unwinder.c > create mode 100644 gdb/testsuite/gdb.guile/scm-frame-unwinder.exp > create mode 100644 gdb/testsuite/gdb.guile/scm-frame-unwinder.scm > > diff --git a/gdb/ChangeLog b/gdb/ChangeLog > index 1f15ae6..9af328d 100644 > --- a/gdb/ChangeLog > +++ b/gdb/ChangeLog > @@ -1,5 +1,34 @@ > 2015-04-09 Andy Wingo > > + * guile/scm-symbol.c (gdbscm_lookup_symbol): Don't error if there > + is no selected frame and no block is selected; instead, fall back > + to the current frame. > + * guile/scm-frame-unwinder.c: New file. > + * guile/lib/gdb/frame-unwinders.scm: New file. > + * guile/guile.c (initialize_gdb_module): Call > + gdbscm_initialize_frame_unwinders. > + * guile/guile-internal.h (gdbscm_initialize_frame_unwinders): New > + declaration. > + * frame.c (get_prev_frame): Detect recursive unwinds, returning > + NULL in that case. > + * frame-unwind.h (frame_unwind_got_bytes): Make buf arg const. > + (frame_unwind_is_unwinding): New declaration. > + * frame-unwind.c (is_unwinding): New file-local variable. > + (set_is_unwinding, unset_is_unwinding): New file-local helpers. > + (frame_unwind_is_unwinding): New exported predicate. > + (frame_unwind_try_handler): Arrange for > + frame_unwind_is_unwinding to return true when unwinding the > + innermost frame. > + (frame_unwind_got_bytes): Make buf arg const. > + * data-directory/Makefile.in (GUILE_SOURCE_FILES): Add > + frame-unwinders.scm. > + (GUILE_COMPILED_FILES): Add frame-unwinders.go. > + * Makefile.in (SUBDIR_GUILE_OBS): Add scm-frame-unwinder.o. > + (SUBDIR_GUILE_SRCS): Add scm-frame-unwinder.c > + (scm-frame-unwinder.o): New target. > + > +2015-04-09 Andy Wingo > + > * guile/scm-frame-filter.c: > * guile/lib/gdb/frame-filters.scm: New files. > * guile/guile.c (guile_extension_ops): Add the Guile frame > diff --git a/gdb/Makefile.in b/gdb/Makefile.in > index 4c664ae..08f432b 100644 > --- a/gdb/Makefile.in > +++ b/gdb/Makefile.in > @@ -322,6 +322,7 @@ SUBDIR_GUILE_OBS = \ > scm-exception.o \ > scm-frame.o \ > scm-frame-filter.o \ > + scm-frame-unwinder.o \ > scm-gsmob.o \ > scm-iterator.o \ > scm-lazy-string.o \ > @@ -349,6 +350,7 @@ SUBDIR_GUILE_SRCS = \ > guile/scm-exception.c \ > guile/scm-frame.c \ > guile/scm-frame-filter.c \ > + guile/scm-frame-unwinder.c \ > guile/scm-gsmob.c \ > guile/scm-iterator.c \ > guile/scm-lazy-string.c \ > @@ -2440,6 +2442,10 @@ scm-frame-filter.o: $(srcdir)/guile/scm-frame-filter.c > $(COMPILE) $(srcdir)/guile/scm-frame-filter.c > $(POSTCOMPILE) > > +scm-frame-unwinder.o: $(srcdir)/guile/scm-frame-unwinder.c > + $(COMPILE) $(srcdir)/guile/scm-frame-unwinder.c > + $(POSTCOMPILE) > + > scm-gsmob.o: $(srcdir)/guile/scm-gsmob.c > $(COMPILE) $(srcdir)/guile/scm-gsmob.c > $(POSTCOMPILE) > diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in > index 21e44b9..b36506c 100644 > --- a/gdb/data-directory/Makefile.in > +++ b/gdb/data-directory/Makefile.in > @@ -90,6 +90,7 @@ GUILE_SOURCE_FILES = \ > gdb/boot.scm \ > gdb/experimental.scm \ > gdb/frame-filters.scm \ > + gdb/frame-unwinders.scm \ > gdb/init.scm \ > gdb/iterator.scm \ > gdb/printing.scm \ > @@ -102,6 +103,7 @@ GUILE_NO_UNBOUND_WARNING_COMPILED_FILES = \ > GUILE_COMPILED_FILES = \ > gdb/experimental.go \ > gdb/frame-filters.go \ > + gdb/frame-unwinders.go \ > gdb/iterator.go \ > gdb/printing.go \ > gdb/support.go \ > diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog > index bcff616..d637482 100644 > --- a/gdb/doc/ChangeLog > +++ b/gdb/doc/ChangeLog > @@ -1,5 +1,9 @@ > 2015-04-09 Andy Wingo > > + * guile.texi (Guile Frame Unwinder API): New section. > + > +2015-04-09 Andy Wingo > + > * guile.texi (Guile Frame Filter API) > (Writing a Frame Filter in Guile): New sections. > > diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi > index e2826f0..6c850a4 100644 > --- a/gdb/doc/guile.texi > +++ b/gdb/doc/guile.texi > @@ -143,6 +143,7 @@ from the Guile interactive prompt. > * Writing a Guile Pretty-Printer:: Writing a pretty-printer > * Guile Frame Filter API:: Filtering frames. > * Writing a Frame Filter in Guile:: Writing a frame filter. > +* Guile Frame Unwinder API:: Programmatically unwinding stack frames > * Commands In Guile:: Implementing new commands in Guile > * Parameters In Guile:: Adding new @value{GDBN} parameters > * Progspaces In Guile:: Program spaces > @@ -2119,6 +2120,215 @@ also possible to do the job of an decorator with a filter. Still, > avoiding the stream interfaces can often be a good reason to use the > simpler decorator layer. > > +@node Guile Frame Unwinder API > +@subsubsection Unwinding Frames in Guile > +@cindex frame unwinder api, guile > + > +In @value{GDBN} terminology, ``unwinding'' is the process of finding > +an older (outer) frame on the stack. Unwinders form the core of > +backtrace computation in @value{GDBN}. @value{GDBN} comes with > +unwinders for each target architecture that it supports, and these > +usually suffice to unwind the stack. However, some target programs > +can have non-standard frame layouts that cannot be unwound by the > +standard unwinders. This is often the case when working with > +just-in-time compilation environments, for example in JavaScript > +implementations. In such cases, users can define custom code in Guile > +to programmatically unwind the problematic stack frames. > + > +Before getting into the API, we should discuss how unwinders work in > +@value{GDBN}. > + > +As an example, consider a stack in which we have already computed > +frame 0 and we want to compute frame 1. We say that frame 0 is the > +``inner'' frame, and frame 1 will be the ``outer'' frame. > + > +Unwinding starts with a model of the state of all registers in an > +inner, already unwound frame. In our case, we start with frame 0. > +@value{GDBN} then constructs a provisional frame object for the outer > +frame that is being built (frame 1) and links it to the inner frame > +(frame 0). @value{GDBN} then goes through its list of registered > +unwinders, searching for one that knows how to compute the register > +state in frame 1, given the state of frame 0. @value{GDBN} will then > +ask the chosen unwinder to compute a frame identifier for the outer > +frame. Once the unwinder has done so, the frame is marked as > +``valid'' and can be accessed using the normal frame API. > + > +A frame identifier (frame ID) consists of code and data pointers > +associated with a frame which will remain valid as long as the frame > +is still alive. Usually a frame ID is built from the code and stack > +pointers as they were when control entered the function associated > +with the frame, though as described below there are other ways to > +build a frame ID@. However as you can see, computing the frame ID > +requires some input from the unwinder to determine the start code > +address (PC) and the frame pointer (FP), especially on platforms that > +don't dedicate a register to the FP. > + > +(Given this description, you might wonder how the frame ID for the > +innermost frame (frame 0) is unwound, given that unwinding requires an > +inner frame. The answer is that internally, @value{GDBN} always has a > +``sentinel'' frame that is inner to the innermost frame, and which has > +a pre-computed unwinder that just returns the registers as they are, > +without unwinding.) > + > +The Guile frame unwinder API loosely follows this protocol as > +described above. Guile will build a special ``pending frame object'' > +corresponding the frame being unwound (in our example, frame 1). It to the frame > +allows the user to read registers from that pending frame, which in > +reality are unwound from the already-existing frame 0. If the > +unwinder decides that it can handle the frame in question, it then > +creates and returns an ``unwind info'' object for that frame. The > +unwind info object contains a frame ID for the pending frame. It also > +records the values of any registers saved in the frame, for use when > +unwinding its outer frame (frame 2). > + > +Frame unwinder objects are managed in Guile much in the same way as > +frame filters. Indeed, users will often want to implement both frame > +unwinders and frame filters: unwinders will compute the correct > +backtrace and register state, and filters can fill in function names, > +line numbers, and the like. @xref{Guile Frame Filter API}, for more > +on frame filters. > + > +As with frame filters, there can be multiple frame unwinders > +registered with @value{GDBN}, and each one may be individually enabled > +or disabled at will. The filters will be tried in priority order, > +from highest to lowest priority, and the first one that sets the frame > +ID will take responsibility for the frame. > + > +To use frame unwinders, first load the @code{(gdb frame-unwinders)} module > +to have access to the procedures that manipulate frame unwinders: > + > +@example > +(use-modules (gdb frame-unwinders)) > +@end example > + > +@deffn {Scheme Procedure} make-frame-unwinder name procedure @ > + @r{[}#:priority priority@r{]} @r{[}#:enabled? boolean@r{]} > +Make a new frame unwinder. > + > +The unwinder will be identified by the string @var{name}. > +@var{procedure} should be a function of one argument, taking a pending > +frame object. If the unwinder procedure decides to handle the frame, > +it should return an unwind info object. Otherwise if the unwinder > +returns @code{#f}, @value{GDBN} will continue to search its list for > +an unwinder. > + > +The unwinder will be initially enabled, unless the keyword argument > +@code{#:enabled? #f} is given. Even if the unwinder is marked as > +enabled, it will need to be registered with @value{GDBN} via > +@code{register-frame-unwinder!} in order to take effect. > +@end deffn > + > +@deffn {Scheme Procedure} all-frame-unwinders > +Return a list of all frame unwinders. > +@end deffn > + > +@deffn {Scheme Procedure} register-frame-unwinder! unwinder @ > + @r{[}#:locus locus@r{]} > +Register the frame unwinder @var{unwinder} with @value{GDBN}. > + > +The unwinder will be associated with a specific ``locus'', which may > +be an objfile, a progspace, or the ``global'' locus, indicating that > +the unwinder should run for all progspaces. The default value of > +@code{#f} for @var{locus} indicates that the unwinder should be > +registered globally. Pass an objfile or a progspace as the > +@code{#:locus} keyword argument to associate the unwinder with a > +specific objfile or progspace, respectively. > +@end deffn > + > +Registering an unwinder on an objfile or a progspace locus has the > +advantage that the unwinder will go away when the objfile or progspace > +is unloaded. To explicitly remove an unwinder from GDB, use > +@code{remove-frame-unwinder!}. > + > +@deffn {Scheme Procedure} remove-frame-unwinder! unwinder > +Removes the frame unwinder @var{unwinder} from @value{GDBN}. > +@end deffn > + > +@deffn {Scheme Procedure} set-frame-unwinder-enabled! unwinder enabled? > +Mark a frame unwinder as enabled, if @var{enabled?} is true, or as > +disabled otherwise. > +@end deffn > + > +Unwinders are enabled when they are created, unless @code{#:enabled? > +#f} is passed to @code{make-frame-unwinder}. This sentence can be deleted, just repeats what's said above. > + > +@deffn {Scheme Procedure} frame-unwinder-name unwinder > +@deffnx {Scheme Procedure} frame-unwinder-enabled? unwinder > +@deffnx {Scheme Procedure} frame-unwinder-registered? unwinder > +@deffnx {Scheme Procedure} frame-unwinder-priority unwinder > +@deffnx {Scheme Procedure} frame-unwinder-procedure unwinder > +@deffnx {Scheme Procedure} frame-unwinder-locus unwinder > +Accessors for a frame unwinder object's fields. The > +@code{registered?} field indicates whether a unwinder has been added > +to @value{GDBN} or not. @code{locus} is the objfile or progspace in > +which the unwinder was registered, or @code{#f} otherwise. > +@end deffn > + > +Frame unwinders operate on ``pending frames''. Pending frames are > +valid only while they are being unwound; any access to a pending frame > +outside the extent of their unwind operation will signal an error. > +Currently, pending frames can only be used in two limited ways: to > +read registers from the frame, and to construct an unwind info object > +for a successful unwinder return. > + > +@deffn {Scheme Procedure} pending-frame-read-register frame register > +Return the value of a register in the pending frame @var{frame}. > +@var{register} should be given as a string. > +@end deffn > + > +If an unwinder successfully unwinds a frame, it should return an > +unwind info object created via the @code{make-unwind-info} procedure. > +An unwind info object specifies the frame ID for its associated > +pending frame, and also records values of registers that are saved > +within the frame. > + > +@deffn {Scheme Procedure} make-unwind-info frame frame-id > +Make an unwind info object for the pending frame @var{frame}, > +specifying @var{frame-id} as its frame ID. @var{frame-id} should be > +the result of a call to @code{make-frame-id}. > +@end deffn > + > +@deffn {Scheme Procedure} make-frame-id fp @r{[}#:pc pc@r{]} @r{[}#:special special@r{]} > +Build a frame ID object from the given @var{fp}, @var{pc}, and > +@var{special} values. > + > +A frame ID is a unique name for a frame that remains valid as long as > +the frame itself is valid. Usually the frame ID is built from from s/from from/from/ > +the frame's stack address and code address. The stack address > +@var{fp} should normally be a pointer to the new end of the stack when > +the function was called, as a @value{GDBN} value. Similarly the code > +address @var{pc} should be given as the address of the entry point of > +the function. > + > +The @var{fp} argument is required. While it is a good idea to specify > +a @var{pc}, a code address is not required to build a frame ID, and so > +@var{pc} is a keyword argument. Call @code{make-frame-id} with > +a @code{#:pc @var{pc}} argument to specify a code address. > + > +Some architectures have another stack or some other frame state store; > +currently this is only the case for ia64. For these odd platforms the > +frame ID needs an additional address, which may be passed as the > +@var{special} argument, via the @code{#:special} keyword. > +@end deffn. > + > +After building an unwind info object for a pending frame, an unwinder > +can call @code{unwind-info-add-saved-register!} to record saved > +registers. The values of the saved registers logically belong to the > +frame that is older than the pending frame being unwound, not to the > +pending frame itself. > + > +@deffn {Scheme Procedure} unwind-info-add-saved-register! unwind-info register value > +Set the saved value of a register in a pending frame. > + > +@var{register} names a register, and should be a string, for example > +@samp{rip}. @var{value} is the register value, as a @value{GDBN} > +value. > +@end deffn > + > +Any register whose value is not recorded as saved via > +@code{unwind-info-add-saved-register!} will be marked as ``not saved'' > +in the outer frame. Copying from discussion in the other thread, how about: "... will be marked as ``not saved'' in the pending frame being unwound." Or one could say both: "... will be marked as ``not saved'' in the outer frame (the pending frame being unwound)." Or some such. > + > @node Commands In Guile > @subsubsection Commands In Guile > > diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c > index bba1ae7..4347dca 100644 > --- a/gdb/frame-unwind.c > +++ b/gdb/frame-unwind.c > @@ -87,6 +87,34 @@ frame_unwind_append_unwinder (struct gdbarch *gdbarch, > (*ip)->unwinder = unwinder; > } > > +/* Nonzero if we are finding the unwinder for a frame; see > + frame_unwind_try_handler. */ > +static int is_unwinding = 0; > + > +/* Return nonzero if we are inside a sniffer call. */ > + > +int > +frame_unwind_is_unwinding (void) > +{ > + return is_unwinding; > +} > + > +/* Cleanup helpers for is_unwinding. */ > + > +static void > +unset_is_unwinding (void *unused) > +{ > + is_unwinding = 0; > +} > + > +static struct cleanup* > +set_is_unwinding (void) > +{ > + is_unwinding = 1; > + > + return make_cleanup (unset_is_unwinding, NULL); > +} > + > /* Call SNIFFER from UNWINDER. If it succeeded set UNWINDER for > THIS_FRAME and return 1. Otherwise the function keeps THIS_FRAME > unchanged and returns 0. */ > @@ -98,11 +126,18 @@ frame_unwind_try_unwinder (struct frame_info *this_frame, void **this_cache, > struct cleanup *old_cleanup; > int res = 0; > > + if (is_unwinding) > + internal_error (__FILE__, __LINE__, > + _("Recursion detected while finding an unwinder.")); > old_cleanup = frame_prepare_for_sniffer (this_frame, unwinder); > > TRY > { > + struct cleanup *cleanup = set_is_unwinding (); > + > res = unwinder->sniffer (unwinder, this_frame, this_cache); > + > + do_cleanups (cleanup); > } > CATCH (ex, RETURN_MASK_ERROR) > { > @@ -252,7 +287,8 @@ frame_unwind_got_constant (struct frame_info *frame, int regnum, > } > > struct value * > -frame_unwind_got_bytes (struct frame_info *frame, int regnum, gdb_byte *buf) > +frame_unwind_got_bytes (struct frame_info *frame, int regnum, > + const gdb_byte *buf) > { > struct gdbarch *gdbarch = frame_unwind_arch (frame); > struct value *reg_val; > diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h > index 44add12..3e322f2 100644 > --- a/gdb/frame-unwind.h > +++ b/gdb/frame-unwind.h > @@ -179,6 +179,11 @@ extern void frame_unwind_append_unwinder (struct gdbarch *gdbarch, > extern void frame_unwind_find_by_frame (struct frame_info *this_frame, > void **this_cache); > > +/* Return nonzero if we are in the process of finding an unwinder for a frame. > + See the comments in get_current_frame. */ > + > +extern int frame_unwind_is_unwinding (void); > + > /* Helper functions for value-based register unwinding. These return > a (possibly lazy) value of the appropriate type. */ > > @@ -210,7 +215,7 @@ struct value *frame_unwind_got_constant (struct frame_info *frame, int regnum, > inside BUF. */ > > struct value *frame_unwind_got_bytes (struct frame_info *frame, int regnum, > - gdb_byte *buf); > + const gdb_byte *buf); > > /* Return a value which indicates that FRAME's saved version of REGNUM > has a known constant (computed) value of ADDR. Convert the > diff --git a/gdb/frame.c b/gdb/frame.c > index b3cbf23..9e63f21 100644 > --- a/gdb/frame.c > +++ b/gdb/frame.c > @@ -2212,6 +2212,22 @@ get_prev_frame (struct frame_info *this_frame) > return NULL; > } > > + /* Unwinders implemented in Python or Scheme could eventually make an API call > + that would cause GDB to try to unwind a frame while unwinding a frame. > + Because already-unwound frames will be found in the frame cache, unwinding > + will only happen at the old end of the stack, which means that any > + recursive unwinding attempt will surely lead to unbounded recursion. Ways > + this can happen include such common functions as `get_current_arch' or > + `lookup_symbol', via `get_selected_frame', so it's impractical to simply > + declare these an error. Instead, we detect this case and return NULL, > + indicating that the known stack of frames ends here. */ > + if (frame_unwind_is_unwinding ()) > + { > + frame_debug_got_null_frame (this_frame, > + "get_prev_frame within unwinder sniffer"); > + return NULL; > + } > + > return get_prev_frame_always (this_frame); > } > > diff --git a/gdb/guile/guile-internal.h b/gdb/guile/guile-internal.h > index d7c166c..9ca807e 100644 > --- a/gdb/guile/guile-internal.h > +++ b/gdb/guile/guile-internal.h > @@ -598,6 +598,7 @@ extern void gdbscm_initialize_disasm (void); > extern void gdbscm_initialize_exceptions (void); > extern void gdbscm_initialize_frames (void); > extern void gdbscm_initialize_frame_filters (void); > +extern void gdbscm_initialize_frame_unwinders (void); > extern void gdbscm_initialize_iterators (void); > extern void gdbscm_initialize_lazy_strings (void); > extern void gdbscm_initialize_math (void); > diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c > index 176e97e..d1850e7 100644 > --- a/gdb/guile/guile.c > +++ b/gdb/guile/guile.c > @@ -670,6 +670,7 @@ initialize_gdb_module (void *data) > gdbscm_initialize_disasm (); > gdbscm_initialize_frames (); > gdbscm_initialize_frame_filters (); > + gdbscm_initialize_frame_unwinders (); > gdbscm_initialize_iterators (); > gdbscm_initialize_lazy_strings (); > gdbscm_initialize_math (); > diff --git a/gdb/guile/lib/gdb/frame-unwinders.scm b/gdb/guile/lib/gdb/frame-unwinders.scm > new file mode 100644 > index 0000000..d668d01 > --- /dev/null > +++ b/gdb/guile/lib/gdb/frame-unwinders.scm > @@ -0,0 +1,235 @@ > +;; Frame unwinder support. > +;; > +;; Copyright (C) 2015 Free Software Foundation, Inc. > +;; > +;; This file is part of GDB. > +;; > +;; This program is free software; you can redistribute it and/or modify > +;; it under the terms of the GNU General Public License as published by > +;; the Free Software Foundation; either version 3 of the License, or > +;; (at your option) any later version. > +;; > +;; This program is distributed in the hope that it will be useful, > +;; but WITHOUT ANY WARRANTY; without even the implied warranty of > +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +;; GNU General Public License for more details. > +;; > +;; You should have received a copy of the GNU General Public License > +;; along with this program. If not, see . > + > +(define-module (gdb frame-unwinders) > + #:use-module ((gdb) #:hide (frame? symbol?)) > + #:use-module (srfi srfi-1) > + #:use-module (srfi srfi-9) > + #:use-module (ice-9 match) > + #:export (pending-frame-read-register > + > + make-frame-id > + > + make-unwind-info > + unwind-info-add-saved-register! > + > + make-frame-unwinder > + frame-unwinder? > + frame-unwinder-name > + frame-unwinder-enabled? > + frame-unwinder-registered? > + frame-unwinder-procedure > + frame-unwinder-locus > + > + find-frame-unwinder-by-name > + > + register-frame-unwinder! > + remove-frame-unwinder! > + set-frame-unwinder-enabled! > + > + all-frame-unwinders)) > + > +(define-record-type > + (%make-frame-id sp pc special) > + frame-id? > + (sp frame-id-sp) > + (pc frame-id-pc) > + (special frame-id-special)) > + > +(define* (make-frame-id sp #:key pc special) > + "Make a new frame identifier, or \"frame ID\". > + > +A frame ID is a unique name for a frame that remains valid as long as > +the frame itself is valid. > + > +Usually the frame ID is built from from the frame's stack address and > +code address. The stack address, SP, should normally be a pointer to > +the new end of the stack when the function was called, as a GDB value. > + > +It is possible to create a frame ID with just an SP, but it's better to > +specify a code address (PC) via the #:pc keyword argument. The PC > +indicates the address of the entry point of the function. > + > +Some architectures have another stack or some other frame state store, > +like ia64, and they need an additional address, which may be passed as > +the #:special keyword argument." > + (%make-frame-id sp pc special)) > + > +;; Silence unbound-toplevel warnings until we can load the extension. > +(define %make-unwind-info #f) > + > +(define (make-unwind-info pending-frame frame-id) > + "Make an unwind info object for a given pending frame. FRAME-ID will > +the frame ID of the frame." be the ... > + (%make-unwind-info pending-frame > + (frame-id-sp frame-id) > + (frame-id-pc frame-id) > + (frame-id-special frame-id))) > + > +(define-record-type > + (%make-frame-unwinder name enabled? registered? procedure locus) > + frame-unwinder? > + ;; string > + (name frame-unwinder-name) > + ;; bool > + (enabled? frame-unwinder-enabled? set-enabled?!) > + ;; bool > + (registered? frame-unwinder-registered? set-registered?!) > + ;; pending-frame -> unwind-info | #f > + (procedure frame-unwinder-procedure) > + ;; objfile | progspace | #f > + (locus frame-unwinder-locus set-locus!)) > + > +(define* (make-frame-unwinder name procedure #:key (enabled? #t)) > + "Make and return a new frame unwinder. NAME and PROCEDURE are > +required arguments. Specify #:enabled? to set the enabled status of the > +unwinder. > + > +The unwinder must be registered with GDB via `register-frame-unwinder!' > +before it is active." > + (let ((registered? #f) (locus #f)) > + (%make-frame-unwinder name enabled? registered? procedure locus))) > + > +;; List of frame unwinders. > +(define *frame-unwinders* '()) > + > +(define (same-scope? a b) > + "Return #t if A and B represent the same scope, for the purposes of > +frame unwinder selection." > + (cond > + ;; If either is the global locus, they share a scope. > + ((or (not a) (not b)) #t) > + ;; If either is an objfile, compare their progspaces, unless the > + ;; objfile is invalid. > + ((objfile? a) (and (objfile-valid? a) > + (same-scope? (objfile-progspace a) b))) > + ((objfile? b) (and (objfile-valid? b) > + (same-scope? a (objfile-progspace b)))) > + ;; Otherwise they are progspaces. If they eq?, it's the same scope. > + (else (eq? a b)))) > + > +(define (is-valid? unwinder) > + "Return #t if the locus of UNWINDER is still valid, or otherwise #f if > +the objfile or progspace has been removed from GDB." > + (let ((locus (frame-unwinder-locus unwinder))) > + (cond > + ((progspace? locus) (progspace-valid? locus)) > + ((objfile? locus) (objfile-valid? locus)) > + (else #t)))) > + > +(define (prune-frame-unwinders!) > + "Prune frame unwinders whose objfile or progspace has gone away." > + (unless (and-map is-valid? *frame-unwinders*) > + (set! *frame-unwinders* > + (let lp ((unwinders *frame-unwinders*)) > + (match unwinders > + (() '()) > + ((f . unwinders) > + (cond > + ((is-valid? f) > + (cons f (lp unwinders))) > + (else > + (set-registered?! f #f) > + (lp unwinders))))))))) > + > +(define (all-frame-unwinders) > + "Return a list of all registered frame unwinders." > + (prune-frame-unwinders!) > + ;; Copy the list to prevent callers from mutating our state. > + (list-copy *frame-unwinders*)) > + > +(define* (register-frame-unwinder! unwinder #:key locus replace?) > + "Register a frame unwinder with GDB. Frame unwinders must be > +registered before they will be used to unwinder backtraces. > + > +By default, the unwinder will be registered globally. Specify an > +objfile or a progspace as the #:locus keyword argument to instead > +register the unwinder as belonging to a specific objfile or progspace. > + > +The unwinder's name must be unique within its locus. GDB will raise an > +exception if the locus already has a unwinder registered with the given > +name, unless a true value is passed as the #:replace? keyword argument, > +in which case any existing unwinder will be removed from its locus." > + (define (check-locus!) > + (cond > + ((objfile? locus) > + (unless (objfile-valid? locus) > + (error "Objfile is not valid" locus))) > + ((progspace? locus) > + (unless (progspace-valid? locus) > + (error "Progspace is not valid" locus))) > + (locus > + (error "Invalid locus" locus)))) > + (define (duplicate-unwinder? other) > + (and (equal? (frame-unwinder-name other) > + (frame-unwinder-name unwinder)) > + (eq? (frame-unwinder-locus other) locus))) > + > + (when (frame-unwinder-registered? unwinder) > + (error "Frame unwinder is already registered with GDB" unwinder)) > + (check-locus!) > + (prune-frame-unwinders!) > + (let ((found (find duplicate-unwinder? *frame-unwinders*))) > + (when found > + (if replace? > + (remove-frame-unwinder! found) > + (error "Frame unwinder with this name already present in locus" > + (frame-unwinder-name filter))))) > + (set-registered?! unwinder #t) > + (set-locus! unwinder locus) > + (set! *frame-unwinders* (cons unwinder *frame-unwinders*))) > + > +(define (unregister-frame-unwinder! unwinder) s/unregister/remove/ > + "Unregister a frame unwinder." > + (set-registered?! unwinder #f) > + (set-locus! unwinder #f) > + (set! *frame-unwinders* (delq unwinder *frame-unwinders*))) > + > +(define* (find-frame-unwinder-by-name name #:optional locus) > + (prune-frame-unwinders!) > + ;; First check for names within the locus, then within the scope. > + (or (find (lambda (unwinder) > + (and (equal? name (frame-unwinder-name unwinder)) > + (eq? (frame-unwinder-locus unwinder) locus))) > + *frame-unwinders*) > + (find (lambda (unwinder) > + (and (equal? name (frame-unwinder-name unwinder)) > + (same-scope? (frame-unwinder-locus unwinder) locus))) > + *frame-unwinders*) > + (error "no frame unwinder found with name" name))) > + > +(define (set-frame-unwinder-enabled! unwinder enabled?) > + "Enable or disable a frame unwinder." > + (set-enabled?! unwinder enabled?) > + *unspecified*) > + > +(define (unwind-frame frame) > + (define (try-unwind locus) > + (or-map (lambda (unwinder) > + (and (frame-unwinder-enabled? unwinder) > + (eq? (frame-unwinder-locus unwinder) locus) > + ((frame-unwinder-procedure unwinder) frame))) > + *frame-unwinders*)) > + (prune-frame-unwinders!) > + (or (try-unwind (current-objfile)) > + (try-unwind (current-progspace)) > + ;; Try global unwinders last. > + (try-unwind #f))) > + > +(load-extension "gdb" "gdbscm_load_frame_unwinders") > diff --git a/gdb/guile/scm-frame-unwinder.c b/gdb/guile/scm-frame-unwinder.c > new file mode 100644 > index 0000000..363830c > --- /dev/null > +++ b/gdb/guile/scm-frame-unwinder.c > @@ -0,0 +1,570 @@ > +/* Scheme frame unwinding interface. > + > + Copyright (C) 2015 Free Software Foundation, Inc. > + > + This file is part of GDB. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 3 of the License, or > + (at your option) any later version. > + > + This program is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + GNU General Public License for more details. > + > + You should have received a copy of the GNU General Public License > + along with this program. If not, see . */ > + > +/* See README file in this directory for implementation notes, coding > + conventions, et.al. */ > + > +#include "defs.h" > +#include "arch-utils.h" > +#include "frame-unwind.h" > +#include "gdb_obstack.h" > +#include "guile-internal.h" > +#include "inferior.h" > +#include "language.h" > +#include "observer.h" > +#include "regcache.h" > +#include "user-regs.h" > +#include "value.h" > + > +/* Non-zero if the (gdb frame-unwinders) module has been loaded. */ > +static int gdbscm_frame_unwinders_loaded = 0; > + > +/* The captured unwind-frame variable. */ > +static SCM unwind_frame = SCM_BOOL_F; > + > +/* Key that we use when associating data with an architecture. */ > +static struct gdbarch_data *uwscm_gdbarch_data; > + > +/* The frame unwinder interface makes pending frame objects when trying > + to unwind frames, and unwind info objects when unwinding is successful. > + Here we define the names for the pending frame and unwind info Scheme > + data types. */ > +static const char pending_frame_smob_name[] = "gdb:pending-frame"; > +static const char unwind_info_smob_name[] = "gdb:unwind-info"; > + > +/* SMOB tag for pending frames and unwind info. */ > +static scm_t_bits pending_frame_smob_tag; > +static scm_t_bits unwind_info_smob_tag; > + > +/* Data associated with a pending frame. */ > +struct uwscm_pending_frame > +{ > + /* The frame being unwound, used for the read-register interface. */ > + struct frame_info *this_frame; > + > + /* The architecture of the frame, here for convenience. */ > + struct gdbarch *gdbarch; > +}; > + > +/* Data associated with an unwind info object. */ > +struct uwscm_unwind_info > +{ > + /* The associated pending frame. */ > + SCM frame; > + > + /* The frame_id for the associated pending frame. */ > + struct frame_id frame_id; > + > + /* A list of (REGNUM . VALUE) pairs, indicating register values for the > + associated pending frame. */ > + SCM registers; > +}; > + > +/* Type predicate for pending frames. */ > + > +static int > +uwscm_is_pending_frame (SCM obj) > +{ > + return SCM_SMOB_PREDICATE (pending_frame_smob_tag, obj); > +} > + > +/* Data accessor for pending frames. */ > + > +static struct uwscm_pending_frame * > +uwscm_pending_frame_data (SCM obj) > +{ > + gdb_assert (uwscm_is_pending_frame (obj)); > + return (struct uwscm_pending_frame *) SCM_SMOB_DATA (obj); > +} > + > +/* Type predicate for unwind info. */ > + > +static int > +uwscm_is_unwind_info (SCM obj) > +{ > + return SCM_SMOB_PREDICATE (unwind_info_smob_tag, obj); > +} > + > +/* Data accessor for unwind_info. */ > + > +static struct uwscm_unwind_info * > +uwscm_unwind_info_data (SCM obj) > +{ > + gdb_assert (uwscm_is_unwind_info (obj)); > + return (struct uwscm_unwind_info *) SCM_SMOB_DATA (obj); > +} > + > +/* Build a pending frame. */ > + > +static SCM > +uwscm_make_pending_frame (struct frame_info *this_frame) > +{ > + struct uwscm_pending_frame *data; > + > + data = scm_gc_malloc (sizeof (*data), pending_frame_smob_name); > + > + data->this_frame = this_frame; > + TRY > + { > + data->gdbarch = get_frame_arch (this_frame); > + } > + CATCH (except, RETURN_MASK_ALL) > + { > + GDBSCM_HANDLE_GDB_EXCEPTION (except); > + } > + END_CATCH > + > + SCM_RETURN_NEWSMOB (pending_frame_smob_tag, data); > +} > + > +/* Pending frames may only be accessed from Scheme within the dynamic > + extent of the unwind callback. */ > + > +static int > +uwscm_pending_frame_is_valid (SCM pending_frame) > +{ > + return uwscm_pending_frame_data (pending_frame)->this_frame != NULL; > +} > + > +/* Is this a pending frame that is accessible from Scheme? */ > + > +static int > +uwscm_is_valid_pending_frame (SCM obj) > +{ > + return uwscm_is_pending_frame (obj) && uwscm_pending_frame_is_valid (obj); > +} > + > +/* Is this an unwind info whose associated pending frame is valid? */ > + > +static int > +uwscm_is_valid_unwind_info (SCM obj) > +{ > + return uwscm_is_unwind_info (obj) > + && uwscm_pending_frame_is_valid (uwscm_unwind_info_data (obj)->frame); > +} > + > +/* Called as the unwind callback finishes to invalidate the pending > + frame. */ > + > +static void > +uwscm_invalidate_pending_frame (SCM pending_frame) > +{ > + gdb_assert (uwscm_pending_frame_is_valid (pending_frame)); > + uwscm_pending_frame_data (pending_frame)->this_frame = NULL; > +} > + > +/* Raise a Scheme exception if OBJ is not a valid pending frame. */ > + > +static void > +uwscm_assert_valid_pending_frame (SCM obj, const char *func_name, int pos) > +{ > + if (!uwscm_is_valid_pending_frame (obj)) > + gdbscm_throw (gdbscm_make_type_error (func_name, pos, obj, > + "valid ")); > +} > + > +/* Raise a Scheme exception if OBJ is not an unwind info object whose > + associated pending frame is valid. */ > + > +static void > +uwscm_assert_valid_unwind_info (SCM obj, const char *func_name, int pos) > +{ > + if (!uwscm_is_valid_unwind_info (obj)) > + gdbscm_throw (gdbscm_make_type_error (func_name, pos, obj, > + "valid ")); > +} > + > +/* Helper to convert a frame ID component to a CORE_ADDR. */ > + > +static CORE_ADDR > +uwscm_value_to_addr (SCM value, int arg) > +{ > + struct value *c_value; > + CORE_ADDR ret; > + > + if (!vlscm_is_value (value)) > + gdbscm_throw (gdbscm_make_type_error ("make-unwind-info", > + arg, value, " object")); > + > + c_value = vlscm_scm_to_value (value); > + > + TRY > + { > + ret = value_as_address (c_value); > + } > + CATCH (except, RETURN_MASK_ALL) > + { > + GDBSCM_HANDLE_GDB_EXCEPTION (except); > + } > + END_CATCH > + > + return ret; > +} > + > +/* (%make-unwind-info pending-frame sp pc special) > + > + Create an unwind info structure for this pending frame. Wrapped in Scheme to > + take a frame ID record. */ > + > +static SCM > +gdbscm_make_unwind_info (SCM pending_frame, SCM sp, SCM pc, SCM special) > +{ > + struct uwscm_unwind_info *data; > + struct frame_id frame_id; > + > + uwscm_assert_valid_pending_frame (pending_frame, FUNC_NAME, SCM_ARG1); > + > + if (gdbscm_is_false (pc)) > + frame_id = frame_id_build_wild (uwscm_value_to_addr (sp, SCM_ARG2)); > + else if (gdbscm_is_false (special)) > + frame_id = frame_id_build (uwscm_value_to_addr (sp, SCM_ARG2), > + uwscm_value_to_addr (pc, SCM_ARG3)); > + else > + frame_id = frame_id_build_special (uwscm_value_to_addr (sp, SCM_ARG2), > + uwscm_value_to_addr (pc, SCM_ARG3), > + uwscm_value_to_addr (special, SCM_ARG4)); > + > + data = scm_gc_malloc (sizeof (*data), unwind_info_smob_name); > + > + data->frame = pending_frame; > + data->frame_id = frame_id; > + data->registers = SCM_EOL; > + > + SCM_RETURN_NEWSMOB (unwind_info_smob_tag, data); > +} > + > +/* Convert the string REGISTER_SCM to a register number for the given > + architecture. */ > + > +static int > +uwscm_scm_to_regnum (SCM register_scm, struct gdbarch *gdbarch, > + const char *func_name, int arg) > +{ > + int regnum; > + struct cleanup *cleanup; > + char *register_str; > + > + gdbscm_parse_function_args (func_name, arg, NULL, > + "s", register_scm, ®ister_str); > + cleanup = make_cleanup (xfree, register_str); > + > + TRY > + { > + regnum = user_reg_map_name_to_regnum (gdbarch, register_str, > + strlen (register_str)); > + } > + CATCH (except, RETURN_MASK_ALL) > + { > + do_cleanups (cleanup); > + GDBSCM_HANDLE_GDB_EXCEPTION (except); > + } > + END_CATCH > + > + do_cleanups (cleanup); > + > + if (regnum < 0) > + gdbscm_out_of_range_error (func_name, arg, > + register_scm, _("unknown register")); > + > + return regnum; > +} > + > +/* (pending-frame-read-register string) > + -> > + > + Sniffs a register value from a pending frame. */ > + > +static SCM > +gdbscm_pending_frame_read_register (SCM pending_frame, SCM register_scm) > +{ > + struct uwscm_pending_frame *data; > + struct value *value = NULL; > + int regnum; > + > + uwscm_assert_valid_pending_frame (pending_frame, FUNC_NAME, SCM_ARG1); > + data = uwscm_pending_frame_data (pending_frame); > + regnum = uwscm_scm_to_regnum (register_scm, data->gdbarch, FUNC_NAME, > + SCM_ARG2); > + > + TRY > + { > + value = get_frame_register_value (data->this_frame, regnum); > + } > + CATCH (except, RETURN_MASK_ALL) > + { > + GDBSCM_HANDLE_GDB_EXCEPTION (except); > + } > + END_CATCH > + > + if (value == NULL) > + gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2, register_scm, > + _("cannot read register from frame")); > + > + return vlscm_scm_from_value (value); > +} > + > +/* (unwind-info-add-saved-register! unwind-info register value) > + > + Records the saved value of a particular register in UNWIND_INFO's > + corresponding pending frame. REGISTER_SCM names the register, as a > + string, and VALUE_SCM is a . */ > + > +static SCM > +gdbscm_unwind_info_add_saved_register_x (SCM unwind_info, SCM register_scm, > + SCM value_scm) > +{ > + struct uwscm_unwind_info *data; > + struct uwscm_pending_frame *frame_data; > + struct value *value; > + int regnum; > + int value_size; > + > + uwscm_assert_valid_unwind_info (unwind_info, FUNC_NAME, SCM_ARG1); > + data = uwscm_unwind_info_data (unwind_info); > + frame_data = uwscm_pending_frame_data (data->frame); > + regnum = uwscm_scm_to_regnum (register_scm, frame_data->gdbarch, > + FUNC_NAME, SCM_ARG2); > + > + if (!vlscm_is_value (value_scm)) > + gdbscm_throw (gdbscm_make_type_error (FUNC_NAME, SCM_ARG3, > + value_scm, > + " object")); > + > + value = vlscm_scm_to_value (value_scm); > + value_size = TYPE_LENGTH (value_enclosing_type (value)); > + > + if (value_size != register_size (frame_data->gdbarch, regnum)) > + gdbscm_invalid_object_error ("unwind-info-add-saved-register!", > + SCM_ARG3, value_scm, > + "wrong sized value for register"); > + > + data->registers = scm_assv_set_x (data->registers, > + scm_from_int (regnum), > + value_scm); > + > + return SCM_UNSPECIFIED; > +} > + > +/* frame_unwind.this_id method. */ > + > +static void > +uwscm_this_id (struct frame_info *this_frame, void **cache_ptr, > + struct frame_id *this_id) > +{ > + SCM unwind_info = PTR2SCM (*cache_ptr); > + struct uwscm_unwind_info *data; > + > + data = uwscm_unwind_info_data (unwind_info); > + *this_id = data->frame_id; > +} > + > +/* frame_unwind.prev_register. */ > + > +static struct value * > +uwscm_prev_register (struct frame_info *this_frame, void **cache_ptr, > + int regnum) > +{ > + SCM unwind_info = PTR2SCM (*cache_ptr); > + struct uwscm_unwind_info *data; > + SCM value_scm; > + struct value *c_value; > + const gdb_byte *buf; > + > + data = uwscm_unwind_info_data (unwind_info); > + value_scm = scm_assv_ref (data->registers, scm_from_int (regnum)); > + if (gdbscm_is_false (value_scm)) > + return frame_unwind_got_optimized (this_frame, regnum); > + > + c_value = vlscm_scm_to_value (value_scm); > + buf = value_contents (c_value); > + > + return frame_unwind_got_bytes (this_frame, regnum, buf); > +} > + > +/* Call unwind-frame on the pending frame and check that the return > + value is either a valid unwind info object, for a successful unwind, or > + #f otherwise. */ > + > +static SCM > +do_call_unwind_frame (void *data) > +{ > + SCM pending_frame = PTR2SCM (data); > + SCM result; > + > + result = scm_call_1 (scm_variable_ref (unwind_frame), pending_frame); > + > + if (!gdbscm_is_false (result)) > + uwscm_assert_valid_unwind_info (result, "unwind-frame", 0); > + > + return result; > +} > + > +/* Sniffer implementation. */ > + > +static int > +uwscm_sniffer (const struct frame_unwind *self, struct frame_info *this_frame, > + void **cache_ptr) > +{ > + SCM pending_frame; > + SCM result; > + > + /* Note that it's possible to have loaded the Guile interface, but not yet > + loaded (gdb frame-unwinders), so checking gdb_scheme_initialized is not > + sufficient. */ > + if (!gdbscm_frame_unwinders_loaded) > + return 0; > + > + pending_frame = uwscm_make_pending_frame (this_frame); > + /* Recurse through gdbscm_call_guile so that we can just throw > + exceptions on error. */ > + result = gdbscm_call_guile (do_call_unwind_frame, > + SCM2PTR (pending_frame), > + gdbscm_memory_error_p); > + > + /* Drop the reference to this_frame, so that future use of > + pending_frame from Scheme will signal an error. */ > + uwscm_invalidate_pending_frame (pending_frame); > + > + if (gdbscm_is_exception (result)) > + { > + gdbscm_print_gdb_exception (SCM_BOOL_F, result); > + return 0; > + } > + > + if (gdbscm_is_false (result)) > + return 0; > + > + *cache_ptr = SCM2PTR (scm_gc_protect_object (result)); > + > + return 1; > +} > + > +/* Frame cache release shim. */ > + > +static void > +uwscm_dealloc_cache (struct frame_info *this_frame, void *cache) > +{ > + scm_gc_unprotect_object (PTR2SCM (cache)); > +} > + > +struct uwscm_gdbarch_data_type > +{ > + /* Has the unwinder shim been prepended? */ > + int unwinder_registered; > +}; > + > +static void * > +uwscm_gdbarch_data_init (struct gdbarch *gdbarch) > +{ > + return GDBARCH_OBSTACK_ZALLOC (gdbarch, struct uwscm_gdbarch_data_type); > +} > + > +/* New inferior architecture callback: register the Guile sniffers > + intermediary. */ > + > +static void > +uwscm_on_new_gdbarch (struct gdbarch *newarch) > +{ > + struct uwscm_gdbarch_data_type *data = > + gdbarch_data (newarch, uwscm_gdbarch_data); > + > + if (!data->unwinder_registered) > + { > + struct frame_unwind *unwinder > + = GDBARCH_OBSTACK_ZALLOC (newarch, struct frame_unwind); > + > + unwinder->type = NORMAL_FRAME; > + unwinder->stop_reason = default_frame_unwind_stop_reason; > + unwinder->this_id = uwscm_this_id; > + unwinder->prev_register = uwscm_prev_register; > + unwinder->unwind_data = (void *) newarch; > + unwinder->sniffer = uwscm_sniffer; > + unwinder->dealloc_cache = uwscm_dealloc_cache; > + frame_unwind_prepend_unwinder (newarch, unwinder); > + data->unwinder_registered = 1; > + } > +} > + > +static const scheme_function unwind_functions[] = > +{ > + { "%make-unwind-info", 4, 0, 0, gdbscm_make_unwind_info, > + /* Wrapped and documented in Scheme; no need for docs here. */ > + ""}, > + > + { "pending-frame-read-register", 2, 0, 0, > + gdbscm_pending_frame_read_register, > + "\ > +Return the value of a register in a pending frame.\n\ > +\n\ > + Arguments: string" }, > + > + { "unwind-info-add-saved-register!", 3, 0, 0, > + gdbscm_unwind_info_add_saved_register_x, > + "\ > +Record the saved value of a register for a pending frame.\n\ > +\n\ > +After reading a pending frame's registers and determining that it\n\ > +can handle the frame, an unwinder will create an unwind info object and\n\ > +call this function to record saved registers for the frame. The values\n\ > +of the saved registers logically belong to the frame that is older than\n\ > +the pending frame being unwound, not to the pending frame itself.\n\ > +\n\ > +The first argument should be a object. The second\n\ > +names a register, and should be a string, for example \"rip\". The\n\ > +third argument is the value, as a GDB value. By default, any register\n\ > +whose value is not added to the unwind info's saved register set\n\ > +will be marked as \"not saved\" in the outer frame." }, > + > + END_FUNCTIONS > +}; > + > +/* Called by lib/gdb/frame-unwinders.scm. */ > + > +static void > +gdbscm_load_frame_unwinders (void *unused) > +{ > + if (gdbscm_frame_unwinders_loaded) > + return; > + > + gdbscm_frame_unwinders_loaded = 1; > + > + gdbscm_define_functions (unwind_functions, 0); > + > + unwind_frame = scm_c_lookup ("unwind-frame"); > +} > + > +/* Initialize the opaque pending frame and unwind info types and > + register gdbscm_load_frame_unwinders for calling by (gdb > + frame-unwinders). */ > + > +void > +gdbscm_initialize_frame_unwinders (void) > +{ > + pending_frame_smob_tag = > + gdbscm_make_smob_type (pending_frame_smob_name, 0); > + unwind_info_smob_tag = > + gdbscm_make_smob_type (unwind_info_smob_name, 0); > + > + uwscm_gdbarch_data = > + gdbarch_data_register_post_init (uwscm_gdbarch_data_init); > + observer_attach_architecture_changed (uwscm_on_new_gdbarch); > + > + scm_c_register_extension ("gdb", "gdbscm_load_frame_unwinders", > + gdbscm_load_frame_unwinders, NULL); > +} > diff --git a/gdb/guile/scm-symbol.c b/gdb/guile/scm-symbol.c > index 99ef928..1ad6d0c 100644 > --- a/gdb/guile/scm-symbol.c > +++ b/gdb/guile/scm-symbol.c > @@ -605,7 +605,9 @@ gdbscm_lookup_symbol (SCM name_scm, SCM rest) > > TRY > { > - selected_frame = get_selected_frame (_("no frame selected")); > + selected_frame = get_selected_frame_if_set (); > + if (selected_frame == NULL) > + selected_frame = get_current_frame (); > block = get_frame_block (selected_frame, NULL); > } > CATCH (except, RETURN_MASK_ALL) > diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog > index e156f57..20e6a70 100644 > --- a/gdb/testsuite/ChangeLog > +++ b/gdb/testsuite/ChangeLog > @@ -1,5 +1,12 @@ > 2015-04-09 Andy Wingo > > + * gdb.guile/scm-frame-unwinder.exp: New file. > + * gdb.guile/scm-frame-unwinder.c: New file. > + * gdb.guile/scm-frame-unwinder-gdb.scm.in: New file. > + * gdb.guile/scm-frame-unwinder.scm: New file. > + > +2015-04-09 Andy Wingo > + > * gdb.guile/amd64-scm-frame-filter-invalidarg.S: > * gdb.guile/scm-frame-filter-gdb.scm.in: > * gdb.guile/scm-frame-filter-invalidarg-gdb.scm.in: > diff --git a/gdb/testsuite/gdb.guile/scm-frame-unwinder-gdb.scm.in b/gdb/testsuite/gdb.guile/scm-frame-unwinder-gdb.scm.in > new file mode 100644 > index 0000000..5c658d5 > --- /dev/null > +++ b/gdb/testsuite/gdb.guile/scm-frame-unwinder-gdb.scm.in > @@ -0,0 +1,31 @@ > +;; Copyright (C) 2015 Free Software Foundation, Inc. > +;; > +;; This program is free software; you can redistribute it and/or modify > +;; it under the terms of the GNU General Public License as published by > +;; the Free Software Foundation; either version 3 of the License, or > +;; (at your option) any later version. > +;; > +;; This program is distributed in the hope that it will be useful, > +;; but WITHOUT ANY WARRANTY; without even the implied warranty of > +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +;; GNU General Public License for more details. > +;; > +;; You should have received a copy of the GNU General Public License > +;; along with this program. If not, see . > + > +;; This file is part of the GDB test-suite. It tests Guile-based frame > +;; filters. pasto: s/filters/unwinders/ > + > +(use-modules (gdb) > + (gdb frame-unwinders)) > + > +(define* (install-unwinders! #:optional (locus (current-objfile))) > + (define (unwind-frame frame) > + #f) > + (define (add-unwinder! name) > + (register-frame-unwinder! (make-frame-unwinder name unwind-frame) > + #:locus locus)) > + (add-unwinder! "Auto-loaded dummy") > + (add-unwinder! "Auto-loaded dummy 2")) > + > +(install-unwinders!) Can we get by with one scm file, instead of the two? [auto-loaded and manual-loaded] > diff --git a/gdb/testsuite/gdb.guile/scm-frame-unwinder.c b/gdb/testsuite/gdb.guile/scm-frame-unwinder.c > new file mode 100644 > index 0000000..6546c67 > --- /dev/null > +++ b/gdb/testsuite/gdb.guile/scm-frame-unwinder.c > @@ -0,0 +1,35 @@ > +/* Test file for the Scheme frame unwinding interface. > + > + Copyright (C) 2015 Free Software Foundation, Inc. > + > + This file is part of GDB. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 3 of the License, or > + (at your option) any later version. > + > + This program is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + GNU General Public License for more details. > + > + You should have received a copy of the GNU General Public License > + along with this program. If not, see . */ > + > +int > +f2 (int a) > +{ > + return ++a; > +} > + > +int > +f1 (int a, int b) > +{ > + return f2 (a) + b; > +} > + > +int main (int argc, char *argv[]) Nit: int main (int argc, char *argv[]) > +{ > + return f1 (1, 2); > +} > diff --git a/gdb/testsuite/gdb.guile/scm-frame-unwinder.exp b/gdb/testsuite/gdb.guile/scm-frame-unwinder.exp > new file mode 100644 > index 0000000..6fbe48a > --- /dev/null > +++ b/gdb/testsuite/gdb.guile/scm-frame-unwinder.exp > @@ -0,0 +1,90 @@ > +# Copyright (C) 2015 Free Software Foundation, Inc. > + > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 3 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program. If not, see . > + > +# This file is part of the GDB testsuite. It tests Guile-based > +# frame-filters. [for completeness sake] Given the use of rip here, this test needs to check for amd64 target. It's still an outstanding question as to whether we can easily make this target independent. > + > +load_lib gdb-guile.exp > + > +standard_testfile > + > +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } { > + return -1 > +} > + > +# Skip all tests if Guile scripting is not enabled. > +if { [skip_guile_tests] } { continue } > + > +# Make the -gdb.scm script available to gdb, it is automagically loaded by gdb. > +# Care is taken to put it in the same directory as the binary so that > +# gdb will find it. > +set remote_obj_guile_file \ > + [remote_download \ > + host ${srcdir}/${subdir}/${testfile}-gdb.scm.in \ > + [standard_output_file ${testfile}-gdb.scm]] > + > +gdb_reinitialize_dir $srcdir/$subdir > +gdb_test_no_output "set auto-load safe-path ${remote_obj_guile_file}" \ > + "set auto-load safe-path" > +gdb_load ${binfile} > +# Verify gdb loaded the script. > +gdb_test "info auto-load guile-scripts" "Yes.*/${testfile}-gdb.scm.*" \ > + "Test auto-load had loaded guile scripts" > + > +if ![runto_main] then { > + return -1 > +} > + > +# Load global frame-unwinders > +set remote_guile_file [gdb_remote_download host \ > + ${srcdir}/${subdir}/${testfile}.scm] > +gdb_scm_load_file ${remote_guile_file} > + > +# Test query > +gdb_test "guile (map frame-unwinder-name (all-frame-unwinders))" \ > + "\\(\"Synthetic\" \"Dummy\" \"Auto-loaded dummy 2\" \"Auto-loaded dummy\"\\)" \ > + "all frame unwinders" > +gdb_test "guile (map frame-unwinder-enabled? (all-frame-unwinders))" \ > + "\\(#f #t #t #t\\)" \ > + "all frame unwinders enabled?" > + > +gdb_test_no_output "guile (set-frame-unwinder-enabled! (find-frame-unwinder-by-name \"Dummy\") #f)" \ > + "disable dummy" > +gdb_test "guile (frame-unwinder-enabled? (find-frame-unwinder-by-name \"Dummy\"))"\ > + "#f" \ > + "dummy not enabled" > +gdb_test_no_output "guile (set-frame-unwinder-enabled! (find-frame-unwinder-by-name \"Dummy\") #t)" \ > + "re-enable dummy" > +gdb_test "guile (frame-unwinder-enabled? (find-frame-unwinder-by-name \"Dummy\"))"\ > + "#t" \ > + "dummy re-enabled" > + > +gdb_test "bt 10" \ > + "\#0 main .*scm-frame-unwinder.c:34" \ > + "backtrace from main" > + > +gdb_test_no_output "guile (set-frame-unwinder-enabled! (find-frame-unwinder-by-name \"Synthetic\") #t)" \ > + "enable synthetic unwinder" > + > +gdb_breakpoint "f2" > +gdb_continue_to_breakpoint "breakpoint at f2" > + > +# It would be nice to the backtrace at this position with and without > +# the unwinder, but currently enabling an unwinder doesn't invalidate > +# the frame cache. Ah, ok. We can fix this as a followup. > + > +gdb_test "bt 10"\ > + "\#0 f2 .*scm-frame-unwinder.c:23\r\n\#1 .*\#9 0x0*cabba9e5 in \\?\\? \\(\\)\r\n\\(More stack frames follow...\\)" \ Fetch the line number with gdb_get_line_number. > + "backtrace full of cabbages" > diff --git a/gdb/testsuite/gdb.guile/scm-frame-unwinder.scm b/gdb/testsuite/gdb.guile/scm-frame-unwinder.scm > new file mode 100644 > index 0000000..8f91527 > --- /dev/null > +++ b/gdb/testsuite/gdb.guile/scm-frame-unwinder.scm > @@ -0,0 +1,41 @@ > +;; Copyright (C) 2015 Free Software Foundation, Inc. > +;; > +;; This program is free software; you can redistribute it and/or modify > +;; it under the terms of the GNU General Public License as published by > +;; the Free Software Foundation; either version 3 of the License, or > +;; (at your option) any later version. > +;; > +;; This program is distributed in the hope that it will be useful, > +;; but WITHOUT ANY WARRANTY; without even the implied warranty of > +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +;; GNU General Public License for more details. > +;; > +;; You should have received a copy of the GNU General Public License > +;; along with this program. If not, see . > + > +;; This file is part of the GDB test-suite. It tests Guile-based frame > +;; filters. > + > +(use-modules (gdb) > + (gdb frame-unwinders)) > + > +(define (dummy-unwinder frame) > + #f) > + > +(register-frame-unwinder! (make-frame-unwinder "Dummy" dummy-unwinder)) > + > + > +(define (synthetic-unwinder frame) > + ;; Increment the stack pointer, set IP to 0xdeadbeef > + (let* ((this-pc (pending-frame-read-register frame "rip")) > + (this-sp (pending-frame-read-register frame "rsp")) > + (prev-pc (value-cast (make-value #xcabba9e5) (value-type this-pc))) > + (prev-sp (value-add this-sp 32)) > + (frame-id (make-frame-id this-sp #:pc this-pc)) > + (unwind-info (make-unwind-info frame frame-id))) > + (unwind-info-add-saved-register! unwind-info "rip" prev-pc) > + (unwind-info-add-saved-register! unwind-info "rsp" prev-sp) > + unwind-info)) > + > +(register-frame-unwinder! > + (make-frame-unwinder "Synthetic" synthetic-unwinder #:enabled? #f))