From 1e611234ee3f4a1d2434f3fe7530cab87c936e0d Mon Sep 17 00:00:00 2001 From: Phil Muldoon Date: Fri, 10 May 2013 10:26:03 +0000 Subject: [PATCH] 2013-05-10 Phil Muldoon * stack.c (backtrace_command_1): Add "no-filters", and Python frame filter logic. (backtrace_command): Add "no-filters" option parsing. (_initialize_stack): Alter help to reflect "no-filters" option. * Makefile.in (SUBDIR_PYTHON_OBS): Add py-framefilter.o (SUBDIR_PYTHON_SRCS): Add py-framefilter.c (py-frame.o): Add target * data-directory/Makefile.in (PYTHON_DIR): Add Python frame filter files. * python/python.h: Add new frame filter constants, and flag enum. (apply_frame_filter): Add definition. * python/python.c (apply_frame_filter): New non-Python enabled function. * python/py-utils.c (py_xdecref): New function. (make_cleanup_py_xdecref): Ditto. * python/py-objfile.c: Declare frame_filters dictionary. (objfpy_dealloc): Add frame_filters dealloc. (objfpy_new): Initialize frame_filters attribute. (objfile_to_objfile_object): Ditto. (objfpy_get_frame_filters): New function. (objfpy_set_frame_filters): New function. * python/py-progspace.c: Declare frame_filters dictionary. (pspy_dealloc): Add frame_filters dealloc. (pspy_new): Initialize frame_filters attribute. (pspacee_to_pspace_object): Ditto. (pspy_get_frame_filters): New function. (pspy_set_frame_filters): New function. * python/py-framefilter.c: New file. * python/lib/gdb/command/frame_filters.py: New file. * python/lib/gdb/frames.py: New file. * python/lib/gdb/__init__.py: Initialize global frame_filters dictionary * python/lib/gdb/FrameDecorator.py: New file. * python/lib/gdb/FrameIterator.py: New file. * mi/mi-cmds.c (mi_cmds): Add frame filters command. * mi/mi-cmds.h: Declare. * mi/mi-cmd-stack.c (mi_cmd_stack_list_frames): Add --no-frame-filter logic, and Python frame filter logic. (stack_enable_frame_filters): New function. (parse_no_frame_option): Ditto. (mi_cmd_stack_list_frames): Add --no-frame-filter and Python frame filter logic. (mi_cmd_stack_list_locals): Ditto. (mi_cmd_stack_list_args): Ditto. (mi_cmd_stack_list_variables): Ditto. * NEWS: Add frame filter note. 2013-05-10 Phil Muldoon * gdb.python/py-framefilter.py: New File. * gdb.python/py-framefilter-mi.exp: Ditto. * gdb.python/py-framefilter.c: Ditto. * gdb.python/py-framefilter-mi.exp: Ditto. * gdb.python/py-framefilter-mi.c: Ditto, * gdb.python/py-framefilter-gdb.py.in: Ditto. 2013-05-10 Phil Muldoon * gdb.texinfo (Backtrace): Add "no-filter" argument. (Python API): Add Frame Filters API, Frame Wrapper API, Writing a Frame Filter/Wrapper, Managing Management of Frame Filters chapter entries. (Frame Filters API): New Node. (Frame Wrapper API): New Node. (Writing a Frame Filter): New Node. (Managing Frame Filters): New Node. (Progspaces In Python): Add note about frame_filters attribute. (Objfiles in Python): Ditto. (GDB/MI Stack Manipulation): Add -enable-frame-filters command, @anchors and --no-frame-filters option to -stack-list-variables, -stack-list-frames, -stack-list-locals and -stack-list-arguments commands. --- gdb/ChangeLog | 49 + gdb/Makefile.in | 6 + gdb/NEWS | 4 + gdb/data-directory/Makefile.in | 4 + gdb/doc/ChangeLog | 17 + gdb/doc/gdb.texinfo | 845 ++++++++- gdb/mi/mi-cmd-stack.c | 229 ++- gdb/mi/mi-cmds.c | 1 + gdb/mi/mi-cmds.h | 1 + gdb/python/lib/gdb/FrameDecorator.py | 285 +++ gdb/python/lib/gdb/FrameIterator.py | 45 + gdb/python/lib/gdb/__init__.py | 2 + gdb/python/lib/gdb/command/frame_filters.py | 461 +++++ gdb/python/lib/gdb/frames.py | 229 +++ gdb/python/py-framefilter.c | 1528 +++++++++++++++++ gdb/python/py-objfile.c | 60 + gdb/python/py-progspace.c | 60 + gdb/python/py-utils.c | 22 + gdb/python/python-internal.h | 3 + gdb/python/python.c | 9 + gdb/python/python.h | 66 + gdb/stack.c | 121 +- gdb/testsuite/ChangeLog | 9 + .../gdb.python/py-framefilter-gdb.py.in | 48 + gdb/testsuite/gdb.python/py-framefilter-mi.c | 138 ++ .../gdb.python/py-framefilter-mi.exp | 179 ++ gdb/testsuite/gdb.python/py-framefilter.c | 155 ++ gdb/testsuite/gdb.python/py-framefilter.exp | 239 +++ gdb/testsuite/gdb.python/py-framefilter.py | 117 ++ 29 files changed, 4845 insertions(+), 87 deletions(-) create mode 100644 gdb/python/lib/gdb/FrameDecorator.py create mode 100644 gdb/python/lib/gdb/FrameIterator.py create mode 100644 gdb/python/lib/gdb/command/frame_filters.py create mode 100644 gdb/python/lib/gdb/frames.py create mode 100644 gdb/python/py-framefilter.c create mode 100644 gdb/testsuite/gdb.python/py-framefilter-gdb.py.in create mode 100644 gdb/testsuite/gdb.python/py-framefilter-mi.c create mode 100644 gdb/testsuite/gdb.python/py-framefilter-mi.exp create mode 100644 gdb/testsuite/gdb.python/py-framefilter.c create mode 100644 gdb/testsuite/gdb.python/py-framefilter.exp create mode 100644 gdb/testsuite/gdb.python/py-framefilter.py diff --git a/gdb/ChangeLog b/gdb/ChangeLog index c3399badcf7..2510f1a8d42 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,52 @@ +2013-05-10 Phil Muldoon + + * stack.c (backtrace_command_1): Add "no-filters", and Python frame + filter logic. + (backtrace_command): Add "no-filters" option parsing. + (_initialize_stack): Alter help to reflect "no-filters" option. + * Makefile.in (SUBDIR_PYTHON_OBS): Add py-framefilter.o + (SUBDIR_PYTHON_SRCS): Add py-framefilter.c + (py-frame.o): Add target + * data-directory/Makefile.in (PYTHON_DIR): Add Python frame + filter files. + * python/python.h: Add new frame filter constants, and flag enum. + (apply_frame_filter): Add definition. + * python/python.c (apply_frame_filter): New non-Python + enabled function. + * python/py-utils.c (py_xdecref): New function. + (make_cleanup_py_xdecref): Ditto. + * python/py-objfile.c: Declare frame_filters dictionary. + (objfpy_dealloc): Add frame_filters dealloc. + (objfpy_new): Initialize frame_filters attribute. + (objfile_to_objfile_object): Ditto. + (objfpy_get_frame_filters): New function. + (objfpy_set_frame_filters): New function. + * python/py-progspace.c: Declare frame_filters dictionary. + (pspy_dealloc): Add frame_filters dealloc. + (pspy_new): Initialize frame_filters attribute. + (pspacee_to_pspace_object): Ditto. + (pspy_get_frame_filters): New function. + (pspy_set_frame_filters): New function. + * python/py-framefilter.c: New file. + * python/lib/gdb/command/frame_filters.py: New file. + * python/lib/gdb/frames.py: New file. + * python/lib/gdb/__init__.py: Initialize global frame_filters + dictionary + * python/lib/gdb/FrameDecorator.py: New file. + * python/lib/gdb/FrameIterator.py: New file. + * mi/mi-cmds.c (mi_cmds): Add frame filters command. + * mi/mi-cmds.h: Declare. + * mi/mi-cmd-stack.c (mi_cmd_stack_list_frames): Add + --no-frame-filter logic, and Python frame filter logic. + (stack_enable_frame_filters): New function. + (parse_no_frame_option): Ditto. + (mi_cmd_stack_list_frames): Add --no-frame-filter and Python frame + filter logic. + (mi_cmd_stack_list_locals): Ditto. + (mi_cmd_stack_list_args): Ditto. + (mi_cmd_stack_list_variables): Ditto. + * NEWS: Add frame filter note. + 2013-05-09 Doug Evans * symfile.c (syms_from_objfile_1): Delete args offsets, num_offsets. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 2597ddb8773..91278945b9d 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -288,6 +288,7 @@ SUBDIR_PYTHON_OBS = \ py-exitedevent.o \ py-finishbreakpoint.o \ py-frame.o \ + py-framefilter.o \ py-function.o \ py-gdb-readline.o \ py-inferior.o \ @@ -322,6 +323,7 @@ SUBDIR_PYTHON_SRCS = \ python/py-exitedevent.c \ python/py-finishbreakpoint.c \ python/py-frame.c \ + python/py-framefilter.c \ python/py-function.c \ python/py-gdb-readline.c \ python/py-inferior.c \ @@ -2144,6 +2146,10 @@ py-frame.o: $(srcdir)/python/py-frame.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-frame.c $(POSTCOMPILE) +py-framefilter.o: $(srcdir)/python/py-framefilter.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-framefilter.c + $(POSTCOMPILE) + py-function.o: $(srcdir)/python/py-function.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-function.c $(POSTCOMPILE) diff --git a/gdb/NEWS b/gdb/NEWS index 65284dc32d4..7cd164600ac 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,10 @@ *** Changes since GDB 7.6 +* Python scripting + + ** Frame filters and frame decorators have been added. + * New targets Nios II ELF nios2*-*-elf diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in index d98ac771459..13433edaad9 100644 --- a/gdb/data-directory/Makefile.in +++ b/gdb/data-directory/Makefile.in @@ -53,10 +53,14 @@ PYTHON_DIR = python PYTHON_INSTALL_DIR = $(DESTDIR)$(GDB_DATADIR)/$(PYTHON_DIR) PYTHON_FILES = \ gdb/__init__.py \ + gdb/frames.py \ + gdb/FrameIterator.py \ + gdb/FrameDecorator.py \ gdb/types.py \ gdb/printing.py \ gdb/prompt.py \ gdb/command/__init__.py \ + gdb/command/frame_filters.py \ gdb/command/type_printers.py \ gdb/command/pretty_printers.py \ gdb/command/prompt.py \ diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 80f08a539bd..908fbb40a0b 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,20 @@ +2013-05-10 Phil Muldoon + + * gdb.texinfo (Backtrace): Add "no-filter" argument. + (Python API): Add Frame Filters API, Frame Wrapper API, + Writing a Frame Filter/Wrapper, Managing Management of Frame + Filters chapter entries. + (Frame Filters API): New Node. + (Frame Wrapper API): New Node. + (Writing a Frame Filter): New Node. + (Managing Frame Filters): New Node. + (Progspaces In Python): Add note about frame_filters attribute. + (Objfiles in Python): Ditto. + (GDB/MI Stack Manipulation): Add -enable-frame-filters command, + @anchors and --no-frame-filters option to -stack-list-variables, + -stack-list-frames, -stack-list-locals and -stack-list-arguments + commands. + 2013-05-08 Joel Brobecker * gdbint.texinfo (Native Debugging): Add "AIX Shared Library diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index f685cd208a6..1869d74de5a 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -6465,6 +6465,7 @@ currently executing frame and describes it briefly, similar to the @menu * Frames:: Stack frames * Backtrace:: Backtraces +* Frame Filter Management:: Managing frame filters * Selection:: Selecting a frame * Frame Info:: Information on a frame @@ -6552,6 +6553,7 @@ line per frame, for many frames, starting with the currently executing frame (frame zero), followed by its caller (frame one), and on up the stack. +@anchor{backtrace-command} @table @code @kindex backtrace @kindex bt @r{(@code{backtrace})} @@ -6577,6 +6579,19 @@ Similar, but print only the outermost @var{n} frames. @itemx bt full -@var{n} Print the values of the local variables also. @var{n} specifies the number of frames to print, as described above. + +@item backtrace no-filters +@itemx bt no-filters +@itemx bt no-filters @var{n} +@itemx bt no-filters -@var{n} +@itemx bt no-filters full +@itemx bt no-filters full @var{n} +@itemx bt no-filters full -@var{n} +Do not run Python frame filters on this backtrace. @xref{Frame +Filter API}, for more information. Additionally use @ref{disable +frame-filter all} to turn off all frame filters. This is only +relevant when @value{GDBN} has been configured with @code{Python} +support. @end table @kindex where @@ -6727,6 +6742,149 @@ Display an absolute filename. Show the current way to display filenames. @end table +@node Frame Filter Management +@section Management of Frame Filters. +@cindex managing frame filters + +Frame filters are Python based utilities to manage and decorate the +output of frames. @xref{Frame Filter API}, for further information. + +Managing frame filters is performed by several commands available +within @value{GDBN}, detailed here. + +@table @code +@kindex info frame-filter +@item info frame-filter +Print a list of installed frame filters from all dictionaries, showing +their name, priority and enabled status. + +@kindex disable frame-filter +@anchor{disable frame-filter all} +@item disable frame-filter @var{filter-dictionary} @var{filter-name} +Disable a frame filter in the dictionary matching +@var{filter-dictionary}, or @code{all}, and @var{filter-name}. +@var{filter-dictionary} may be @code{all}, @code{global}, +@code{progspace} or the name of the object file where the frame filter +dictionary resides. When @code{all} is specified, all frame filters +across all dictionaries are disabled. @var{filter-name} is the name +of the frame filter and is used when @code{all} is not the option for +@var{filter-dictionary}. A disabled frame-filter is not deleted, it +may be enabled again later. + +@kindex enable frame-filter +@item enable frame-filter @var{filter-dictionary} @var{filter-name} +Enable a frame filter in the dictionary matching +@var{filter-dictionary}, or @code{all}, and @var{filter-name}. +@var{filter-dictionary} may be @code{all}, @code{global}, +@code{progspace} or the name of the object file where the frame filter +dictionary resides. When @code{all} is specified, all frame filters across +all dictionaries are enabled. @var{filter-name} is the name of the frame +filter and is used when @code{all} is not the option for +@var{filter-dictionary}. + +Example: + +@smallexample +(gdb) info frame-filter + +global frame-filters: + Priority Enabled Name + 1000 No PrimaryFunctionFilter + 100 Yes Reverse + +progspace /build/test frame-filters: + Priority Enabled Name + 100 Yes ProgspaceFilter + +objfile /build/test frame-filters: + Priority Enabled Name + 999 Yes BuildProgra Filter + +(gdb) disable frame-filter /build/test BuildProgramFilter +(gdb) info frame-filter + +global frame-filters: + Priority Enabled Name + 1000 No PrimaryFunctionFilter + 100 Yes Reverse + +progspace /build/test frame-filters: + Priority Enabled Name + 100 Yes ProgspaceFilter + +objfile /build/test frame-filters: + Priority Enabled Name + 999 No BuildProgramFilter + +(gdb) enable frame-filter global PrimaryFunctionFilter +(gdb) info frame-filter + +global frame-filters: + Priority Enabled Name + 1000 Yes PrimaryFunctionFilter + 100 Yes Reverse + +progspace /build/test frame-filters: + Priority Enabled Name + 100 Yes ProgspaceFilter + +objfile /build/test frame-filters: + Priority Enabled Name + 999 No BuildProgramFilter +@end smallexample + +@kindex set frame-filter priority +@item set frame-filter priority @var{filter-dictionary} @var{filter-name} @var{priority} +Set the @var{priority} of a frame filter in the dictionary matching +@var{filter-dictionary}, and the frame filter name matching +@var{filter-name}. @var{filter-dictionary} may be @code{global}, +@code{progspace} or the name of the object file where the frame filter +dictionary resides. @var{priority} is an integer. + +@kindex show frame-filter priority +@item show frame-filter priority @var{filter-dictionary} @var{filter-name} +Show the @var{priority} of a frame filter in the dictionary matching +@var{filter-dictionary}, and the frame filter name matching +@var{filter-name}. @var{filter-dictionary} may be @code{global}, +@code{progspace} or the name of the object file where the frame filter +dictionary resides. + +Example: + +@smallexample +(gdb) info frame-filter + +global frame-filters: + Priority Enabled Name + 1000 Yes PrimaryFunctionFilter + 100 Yes Reverse + +progspace /build/test frame-filters: + Priority Enabled Name + 100 Yes ProgspaceFilter + +objfile /build/test frame-filters: + Priority Enabled Name + 999 No BuildProgramFilter + +(gdb) set frame-filter priority global Reverse 50 +(gdb) info frame-filter + +global frame-filters: + Priority Enabled Name + 1000 Yes PrimaryFunctionFilter + 50 Yes Reverse + +progspace /build/test frame-filters: + Priority Enabled Name + 100 Yes ProgspaceFilter + +objfile /build/test frame-filters: + Priority Enabled Name + 999 No BuildProgramFilter +@end smallexample +@end table + @node Selection @section Selecting a Frame @@ -23026,6 +23184,9 @@ optional arguments while skipping others. Example: * Selecting Pretty-Printers:: How GDB chooses a pretty-printer. * Writing a Pretty-Printer:: Writing a Pretty-Printer. * Type Printing API:: Pretty-printing types. +* Frame Filter API:: Filtering Frames. +* Frame Decorator API:: Decorating Frames. +* Writing a Frame Filter:: Writing a Frame Filter. * Inferiors In Python:: Python representation of inferiors (processes) * Events In Python:: Listening for events from @value{GDBN}. * Threads In Python:: Accessing inferior threads from Python. @@ -24405,6 +24566,636 @@ done then type printers would have to make use of the event system in order to avoid holding information that could become stale as the inferior changed. +@node Frame Filter API +@subsubsection Filtering Frames. +@cindex frame filters api + +Frame filters are Python objects that manipulate the visibility of a +frame or frames when a backtrace (@pxref{Backtrace}) is printed by +@value{GDBN}. + +Only commands that print a backtrace, or, in the case of @sc{gdb/mi} +commands (@pxref{GDB/MI}), those that return a collection of frames +are affected. The commands that work with frame filters are: + +@code{backtrace} (@pxref{backtrace-command,, The backtrace command}), +@code{-stack-list-frames} +(@pxref{-stack-list-frames,, The -stack-list-frames command}), +@code{-stack-list-variables} (@pxref{-stack-list-variables,, The +-stack-list-variables command}), @code{-stack-list-arguments} +@pxref{-stack-list-arguments,, The -stack-list-arguments command}) and +@code{-stack-list-locals} (@pxref{-stack-list-locals,, The +-stack-list-locals command}). + +A frame filter works by taking an iterator as an argument, applying +actions to the contents of that iterator, and returning another +iterator (or, possibly, the same iterator it was provided in the case +where the filter does not perform any operations). Typically, frame +filters utilize tools such as the Python's @code{itertools} module to +work with and create new iterators from the source iterator. +Regardless of how a filter chooses to apply actions, it must not alter +the underlying @value{GDBN} frame or frames, or attempt to alter the +call-stack within @value{GDBN}. This preserves data integrity within +@value{GDBN}. Frame filters are executed on a priority basis and care +should be taken that some frame filters may have been executed before, +and that some frame filters will be executed after. + +An important consideration when designing frame filters, and well +worth reflecting upon, is that frame filters should avoid unwinding +the call stack if possible. Some stacks can run very deep, into the +tens of thousands in some cases. To search every frame when a frame +filter executes may be too expensive at that step. The frame filter +cannot know how many frames it has to iterate over, and it may have to +iterate through them all. This ends up duplicating effort as +@value{GDBN} performs this iteration when it prints the frames. If +the filter can defer unwinding frames until frame decorators are +executed, after the last filter has executed, it should. @xref{Frame +Decorator API}, for more information on decorators. Also, there are +examples for both frame decorators and filters in later chapters. +@xref{Writing a Frame Filter}, for more information. + +The Python dictionary @code{gdb.frame_filters} contains key/object +pairings that comprise a frame filter. Frame filters in this +dictionary are called @code{global} frame filters, and they are +available when debugging all inferiors. These frame filters must +register with the dictionary directly. In addition to the +@code{global} dictionary, there are other dictionaries that are loaded +with different inferiors via auto-loading (@pxref{Python +Auto-loading}). The two other areas where frame filter dictionaries +can be found are: @code{gdb.Progspace} which contains a +@code{frame_filters} dictionary attribute, and each @code{gdb.Objfile} +object which also contains a @code{frame_filters} dictionary +attribute. + +When a command is executed from @value{GDBN} that is compatible with +frame filters, @value{GDBN} combines the @code{global}, +@code{gdb.Progspace} and all @code{gdb.Objfile} dictionaries currently +loaded. All of the @code{gdb.Objfile} dictionaries are combined, as +several frames, and thus several object files, might be in use. +@value{GDBN} then prunes any frame filter whose @code{enabled} +attribute is @code{False}. This pruned list is then sorted according +to the @code{priority} attribute in each filter. + +Once the dictionaries are combined, pruned and sorted, @value{GDBN} +creates an iterator which wraps each frame in the call stack in a +@code{FrameDecorator} object, and calls each filter in order. The +output from the previous filter will always be the input to the next +filter, and so on. + +Frame filters have a mandatory interface which each frame filter must +implement, defined here: + +@defun FrameFilter.filter (iterator) +@value{GDBN} will call this method on a frame filter when it has +reached the order in the priority list for that filter. + +For example, if there are four frame filters: + +@smallexample +Name Priority + +Filter1 5 +Filter2 10 +Filter3 100 +Filter4 1 +@end smallexample + +The order that the frame filters will be called is: + +@smallexample +Filter3 -> Filter2 -> Filter1 -> Filter4 +@end smallexample + +Note that the output from @code{Filter3} is passed to the input of +@code{Filter2}, and so on. + +This @code{filter} method is passed a Python iterator. This iterator +contains a sequence of frame decorators that wrap each +@code{gdb.Frame}, or a frame decorator that wraps another frame +decorator. The first filter that is executed in the sequence of frame +filters will receive an iterator entirely comprised of default +@code{FrameDecorator} objects. However, after each frame filter is +executed, the previous frame filter may have wrapped some or all of +the frame decorators with their own frame decorator. As frame +decorators must also conform to a mandatory interface, these +decorators can be assumed to act in a uniform manner (@pxref{Frame +Decorator API}). + +This method must return an object conforming to the Python iterator +protocol. Each item in the iterator must be an object conforming to +the frame decorator interface. If a frame filter does not wish to +perform any operations on this iterator, it should return that +iterator untouched. + +This method is not optional. If it does not exist, @value{GDBN} will +raise and print an error. +@end defun + +@defvar FrameFilter.name +The @code{name} attribute must be Python string which contains the +name of the filter displayed by @value{GDBN} (@pxref{Frame Filter +Management}). This attribute may contain any combination of letters +or numbers. Care should be taken to ensure that it is unique. This +attribute is mandatory. +@end defvar + +@defvar FrameFilter.enabled +The @code{enabled} attribute must be Python boolean. This attribute +indicates to @value{GDBN} whether the frame filter is enabled, and +should be considered when frame filters are executed. If +@code{enabled} is @code{True}, then the frame filter will be executed +when any of the backtrace commands detailed earlier in this chapter +are executed. If @code{enabled} is @code{False}, then the frame +filter will not be executed. This attribute is mandatory. +@end defvar + +@defvar FrameFilter.priority +The @code{priority} attribute must be Python integer. This attribute +controls the order of execution in relation to other frame filters. +There are no imposed limits on the range of @code{priority} other than +it must be a valid integer. The higher the @code{priority} attribute, +the sooner the frame filter will be executed in relation to other +frame filters. Although @code{priority} can be negative, it is +recommended practice to assume zero is the lowest priority that a +frame filter can be assigned. Frame filters that have the same +priority are executed in unsorted order in that priority slot. This +attribute is mandatory. +@end defvar + +@node Frame Decorator API +@subsubsection Decorating Frames. +@cindex frame decorator api + +Frame decorators are sister objects to frame filters (@pxref{Frame +Filter API}). Frame decorators are applied by a frame filter and can +only be used in conjunction with frame filters. + +The purpose of a frame decorator is to customize the printed content +of each @code{gdb.Frame} in commands where frame filters are executed. +This concept is called decorating a frame. Frame decorators decorate +a @code{gdb.Frame} with Python code contained within each API call. +This separates the actual data contained in a @code{gdb.Frame} from +the decorated data produced by a frame decorator. This abstraction is +necessary to maintain integrity of the data contained in each +@code{gdb.Frame}. + +Frame decorators have a mandatory interface, defined below. + +@value{GDBN} already contains a frame decorator called +@code{FrameDecorator}. This contains substantial amounts of +boilerplate code to decorate the content of a @code{gdb.Frame}. It is +recommended that other frame decorators inherit and extend this +object, and only to override the methods needed. + +@defun FrameDecorator.elided (self) + +The @code{elided} method groups frames together in a hierarchical +system. An example would be an interpreter, where multiple low-level +frames make up a single call in the interpreted language. In this +example, the frame filter would elide the low-level frames and present +a single high-level frame, representing the call in the interpreted +language, to the user. + +The @code{elided} function must return an iterable and this iterable +must contain the frames that are being elided wrapped in a suitable +frame decorator. If no frames are being elided this function may +return an empty iterable, or @code{None}. Elided frames are indented +from normal frames in a @code{CLI} backtrace, or in the case of +@code{GDB/MI}, are placed in the @code{children} field of the eliding +frame. + +It is the frame filter's task to also filter out the elided frames from +the source iterator. This will avoid printing the frame twice. +@end defun + +@defun FrameDecorator.function (self) + +This method returns the name of the function in the frame that is to +be printed. + +This method must return a Python string describing the function, or +@code{None}. + +If this function returns @code{None}, @value{GDBN} will not print any +data for this field. +@end defun + +@defun FrameDecorator.address (self) + +This method returns the address of the frame that is to be printed. + +This method must return a Python numeric integer type of sufficient +size to describe the address of the frame, or @code{None}. + +If this function returns a @code{None}, @value{GDBN} will not print +any data for this field. +@end defun + +@defun FrameDecorator.filename (self) + +This method returns the filename and path associated with this frame. + +This method must return a Python string containing the filename and +the path to the object file backing the frame, or @code{None}. + +If this function returns a @code{None}, @value{GDBN} will not print +any data for this field. +@end defun + +@defun FrameDecorator.line (self): + +This method returns the line number associated with the current +position within the function addressed by this frame. + +This method must return a Python integer type, or @code{None}. + +If this function returns a @code{None}, @value{GDBN} will not print +any data for this field. +@end defun + +@defun FrameDecorator.frame_args (self) +@anchor{frame_args} + +This method must return an iterable, or @code{None}. Returning an +empty iterable, or @code{None} means frame arguments will not be +printed for this frame. This iterable must contain objects that +implement two methods, described here. + +This object must implement a @code{argument} method which takes a +single @code{self} parameter and must return a @code{gdb.Symbol} +(@pxref{Symbols In Python}), or a Python string. The object must also +implement a @code{value} method which takes a single @code{self} +parameter and must return a @code{gdb.Value} (@pxref{Values From +Inferior}), a Python value, or @code{None}. If the @code{value} +method returns @code{None}, and the @code{argument} method returns a +@code{gdb.Symbol}, @value{GDBN} will look-up and print the value of +the @code{gdb.Symbol} automatically. + +A brief example: + +@smallexample +class SymValueWrapper(): + + def __init__(self, symbol, value): + self.sym = symbol + self.val = value + + def value(self): + return self.val + + def symbol(self): + return self.sym + +class SomeFrameDecorator() +... +... + def frame_args(self): + args = [] + try: + block = self.inferior_frame.block() + except: + return None + + # Iterate over all symbols in a block. Only add + # symbols that are arguments. + for sym in block: + if not sym.is_argument: + continue + args.append(SymValueWrapper(sym,None)) + + # Add example synthetic argument. + args.append(SymValueWrapper(``foo'', 42)) + + return args +@end smallexample +@end defun + +@defun FrameDecorator.frame_locals (self) + +This method must return an iterable or @code{None}. Returning an +empty iterable, or @code{None} means frame local arguments will not be +printed for this frame. + +The object interface, the description of the various strategies for +reading frame locals, and the example are largely similar to those +described in the @code{frame_args} function, (@pxref{frame_args,,The +frame filter frame_args function}). Below is a modified example: + +@smallexample +class SomeFrameDecorator() +... +... + def frame_locals(self): + vars = [] + try: + block = self.inferior_frame.block() + except: + return None + + # Iterate over all symbols in a block. Add all + # symbols, except arguments. + for sym in block: + if sym.is_argument: + continue + vars.append(SymValueWrapper(sym,None)) + + # Add an example of a synthetic local variable. + vars.append(SymValueWrapper(``bar'', 99)) + + return vars +@end smallexample +@end defun + +@defun FrameDecorator.inferior_frame (self): + +This method must return the underlying @code{gdb.Frame} that this +frame decorator is decorating. @value{GDBN} requires the underlying +frame for internal frame information to determine how to print certain +values when printing a frame. +@end defun + +@node Writing a Frame Filter +@subsubsection Writing a Frame Filter +@cindex writing a frame filter + +There are three basic elements that a frame filter must implement: it +must correctly implement the documented interface (@pxref{Frame Filter +API}), it must register itself with @value{GDBN}, and finally, it must +decide if it is to work on the data provided by @value{GDBN}. In all +cases, whether it works on the iterator or not, each frame filter must +return an iterator. A bare-bones frame filter follows the pattern in +the following example. + +@smallexample +import gdb + +class FrameFilter(): + + def __init__(self): + # Frame filter attribute creation. + # + # 'name' is the name of the filter that GDB will display. + # + # 'priority' is the priority of the filter relative to other + # filters. + # + # 'enabled' is a boolean that indicates whether this filter is + # enabled and should be executed. + + self.name = "Foo" + self.priority = 100 + self.enabled = True + + # Register this frame filter with the global frame_filters + # dictionary. + gdb.frame_filters[self.name] = self + + def filter(self, frame_iter): + # Just return the iterator. + return frame_iter +@end smallexample + +The frame filter in the example above implements the three +requirements for all frame filters. It implements the API, self +registers, and makes a decision on the iterator (in this case, it just +returns the iterator untouched). + +The first step is attribute creation and assignment, and as shown in +the comments the filter assigns the following attributes: @code{name}, +@code{priority} and whether the filter should be enabled with the +@code{enabled} attribute. + +The second step is registering the frame filter with the dictionary or +dictionaries that the frame filter has interest in. As shown in the +comments, this filter just registers itself with the global dictionary +@code{gdb.frame_filters}. As noted earlier, @code{gdb.frame_filters} +is a dictionary that is initialized in the @code{gdb} module when +@value{GDBN} starts. What dictionary a filter registers with is an +important consideration. Generally, if a filter is specific to a set +of code, it should be registered either in the @code{objfile} or +@code{progspace} dictionaries as they are specific to the program +currently loaded in @value{GDBN}. The global dictionary is always +present in @value{GDBN} and is never unloaded. Any filters registered +with the global dictionary will exist until @value{GDBN} exits. To +avoid filters that may conflict, it is generally better to register +frame filters against the dictionaries that more closely align with +the usage of the filter currently in question. @xref{Python +Auto-loading}, for further information on auto-loading Python scripts. + +@value{GDBN} takes a hands-off approach to frame filter registration, +therefore it is the frame filter's responsibility to ensure +registration has occurred, and that any exceptions are handled +appropriately. In particular, you may wish to handle exceptions +relating to Python dictionary key uniqueness. It is mandatory that +the dictionary key is the same as frame filter's @code{name} +attribute. When a user manages frame filters (@pxref{Frame Filter +Management}), the names @value{GDBN} will display are those contained +in the @code{name} attribute. + +The final step of this example is the implementation of the +@code{filter} method. As shown in the example comments, we define the +@code{filter} method and note that the method must take an iterator, +and also must return an iterator. In this bare-bones example, the +frame filter is not very useful as it just returns the iterator +untouched. However this is a valid operation for frame filters that +have the @code{enabled} attribute set, but decide not to operate on +any frames. + +In the next example, the frame filter operates on all frames and +utilizes a frame decorator to perform some work on the frames. +@xref{Frame Decorator API}, for further information on the frame +decorator interface. + +This example works on inlined frames. It highlights frames which are +inlined by tagging them with an ``[inlined]'' tag. By applying a +frame decorator to all frames with the Python @code{itertools imap} +method, the example defers actions to the frame decorator. Frame +decorators are only processed when @value{GDBN} prints the backtrace. + +This introduces a new decision making topic: whether to perform +decision making operations at the filtering step, or at the printing +step. In this example's approach, it does not perform any filtering +decisions at the filtering step beyond mapping a frame decorator to +each frame. This allows the actual decision making to be performed +when each frame is printed. This is an important consideration, and +well worth reflecting upon when designing a frame filter. An issue +that frame filters should avoid is unwinding the stack if possible. +Some stacks can run very deep, into the tens of thousands in some +cases. To search every frame to determine if it is inlined ahead of +time may be too expensive at the filtering step. The frame filter +cannot know how many frames it has to iterate over, and it would have +to iterate through them all. This ends up duplicating effort as +@value{GDBN} performs this iteration when it prints the frames. + +In this example decision making can be deferred to the printing step. +As each frame is printed, the frame decorator can examine each frame +in turn when @value{GDBN} iterates. From a performance viewpoint, +this is the most appropriate decision to make as it avoids duplicating +the effort that the printing step would undertake anyway. Also, if +there are many frame filters unwinding the stack during filtering, it +can substantially delay the printing of the backtrace which will +result in large memory usage, and a poor user experience. + +@smallexample +class InlineFilter(): + + def __init__(self): + self.name = "InlinedFrameFilter" + self.priority = 100 + self.enabled = True + gdb.frame_filters[self.name] = self + + def filter(self, frame_iter): + frame_iter = itertools.imap(InlinedFrameDecorator, + frame_iter) + return frame_iter +@end smallexample + +This frame filter is somewhat similar to the earlier example, except +that the @code{filter} method applies a frame decorator object called +@code{InlinedFrameDecorator} to each element in the iterator. The +@code{imap} Python method is light-weight. It does not proactively +iterate over the iterator, but rather creates a new iterator which +wraps the existing one. + +Below is the frame decorator for this example. + +@smallexample +class InlinedFrameDecorator(FrameDecorator): + + def __init__(self, fobj): + super(InlinedFrameDecorator, self).__init__(fobj) + + def function(self): + frame = fobj.inferior_frame() + name = str(frame.name()) + + if frame.type() == gdb.INLINE_FRAME: + name = name + " [inlined]" + + return name +@end smallexample + +This frame decorator only defines and overrides the @code{function} +method. It lets the supplied @code{FrameDecorator}, which is shipped +with @value{GDBN}, perform the other work associated with printing +this frame. + +The combination of these two objects create this output from a +backtrace: + +@smallexample +#0 0x004004e0 in bar () at inline.c:11 +#1 0x00400566 in max [inlined] (b=6, a=12) at inline.c:21 +#2 0x00400566 in main () at inline.c:31 +@end smallexample + +So in the case of this example, a frame decorator is applied to all +frames, regardless of whether they may be inlined or not. As +@value{GDBN} iterates over the iterator produced by the frame filters, +@value{GDBN} executes each frame decorator which then makes a decision +on what to print in the @code{function} callback. Using a strategy +like this is a way to defer decisions on the frame content to printing +time. + +@subheading Eliding Frames + +It might be that the above example is not desirable for representing +inlined frames, and a hierarchical approach may be preferred. If we +want to hierarchically represent frames, the @code{elided} frame +decorator interface might be preferable. + +This example approaches the issue with the @code{elided} method. This +example is quite long, but very simplistic. It is out-of-scope for +this section to write a complete example that comprehensively covers +all approaches of finding and printing inlined frames. However, this +example illustrates the approach an author might use. + +This example comprises of three sections. + +@smallexample +class InlineFrameFilter(): + + def __init__(self): + self.name = "InlinedFrameFilter" + self.priority = 100 + self.enabled = True + gdb.frame_filters[self.name] = self + + def filter(self, frame_iter): + return ElidingInlineIterator(frame_iter) +@end smallexample + +This frame filter is very similar to the other examples. The only +difference is this frame filter is wrapping the iterator provided to +it (@code{frame_iter}) with a custom iterator called +@code{ElidingInlineIterator}. This again defers actions to when +@value{GDBN} prints the backtrace, as the iterator is not traversed +until printing. + +The iterator for this example is as follows. It is in this section of +the example where decisions are made on the content of the backtrace. + +@smallexample +class ElidingInlineIterator: + def __init__(self, ii): + self.input_iterator = ii + + def __iter__(self): + return self + + def next(self): + frame = next(self.input_iterator) + + if frame.inferior_frame().type() != gdb.INLINE_FRAME: + return frame + + try: + eliding_frame = next(self.input_iterator) + except StopIteration: + return frame + return ElidingFrameDecorator(eliding_frame, [frame]) +@end smallexample + +This iterator implements the Python iterator protocol. When the +@code{next} function is called (when @value{GDBN} prints each frame), +the iterator checks if this frame decorator, @code{frame}, is wrapping +an inlined frame. If it is not, it returns the existing frame decorator +untouched. If it is wrapping an inlined frame, it assumes that the +inlined frame was contained within the next oldest frame, +@code{eliding_frame}, which it fetches. It then creates and returns a +frame decorator, @code{ElidingFrameDecorator}, which contains both the +elided frame, and the eliding frame. + +@smallexample +class ElidingInlineDecorator(FrameDecorator): + + def __init__(self, frame, elided_frames): + super(ElidingInlineDecorator, self).__init__(frame) + self.frame = frame + self.elided_frames = elided_frames + + def elided(self): + return iter(self.elided_frames) +@end smallexample + +This frame decorator overrides one function and returns the inlined +frame in the @code{elided} method. As before it lets +@code{FrameDecorator} do the rest of the work involved in printing +this frame. This produces the following output. + +@smallexample +#0 0x004004e0 in bar () at inline.c:11 +#2 0x00400529 in main () at inline.c:25 + #1 0x00400529 in max (b=6, a=12) at inline.c:15 +@end smallexample + +In that output, @code{max} which has been inlined into @code{main} is +printed hierarchically. Another approach would be to combine the +@code{function} method, and the @code{elided} method to both print a +marker in the inlined frame, and also show the hierarchical +relationship. + @node Inferiors In Python @subsubsection Inferiors In Python @cindex inferiors in Python @@ -25235,6 +26026,11 @@ The @code{type_printers} attribute is a list of type printer objects. @xref{Type Printing API}, for more information. @end defvar +@defvar Progspace.frame_filters +The @code{frame_filters} attribute is a dictionary of frame filter +objects. @xref{Frame Filter API}, for more information. +@end defvar + @node Objfiles In Python @subsubsection Objfiles In Python @@ -25285,6 +26081,11 @@ The @code{type_printers} attribute is a list of type printer objects. @xref{Type Printing API}, for more information. @end defvar +@defvar Objfile.frame_filters +The @code{frame_filters} attribute is a dictionary of frame filter +objects. @xref{Frame Filter API}, for more information. +@end defvar + A @code{gdb.Objfile} object has the following methods: @defun Objfile.is_valid () @@ -26351,7 +27152,7 @@ No my-foo-pretty-printers.py When reading an auto-loaded file, @value{GDBN} sets the @dfn{current objfile}. This is available via the @code{gdb.current_objfile} function (@pxref{Objfiles In Python}). This can be useful for -registering objfile-specific pretty-printers. +registering objfile-specific pretty-printers and frame-filters. @menu * objfile-gdb.py file:: The @file{@var{objfile}-gdb.py} file @@ -30222,6 +31023,22 @@ Is this going away???? @node GDB/MI Stack Manipulation @section @sc{gdb/mi} Stack Manipulation Commands +@subheading The @code{-enable-frame-filters} Command +@findex -enable-frame-filters + +@smallexample +-enable-frame-filters +@end smallexample + +@value{GDBN} allows Python-based frame filters to affect the output of +the MI commands relating to stack traces. As there is no way to +implement this in a fully backward-compatible way, a front end must +request that this functionality be enabled. + +Once enabled, this feature cannot be disabled. + +Note that if Python support has not been compiled into @value{GDBN}, +this command will still succeed (and do nothing). @subheading The @code{-stack-info-frame} Command @findex -stack-info-frame @@ -30289,13 +31106,14 @@ For a stack with frame levels 0 through 11: (gdb) @end smallexample +@anchor{-stack-list-arguments} @subheading The @code{-stack-list-arguments} Command @findex -stack-list-arguments @subsubheading Synopsis @smallexample - -stack-list-arguments @var{print-values} + -stack-list-arguments [ --no-frame-filters ] @var{print-values} [ @var{low-frame} @var{high-frame} ] @end smallexample @@ -30312,7 +31130,9 @@ If @var{print-values} is 0 or @code{--no-values}, print only the names of the variables; if it is 1 or @code{--all-values}, print also their values; and if it is 2 or @code{--simple-values}, print the name, type and value for simple data types, and the name and type for arrays, -structures and unions. +structures and unions. If the option @code{--no-frame-filters} is +supplied, then Python frame filters will not be executed. + Use of this command to obtain arguments in a single frame is deprecated in favor of the @samp{-stack-list-variables} command. @@ -30383,13 +31203,14 @@ args=[@{name="intarg",value="2"@}, @c @subheading -stack-list-exception-handlers +@anchor{-stack-list-frames} @subheading The @code{-stack-list-frames} Command @findex -stack-list-frames @subsubheading Synopsis @smallexample - -stack-list-frames [ @var{low-frame} @var{high-frame} ] + -stack-list-frames [ --no-frame-filters @var{low-frame} @var{high-frame} ] @end smallexample List the frames currently on the stack. For each frame it displays the @@ -30419,7 +31240,9 @@ levels are between the two arguments (inclusive). If the two arguments are equal, it shows the single frame at the corresponding level. It is an error if @var{low-frame} is larger than the actual number of frames. On the other hand, @var{high-frame} may be larger than the -actual number of frames, in which case only existing frames will be returned. +actual number of frames, in which case only existing frames will be +returned. If the option @code{--no-frame-filters} is supplied, then +Python frame filters will not be executed. @subsubheading @value{GDBN} Command @@ -30489,11 +31312,12 @@ Show a single frame: @subheading The @code{-stack-list-locals} Command @findex -stack-list-locals +@anchor{-stack-list-locals} @subsubheading Synopsis @smallexample - -stack-list-locals @var{print-values} + -stack-list-locals [ --no-frame-filters ] @var{print-values} @end smallexample Display the local variable names for the selected frame. If @@ -30504,7 +31328,8 @@ type and value for simple data types, and the name and type for arrays, structures and unions. In this last case, a frontend can immediately display the value of simple data types and create variable objects for other data types when the user wishes to explore their values in -more detail. +more detail. If the option @code{--no-frame-filters} is supplied, then +Python frame filters will not be executed. This command is deprecated in favor of the @samp{-stack-list-variables} command. @@ -30529,13 +31354,14 @@ This command is deprecated in favor of the (gdb) @end smallexample +@anchor{-stack-list-variables} @subheading The @code{-stack-list-variables} Command @findex -stack-list-variables @subsubheading Synopsis @smallexample - -stack-list-variables @var{print-values} + -stack-list-variables [ --no-frame-filters ] @var{print-values} @end smallexample Display the names of local variables and function arguments for the selected frame. If @@ -30543,7 +31369,8 @@ Display the names of local variables and function arguments for the selected fra the variables; if it is 1 or @code{--all-values}, print also their values; and if it is 2 or @code{--simple-values}, print the name, type and value for simple data types, and the name and type for arrays, -structures and unions. +structures and unions. If the option @code{--no-frame-filters} is +supplied, then Python frame filters will not be executed. @subsubheading Example diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c index 062cd5dc387..1cd9a47586f 100644 --- a/gdb/mi/mi-cmd-stack.c +++ b/gdb/mi/mi-cmd-stack.c @@ -31,6 +31,10 @@ #include "language.h" #include "valprint.h" #include "exceptions.h" +#include "utils.h" +#include "mi-getopt.h" +#include "python/python.h" +#include enum what_to_list { locals, arguments, all }; @@ -38,6 +42,28 @@ static void list_args_or_locals (enum what_to_list what, enum print_values values, struct frame_info *fi); +/* True if we want to allow Python-based frame filters. */ +static int frame_filters = 0; + +void +mi_cmd_enable_frame_filters (char *command, char **argv, int argc) +{ + if (argc != 0) + error (_("-enable-frame-filters: no arguments allowed")); + frame_filters = 1; +} + +/* Parse the --no-frame-filters option in commands where we cannot use + mi_getopt. */ +static int +parse_no_frames_option (const char *arg) +{ + if (arg && (strcmp (arg, "--no-frame-filters") == 0)) + return 1; + + return 0; +} + /* Print a list of the stack frames. Args can be none, in which case we want to print the whole backtrace, or a pair of numbers specifying the frame numbers at which to start and stop the @@ -52,14 +78,46 @@ mi_cmd_stack_list_frames (char *command, char **argv, int argc) int i; struct cleanup *cleanup_stack; struct frame_info *fi; + enum py_bt_status result = PY_BT_ERROR; + int raw_arg = 0; + int oind = 0; + enum opt + { + NO_FRAME_FILTERS + }; + static const struct mi_opt opts[] = + { + {"-no-frame-filters", NO_FRAME_FILTERS, 0}, + { 0, 0, 0 } + }; - if (argc > 2 || argc == 1) - error (_("-stack-list-frames: Usage: [FRAME_LOW FRAME_HIGH]")); + /* Parse arguments. In this instance we are just looking for + --no-frame-filters. */ + while (1) + { + char *oarg; + int opt = mi_getopt ("-stack-list-frames", argc, argv, + opts, &oind, &oarg); + if (opt < 0) + break; + switch ((enum opt) opt) + { + case NO_FRAME_FILTERS: + raw_arg = oind; + break; + } + } + + /* After the last option is parsed, there should either be low - + high range, or no further arguments. */ + if ((argc - oind != 0) && (argc - oind != 2)) + error (_("-stack-list-frames: Usage: [--no-frame-filters] [FRAME_LOW FRAME_HIGH]")); - if (argc == 2) + /* If there is a range, set it. */ + if (argc - oind == 2) { - frame_low = atoi (argv[0]); - frame_high = atoi (argv[1]); + frame_low = atoi (argv[0 + oind]); + frame_high = atoi (argv[1 + oind]); } else { @@ -81,16 +139,37 @@ mi_cmd_stack_list_frames (char *command, char **argv, int argc) cleanup_stack = make_cleanup_ui_out_list_begin_end (current_uiout, "stack"); - /* Now let's print the frames up to frame_high, or until there are - frames in the stack. */ - for (; - fi && (i <= frame_high || frame_high == -1); - i++, fi = get_prev_frame (fi)) + if (! raw_arg && frame_filters) { - QUIT; - /* Print the location and the address always, even for level 0. - If args is 0, don't print the arguments. */ - print_frame_info (fi, 1, LOC_AND_ADDRESS, 0 /* args */ ); + int flags = PRINT_LEVEL | PRINT_FRAME_INFO; + int py_frame_low = frame_low; + + /* We cannot pass -1 to frame_low, as that would signify a + relative backtrace from the tail of the stack. So, in the case + of frame_low == -1, assign and increment it. */ + if (py_frame_low == -1) + py_frame_low++; + + result = apply_frame_filter (get_current_frame (), flags, + NO_VALUES, current_uiout, + py_frame_low, frame_high); + } + + /* Run the inbuilt backtrace if there are no filters registered, or + if "--no-frame-filters" has been specified from the command. */ + if (! frame_filters || raw_arg || result == PY_BT_NO_FILTERS) + { + /* Now let's print the frames up to frame_high, or until there are + frames in the stack. */ + for (; + fi && (i <= frame_high || frame_high == -1); + i++, fi = get_prev_frame (fi)) + { + QUIT; + /* Print the location and the address always, even for level 0. + If args is 0, don't print the arguments. */ + print_frame_info (fi, 1, LOC_AND_ADDRESS, 0 /* args */ ); + } } do_cleanups (cleanup_stack); @@ -147,13 +226,34 @@ void mi_cmd_stack_list_locals (char *command, char **argv, int argc) { struct frame_info *frame; + int raw_arg = 0; + enum py_bt_status result = PY_BT_ERROR; + int print_value; - if (argc != 1) - error (_("-stack-list-locals: Usage: PRINT_VALUES")); + if (argc > 0) + raw_arg = parse_no_frames_option (argv[0]); - frame = get_selected_frame (NULL); + if (argc < 1 || argc > 2 || (argc == 2 && ! raw_arg) + || (argc == 1 && raw_arg)) + error (_("-stack-list-locals: Usage: [--no-frame-filters] PRINT_VALUES")); - list_args_or_locals (locals, parse_print_values (argv[0]), frame); + frame = get_selected_frame (NULL); + print_value = parse_print_values (argv[raw_arg]); + + if (! raw_arg && frame_filters) + { + int flags = PRINT_LEVEL | PRINT_LOCALS; + + result = apply_frame_filter (frame, flags, print_value, + current_uiout, 0, 0); + } + + /* Run the inbuilt backtrace if there are no filters registered, or + if "--no-frame-filters" has been specified from the command. */ + if (! frame_filters || raw_arg || result == PY_BT_NO_FILTERS) + { + list_args_or_locals (locals, print_value, frame); + } } /* Print a list of the arguments for the current frame. With argument @@ -170,15 +270,20 @@ mi_cmd_stack_list_args (char *command, char **argv, int argc) struct cleanup *cleanup_stack_args; enum print_values print_values; struct ui_out *uiout = current_uiout; + int raw_arg = 0; + enum py_bt_status result = PY_BT_ERROR; + + if (argc > 0) + raw_arg = parse_no_frames_option (argv[0]); - if (argc < 1 || argc > 3 || argc == 2) - error (_("-stack-list-arguments: Usage: " - "PRINT_VALUES [FRAME_LOW FRAME_HIGH]")); + if (argc < 1 || (argc > 3 && ! raw_arg) || (argc == 2 && ! raw_arg)) + error (_("-stack-list-arguments: Usage: " \ + "[--no-frame-filters] PRINT_VALUES [FRAME_LOW FRAME_HIGH]")); - if (argc == 3) + if (argc >= 3) { - frame_low = atoi (argv[1]); - frame_high = atoi (argv[2]); + frame_low = atoi (argv[1 + raw_arg]); + frame_high = atoi (argv[2 + raw_arg]); } else { @@ -188,7 +293,7 @@ mi_cmd_stack_list_args (char *command, char **argv, int argc) frame_high = -1; } - print_values = parse_print_values (argv[0]); + print_values = parse_print_values (argv[raw_arg]); /* Let's position fi on the frame at which to start the display. Could be the innermost frame if the whole stack needs @@ -203,21 +308,41 @@ mi_cmd_stack_list_args (char *command, char **argv, int argc) cleanup_stack_args = make_cleanup_ui_out_list_begin_end (uiout, "stack-args"); - /* Now let's print the frames up to frame_high, or until there are - frames in the stack. */ - for (; - fi && (i <= frame_high || frame_high == -1); - i++, fi = get_prev_frame (fi)) + if (! raw_arg && frame_filters) { - struct cleanup *cleanup_frame; - - QUIT; - cleanup_frame = make_cleanup_ui_out_tuple_begin_end (uiout, "frame"); - ui_out_field_int (uiout, "level", i); - list_args_or_locals (arguments, print_values, fi); - do_cleanups (cleanup_frame); + int flags = PRINT_LEVEL | PRINT_ARGS; + int py_frame_low = frame_low; + + /* We cannot pass -1 to frame_low, as that would signify a + relative backtrace from the tail of the stack. So, in the case + of frame_low == -1, assign and increment it. */ + if (py_frame_low == -1) + py_frame_low++; + + result = apply_frame_filter (get_current_frame (), flags, + print_values, current_uiout, + py_frame_low, frame_high); } + /* Run the inbuilt backtrace if there are no filters registered, or + if "--no-frame-filters" has been specified from the command. */ + if (! frame_filters || raw_arg || result == PY_BT_NO_FILTERS) + { + /* Now let's print the frames up to frame_high, or until there are + frames in the stack. */ + for (; + fi && (i <= frame_high || frame_high == -1); + i++, fi = get_prev_frame (fi)) + { + struct cleanup *cleanup_frame; + + QUIT; + cleanup_frame = make_cleanup_ui_out_tuple_begin_end (uiout, "frame"); + ui_out_field_int (uiout, "level", i); + list_args_or_locals (arguments, print_values, fi); + do_cleanups (cleanup_frame); + } + } do_cleanups (cleanup_stack_args); } @@ -230,13 +355,35 @@ void mi_cmd_stack_list_variables (char *command, char **argv, int argc) { struct frame_info *frame; + int raw_arg = 0; + enum py_bt_status result = PY_BT_ERROR; + int print_value; - if (argc != 1) - error (_("Usage: PRINT_VALUES")); + if (argc > 0) + raw_arg = parse_no_frames_option (argv[0]); - frame = get_selected_frame (NULL); + if (argc < 1 || argc > 2 || (argc == 2 && ! raw_arg) + || (argc == 1 && raw_arg)) + error (_("-stack-list-variables: Usage: " \ + "[--no-frame-filters] PRINT_VALUES")); - list_args_or_locals (all, parse_print_values (argv[0]), frame); + frame = get_selected_frame (NULL); + print_value = parse_print_values (argv[raw_arg]); + + if (! raw_arg && frame_filters) + { + int flags = PRINT_LEVEL | PRINT_ARGS | PRINT_LOCALS; + + result = apply_frame_filter (frame, flags, print_value, + current_uiout, 0, 0); + } + + /* Run the inbuilt backtrace if there are no filters registered, or + if "--no-frame-filters" has been specified from the command. */ + if (! frame_filters || raw_arg || result == PY_BT_NO_FILTERS) + { + list_args_or_locals (all, print_value, frame); + } } /* Print single local or argument. ARG must be already read in. For diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c index df173fe4936..c9efa815af0 100644 --- a/gdb/mi/mi-cmds.c +++ b/gdb/mi/mi-cmds.c @@ -86,6 +86,7 @@ static struct mi_cmd mi_cmds[] = mi_cmd_data_write_register_values), DEF_MI_CMD_MI ("enable-timings", mi_cmd_enable_timings), DEF_MI_CMD_MI ("enable-pretty-printing", mi_cmd_enable_pretty_printing), + DEF_MI_CMD_MI ("enable-frame-filters", mi_cmd_enable_frame_filters), DEF_MI_CMD_MI ("environment-cd", mi_cmd_env_cd), DEF_MI_CMD_MI ("environment-directory", mi_cmd_env_dir), DEF_MI_CMD_MI ("environment-path", mi_cmd_env_path), diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h index fdf6f9ca545..4b9fb7ade6c 100644 --- a/gdb/mi/mi-cmds.h +++ b/gdb/mi/mi-cmds.h @@ -118,6 +118,7 @@ extern mi_cmd_argv_ftype mi_cmd_var_show_attributes; extern mi_cmd_argv_ftype mi_cmd_var_show_format; extern mi_cmd_argv_ftype mi_cmd_var_update; extern mi_cmd_argv_ftype mi_cmd_enable_pretty_printing; +extern mi_cmd_argv_ftype mi_cmd_enable_frame_filters; extern mi_cmd_argv_ftype mi_cmd_var_set_update_range; /* Description of a single command. */ diff --git a/gdb/python/lib/gdb/FrameDecorator.py b/gdb/python/lib/gdb/FrameDecorator.py new file mode 100644 index 00000000000..cacab4dde14 --- /dev/null +++ b/gdb/python/lib/gdb/FrameDecorator.py @@ -0,0 +1,285 @@ +# Copyright (C) 2013 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 . + +import gdb + +class FrameDecorator(object): + """Basic implementation of a Frame Decorator""" + + """ This base frame decorator decorates a frame or another frame + decorator, and provides convenience methods. If this object is + wrapping a frame decorator, defer to that wrapped object's method + if it has one. This allows for frame decorators that have + sub-classed FrameDecorator object, but also wrap other frame + decorators on the same frame to correctly execute. + + E.g + + If the result of frame filters running means we have one gdb.Frame + wrapped by multiple frame decorators, all sub-classed from + FrameDecorator, the resulting hierarchy will be: + + Decorator1 + -- (wraps) Decorator2 + -- (wraps) FrameDecorator + -- (wraps) gdb.Frame + + In this case we have two frame decorators, both of which are + sub-classed from FrameDecorator. If Decorator1 just overrides the + 'function' method, then all of the other methods are carried out + by the super-class FrameDecorator. But Decorator2 may have + overriden other methods, so FrameDecorator will look at the + 'base' parameter and defer to that class's methods. And so on, + down the chain.""" + + # 'base' can refer to a gdb.Frame or another frame decorator. In + # the latter case, the child class will have called the super + # method and _base will be an object conforming to the Frame Filter + # class. + def __init__(self, base): + self._base = base + + @staticmethod + def _is_limited_frame(frame): + """Internal utility to determine if the frame is special or + limited.""" + sal = frame.find_sal() + + if (not sal.symtab or not sal.symtab.filename + or frame.type() == gdb.DUMMY_FRAME + or frame.type() == gdb.SIGTRAMP_FRAME): + + return True + + return False + + def elided(self): + """Return any elided frames that this class might be + wrapping, or None.""" + if hasattr(self._base, "elided"): + return self._base.elided() + + return None + + def function(self): + """ Return the name of the frame's function or an address of + the function of the frame. First determine if this is a + special frame. If not, try to determine filename from GDB's + frame internal function API. Finally, if a name cannot be + determined return the address. If this function returns an + address, GDB will attempt to determine the function name from + its internal minimal symbols store (for example, for inferiors + without debug-info).""" + + # Both gdb.Frame, and FrameDecorator have a method called + # "function", so determine which object this is. + if not isinstance(self._base, gdb.Frame): + if hasattr(self._base, "function"): + # If it is not a gdb.Frame, and there is already a + # "function" method, use that. + return self._base.function() + + frame = self.inferior_frame() + + if frame.type() == gdb.DUMMY_FRAME: + return "" + elif frame.type() == gdb.SIGTRAMP_FRAME: + return "" + + func = frame.function() + + # If we cannot determine the function name, return the + # address. If GDB detects an integer value from this function + # it will attempt to find the function name from minimal + # symbols via its own internal functions. + if func == None: + pc = frame.pc() + return pc + + return str(func) + + def address(self): + """ Return the address of the frame's pc""" + + if hasattr(self._base, "address"): + return self._base.address() + + frame = self.inferior_frame() + return frame.pc() + + def filename(self): + """ Return the filename associated with this frame, detecting + and returning the appropriate library name is this is a shared + library.""" + + if hasattr(self._base, "filename"): + return self._base.filename() + + frame = self.inferior_frame() + sal = frame.find_sal() + if not sal.symtab or not sal.symtab.filename: + pc = frame.pc() + return gdb.solib_name(pc) + else: + return sal.symtab.filename + + def frame_args(self): + """ Return an iterable of frame arguments for this frame, if + any. The iterable object contains objects conforming with the + Symbol/Value interface. If there are no frame arguments, or + if this frame is deemed to be a special case, return None.""" + + if hasattr(self._base, "frame_args"): + return self._base.frame_args() + + frame = self.inferior_frame() + if self._is_limited_frame(frame): + return None + + args = FrameVars(frame) + return args.fetch_frame_args() + + def frame_locals(self): + """ Return an iterable of local variables for this frame, if + any. The iterable object contains objects conforming with the + Symbol/Value interface. If there are no frame locals, or if + this frame is deemed to be a special case, return None.""" + + if hasattr(self._base, "frame_locals"): + return self._base.frame_locals() + + frame = self.inferior_frame() + if self._is_limited_frame(frame): + return None + + args = FrameVars(frame) + return args.fetch_frame_locals() + + def line(self): + """ Return line number information associated with the frame's + pc. If symbol table/line information does not exist, or if + this frame is deemed to be a special case, return None""" + + if hasattr(self._base, "line"): + return self._base.line() + + frame = self.inferior_frame() + if self._is_limited_frame(frame): + return None + + sal = frame.find_sal() + if (sal): + return sal.line + else: + return None + + def inferior_frame(self): + """ Return the gdb.Frame underpinning this frame decorator.""" + + # If 'base' is a frame decorator, we want to call its inferior + # frame method. If '_base' is a gdb.Frame, just return that. + if hasattr(self._base, "inferior_frame"): + return self._base.inferior_frame() + return self._base + +class SymValueWrapper(object): + """A container class conforming to the Symbol/Value interface + which holds frame locals or frame arguments.""" + def __init__(self, symbol, value): + self.sym = symbol + self.val = value + + def value(self): + """ Return the value associated with this symbol, or None""" + return self.val + + def symbol(self): + """ Return the symbol, or Python text, associated with this + symbol, or None""" + return self.sym + +class FrameVars(object): + + """Utility class to fetch and store frame local variables, or + frame arguments.""" + + def __init__(self, frame): + self.frame = frame + self.symbol_class = { + gdb.SYMBOL_LOC_STATIC: True, + gdb.SYMBOL_LOC_REGISTER: True, + gdb.SYMBOL_LOC_ARG: True, + gdb.SYMBOL_LOC_REF_ARG: True, + gdb.SYMBOL_LOC_LOCAL: True, + gdb.SYMBOL_LOC_REGPARM_ADDR: True, + gdb.SYMBOL_LOC_COMPUTED: True + } + + def fetch_b(self, sym): + """ Local utility method to determine if according to Symbol + type whether it should be included in the iterator. Not all + symbols are fetched, and only symbols that return + True from this method should be fetched.""" + + # SYM may be a string instead of a symbol in the case of + # synthetic local arguments or locals. If that is the case, + # always fetch. + if isinstance(sym, basestring): + return True + + sym_type = sym.addr_class + + return self.symbol_class.get(sym_type, False) + + def fetch_frame_locals(self): + """Public utility method to fetch frame local variables for + the stored frame. Frame arguments are not fetched. If there + are no frame local variables, return an empty list.""" + lvars = [] + + block = self.frame.block() + + while block != None: + if block.is_global or block.is_static: + break + for sym in block: + if sym.is_argument: + continue; + if self.fetch_b(sym): + lvars.append(SymValueWrapper(sym, None)) + + block = block.superblock + + return lvars + + def fetch_frame_args(self): + """Public utility method to fetch frame arguments for the + stored frame. Frame arguments are the only type fetched. If + there are no frame argument variables, return an empty list.""" + + args = [] + block = self.frame.block() + while block != None: + if block.function != None: + break + block = block.superblock + + if block != None: + for sym in block: + if not sym.is_argument: + continue; + args.append(SymValueWrapper(sym, None)) + + return args diff --git a/gdb/python/lib/gdb/FrameIterator.py b/gdb/python/lib/gdb/FrameIterator.py new file mode 100644 index 00000000000..b3af94b8acd --- /dev/null +++ b/gdb/python/lib/gdb/FrameIterator.py @@ -0,0 +1,45 @@ +# Copyright (C) 2013 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 . + +import gdb +import itertools + +class FrameIterator(object): + """A gdb.Frame iterator. Iterates over gdb.Frames or objects that + conform to that interface.""" + + def __init__(self, frame_obj): + """Initialize a FrameIterator. + + Arguments: + frame_obj the starting frame.""" + + super(FrameIterator, self).__init__() + self.frame = frame_obj + + def __iter__(self): + return self + + def next(self): + """next implementation. + + Returns: + The next oldest frame.""" + + result = self.frame + if result is None: + raise StopIteration + self.frame = result.older() + return result diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py index 63115830925..61f5b5ee79a 100644 --- a/gdb/python/lib/gdb/__init__.py +++ b/gdb/python/lib/gdb/__init__.py @@ -67,6 +67,8 @@ pretty_printers = [] # Initial type printers. type_printers = [] +# Initial frame filters. +frame_filters = {} # Convenience variable to GDB's python directory PYTHONDIR = os.path.dirname(os.path.dirname(__file__)) diff --git a/gdb/python/lib/gdb/command/frame_filters.py b/gdb/python/lib/gdb/command/frame_filters.py new file mode 100644 index 00000000000..1b7305927e5 --- /dev/null +++ b/gdb/python/lib/gdb/command/frame_filters.py @@ -0,0 +1,461 @@ +# Frame-filter commands. +# Copyright (C) 2013 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 . + +"""GDB commands for working with frame-filters.""" + +import gdb +import copy +from gdb.FrameIterator import FrameIterator +from gdb.FrameDecorator import FrameDecorator +import gdb.frames +import itertools + +# GDB Commands. +class SetFilterPrefixCmd(gdb.Command): + """Prefix command for 'set' frame-filter related operations.""" + + def __init__(self): + super(SetFilterPrefixCmd, self).__init__("set frame-filter", + gdb.COMMAND_OBSCURE, + gdb.COMPLETE_NONE, True) + +class ShowFilterPrefixCmd(gdb.Command): + """Prefix command for 'show' frame-filter related operations.""" + def __init__(self): + super(ShowFilterPrefixCmd, self).__init__("show frame-filter", + gdb.COMMAND_OBSCURE, + gdb.COMPLETE_NONE, True) +class InfoFrameFilter(gdb.Command): + """List all registered Python frame-filters. + + Usage: info frame-filters + """ + + def __init__(self): + super(InfoFrameFilter, self).__init__("info frame-filter", + gdb.COMMAND_DATA) + @staticmethod + def enabled_string(state): + """Return "Yes" if filter is enabled, otherwise "No".""" + if state: + return "Yes" + else: + return "No" + + def list_frame_filters(self, frame_filters): + """ Internal worker function to list and print frame filters + in a dictionary. + + Arguments: + frame_filters: The name of the dictionary, as + specified by GDB user commands. + """ + + sorted_frame_filters = sorted(frame_filters.items(), + key=lambda i: gdb.frames.get_priority(i[1]), + reverse=True) + + if len(sorted_frame_filters) == 0: + print(" No frame filters registered.") + else: + print(" Priority Enabled Name") + for frame_filter in sorted_frame_filters: + name = frame_filter[0] + try: + priority = '{:<8}'.format( + str(gdb.frames.get_priority(frame_filter[1]))) + enabled = '{:<7}'.format( + self.enabled_string(gdb.frames.get_enabled(frame_filter[1]))) + except Exception as e: + print(" Error printing filter '"+name+"': "+str(e)) + else: + print(" %s %s %s" % (priority, enabled, name)) + + def print_list(self, title, filter_list, blank_line): + print(title) + self.list_frame_filters(filter_list) + if blank_line: + print("") + + def invoke(self, arg, from_tty): + self.print_list("global frame-filters:", gdb.frame_filters, True) + + cp = gdb.current_progspace() + self.print_list("progspace %s frame-filters:" % cp.filename, + cp.frame_filters, True) + + for objfile in gdb.objfiles(): + self.print_list("objfile %s frame-filters:" % objfile.filename, + objfile.frame_filters, False) + +# Internal enable/disable functions. + +def _enable_parse_arg(cmd_name, arg): + """ Internal worker function to take an argument from + enable/disable and return a tuple of arguments. + + Arguments: + cmd_name: Name of the command invoking this function. + args: The argument as a string. + + Returns: + A tuple containing the dictionary, and the argument, or just + the dictionary in the case of "all". + """ + + argv = gdb.string_to_argv(arg); + argc = len(argv) + if argv[0] == "all" and argc > 1: + raise gdb.GdbError(cmd_name + ": with 'all' " \ + "you may not specify a filter.") + else: + if argv[0] != "all" and argc != 2: + raise gdb.GdbError(cmd_name + " takes exactly two arguments.") + + return argv + +def _do_enable_frame_filter(command_tuple, flag): + """Worker for enabling/disabling frame_filters. + + Arguments: + command_type: A tuple with the first element being the + frame filter dictionary, and the second being + the frame filter name. + flag: True for Enable, False for Disable. + """ + + list_op = command_tuple[0] + op_list = gdb.frames.return_list(list_op) + + if list_op == "all": + for item in op_list: + gdb.frames.set_enabled(item, flag) + else: + frame_filter = command_tuple[1] + try: + ff = op_list[frame_filter] + except KeyError: + msg = "frame-filter '" + str(name) + "' not found." + raise gdb.GdbError(msg) + + gdb.frames.set_enabled(ff, flag) + +def _complete_frame_filter_list(text, word, all_flag): + """Worker for frame filter dictionary name completion. + + Arguments: + text: The full text of the command line. + word: The most recent word of the command line. + all_flag: Whether to include the word "all" in completion. + + Returns: + A list of suggested frame filter dictionary name completions + from text/word analysis. This list can be empty when there + are no suggestions for completion. + """ + if all_flag == True: + filter_locations = ["all", "global", "progspace"] + else: + filter_locations = ["global", "progspace"] + for objfile in gdb.objfiles(): + filter_locations.append(objfile.filename) + + # If the user just asked for completions with no completion + # hints, just return all the frame filter dictionaries we know + # about. + if (text == ""): + return filter_locations + + # Otherwise filter on what we know. + flist = filter(lambda x,y=text:x.startswith(y), filter_locations) + + # If we only have one completion, complete it and return it. + if len(flist) == 1: + flist[0] = flist[0][len(text)-len(word):] + + # Otherwise, return an empty list, or a list of frame filter + # dictionaries that the previous filter operation returned. + return flist + +def _complete_frame_filter_name(word, printer_dict): + """Worker for frame filter name completion. + + Arguments: + + word: The most recent word of the command line. + + printer_dict: The frame filter dictionary to search for frame + filter name completions. + + Returns: A list of suggested frame filter name completions + from word analysis of the frame filter dictionary. This list + can be empty when there are no suggestions for completion. + """ + + printer_keys = printer_dict.keys() + if (word == ""): + return printer_keys + + flist = filter(lambda x,y=word:x.startswith(y), printer_keys) + return flist + +class EnableFrameFilter(gdb.Command): + """GDB command to disable the specified frame-filter. + + Usage: enable frame-filter enable DICTIONARY [NAME] + + DICTIONARY is the name of the frame filter dictionary on which to + operate. If dictionary is set to "all", perform operations on all + dictionaries. Named dictionaries are: "global" for the global + frame filter dictionary, "progspace" for the program space's frame + filter dictionary. If either all, or the two named dictionaries + are not specified, the dictionary name is assumed to be the name + of the object-file name. + + NAME matches the name of the frame-filter to operate on. If + DICTIONARY is "all", NAME is ignored. + """ + def __init__(self): + super(EnableFrameFilter, self).__init__("enable frame-filter", + gdb.COMMAND_DATA) + def complete(self, text, word): + """Completion function for both frame filter dictionary, and + frame filter name.""" + if text.count(" ") == 0: + return _complete_frame_filter_list(text, word, True) + else: + printer_list = gdb.frames.return_list(text.split()[0].rstrip()) + return _complete_frame_filter_name(word, printer_list) + + def invoke(self, arg, from_tty): + command_tuple = _enable_parse_arg("enable frame-filter", arg) + _do_enable_frame_filter(command_tuple, True) + + +class DisableFrameFilter(gdb.Command): + """GDB command to disable the specified frame-filter. + + Usage: disable frame-filter disable DICTIONARY [NAME] + + DICTIONARY is the name of the frame filter dictionary on which to + operate. If dictionary is set to "all", perform operations on all + dictionaries. Named dictionaries are: "global" for the global + frame filter dictionary, "progspace" for the program space's frame + filter dictionary. If either all, or the two named dictionaries + are not specified, the dictionary name is assumed to be the name + of the object-file name. + + NAME matches the name of the frame-filter to operate on. If + DICTIONARY is "all", NAME is ignored. + """ + def __init__(self): + super(DisableFrameFilter, self).__init__("disable frame-filter", + gdb.COMMAND_DATA) + + def complete(self, text, word): + """Completion function for both frame filter dictionary, and + frame filter name.""" + if text.count(" ") == 0: + return _complete_frame_filter_list(text, word, True) + else: + printer_list = gdb.frames.return_list(text.split()[0].rstrip()) + return _complete_frame_filter_name(word, printer_list) + + def invoke(self, arg, from_tty): + command_tuple = _enable_parse_arg("disable frame-filter", arg) + _do_enable_frame_filter(command_tuple, False) + +class SetFrameFilterPriority(gdb.Command): + """GDB command to set the priority of the specified frame-filter. + + Usage: set frame-filter priority DICTIONARY NAME PRIORITY + + DICTIONARY is the name of the frame filter dictionary on which to + operate. Named dictionaries are: "global" for the global frame + filter dictionary, "progspace" for the program space's framefilter + dictionary. If either of these two are not specified, the + dictionary name is assumed to be the name of the object-file name. + + NAME matches the name of the frame filter to operate on. + + PRIORITY is the an integer to assign the new priority to the frame + filter. + """ + + def __init__(self): + super(SetFrameFilterPriority, self).__init__("set frame-filter " \ + "priority", + gdb.COMMAND_DATA) + + def _parse_pri_arg(self, arg): + """Internal worker to parse a priority from a tuple. + + Arguments: + arg: Tuple which contains the arguments from the command. + + Returns: + A tuple containing the dictionary, name and priority from + the arguments. + + Raises: + gdb.GdbError: An error parsing the arguments. + """ + + argv = gdb.string_to_argv(arg); + argc = len(argv) + if argc != 3: + print("set frame-filter priority " \ + "takes exactly three arguments.") + return None + + return argv + + def _set_filter_priority(self, command_tuple): + """Internal worker for setting priority of frame-filters, by + parsing a tuple and calling _set_priority with the parsed + tuple. + + Arguments: + command_tuple: Tuple which contains the arguments from the + command. + """ + + list_op = command_tuple[0] + frame_filter = command_tuple[1] + priority = command_tuple[2] + + op_list = gdb.frames.return_list(list_op) + + try: + ff = op_list[frame_filter] + except KeyError: + msg = "frame-filter '" + str(name) + "' not found." + raise gdb.GdbError(msg) + + gdb.frames.set_priority(ff, priority) + + def complete(self, text, word): + """Completion function for both frame filter dictionary, and + frame filter name.""" + if text.count(" ") == 0: + return _complete_frame_filter_list(text, word, False) + else: + printer_list = gdb.frames.return_list(text.split()[0].rstrip()) + return _complete_frame_filter_name(word, printer_list) + + def invoke(self, arg, from_tty): + command_tuple = self._parse_pri_arg(arg) + if command_tuple != None: + self._set_filter_priority(command_tuple) + +class ShowFrameFilterPriority(gdb.Command): + """GDB command to show the priority of the specified frame-filter. + + Usage: show frame-filter priority DICTIONARY NAME + + DICTIONARY is the name of the frame filter dictionary on which to + operate. Named dictionaries are: "global" for the global frame + filter dictionary, "progspace" for the program space's framefilter + dictionary. If either of these two are not specified, the + dictionary name is assumed to be the name of the object-file name. + + NAME matches the name of the frame-filter to operate on. + """ + + def __init__(self): + super(ShowFrameFilterPriority, self).__init__("show frame-filter " \ + "priority", + gdb.COMMAND_DATA) + + def _parse_pri_arg(self, arg): + """Internal worker to parse a dictionary and name from a + tuple. + + Arguments: + arg: Tuple which contains the arguments from the command. + + Returns: + A tuple containing the dictionary, and frame filter name. + + Raises: + gdb.GdbError: An error parsing the arguments. + """ + + argv = gdb.string_to_argv(arg); + argc = len(argv) + if argc != 2: + print("show frame-filter priority " \ + "takes exactly two arguments.") + return None + + return argv + + def get_filter_priority(self, frame_filters, name): + """Worker for retrieving the priority of frame_filters. + + Arguments: + frame_filters: Name of frame filter dictionary. + name: object to select printers. + + Returns: + The priority of the frame filter. + + Raises: + gdb.GdbError: A frame filter cannot be found. + """ + + op_list = gdb.frames.return_list(frame_filters) + + try: + ff = op_list[name] + except KeyError: + msg = "frame-filter '" + str(name) + "' not found." + raise gdb.GdbError(msg) + + return gdb.frames.get_priority(ff) + + def complete(self, text, word): + """Completion function for both frame filter dictionary, and + frame filter name.""" + + if text.count(" ") == 0: + return _complete_frame_filter_list(text, word, False) + else: + printer_list = frame._return_list(text.split()[0].rstrip()) + return _complete_frame_filter_name(word, printer_list) + + def invoke(self, arg, from_tty): + command_tuple = self._parse_pri_arg(arg) + if command_tuple == None: + return + filter_name = command_tuple[1] + list_name = command_tuple[0] + try: + priority = self.get_filter_priority(list_name, filter_name); + except Exception as e: + print("Error printing filter priority for '"+name+"':"+str(e)) + else: + print("Priority of filter '" + filter_name + "' in list '" \ + + list_name + "' is: " + str(priority)) + +# Register commands +SetFilterPrefixCmd() +ShowFilterPrefixCmd() +InfoFrameFilter() +EnableFrameFilter() +DisableFrameFilter() +SetFrameFilterPriority() +ShowFrameFilterPriority() diff --git a/gdb/python/lib/gdb/frames.py b/gdb/python/lib/gdb/frames.py new file mode 100644 index 00000000000..10dce8e7b42 --- /dev/null +++ b/gdb/python/lib/gdb/frames.py @@ -0,0 +1,229 @@ +# Frame-filter commands. +# Copyright (C) 2013 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 . + +"""Internal functions for working with frame-filters.""" + +import gdb +from gdb.FrameIterator import FrameIterator +from gdb.FrameDecorator import FrameDecorator +import itertools +import collections + +def get_priority(filter_item): + """ Internal worker function to return the frame-filter's priority + from a frame filter object. This is a fail free function as it is + used in sorting and filtering. If a badly implemented frame + filter does not implement the priority attribute, return zero + (otherwise sorting/filtering will fail and prevent other frame + filters from executing). + + Arguments: + filter_item: An object conforming to the frame filter + interface. + + Returns: + The priority of the frame filter from the "priority" + attribute, or zero. + """ + # Do not fail here, as the sort will fail. If a filter has not + # (incorrectly) set a priority, set it to zero. + return getattr(filter_item, "priority", 0) + +def set_priority(filter_item, priority): + """ Internal worker function to set the frame-filter's priority. + + Arguments: + filter_item: An object conforming to the frame filter + interface. + priority: The priority to assign as an integer. + """ + + filter_item.priority = priority + +def get_enabled(filter_item): + """ Internal worker function to return a filter's enabled state + from a frame filter object. This is a fail free function as it is + used in sorting and filtering. If a badly implemented frame + filter does not implement the enabled attribute, return False + (otherwise sorting/filtering will fail and prevent other frame + filters from executing). + + Arguments: + filter_item: An object conforming to the frame filter + interface. + + Returns: + The enabled state of the frame filter from the "enabled" + attribute, or False. + """ + + # If the filter class is badly implemented when called from the + # Python filter command, do not cease filter operations, just set + # enabled to False. + return getattr(filter_item, "enabled", False) + +def set_enabled(filter_item, state): + """ Internal Worker function to set the frame-filter's enabled + state. + + Arguments: + filter_item: An object conforming to the frame filter + interface. + state: True or False, depending on desired state. + """ + + filter_item.enabled = state + +def return_list(name): + """ Internal Worker function to return the frame filter + dictionary, depending on the name supplied as an argument. If the + name is not "all", "global" or "progspace", it is assumed to name + an object-file. + + Arguments: + name: The name of the list, as specified by GDB user commands. + + Returns: + A dictionary object for a single specified dictionary, or a + list containing all the items for "all" + + Raises: + gdb.GdbError: A dictionary of that name cannot be found. + """ + + # If all dictionaries are wanted in the case of "all" we + # cannot return a combined dictionary as keys() may clash in + # between different dictionaries. As we just want all the frame + # filters to enable/disable them all, just return the combined + # items() as a list. + if name == "all": + all_dicts = gdb.frame_filters.values() + all_dicts = all_dicts + gdb.current_progspace().frame_filters.values() + for objfile in gdb.objfiles(): + all_dicts = all_dicts + objfile.frame_filters.values() + return all_dicts + + if name == "global": + return gdb.frame_filters + else: + if name == "progspace": + cp = gdb.current_progspace() + return cp.frame_filters + else: + for objfile in gdb.objfiles(): + if name == objfile.filename: + return objfile.frame_filters + + msg = "Cannot find frame-filter dictionary for '" + name + "'" + raise gdb.GdbError(msg) + +def _sort_list(): + """ Internal Worker function to merge all known frame-filter + lists, prune any filters with the state set to "disabled", and + sort the list on the frame-filter's "priority" attribute. + + Returns: + sorted_list: A sorted, pruned list of frame filters to + execute. + """ + + all_filters = [] + for objfile in gdb.objfiles(): + all_filters = all_filters + objfile.frame_filters.values() + cp = gdb.current_progspace() + + all_filters = all_filters + cp.frame_filters.values() + all_filters = all_filters + gdb.frame_filters.values() + + sorted_frame_filters = sorted(all_filters, key = get_priority, + reverse = True) + + sorted_frame_filters = filter(get_enabled, + sorted_frame_filters) + + return sorted_frame_filters + +def execute_frame_filters(frame, frame_low, frame_high): + """ Internal function called from GDB that will execute the chain + of frame filters. Each filter is executed in priority order. + After the execution completes, slice the iterator to frame_low - + frame_high range. + + Arguments: + frame: The initial frame. + + frame_low: The low range of the slice. If this is a negative + integer then it indicates a backward slice (ie bt -4) which + counts backward from the last frame in the backtrace. + + frame_high: The high range of the slice. If this is -1 then + it indicates all frames until the end of the stack from + frame_low. + + Returns: + frame_iterator: The sliced iterator after all frame + filters have had a change to execute, or None if no frame + filters are registered. + """ + + # Get a sorted list of frame filters. + sorted_list = _sort_list() + + # Check to see if there are any frame-filters. If not, just + # return None and let default backtrace printing occur. + if len(sorted_list) == 0: + return None + + frame_iterator = FrameIterator(frame) + + # Apply a basic frame decorator to all gdb.Frames. This unifies the + # interface. + frame_iterator = itertools.imap(FrameDecorator, frame_iterator) + + for ff in sorted_list: + frame_iterator = ff.filter(frame_iterator) + + # Slicing + + # Is this a slice from the end of the backtrace, ie bt -2? + if frame_low < 0: + count = 0 + slice_length = abs(frame_low) + # We cannot use MAXLEN argument for deque as it is 2.6 onwards + # and some GDB versions might be < 2.6. + sliced = collections.deque() + + for frame_item in frame_iterator: + if count >= slice_length: + sliced.popleft(); + count = count + 1 + sliced.append(frame_item) + + return iter(sliced) + + # -1 for frame_high means until the end of the backtrace. Set to + # None if that is the case, to indicate to itertools.islice to + # slice to the end of the iterator. + if frame_high == -1: + frame_high = None + else: + # As frames start from 0, add one to frame_high so islice + # correctly finds the end + frame_high = frame_high + 1; + + sliced = itertools.islice(frame_iterator, frame_low, frame_high) + + return sliced diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c new file mode 100644 index 00000000000..d62c5963386 --- /dev/null +++ b/gdb/python/py-framefilter.c @@ -0,0 +1,1528 @@ +/* Python frame filters + + Copyright (C) 2013 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 . */ + +#include "defs.h" +#include "objfiles.h" +#include "symtab.h" +#include "language.h" +#include "exceptions.h" +#include "arch-utils.h" +#include "python.h" +#include "ui-out.h" +#include "valprint.h" +#include "annotate.h" +#include "hashtab.h" +#include "demangle.h" +#include "mi/mi-cmds.h" +#include "python-internal.h" + +enum mi_print_types +{ + MI_PRINT_ARGS, + MI_PRINT_LOCALS +}; + +/* Helper function to extract a symbol, a name and a language + definition from a Python object that conforms to the "Symbol Value" + interface. OBJ is the Python object to extract the values from. + NAME is a pass-through argument where the name of the symbol will + be written. NAME is allocated in this function, but the caller is + responsible for clean up. SYM is a pass-through argument where the + symbol will be written. In the case of the API returning a string, + this will be set to NULL. LANGUAGE is also a pass-through argument + denoting the language attributed to the Symbol. In the case of SYM + being NULL, this will be set to the current language. Returns + PY_BT_ERROR on error with the appropriate Python exception set, and + PY_BT_OK on success. */ + +static enum py_bt_status +extract_sym (PyObject *obj, char **name, struct symbol **sym, + const struct language_defn **language) +{ + PyObject *result = PyObject_CallMethod (obj, "symbol", NULL); + + if (result == NULL) + return PY_BT_ERROR; + + /* For 'symbol' callback, the function can return a symbol or a + string. */ + if (gdbpy_is_string (result)) + { + *name = python_string_to_host_string (result); + Py_DECREF (result); + + if (*name == NULL) + return PY_BT_ERROR; + /* If the API returns a string (and not a symbol), then there is + no symbol derived language available and the frame filter has + either overridden the symbol with a string, or supplied a + entirely synthetic symbol/value pairing. In that case, use + python_language. */ + *language = python_language; + *sym = NULL; + } + else + { + /* This type checks 'result' during the conversion so we + just call it unconditionally and check the return. */ + *sym = symbol_object_to_symbol (result); + + Py_DECREF (result); + + if (*sym == NULL) + { + PyErr_SetString (PyExc_RuntimeError, + _("Unexpected value. Expecting a " + "gdb.Symbol or a Python string.")); + return PY_BT_ERROR; + } + + /* Duplicate the symbol name, so the caller has consistency + in garbage collection. */ + *name = xstrdup (SYMBOL_PRINT_NAME (*sym)); + + /* If a symbol is specified attempt to determine the language + from the symbol. If mode is not "auto", then the language + has been explicitly set, use that. */ + if (language_mode == language_mode_auto) + *language = language_def (SYMBOL_LANGUAGE (*sym)); + else + *language = current_language; + } + + return PY_BT_OK; +} + +/* Helper function to extract a value from an object that conforms to + the "Symbol Value" interface. OBJ is the Python object to extract + the value from. VALUE is a pass-through argument where the value + will be written. If the object does not have the value attribute, + or provides the Python None for a value, VALUE will be set to NULL + and this function will return as successful. Returns PY_BT_ERROR + on error with the appropriate Python exception set, and PY_BT_OK on + success. */ + +static enum py_bt_status +extract_value (PyObject *obj, struct value **value) +{ + if (PyObject_HasAttrString (obj, "value")) + { + PyObject *vresult = PyObject_CallMethod (obj, "value", NULL); + + if (vresult == NULL) + return PY_BT_ERROR; + + /* The Python code has returned 'None' for a value, so we set + value to NULL. This flags that GDB should read the + value. */ + if (vresult == Py_None) + { + Py_DECREF (vresult); + *value = NULL; + return PY_BT_OK; + } + else + { + *value = convert_value_from_python (vresult); + Py_DECREF (vresult); + + if (*value == NULL) + return PY_BT_ERROR; + + return PY_BT_OK; + } + } + else + *value = NULL; + + return PY_BT_OK; +} + +/* MI prints only certain values according to the type of symbol and + also what the user has specified. SYM is the symbol to check, and + MI_PRINT_TYPES is an enum specifying what the user wants emitted + for the MI command in question. */ +static int +mi_should_print (struct symbol *sym, enum mi_print_types type) +{ + int print_me = 0; + + switch (SYMBOL_CLASS (sym)) + { + default: + case LOC_UNDEF: /* catches errors */ + case LOC_CONST: /* constant */ + case LOC_TYPEDEF: /* local typedef */ + case LOC_LABEL: /* local label */ + case LOC_BLOCK: /* local function */ + case LOC_CONST_BYTES: /* loc. byte seq. */ + case LOC_UNRESOLVED: /* unresolved static */ + case LOC_OPTIMIZED_OUT: /* optimized out */ + print_me = 0; + break; + + case LOC_ARG: /* argument */ + case LOC_REF_ARG: /* reference arg */ + case LOC_REGPARM_ADDR: /* indirect register arg */ + case LOC_LOCAL: /* stack local */ + case LOC_STATIC: /* static */ + case LOC_REGISTER: /* register */ + case LOC_COMPUTED: /* computed location */ + if (type == MI_PRINT_LOCALS) + print_me = ! SYMBOL_IS_ARGUMENT (sym); + else + print_me = SYMBOL_IS_ARGUMENT (sym); + } + return print_me; +} + +/* Helper function which outputs a type name extracted from VAL to a + "type" field in the output stream OUT. OUT is the ui-out structure + the type name will be output too, and VAL is the value that the + type will be extracted from. Returns PY_BT_ERROR on error, with + any GDB exceptions converted to a Python exception, or PY_BT_OK on + success. */ + +static enum py_bt_status +py_print_type (struct ui_out *out, struct value *val) +{ + volatile struct gdb_exception except; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + struct type *type; + struct ui_file *stb; + struct cleanup *cleanup; + + stb = mem_fileopen (); + cleanup = make_cleanup_ui_file_delete (stb); + type = check_typedef (value_type (val)); + type_print (value_type (val), "", stb, -1); + ui_out_field_stream (out, "type", stb); + do_cleanups (cleanup); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + return PY_BT_ERROR; + } + + return PY_BT_OK; +} + +/* Helper function which outputs a value to an output field in a + stream. OUT is the ui-out structure the value will be output to, + VAL is the value that will be printed, OPTS contains the value + printing options, ARGS_TYPE is an enumerator describing the + argument format, and LANGUAGE is the language_defn that the value + will be printed with. Returns PY_BT_ERROR on error, with any GDB + exceptions converted to a Python exception, or PY_BT_OK on + success. */ + +static enum py_bt_status +py_print_value (struct ui_out *out, struct value *val, + const struct value_print_options *opts, + int indent, + enum py_frame_args args_type, + const struct language_defn *language) +{ + int should_print = 0; + volatile struct gdb_exception except; + int local_indent = (4 * indent); + + /* Never set an indent level for common_val_print if MI. */ + if (ui_out_is_mi_like_p (out)) + local_indent = 0; + + /* MI does not print certain values, differentiated by type, + depending on what ARGS_TYPE indicates. Test type against option. + For CLI print all values. */ + if (args_type == MI_PRINT_SIMPLE_VALUES + || args_type == MI_PRINT_ALL_VALUES) + { + struct type *type = NULL; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + type = check_typedef (value_type (val)); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + return PY_BT_ERROR; + } + + if (args_type == MI_PRINT_ALL_VALUES) + should_print = 1; + else if (args_type == MI_PRINT_SIMPLE_VALUES + && TYPE_CODE (type) != TYPE_CODE_ARRAY + && TYPE_CODE (type) != TYPE_CODE_STRUCT + && TYPE_CODE (type) != TYPE_CODE_UNION) + should_print = 1; + } + else if (args_type != NO_VALUES) + should_print = 1; + + if (should_print) + { + TRY_CATCH (except, RETURN_MASK_ALL) + { + struct ui_file *stb; + struct cleanup *cleanup; + + stb = mem_fileopen (); + cleanup = make_cleanup_ui_file_delete (stb); + common_val_print (val, stb, indent, opts, language); + ui_out_field_stream (out, "value", stb); + do_cleanups (cleanup); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + return PY_BT_ERROR; + } + } + + return PY_BT_OK; +} + +/* Helper function to call a Python method and extract an iterator + from the result. If the function returns anything but an iterator + the exception is preserved and NULL is returned. FILTER is the + Python object to call, and FUNC is the name of the method. Returns + a PyObject, or NULL on error with the appropriate exception set. + This function can return an iterator, or NULL. */ + +static PyObject * +get_py_iter_from_func (PyObject *filter, char *func) +{ + if (PyObject_HasAttrString (filter, func)) + { + PyObject *result = PyObject_CallMethod (filter, func, NULL); + + if (result != NULL) + { + if (result == Py_None) + { + return result; + } + else + { + PyObject *iterator = PyObject_GetIter (result); + + Py_DECREF (result); + return iterator; + } + } + } + else + Py_RETURN_NONE; + + return NULL; +} + +/* Helper function to output a single frame argument and value to an + output stream. This function will account for entry values if the + FV parameter is populated, the frame argument has entry values + associated with them, and the appropriate "set entry-value" + options are set. Will output in CLI or MI like format depending + on the type of output stream detected. OUT is the output stream, + SYM_NAME is the name of the symbol. If SYM_NAME is populated then + it must have an accompanying value in the parameter FV. FA is a + frame argument structure. If FA is populated, both SYM_NAME and + FV are ignored. OPTS contains the value printing options, + ARGS_TYPE is an enumerator describing the argument format, + PRINT_ARGS_FIELD is a flag which indicates if we output "ARGS=1" + in MI output in commands where both arguments and locals are + printed. Returns PY_BT_ERROR on error, with any GDB exceptions + converted to a Python exception, or PY_BT_OK on success. */ + +static enum py_bt_status +py_print_single_arg (struct ui_out *out, + const char *sym_name, + struct frame_arg *fa, + struct value *fv, + const struct value_print_options *opts, + enum py_frame_args args_type, + int print_args_field, + const struct language_defn *language) +{ + struct value *val; + volatile struct gdb_exception except; + + if (fa != NULL) + { + language = language_def (SYMBOL_LANGUAGE (fa->sym)); + val = fa->val; + } + else + val = fv; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + struct cleanup *cleanups = make_cleanup (null_cleanup, NULL); + + /* MI has varying rules for tuples, but generally if there is only + one element in each item in the list, do not start a tuple. The + exception is -stack-list-variables which emits an ARGS="1" field + if the value is a frame argument. This is denoted in this + function with PRINT_ARGS_FIELD which is flag from the caller to + emit the ARGS field. */ + if (ui_out_is_mi_like_p (out)) + { + if (print_args_field || args_type != NO_VALUES) + make_cleanup_ui_out_tuple_begin_end (out, NULL); + } + + annotate_arg_begin (); + + /* If frame argument is populated, check for entry-values and the + entry value options. */ + if (fa != NULL) + { + struct ui_file *stb; + + stb = mem_fileopen (); + make_cleanup_ui_file_delete (stb); + fprintf_symbol_filtered (stb, SYMBOL_PRINT_NAME (fa->sym), + SYMBOL_LANGUAGE (fa->sym), + DMGL_PARAMS | DMGL_ANSI); + if (fa->entry_kind == print_entry_values_compact) + { + fputs_filtered ("=", stb); + + fprintf_symbol_filtered (stb, SYMBOL_PRINT_NAME (fa->sym), + SYMBOL_LANGUAGE (fa->sym), + DMGL_PARAMS | DMGL_ANSI); + } + if (fa->entry_kind == print_entry_values_only + || fa->entry_kind == print_entry_values_compact) + { + fputs_filtered ("@entry", stb); + } + ui_out_field_stream (out, "name", stb); + } + else + /* Otherwise, just output the name. */ + ui_out_field_string (out, "name", sym_name); + + annotate_arg_name_end (); + + if (! ui_out_is_mi_like_p (out)) + ui_out_text (out, "="); + + if (print_args_field) + ui_out_field_int (out, "arg", 1); + + /* For MI print the type, but only for simple values. This seems + weird, but this is how MI choose to format the various output + types. */ + if (args_type == MI_PRINT_SIMPLE_VALUES) + { + if (py_print_type (out, val) == PY_BT_ERROR) + { + do_cleanups (cleanups); + goto error; + } + } + + annotate_arg_value (value_type (val)); + + /* If the output is to the CLI, and the user option "set print + frame-arguments" is set to none, just output "...". */ + if (! ui_out_is_mi_like_p (out) && args_type == NO_VALUES) + ui_out_field_string (out, "value", "..."); + else + { + /* Otherwise, print the value for both MI and the CLI, except + for the case of MI_PRINT_NO_VALUES. */ + if (args_type != NO_VALUES) + { + if (py_print_value (out, val, opts, 0, args_type, language) + == PY_BT_ERROR) + { + do_cleanups (cleanups); + goto error; + } + } + } + + do_cleanups (cleanups); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + goto error; + } + + return PY_BT_OK; + + error: + return PY_BT_ERROR; +} + +/* Helper function to loop over frame arguments provided by the + "frame_arguments" Python API. Elements in the iterator must + conform to the "Symbol Value" interface. ITER is the Python + iterable object, OUT is the output stream, ARGS_TYPE is an + enumerator describing the argument format, PRINT_ARGS_FIELD is a + flag which indicates if we output "ARGS=1" in MI output in commands + where both arguments and locals are printed, and FRAME is the + backing frame. Returns PY_BT_ERROR on error, with any GDB + exceptions converted to a Python exception, or PY_BT_OK on + success. */ + +static enum py_bt_status +enumerate_args (PyObject *iter, + struct ui_out *out, + enum py_frame_args args_type, + int print_args_field, + struct frame_info *frame) +{ + PyObject *item; + struct value_print_options opts; + volatile struct gdb_exception except; + + get_user_print_options (&opts); + + if (args_type == CLI_SCALAR_VALUES) + { + /* True in "summary" mode, false otherwise. */ + opts.summary = 1; + } + + opts.deref_ref = 1; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + annotate_frame_args (); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + goto error; + } + + /* Collect the first argument outside of the loop, so output of + commas in the argument output is correct. At the end of the + loop block collect another item from the iterator, and, if it is + not null emit a comma. */ + item = PyIter_Next (iter); + if (item == NULL && PyErr_Occurred ()) + goto error; + + while (item) + { + const struct language_defn *language; + char *sym_name; + struct symbol *sym; + struct value *val; + enum py_bt_status success = PY_BT_ERROR; + + success = extract_sym (item, &sym_name, &sym, &language); + if (success == PY_BT_ERROR) + { + Py_DECREF (item); + goto error; + } + + success = extract_value (item, &val); + if (success == PY_BT_ERROR) + { + xfree (sym_name); + Py_DECREF (item); + goto error; + } + + Py_DECREF (item); + item = NULL; + + if (sym && ui_out_is_mi_like_p (out) + && ! mi_should_print (sym, MI_PRINT_ARGS)) + { + xfree (sym_name); + continue; + } + + /* If the object did not provide a value, read it using + read_frame_args and account for entry values, if any. */ + if (val == NULL) + { + struct frame_arg arg, entryarg; + + /* If there is no value, and also no symbol, set error and + exit. */ + if (sym == NULL) + { + PyErr_SetString (PyExc_RuntimeError, + _("No symbol or value provided.")); + xfree (sym_name); + goto error; + } + + TRY_CATCH (except, RETURN_MASK_ALL) + { + read_frame_arg (sym, frame, &arg, &entryarg); + } + if (except.reason < 0) + { + xfree (sym_name); + gdbpy_convert_exception (except); + goto error; + } + + /* The object has not provided a value, so this is a frame + argument to be read by GDB. In this case we have to + account for entry-values. */ + + if (arg.entry_kind != print_entry_values_only) + { + if (py_print_single_arg (out, NULL, &arg, + NULL, &opts, + args_type, + print_args_field, + NULL) == PY_BT_ERROR) + { + xfree (arg.error); + xfree (entryarg.error); + xfree (sym_name); + goto error; + } + } + + if (entryarg.entry_kind != print_entry_values_no) + { + if (arg.entry_kind != print_entry_values_only) + { + TRY_CATCH (except, RETURN_MASK_ALL) + { + ui_out_text (out, ", "); + ui_out_wrap_hint (out, " "); + } + if (except.reason < 0) + { + xfree (arg.error); + xfree (entryarg.error); + xfree (sym_name); + gdbpy_convert_exception (except); + goto error; + } + } + + if (py_print_single_arg (out, NULL, &entryarg, NULL, + &opts, args_type, + print_args_field, NULL) == PY_BT_ERROR) + { + xfree (arg.error); + xfree (entryarg.error); + xfree (sym_name); + goto error; + } + } + + xfree (arg.error); + xfree (entryarg.error); + } + else + { + /* If the object has provided a value, we just print that. */ + if (val != NULL) + { + if (py_print_single_arg (out, sym_name, NULL, val, &opts, + args_type, print_args_field, + language) == PY_BT_ERROR) + { + xfree (sym_name); + goto error; + } + } + } + + xfree (sym_name); + + /* Collect the next item from the iterator. If + this is the last item, do not print the + comma. */ + item = PyIter_Next (iter); + if (item != NULL) + { + TRY_CATCH (except, RETURN_MASK_ALL) + { + ui_out_text (out, ", "); + } + if (except.reason < 0) + { + Py_DECREF (item); + gdbpy_convert_exception (except); + goto error; + } + } + else if (PyErr_Occurred ()) + goto error; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + annotate_arg_end (); + } + if (except.reason < 0) + { + Py_DECREF (item); + gdbpy_convert_exception (except); + goto error; + } + } + + return PY_BT_OK; + + error: + return PY_BT_ERROR; +} + + +/* Helper function to loop over variables provided by the + "frame_locals" Python API. Elements in the iterable must conform + to the "Symbol Value" interface. ITER is the Python iterable + object, OUT is the output stream, INDENT is whether we should + indent the output (for CLI), ARGS_TYPE is an enumerator describing + the argument format, PRINT_ARGS_FIELD is flag which indicates + whether to output the ARGS field in the case of + -stack-list-variables and FRAME is the backing frame. Returns + PY_BT_ERROR on error, with any GDB exceptions converted to a Python + exception, or PY_BT_OK on success. */ + +static enum py_bt_status +enumerate_locals (PyObject *iter, + struct ui_out *out, + int indent, + enum py_frame_args args_type, + int print_args_field, + struct frame_info *frame) +{ + PyObject *item; + struct value_print_options opts; + + get_user_print_options (&opts); + opts.deref_ref = 1; + + while ((item = PyIter_Next (iter))) + { + const struct language_defn *language; + char *sym_name; + struct value *val; + enum py_bt_status success = PY_BT_ERROR; + struct symbol *sym; + volatile struct gdb_exception except; + int local_indent = 8 + (8 * indent); + struct cleanup *locals_cleanups; + + locals_cleanups = make_cleanup_py_decref (item); + + success = extract_sym (item, &sym_name, &sym, &language); + if (success == PY_BT_ERROR) + { + do_cleanups (locals_cleanups); + goto error; + } + + make_cleanup (xfree, sym_name); + + success = extract_value (item, &val); + if (success == PY_BT_ERROR) + { + do_cleanups (locals_cleanups); + goto error; + } + + if (sym != NULL && ui_out_is_mi_like_p (out) + && ! mi_should_print (sym, MI_PRINT_LOCALS)) + { + do_cleanups (locals_cleanups); + continue; + } + + /* If the object did not provide a value, read it. */ + if (val == NULL) + { + TRY_CATCH (except, RETURN_MASK_ALL) + { + val = read_var_value (sym, frame); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + do_cleanups (locals_cleanups); + goto error; + } + } + + /* With PRINT_NO_VALUES, MI does not emit a tuple normally as + each output contains only one field. The exception is + -stack-list-variables, which always provides a tuple. */ + if (ui_out_is_mi_like_p (out)) + { + if (print_args_field || args_type != NO_VALUES) + make_cleanup_ui_out_tuple_begin_end (out, NULL); + } + TRY_CATCH (except, RETURN_MASK_ALL) + { + if (! ui_out_is_mi_like_p (out)) + { + /* If the output is not MI we indent locals. */ + ui_out_spaces (out, local_indent); + } + + ui_out_field_string (out, "name", sym_name); + + if (! ui_out_is_mi_like_p (out)) + ui_out_text (out, " = "); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + do_cleanups (locals_cleanups); + goto error; + } + + if (args_type == MI_PRINT_SIMPLE_VALUES) + { + if (py_print_type (out, val) == PY_BT_ERROR) + { + do_cleanups (locals_cleanups); + goto error; + } + } + + /* CLI always prints values for locals. MI uses the + simple/no/all system. */ + if (! ui_out_is_mi_like_p (out)) + { + int val_indent = (indent + 1) * 4; + + if (py_print_value (out, val, &opts, val_indent, args_type, + language) == PY_BT_ERROR) + { + do_cleanups (locals_cleanups); + goto error; + } + } + else + { + if (args_type != NO_VALUES) + { + if (py_print_value (out, val, &opts, 0, args_type, + language) == PY_BT_ERROR) + { + do_cleanups (locals_cleanups); + goto error; + } + } + } + + do_cleanups (locals_cleanups); + + TRY_CATCH (except, RETURN_MASK_ALL) + { + ui_out_text (out, "\n"); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + goto error; + } + } + + if (item == NULL && PyErr_Occurred ()) + goto error; + + return PY_BT_OK; + + error: + return PY_BT_ERROR; +} + +/* Helper function for -stack-list-variables. Returns PY_BT_ERROR on + error, or PY_BT_OK on success. */ + +static enum py_bt_status +py_mi_print_variables (PyObject *filter, struct ui_out *out, + struct value_print_options *opts, + enum py_frame_args args_type, + struct frame_info *frame) +{ + struct cleanup *old_chain; + PyObject *args_iter; + PyObject *locals_iter; + + args_iter = get_py_iter_from_func (filter, "frame_args"); + old_chain = make_cleanup_py_xdecref (args_iter); + if (args_iter == NULL) + goto error; + + locals_iter = get_py_iter_from_func (filter, "frame_locals"); + if (locals_iter == NULL) + goto error; + + make_cleanup_py_decref (locals_iter); + make_cleanup_ui_out_list_begin_end (out, "variables"); + + if (args_iter != Py_None) + if (enumerate_args (args_iter, out, args_type, 1, frame) == PY_BT_ERROR) + goto error; + + if (locals_iter != Py_None) + if (enumerate_locals (locals_iter, out, 1, args_type, 1, frame) + == PY_BT_ERROR) + goto error; + + do_cleanups (old_chain); + return PY_BT_OK; + + error: + do_cleanups (old_chain); + return PY_BT_ERROR; +} + +/* Helper function for printing locals. This function largely just + creates the wrapping tuple, and calls enumerate_locals. Returns + PY_BT_ERROR on error, or PY_BT_OK on success.*/ + +static enum py_bt_status +py_print_locals (PyObject *filter, + struct ui_out *out, + enum py_frame_args args_type, + int indent, + struct frame_info *frame) +{ + PyObject *locals_iter = get_py_iter_from_func (filter, + "frame_locals"); + struct cleanup *old_chain = make_cleanup_py_xdecref (locals_iter); + + if (locals_iter == NULL) + goto locals_error; + + make_cleanup_ui_out_list_begin_end (out, "locals"); + + if (locals_iter != Py_None) + if (enumerate_locals (locals_iter, out, indent, args_type, + 0, frame) == PY_BT_ERROR) + goto locals_error; + + do_cleanups (old_chain); + return PY_BT_OK;; + + locals_error: + do_cleanups (old_chain); + return PY_BT_ERROR; +} + +/* Helper function for printing frame arguments. This function + largely just creates the wrapping tuple, and calls enumerate_args. + Returns PY_BT_ERROR on error, with any GDB exceptions converted to + a Python exception, or PY_BT_OK on success. */ + +static enum py_bt_status +py_print_args (PyObject *filter, + struct ui_out *out, + enum py_frame_args args_type, + struct frame_info *frame) +{ + PyObject *args_iter = get_py_iter_from_func (filter, "frame_args"); + struct cleanup *old_chain = make_cleanup_py_xdecref (args_iter); + volatile struct gdb_exception except; + + if (args_iter == NULL) + goto args_error; + + make_cleanup_ui_out_list_begin_end (out, "args"); + + TRY_CATCH (except, RETURN_MASK_ALL) + { + annotate_frame_args (); + if (! ui_out_is_mi_like_p (out)) + ui_out_text (out, " ("); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + goto args_error; + } + + if (args_iter != Py_None) + if (enumerate_args (args_iter, out, args_type, 0, frame) == PY_BT_ERROR) + goto args_error; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + if (! ui_out_is_mi_like_p (out)) + ui_out_text (out, ")"); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + goto args_error; + } + + do_cleanups (old_chain); + return PY_BT_OK; + + args_error: + do_cleanups (old_chain); + return PY_BT_ERROR; +} + +/* Print a single frame to the designated output stream, detecting + whether the output is MI or console, and formatting the output + according to the conventions of that protocol. FILTER is the + frame-filter associated with this frame. FLAGS is an integer + describing the various print options. The FLAGS variables is + described in "apply_frame_filter" function. ARGS_TYPE is an + enumerator describing the argument format. OUT is the output + stream to print, INDENT is the level of indention for this frame + (in the case of elided frames), and LEVELS_PRINTED is a hash-table + containing all the frames level that have already been printed. + If a frame level has been printed, do not print it again (in the + case of elided frames). Returns PY_BT_ERROR on error, with any + GDB exceptions converted to a Python exception, or PY_BT_COMPLETED + on success. */ + +static enum py_bt_status +py_print_frame (PyObject *filter, int flags, enum py_frame_args args_type, + struct ui_out *out, int indent, htab_t levels_printed) +{ + int has_addr = 0; + CORE_ADDR address = 0; + struct gdbarch *gdbarch = NULL; + struct frame_info *frame = NULL; + struct cleanup *cleanup_stack = make_cleanup (null_cleanup, NULL); + struct value_print_options opts; + PyObject *py_inf_frame, *elided; + int print_level, print_frame_info, print_args, print_locals; + volatile struct gdb_exception except; + + /* Extract print settings from FLAGS. */ + print_level = (flags & PRINT_LEVEL) ? 1 : 0; + print_frame_info = (flags & PRINT_FRAME_INFO) ? 1 : 0; + print_args = (flags & PRINT_ARGS) ? 1 : 0; + print_locals = (flags & PRINT_LOCALS) ? 1 : 0; + + get_user_print_options (&opts); + + /* Get the underlying frame. This is needed to determine GDB + architecture, and also, in the cases of frame variables/arguments to + read them if they returned filter object requires us to do so. */ + py_inf_frame = PyObject_CallMethod (filter, "inferior_frame", NULL); + if (py_inf_frame == NULL) + goto error; + + frame = frame_object_to_frame_info (py_inf_frame);; + + Py_DECREF (py_inf_frame); + + if (frame == NULL) + goto error; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + gdbarch = get_frame_arch (frame); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + goto error; + } + + + /* stack-list-variables. */ + if (print_locals && print_args && ! print_frame_info) + { + if (py_mi_print_variables (filter, out, &opts, + args_type, frame) == PY_BT_ERROR) + goto error; + else + { + do_cleanups (cleanup_stack); + return PY_BT_COMPLETED; + } + } + + /* -stack-list-locals does not require a + wrapping frame attribute. */ + if (print_frame_info || (print_args && ! print_locals)) + make_cleanup_ui_out_tuple_begin_end (out, "frame"); + + if (print_frame_info) + { + /* Elided frames are also printed with this function (recursively) + and are printed with indention. */ + if (indent > 0) + { + TRY_CATCH (except, RETURN_MASK_ALL) + { + ui_out_spaces (out, indent*4); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + goto error; + } + } + + /* The address is required for frame annotations, and also for + address printing. */ + if (PyObject_HasAttrString (filter, "address")) + { + PyObject *paddr = PyObject_CallMethod (filter, "address", NULL); + if (paddr != NULL) + { + if (paddr != Py_None) + { + address = PyLong_AsLong (paddr); + has_addr = 1; + } + Py_DECREF (paddr); + } + else + goto error; + } + } + + /* Print frame level. MI does not require the level if + locals/variables only are being printed. */ + if ((print_frame_info || print_args) && print_level) + { + struct frame_info **slot; + int level; + volatile struct gdb_exception except; + + slot = (struct frame_info **) htab_find_slot (levels_printed, + frame, INSERT); + TRY_CATCH (except, RETURN_MASK_ALL) + { + level = frame_relative_level (frame); + + /* Check if this frame has already been printed (there are cases + where elided synthetic dummy-frames have to 'borrow' the frame + architecture from the eliding frame. If that is the case, do + not print 'level', but print spaces. */ + if (*slot == frame) + ui_out_field_skip (out, "level"); + else + { + *slot = frame; + annotate_frame_begin (print_level ? level : 0, + gdbarch, address); + ui_out_text (out, "#"); + ui_out_field_fmt_int (out, 2, ui_left, "level", + level); + } + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + goto error; + } + } + + if (print_frame_info) + { + /* Print address to the address field. If an address is not provided, + print nothing. */ + if (opts.addressprint && has_addr) + { + TRY_CATCH (except, RETURN_MASK_ALL) + { + annotate_frame_address (); + ui_out_field_core_addr (out, "addr", gdbarch, address); + annotate_frame_address_end (); + ui_out_text (out, " in "); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + goto error; + } + } + + /* Print frame function name. */ + if (PyObject_HasAttrString (filter, "function")) + { + PyObject *py_func = PyObject_CallMethod (filter, "function", NULL); + + if (py_func != NULL) + { + const char *function = NULL; + + if (gdbpy_is_string (py_func)) + { + function = PyString_AsString (py_func); + + if (function == NULL) + { + Py_DECREF (py_func); + goto error; + } + } + else if (PyLong_Check (py_func)) + { + CORE_ADDR addr = PyLong_AsUnsignedLongLong (py_func); + struct bound_minimal_symbol msymbol; + + if (PyErr_Occurred ()) + goto error; + + msymbol = lookup_minimal_symbol_by_pc (addr); + if (msymbol.minsym != NULL) + function = SYMBOL_PRINT_NAME (msymbol.minsym); + } + else if (py_func != Py_None) + { + PyErr_SetString (PyExc_RuntimeError, + _("FrameDecorator.function: expecting a " \ + "String, integer or None.")); + Py_DECREF (py_func); + goto error; + } + + + TRY_CATCH (except, RETURN_MASK_ALL) + { + annotate_frame_function_name (); + if (function == NULL) + ui_out_field_skip (out, "func"); + else + ui_out_field_string (out, "func", function); + } + if (except.reason < 0) + { + Py_DECREF (py_func); + gdbpy_convert_exception (except); + goto error; + } + } + Py_DECREF (py_func); + } + else + goto error; + } + + + /* Frame arguments. Check the result, and error if something went + wrong. */ + if (print_args) + { + if (py_print_args (filter, out, args_type, frame) == PY_BT_ERROR) + goto error; + } + + /* File name/source/line number information. */ + if (print_frame_info) + { + TRY_CATCH (except, RETURN_MASK_ALL) + { + annotate_frame_source_begin (); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + goto error; + } + + if (PyObject_HasAttrString (filter, "filename")) + { + PyObject *py_fn = PyObject_CallMethod (filter, "filename", + NULL); + if (py_fn != NULL) + { + if (py_fn != Py_None) + { + char *filename = PyString_AsString (py_fn); + + if (filename == NULL) + { + Py_DECREF (py_fn); + goto error; + } + TRY_CATCH (except, RETURN_MASK_ALL) + { + ui_out_wrap_hint (out, " "); + ui_out_text (out, " at "); + annotate_frame_source_file (); + ui_out_field_string (out, "file", filename); + annotate_frame_source_file_end (); + } + if (except.reason < 0) + { + Py_DECREF (py_fn); + gdbpy_convert_exception (except); + goto error; + } + } + Py_DECREF (py_fn); + } + else + goto error; + } + + if (PyObject_HasAttrString (filter, "line")) + { + PyObject *py_line = PyObject_CallMethod (filter, "line", NULL); + int line; + + if (py_line != NULL) + { + if (py_line != Py_None) + { + line = PyLong_AsLong (py_line); + TRY_CATCH (except, RETURN_MASK_ALL) + { + ui_out_text (out, ":"); + annotate_frame_source_line (); + ui_out_field_int (out, "line", line); + } + if (except.reason < 0) + { + Py_DECREF (py_line); + gdbpy_convert_exception (except); + goto error; + } + } + Py_DECREF (py_line); + } + else + goto error; + } + } + + /* For MI we need to deal with the "children" list population of + elided frames, so if MI output detected do not send newline. */ + if (! ui_out_is_mi_like_p (out)) + { + TRY_CATCH (except, RETURN_MASK_ALL) + { + annotate_frame_end (); + ui_out_text (out, "\n"); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + goto error; + } + } + + if (print_locals) + { + if (py_print_locals (filter, out, args_type, indent, + frame) == PY_BT_ERROR) + goto error; + } + + /* Finally recursively print elided frames, if any. */ + elided = get_py_iter_from_func (filter, "elided"); + if (elided == NULL) + goto error; + + make_cleanup_py_decref (elided); + if (elided != Py_None) + { + PyObject *item; + + make_cleanup_ui_out_list_begin_end (out, "children"); + + if (! ui_out_is_mi_like_p (out)) + indent++; + + while ((item = PyIter_Next (elided))) + { + enum py_bt_status success = py_print_frame (item, flags, + args_type, out, + indent, + levels_printed); + + if (success == PY_BT_ERROR) + { + Py_DECREF (item); + goto error; + } + + Py_DECREF (item); + } + if (item == NULL && PyErr_Occurred ()) + goto error; + } + + + do_cleanups (cleanup_stack); + return PY_BT_COMPLETED; + + error: + do_cleanups (cleanup_stack); + return PY_BT_ERROR; +} + +/* Helper function to initiate frame filter invocation at starting + frame FRAME. */ + +static PyObject * +bootstrap_python_frame_filters (struct frame_info *frame, + int frame_low, int frame_high) +{ + struct cleanup *cleanups = + make_cleanup (null_cleanup, NULL); + PyObject *module, *sort_func, *iterable, *frame_obj, *iterator; + PyObject *py_frame_low, *py_frame_high; + + frame_obj = frame_info_to_frame_object (frame); + if (frame_obj == NULL) + goto error; + make_cleanup_py_decref (frame_obj); + + module = PyImport_ImportModule ("gdb.frames"); + if (module == NULL) + goto error; + make_cleanup_py_decref (module); + + sort_func = PyObject_GetAttrString (module, "execute_frame_filters"); + if (sort_func == NULL) + goto error; + make_cleanup_py_decref (sort_func); + + py_frame_low = PyInt_FromLong (frame_low); + if (py_frame_low == NULL) + goto error; + make_cleanup_py_decref (py_frame_low); + + py_frame_high = PyInt_FromLong (frame_high); + if (py_frame_high == NULL) + goto error; + make_cleanup_py_decref (py_frame_high); + + iterable = PyObject_CallFunctionObjArgs (sort_func, frame_obj, + py_frame_low, + py_frame_high, + NULL); + if (iterable == NULL) + goto error; + + do_cleanups (cleanups); + + if (iterable != Py_None) + { + iterator = PyObject_GetIter (iterable); + Py_DECREF (iterable); + } + else + { + return iterable; + } + + return iterator; + + error: + do_cleanups (cleanups); + return NULL; +} + +/* This is the only publicly exported function in this file. FRAME + is the source frame to start frame-filter invocation. FLAGS is an + integer holding the flags for printing. The following elements of + the FRAME_FILTER_FLAGS enum denotes the make-up of FLAGS: + PRINT_LEVEL is a flag indicating whether to print the frame's + relative level in the output. PRINT_FRAME_INFO is a flag that + indicates whether this function should print the frame + information, PRINT_ARGS is a flag that indicates whether to print + frame arguments, and PRINT_LOCALS, likewise, with frame local + variables. ARGS_TYPE is an enumerator describing the argument + format, OUT is the output stream to print. FRAME_LOW is the + beginning of the slice of frames to print, and FRAME_HIGH is the + upper limit of the frames to count. Returns PY_BT_ERROR on error, + or PY_BT_COMPLETED on success.*/ + +enum py_bt_status +apply_frame_filter (struct frame_info *frame, int flags, + enum py_frame_args args_type, + struct ui_out *out, int frame_low, + int frame_high) + +{ + struct gdbarch *gdbarch = NULL; + struct cleanup *cleanups; + enum py_bt_status success = PY_BT_ERROR; + PyObject *iterable; + volatile struct gdb_exception except; + PyObject *item; + htab_t levels_printed; + + cleanups = ensure_python_env (gdbarch, current_language); + + TRY_CATCH (except, RETURN_MASK_ALL) + { + gdbarch = get_frame_arch (frame); + } + if (except.reason < 0) + { + gdbpy_convert_exception (except); + goto error; + } + + iterable = bootstrap_python_frame_filters (frame, frame_low, frame_high); + + if (iterable == NULL) + goto error; + + /* If iterable is None, then there are no frame filters registered. + If this is the case, defer to default GDB printing routines in MI + and CLI. */ + make_cleanup_py_decref (iterable); + if (iterable == Py_None) + { + success = PY_BT_NO_FILTERS; + goto done; + } + + levels_printed = htab_create (20, + htab_hash_pointer, + htab_eq_pointer, + NULL); + make_cleanup_htab_delete (levels_printed); + + while ((item = PyIter_Next (iterable))) + { + success = py_print_frame (item, flags, args_type, out, 0, + levels_printed); + + /* Do not exit on error printing a single frame. Print the + error and continue with other frames. */ + if (success == PY_BT_ERROR) + gdbpy_print_stack (); + + Py_DECREF (item); + } + + if (item == NULL && PyErr_Occurred ()) + goto error; + + done: + do_cleanups (cleanups); + return success; + + error: + gdbpy_print_stack (); + do_cleanups (cleanups); + return PY_BT_ERROR; +} diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c index db51f508b5b..6fa303561fb 100644 --- a/gdb/python/py-objfile.c +++ b/gdb/python/py-objfile.c @@ -33,6 +33,8 @@ typedef struct /* The pretty-printer list of functions. */ PyObject *printers; + /* The frame filter list of functions. */ + PyObject *frame_filters; /* The type-printer list. */ PyObject *type_printers; } objfile_object; @@ -61,6 +63,7 @@ objfpy_dealloc (PyObject *o) objfile_object *self = (objfile_object *) o; Py_XDECREF (self->printers); + Py_XDECREF (self->frame_filters); Py_XDECREF (self->type_printers); Py_TYPE (self)->tp_free (self); } @@ -81,6 +84,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords) return NULL; } + self->frame_filters = PyDict_New (); + if (!self->frame_filters) + { + Py_DECREF (self); + return NULL; + } + self->type_printers = PyList_New (0); if (!self->type_printers) { @@ -129,6 +139,47 @@ objfpy_set_printers (PyObject *o, PyObject *value, void *ignore) return 0; } +/* Return the Python dictionary attribute containing frame filters for + this object file. */ +PyObject * +objfpy_get_frame_filters (PyObject *o, void *ignore) +{ + objfile_object *self = (objfile_object *) o; + + Py_INCREF (self->frame_filters); + return self->frame_filters; +} + +/* Set this object file's frame filters dictionary to FILTERS. */ +static int +objfpy_set_frame_filters (PyObject *o, PyObject *filters, void *ignore) +{ + PyObject *tmp; + objfile_object *self = (objfile_object *) o; + + if (! filters) + { + PyErr_SetString (PyExc_TypeError, + _("Cannot delete the frame filters attribute.")); + return -1; + } + + if (! PyDict_Check (filters)) + { + PyErr_SetString (PyExc_TypeError, + _("The frame_filters attribute must be a dictionary.")); + return -1; + } + + /* Take care in case the LHS and RHS are related somehow. */ + tmp = self->frame_filters; + Py_INCREF (filters); + self->frame_filters = filters; + Py_XDECREF (tmp); + + return 0; +} + /* Get the 'type_printers' attribute. */ static PyObject * @@ -225,6 +276,13 @@ objfile_to_objfile_object (struct objfile *objfile) return NULL; } + object->frame_filters = PyDict_New (); + if (!object->frame_filters) + { + Py_DECREF (object); + return NULL; + } + object->type_printers = PyList_New (0); if (!object->type_printers) { @@ -270,6 +328,8 @@ static PyGetSetDef objfile_getset[] = "The objfile's filename, or None.", NULL }, { "pretty_printers", objfpy_get_printers, objfpy_set_printers, "Pretty printers.", NULL }, + { "frame_filters", objfpy_get_frame_filters, + objfpy_set_frame_filters, "Frame Filters.", NULL }, { "type_printers", objfpy_get_type_printers, objfpy_set_type_printers, "Type printers.", NULL }, { NULL } diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c index 45a5193b0ad..104b36d1b91 100644 --- a/gdb/python/py-progspace.c +++ b/gdb/python/py-progspace.c @@ -35,6 +35,8 @@ typedef struct /* The pretty-printer list of functions. */ PyObject *printers; + /* The frame filter list of functions. */ + PyObject *frame_filters; /* The type-printer list. */ PyObject *type_printers; } pspace_object; @@ -69,6 +71,7 @@ pspy_dealloc (PyObject *self) pspace_object *ps_self = (pspace_object *) self; Py_XDECREF (ps_self->printers); + Py_XDECREF (ps_self->frame_filters); Py_XDECREF (ps_self->type_printers); Py_TYPE (self)->tp_free (self); } @@ -89,6 +92,13 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords) return NULL; } + self->frame_filters = PyDict_New (); + if (!self->frame_filters) + { + Py_DECREF (self); + return NULL; + } + self->type_printers = PyList_New (0); if (!self->type_printers) { @@ -137,6 +147,47 @@ pspy_set_printers (PyObject *o, PyObject *value, void *ignore) return 0; } +/* Return the Python dictionary attribute containing frame filters for + this program space. */ +PyObject * +pspy_get_frame_filters (PyObject *o, void *ignore) +{ + pspace_object *self = (pspace_object *) o; + + Py_INCREF (self->frame_filters); + return self->frame_filters; +} + +/* Set this object file's frame filters dictionary to FILTERS. */ +static int +pspy_set_frame_filters (PyObject *o, PyObject *frame, void *ignore) +{ + PyObject *tmp; + pspace_object *self = (pspace_object *) o; + + if (! frame) + { + PyErr_SetString (PyExc_TypeError, + "cannot delete the frame filter attribute"); + return -1; + } + + if (! PyDict_Check (frame)) + { + PyErr_SetString (PyExc_TypeError, + "the frame filter attribute must be a dictionary"); + return -1; + } + + /* Take care in case the LHS and RHS are related somehow. */ + tmp = self->frame_filters; + Py_INCREF (frame); + self->frame_filters = frame; + Py_XDECREF (tmp); + + return 0; +} + /* Get the 'type_printers' attribute. */ static PyObject * @@ -221,6 +272,13 @@ pspace_to_pspace_object (struct program_space *pspace) return NULL; } + object->frame_filters = PyDict_New (); + if (!object->frame_filters) + { + Py_DECREF (object); + return NULL; + } + object->type_printers = PyList_New (0); if (!object->type_printers) { @@ -257,6 +315,8 @@ static PyGetSetDef pspace_getset[] = "The progspace's main filename, or None.", NULL }, { "pretty_printers", pspy_get_printers, pspy_set_printers, "Pretty printers.", NULL }, + { "frame_filters", pspy_get_frame_filters, pspy_set_frame_filters, + "Frame filters.", NULL }, { "type_printers", pspy_get_type_printers, pspy_set_type_printers, "Type printers.", NULL }, { NULL } diff --git a/gdb/python/py-utils.c b/gdb/python/py-utils.c index b280c8cc716..890b65d346e 100644 --- a/gdb/python/py-utils.c +++ b/gdb/python/py-utils.c @@ -48,6 +48,28 @@ make_cleanup_py_decref (PyObject *py) return make_cleanup (py_decref, (void *) py); } +/* This is a cleanup function which decrements the refcount on a + Python object. This function accounts appropriately for NULL + references. */ + +static void +py_xdecref (void *p) +{ + PyObject *py = p; + + Py_XDECREF (py); +} + +/* Return a new cleanup which will decrement the Python object's + refcount when run. Account for and operate on NULL references + correctly. */ + +struct cleanup * +make_cleanup_py_xdecref (PyObject *py) +{ + return make_cleanup (py_xdecref, py); +} + /* Converts a Python 8-bit string to a unicode string object. Assumes the 8-bit string is in the host charset. If an error occurs during conversion, returns NULL with a python exception set. diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index ea97226f473..7337bffc380 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -251,9 +251,11 @@ PyObject *frame_info_to_frame_object (struct frame_info *frame); PyObject *pspace_to_pspace_object (struct program_space *); PyObject *pspy_get_printers (PyObject *, void *); +PyObject *pspy_get_frame_filters (PyObject *, void *); PyObject *objfile_to_objfile_object (struct objfile *); PyObject *objfpy_get_printers (PyObject *, void *); +PyObject *objfpy_get_frame_filters (PyObject *, void *); PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch); @@ -304,6 +306,7 @@ void gdbpy_initialize_new_objfile_event (void); void gdbpy_initialize_arch (void); struct cleanup *make_cleanup_py_decref (PyObject *py); +struct cleanup *make_cleanup_py_xdecref (PyObject *py); struct cleanup *ensure_python_env (struct gdbarch *gdbarch, const struct language_defn *language); diff --git a/gdb/python/python.c b/gdb/python/python.c index 67d06e5364b..3e2a852432c 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -1393,6 +1393,15 @@ free_type_printers (void *arg) { } +enum py_bt_status +apply_frame_filter (struct frame_info *frame, int flags, + enum py_frame_args args_type, + struct ui_out *out, int frame_low, + int frame_high) +{ + return PY_BT_NO_FILTERS; +} + #endif /* HAVE_PYTHON */ diff --git a/gdb/python/python.h b/gdb/python/python.h index 24e3077a003..1a1e5c23342 100644 --- a/gdb/python/python.h +++ b/gdb/python/python.h @@ -21,6 +21,7 @@ #define GDB_PYTHON_H #include "value.h" +#include "mi/mi-cmds.h" struct breakpoint_object; @@ -28,6 +29,66 @@ struct breakpoint_object; E.g. When the program loads libfoo.so, look for libfoo-gdb.py. */ #define GDBPY_AUTO_FILE_NAME "-gdb.py" +/* Python frame-filter status return values. */ +enum py_bt_status + { + /* Return when an error has occurred in processing frame filters, + or when printing the stack. */ + PY_BT_ERROR = -1, + + /* Return from internal routines to indicate that the function + succeeded. */ + PY_BT_OK = 1, + + /* Return when the frame filter process is complete, and all + operations have succeeded. */ + PY_BT_COMPLETED = 2, + + /* Return when the frame filter process is complete, but there + were no filter registered and enabled to process. */ + PY_BT_NO_FILTERS = 3 + }; + +/* Flags to pass to apply_frame_filter. */ + +enum frame_filter_flags + { + /* Set this flag if frame level is to be printed. */ + PRINT_LEVEL = 1, + + /* Set this flag if frame information is to be printed. */ + PRINT_FRAME_INFO = 2, + + /* Set this flag if frame arguments are to be printed. */ + PRINT_ARGS = 4, + + /* Set this flag if frame locals are to be printed. */ + PRINT_LOCALS = 8, + }; + +/* A choice of the different frame argument printing strategies that + can occur in different cases of frame filter instantiation. */ +typedef enum py_frame_args +{ + /* Print no values for arguments when invoked from the MI. */ + NO_VALUES = PRINT_NO_VALUES, + + MI_PRINT_ALL_VALUES = PRINT_ALL_VALUES, + + /* Print only simple values (what MI defines as "simple") for + arguments when invoked from the MI. */ + MI_PRINT_SIMPLE_VALUES = PRINT_SIMPLE_VALUES, + + + /* Print only scalar values for arguments when invoked from the + CLI. */ + CLI_SCALAR_VALUES, + + /* Print all values for arguments when invoked from the + CLI. */ + CLI_ALL_VALUES +} py_frame_args; + extern void finish_python_initialization (void); void eval_python_from_control_command (struct command_line *); @@ -41,6 +102,11 @@ int apply_val_pretty_printer (struct type *type, const gdb_byte *valaddr, const struct value_print_options *options, const struct language_defn *language); +enum py_bt_status apply_frame_filter (struct frame_info *frame, int flags, + enum py_frame_args args_type, + struct ui_out *out, int frame_low, + int frame_high); + void preserve_python_values (struct objfile *objfile, htab_t copied_types); void gdbpy_load_auto_scripts_for_objfile (struct objfile *objfile); diff --git a/gdb/stack.c b/gdb/stack.c index e13a5a040ea..d10e9b49d02 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -54,6 +54,7 @@ #include "psymtab.h" #include "symfile.h" +#include "python/python.h" void (*deprecated_selected_frame_level_changed_hook) (int); @@ -1651,13 +1652,15 @@ frame_info (char *addr_exp, int from_tty) frames. */ static void -backtrace_command_1 (char *count_exp, int show_locals, int from_tty) +backtrace_command_1 (char *count_exp, int show_locals, int no_filters, + int from_tty) { struct frame_info *fi; int count; int i; struct frame_info *trailing; - int trailing_level; + int trailing_level, py_start = 0, py_end = 0; + enum py_bt_status result = PY_BT_ERROR; if (!target_has_stack) error (_("No stack.")); @@ -1676,6 +1679,7 @@ backtrace_command_1 (char *count_exp, int show_locals, int from_tty) { struct frame_info *current; + py_start = count; count = -count; current = trailing; @@ -1697,9 +1701,17 @@ backtrace_command_1 (char *count_exp, int show_locals, int from_tty) count = -1; } + else + { + py_start = 0; + py_end = count; + } } else - count = -1; + { + py_end = -1; + count = -1; + } if (info_verbose) { @@ -1719,16 +1731,40 @@ backtrace_command_1 (char *count_exp, int show_locals, int from_tty) } } - for (i = 0, fi = trailing; fi && count--; i++, fi = get_prev_frame (fi)) + if (! no_filters) { - QUIT; + int flags = PRINT_LEVEL | PRINT_FRAME_INFO | PRINT_ARGS; + enum py_frame_args arg_type; - /* Don't use print_stack_frame; if an error() occurs it probably - means further attempts to backtrace would fail (on the other - hand, perhaps the code does or could be fixed to make sure - the frame->prev field gets set to NULL in that case). */ - print_frame_info (fi, 1, LOCATION, 1); if (show_locals) + flags |= PRINT_LOCALS; + + if (!strcmp (print_frame_arguments, "scalars")) + arg_type = CLI_SCALAR_VALUES; + else if (!strcmp (print_frame_arguments, "all")) + arg_type = CLI_ALL_VALUES; + else + arg_type = NO_VALUES; + + result = apply_frame_filter (get_current_frame (), flags, arg_type, + current_uiout, py_start, py_end); + + } + /* Run the inbuilt backtrace if there are no filters registered, or + "no-filters" has been specified from the command. */ + if (no_filters || result == PY_BT_NO_FILTERS) + { + for (i = 0, fi = trailing; fi && count--; i++, fi = get_prev_frame (fi)) + { + QUIT; + + /* Don't use print_stack_frame; if an error() occurs it probably + means further attempts to backtrace would fail (on the other + hand, perhaps the code does or could be fixed to make sure + the frame->prev field gets set to NULL in that case). */ + + print_frame_info (fi, 1, LOCATION, 1); + if (show_locals) { struct frame_id frame_id = get_frame_id (fi); @@ -1744,24 +1780,25 @@ backtrace_command_1 (char *count_exp, int show_locals, int from_tty) } } - /* Save the last frame to check for error conditions. */ - trailing = fi; - } + /* Save the last frame to check for error conditions. */ + trailing = fi; + } - /* If we've stopped before the end, mention that. */ - if (fi && from_tty) - printf_filtered (_("(More stack frames follow...)\n")); + /* If we've stopped before the end, mention that. */ + if (fi && from_tty) + printf_filtered (_("(More stack frames follow...)\n")); - /* If we've run out of frames, and the reason appears to be an error - condition, print it. */ - if (fi == NULL && trailing != NULL) - { - enum unwind_stop_reason reason; + /* If we've run out of frames, and the reason appears to be an error + condition, print it. */ + if (fi == NULL && trailing != NULL) + { + enum unwind_stop_reason reason; - reason = get_frame_unwind_stop_reason (trailing); - if (reason >= UNWIND_FIRST_ERROR) - printf_filtered (_("Backtrace stopped: %s\n"), - frame_stop_reason_string (reason)); + reason = get_frame_unwind_stop_reason (trailing); + if (reason >= UNWIND_FIRST_ERROR) + printf_filtered (_("Backtrace stopped: %s\n"), + frame_stop_reason_string (reason)); + } } } @@ -1769,7 +1806,8 @@ static void backtrace_command (char *arg, int from_tty) { struct cleanup *old_chain = make_cleanup (null_cleanup, NULL); - int fulltrace_arg = -1, arglen = 0, argc = 0; + int fulltrace_arg = -1, arglen = 0, argc = 0, no_filters = -1; + int user_arg = 0; if (arg) { @@ -1786,25 +1824,31 @@ backtrace_command (char *arg, int from_tty) for (j = 0; j < strlen (argv[i]); j++) argv[i][j] = tolower (argv[i][j]); - if (fulltrace_arg < 0 && subset_compare (argv[i], "full")) - fulltrace_arg = argc; + if (no_filters < 0 && subset_compare (argv[i], "no-filters")) + no_filters = argc; else { - arglen += strlen (argv[i]); - argc++; + if (fulltrace_arg < 0 && subset_compare (argv[i], "full")) + fulltrace_arg = argc; + else + { + user_arg++; + arglen += strlen (argv[i]); + } } + argc++; } - arglen += argc; - if (fulltrace_arg >= 0) + arglen += user_arg; + if (fulltrace_arg >= 0 || no_filters >= 0) { if (arglen > 0) { arg = xmalloc (arglen + 1); make_cleanup (xfree, arg); arg[0] = 0; - for (i = 0; i < (argc + 1); i++) + for (i = 0; i < argc; i++) { - if (i != fulltrace_arg) + if (i != fulltrace_arg && i != no_filters) { strcat (arg, argv[i]); strcat (arg, " "); @@ -1816,7 +1860,8 @@ backtrace_command (char *arg, int from_tty) } } - backtrace_command_1 (arg, fulltrace_arg >= 0 /* show_locals */, from_tty); + backtrace_command_1 (arg, fulltrace_arg >= 0 /* show_locals */, + no_filters >= 0 /* no frame-filters */, from_tty); do_cleanups (old_chain); } @@ -1824,7 +1869,7 @@ backtrace_command (char *arg, int from_tty) static void backtrace_full_command (char *arg, int from_tty) { - backtrace_command_1 (arg, 1 /* show_locals */, from_tty); + backtrace_command_1 (arg, 1 /* show_locals */, 0, from_tty); } @@ -2558,7 +2603,9 @@ It can be a stack frame number or the address of the frame.\n")); add_com ("backtrace", class_stack, backtrace_command, _("\ Print backtrace of all stack frames, or innermost COUNT frames.\n\ With a negative argument, print outermost -COUNT frames.\nUse of the \ -'full' qualifier also prints the values of the local variables.\n")); +'full' qualifier also prints the values of the local variables.\n\ +Use of the 'no-filters' qualifier prohibits frame filters from executing\n\ +on this backtrace.\n")); add_com_alias ("bt", "backtrace", class_stack, 0); if (xdb_commands) { diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index d01aa9e60df..c6351fc9a8e 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2013-05-10 Phil Muldoon + + * gdb.python/py-framefilter.py: New File. + * gdb.python/py-framefilter-mi.exp: Ditto. + * gdb.python/py-framefilter.c: Ditto. + * gdb.python/py-framefilter-mi.exp: Ditto. + * gdb.python/py-framefilter-mi.c: Ditto, + * gdb.python/py-framefilter-gdb.py.in: Ditto. + 2013-05-08 Tom Tromey * gdb.base/solib-search.exp: Set test name for "set diff --git a/gdb/testsuite/gdb.python/py-framefilter-gdb.py.in b/gdb/testsuite/gdb.python/py-framefilter-gdb.py.in new file mode 100644 index 00000000000..2c1d8707072 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-framefilter-gdb.py.in @@ -0,0 +1,48 @@ +# Copyright (C) 2013 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 Python-based +# frame-filters. +import gdb +import itertools +from gdb.FrameDecorator import FrameDecorator + + +class FrameObjFile (): + + def __init__ (self): + self.name = "Filter1" + self.priority = 1 + self.enabled = False + gdb.current_progspace().frame_filters ["Progspace" + self.name] = self + gdb.current_objfile().frame_filters ["ObjectFile" + self.name] = self + + def filter (self, frame_iter): + return frame_iter + +class FrameObjFile2 (): + + def __init__ (self): + self.name = "Filter2" + self.priority = 100 + self.enabled = True + gdb.current_progspace().frame_filters ["Progspace" + self.name] = self + gdb.current_objfile().frame_filters ["ObjectFile" + self.name] = self + + def filter (self, frame_iter): + return frame_iter + +FrameObjFile() +FrameObjFile2() diff --git a/gdb/testsuite/gdb.python/py-framefilter-mi.c b/gdb/testsuite/gdb.python/py-framefilter-mi.c new file mode 100644 index 00000000000..18a1d5b1dde --- /dev/null +++ b/gdb/testsuite/gdb.python/py-framefilter-mi.c @@ -0,0 +1,138 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2013 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 . */ + +#include + +void funca(void); +int count = 0; + +typedef struct +{ + char *nothing; + int f; + short s; +} foobar; + +void end_func (int foo, char *bar, foobar *fb, foobar bf) +{ + const char *str = "The End"; + const char *st2 = "Is Near"; + int b = 12; + short c = 5; + { + int d = 15; + int e = 14; + const char *foo = "Inside block"; + { + int f = 42; + int g = 19; + const char *bar = "Inside block x2"; + { + short h = 9; + h = h +1; /* Inner test breakpoint */ + } + } + } + + return; /* Backtrace end breakpoint */ +} + +void funcb(int j) +{ + struct foo + { + int a; + int b; + }; + + struct foo bar; + + bar.a = 42; + bar.b = 84; + + funca(); + return; +} + +void funca(void) +{ + foobar fb; + foobar *bf; + + if (count < 10) + { + count++; + funcb(count); + } + + fb.nothing = "Foo Bar"; + fb.f = 42; + fb.s = 19; + + bf = malloc (sizeof (foobar)); + bf->nothing = malloc (128); + bf->nothing = "Bar Foo"; + bf->f = 24; + bf->s = 91; + + end_func(21, "Param", bf, fb); + free (bf->nothing); + free (bf); + return; +} + + +void func1(void) +{ + funca(); + return; +} + +int func2(void) +{ + func1(); + return 1; +} + +void func3(int i) +{ + func2(); + + return; +} + +int func4(int j) +{ + func3(j); + + return 2; +} + +int func5(int f, int d) +{ + int i = 0; + char *random = "random"; + i=i+f; + + func4(i); + return i; +} + +main() +{ + func5(3,5); +} diff --git a/gdb/testsuite/gdb.python/py-framefilter-mi.exp b/gdb/testsuite/gdb.python/py-framefilter-mi.exp new file mode 100644 index 00000000000..54fedf82ec2 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-framefilter-mi.exp @@ -0,0 +1,179 @@ +# Copyright (C) 2013 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 Python-based +# frame-filters. +load_lib mi-support.exp +load_lib gdb-python.exp + +set MIFLAGS "-i=mi2" + +gdb_exit +if [mi_gdb_start] { + continue +} + +standard_testfile py-framefilter-mi.c +set pyfile py-framefilter.py + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DMI}] != "" } { + untested ${testfile}.exp + return -1 +} + +mi_delete_breakpoints +mi_gdb_reinitialize_dir $srcdir/$subdir +mi_gdb_load ${binfile} + +if {[lsearch -exact [mi_get_features] python] < 0} { + unsupported "python support is disabled" + return -1 +} + +mi_runto main + +set remote_python_file [remote_download host ${srcdir}/${subdir}/${pyfile}] + +mi_gdb_test "python execfile ('${remote_python_file}')" ".*\\^done." \ + "Load python file" + +# Multiple blocks test +mi_continue_to_line [gdb_get_line_number {Inner test breakpoint} ${srcfile}] \ + "step to breakpoint" + +mi_gdb_test "-stack-list-locals --all-values" \ + "\\^done,locals=\\\[{name=\"h\",value=\"9\"},{name=\"f\",value=\"42\"},{name=\"g\",value=\"19\"},{name=\"bar\",value=\"$hex \\\\\"Inside block x2\\\\\"\"},{name=\"d\",value=\"15\"},{name=\"e\",value=\"14\"},{name=\"foo\",value=\"$hex \\\\\"Inside block\\\\\"\"},{name=\"str\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",value=\"12\"},{name=\"c\",value=\"5\"}\\\]" \ + "stack-list-locals --all-values" + +mi_gdb_test "-enable-frame-filters" ".*\\^done." "enable frame filters" +mi_gdb_test "-stack-list-locals --all-values" \ + "\\^done,locals=\\\[{name=\"h\",value=\"9\"},{name=\"f\",value=\"42\"},{name=\"g\",value=\"19\"},{name=\"bar\",value=\"$hex \\\\\"Inside block x2\\\\\"\"},{name=\"d\",value=\"15\"},{name=\"e\",value=\"14\"},{name=\"foo\",value=\"$hex \\\\\"Inside block\\\\\"\"},{name=\"str\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",value=\"12\"},{name=\"c\",value=\"5\"}\\\]" \ + "stack-list-locals --all-values frame filters enabled" + +mi_continue_to_line [gdb_get_line_number {Backtrace end breakpoint} ${srcfile}] \ + "step to breakpoint" + +mi_gdb_test "-stack-list-frames" \ + "\\^done,stack=\\\[frame={level=\"0\",addr=\"$hex\",func=\"cnuf_dne\".*},frame={level=\"1\",addr=\"$hex\",func=\"acnuf\".*},frame={level=\"2\",addr=\"$hex\",func=\"bcnuf\".*},frame={level=\"3\",addr=\"$hex\",func=\"acnuf\".*},frame={level=\"22\",addr=\"$hex\",func=\"1cnuf\".*,children=\\\[frame={level=\"23\",addr=\"$hex\",func=\"func2\".*}\\\]},frame={level=\"24\",addr=\"$hex\",func=\"3cnuf\".*},frame={level=\"27\",addr=\"$hex\",func=\"niam\".*}\\\].*" \ + "filtered stack listing" +mi_gdb_test "-stack-list-frames 0 3" \ + "\\^done,stack=\\\[frame={level=\"0\",addr=\"$hex\",func=\"cnuf_dne\".*},frame={level=\"1\",addr=\"$hex\",func=\"acnuf\".*},frame={level=\"2\",addr=\"$hex\",func=\"bcnuf\".*},frame={level=\"3\",addr=\"$hex\",func=\"acnuf\".*}\\\]" \ + "filtered stack list 0 3" +mi_gdb_test "-stack-list-frames 22 24" \ + "\\^done,stack=\\\[frame={level=\"22\",addr=\"$hex\",func=\"1cnuf\".*,children=\\\[frame={level=\"23\",addr=\"$hex\",func=\"func2\".*}\\\]},frame={level=\"24\",addr=\"$hex\",func=\"3cnuf\".*}\\\]" \ + "filtered stack list 22 24" + +#stack list arguments + + +mi_gdb_test "-stack-list-arguments 0" \ + "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[name=\"foo\",name=\"bar\",name=\"fb\",name=\"bf\"\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[name=\"j\"\\\]},.*frame={level=\"22\",args=\\\[\\\],children=\\\[frame={level=\"23\",args=\\\[\\\]}\\\]},.*frame={level=\"26\",args=\\\[name=\"f\",name=\"d\"\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \ + "stack-list-arguments 0" + +mi_gdb_test "-stack-list-arguments --no-frame-filters 0" \ + "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[name=\"foo\",name=\"bar\",name=\"fb\",name=\"bf\"\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[name=\"j\"\\\]},.*frame={level=\"22\",args=\\\[\\\]},frame={level=\"23\",args=\\\[\\\]},.*frame={level=\"26\",args=\\\[name=\"f\",name=\"d\"\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \ + "stack-list-arguments --no-frame-filters 0" + +mi_gdb_test "-stack-list-arguments 0 0 3" \ + "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[name=\"foo\",name=\"bar\",name=\"fb\",name=\"bf\"\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[name=\"j\"\\\]},frame={level=\"3\",args=\\\[\\\]}\\\]" \ + "stack-list-arguments 0 0 3" + +mi_gdb_test "-stack-list-arguments 0 22 27" \ + "\\^done,stack-args=\\\[frame={level=\"22\",args=\\\[\\\],children=\\\[frame={level=\"23\",args=\\\[\\\]}\\\]},.*frame={level=\"26\",args=\\\[name=\"f\",name=\"d\"\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \ + "stack-list-arguments 0 22 27" + +mi_gdb_test "-stack-list-arguments 1" \ + "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[{name=\"foo\",value=\"21\"},{name=\"bar\",value=\"$hex \\\\\"Param\\\\\"\"},{name=\"fb\",value=\"$hex\"},{name=\"bf\",value=\"{nothing = $hex \\\\\"Foo Bar\\\\\", f = 42, s = 19}\"}\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[{name=\"j\",value=\"10\"}\\\]},.*frame={level=\"22\",args=\\\[\\\],children=\\\[frame={level=\"23\",args=\\\[\\\]}\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",value=\"3\"},{name=\"d\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \ + "stack-list-arguments 1" + +mi_gdb_test "-stack-list-arguments --no-frame-filters 1" \ + "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[{name=\"foo\",value=\"21\"},{name=\"bar\",value=\"$hex \\\\\"Param\\\\\"\"},{name=\"fb\",value=\"$hex\"},{name=\"bf\",value=\"{nothing = $hex \\\\\"Foo Bar\\\\\", f = 42, s = 19}\"}\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[{name=\"j\",value=\"10\"}\\\]},.*frame={level=\"22\",args=\\\[\\\]},frame={level=\"23\",args=\\\[\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",value=\"3\"},{name=\"d\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \ + "stack-list-arguments --no-frame-filters 1" + + +mi_gdb_test "-stack-list-arguments 1 0 3" \ + "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[{name=\"foo\",value=\"21\"},{name=\"bar\",value=\"$hex \\\\\"Param\\\\\"\"},{name=\"fb\",value=\"$hex\"},{name=\"bf\",value=\"{nothing = $hex \\\\\"Foo Bar\\\\\", f = 42, s = 19}\"}\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[{name=\"j\",value=\"10\"}\\\]},frame={level=\"3\",args=\\\[\\\]}\\\]" \ + "stack-list-arguments 1 0 3" + +mi_gdb_test "-stack-list-arguments 1 22 27" \ + "\\^done,stack-args=\\\[frame={level=\"22\",args=\\\[\\\],children=\\\[frame={level=\"23\",args=\\\[\\\]}\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",value=\"3\"},{name=\"d\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \ + "stack-list-arguments 1 22 27" + +mi_gdb_test "-stack-list-arguments 2" \ + "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[{name=\"foo\",type=\"int\",value=\"21\"},{name=\"bar\",type=\"char \\\*\",value=\"$hex \\\\\"Param\\\\\"\"},{name=\"fb\",type=\"foobar \\\*\",value=\"$hex\"},{name=\"bf\",type=\"foobar\"\}\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[{name=\"j\",type=\"int\",value=\"10\"}\\\]},.*frame={level=\"22\",args=\\\[\\\],children=\\\[frame={level=\"23\",args=\\\[\\\]}\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",type=\"int\",value=\"3\"},{name=\"d\",type=\"int\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \ + "stack-list-arguments 2" + +mi_gdb_test "-stack-list-arguments --no-frame-filters 2" \ + "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[{name=\"foo\",type=\"int\",value=\"21\"},{name=\"bar\",type=\"char \\\*\",value=\"$hex \\\\\"Param\\\\\"\"},{name=\"fb\",type=\"foobar \\\*\",value=\"$hex\"},{name=\"bf\",type=\"foobar\"}\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[{name=\"j\",type=\"int\",value=\"10\"}\\\]},.*frame={level=\"22\",args=\\\[\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",type=\"int\",value=\"3\"},{name=\"d\",type=\"int\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \ + "stack-list-arguments --no-frame-filters 2" + + +mi_gdb_test "-stack-list-arguments 2 0 3" \ + "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[{name=\"foo\",type=\"int\",value=\"21\"},{name=\"bar\",type=\"char \\\*\",value=\"$hex \\\\\"Param\\\\\"\"},{name=\"fb\",type=\"foobar \\\*\",value=\"$hex\"},{name=\"bf\",type=\"foobar\"}\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[{name=\"j\",type=\"int\",value=\"10\"}\\\]},frame={level=\"3\",args=\\\[\\\]}\\\]" \ + "stack-list-arguments 2 0 3" + +mi_gdb_test "-stack-list-arguments 2 22 27" \ + "\\^done,stack-args=\\\[frame={level=\"22\",args=\\\[\\\],children=\\\[frame={level=\"23\",args=\\\[\\\]}\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",type=\"int\",value=\"3\"},{name=\"d\",type=\"int\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \ + "stack-list-arguments 2 22 27" + +mi_gdb_test "-stack-list-arguments --no-frame-filters 2 22 27" \ + "\\^done,stack-args=\\\[frame={level=\"22\",args=\\\[\\\]},frame={level=\"23\",args=\\\[\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",type=\"int\",value=\"3\"},{name=\"d\",type=\"int\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \ + "stack-list-arguments --no-frame-filters 2 22 27" + +#stack-list-locals +mi_gdb_test "-stack-list-locals --no-frame-filters 0" \ + "\\^done,locals=\\\[name=\"str\",name=\"st2\",name=\"b\",name=\"c\"\\\]" \ + "stack-list-locals --no-frame-filters 0" + +mi_gdb_test "-stack-list-locals --no-frame-filters 1" \ + "\\^done,locals=\\\[{name=\"str\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",value=\"12\"},{name=\"c\",value=\"5\"}\\\]" \ + "stack-list-locals --no-frame-filters 1" + +mi_gdb_test "-stack-list-locals --no-frame-filters 2" \ + "\\^done,locals=\\\[{name=\"str\",type=\"const char \\\*\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",type=\"const char \\\*\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",type=\"int\",value=\"12\"},{name=\"c\",type=\"short\",value=\"5\"}\\\]" \ + "stack-list-locals --no-frame-filters 2" + +mi_gdb_test "-stack-list-locals --no-frame-filters --no-values" \ + "\\^done,locals=\\\[name=\"str\",name=\"st2\",name=\"b\",name=\"c\"\\\]" \ + "stack-list-locals --no-frame-filters --no-values" + +mi_gdb_test "-stack-list-locals --no-frame-filters --all-values" \ + "\\^done,locals=\\\[{name=\"str\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",value=\"12\"},{name=\"c\",value=\"5\"}\\\]" \ + "stack-list-locals --no-frame-filters --all-values" + +mi_gdb_test "-stack-list-locals --no-frame-filters --simple-values" \ + "\\^done,locals=\\\[{name=\"str\",type=\"const char \\\*\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",type=\"const char \\\*\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",type=\"int\",value=\"12\"},{name=\"c\",type=\"short\",value=\"5\"}\\\]" \ + "stack-list-locals --no-frame-filters --simple-values" + +mi_gdb_test "-stack-list-locals 0" \ + "\\^done,locals=\\\[name=\"str\",name=\"st2\",name=\"b\",name=\"c\"\\\]" \ + "stack-list-locals 0" + +mi_gdb_test "-stack-list-locals 1" \ + "\\^done,locals=\\\[{name=\"str\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",value=\"12\"},{name=\"c\",value=\"5\"}\\\]" \ + "stack-list-locals 1" + +mi_gdb_test "-stack-list-locals 2" \ + "\\^done,locals=\\\[{name=\"str\",type=\"const char \\\*\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",type=\"const char \\\*\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",type=\"int\",value=\"12\"},{name=\"c\",type=\"short\",value=\"5\"}\\\]" \ + "stack-list-locals 2" + +# stack-list-variables +mi_gdb_test "-stack-list-variables --no-frame-filters 0" \ + "\\^done,variables=\\\[{name=\"foo\",arg=\"1\"},{name=\"bar\",arg=\"1\"},{name=\"fb\",arg=\"1\"},{name=\"bf\",arg=\"1\"},{name=\"str\"},{name=\"st2\"},{name=\"b\"},{name=\"c\"}\\\]" \ + "stack-list-variables --no-frame-filters 0" + +mi_gdb_test "-stack-list-variables 0" \ + "\\^done,variables=\\\[{name=\"foo\",arg=\"1\"},{name=\"bar\",arg=\"1\"},{name=\"fb\",arg=\"1\"},{name=\"bf\",arg=\"1\"},{name=\"str\"},{name=\"st2\"},{name=\"b\"},{name=\"c\"}\\\]" \ + "stack-list-variables 0" diff --git a/gdb/testsuite/gdb.python/py-framefilter.c b/gdb/testsuite/gdb.python/py-framefilter.c new file mode 100644 index 00000000000..a5bf9978e68 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-framefilter.c @@ -0,0 +1,155 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2013 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 . */ + +#include + +void funca(void); +int count = 0; + +typedef struct +{ + char *nothing; + int f; + short s; +} foobar; + +void end_func (int foo, char *bar, foobar *fb, foobar bf) +{ + const char *str = "The End"; + const char *st2 = "Is Near"; + int b = 12; + short c = 5; + + { + int d = 15; + int e = 14; + const char *foo = "Inside block"; + { + int f = 42; + int g = 19; + const char *bar = "Inside block x2"; + { + short h = 9; + h = h +1; /* Inner test breakpoint */ + } + } + } + + return; /* Backtrace end breakpoint */ +} + +void funcb(int j) +{ + struct foo + { + int a; + int b; + }; + + struct foo bar; + + bar.a = 42; + bar.b = 84; + + funca(); + return; +} + +void funca(void) +{ + foobar fb; + foobar *bf = NULL; + + if (count < 10) + { + count++; + funcb(count); + } + + fb.nothing = "Foo Bar"; + fb.f = 42; + fb.s = 19; + + bf = alloca (sizeof (foobar)); + bf->nothing = alloca (128); + bf->nothing = "Bar Foo"; + bf->f = 24; + bf->s = 91; + + end_func(21, "Param", bf, fb); + return; +} + + +void func1(void) +{ + funca(); + return; +} + +int func2(int f) +{ + int c; + const char *elided = "Elided frame"; + foobar fb; + foobar *bf = NULL; + + fb.nothing = "Elided Foo Bar"; + fb.f = 84; + fb.s = 38; + + bf = alloca (sizeof (foobar)); + bf->nothing = alloca (128); + bf->nothing = "Elided Bar Foo"; + bf->f = 48; + bf->s = 182; + + func1(); + return 1; +} + +void func3(int i) +{ + func2(i); + + return; +} + +int func4(int j) +{ + func3(j); + + return 2; +} + +int func5(int f, int d) +{ + int i = 0; + char *random = "random"; + i=i+f; + + func4(i); + return i; +} + +main() +{ + int z = 32; + int y = 44; + const char *foo1 = "Test"; + func5(3,5); +} diff --git a/gdb/testsuite/gdb.python/py-framefilter.exp b/gdb/testsuite/gdb.python/py-framefilter.exp new file mode 100644 index 00000000000..6c9946bb0fc --- /dev/null +++ b/gdb/testsuite/gdb.python/py-framefilter.exp @@ -0,0 +1,239 @@ +# Copyright (C) 2013 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 Python-based +# frame-filters. + +load_lib gdb-python.exp + +standard_testfile + +# We cannot use prepare_for_testing as we have to set the safe-patch +# to check objfile and progspace printers. +if {[build_executable $testfile.exp $testfile $srcfile debug] == -1} { + return -1 +} + +# Start with a fresh gdb. +gdb_exit +gdb_start + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +# Make the -gdb.py 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_python_file \ + [remote_download \ + host ${srcdir}/${subdir}/${testfile}-gdb.py.in \ + ${subdir}/${testfile}-gdb.py] + +gdb_reinitialize_dir $srcdir/$subdir +gdb_test_no_output "set auto-load safe-path ${remote_obj_python_file}" \ + "set auto-load safe-path" +gdb_load ${binfile} +# Verify gdb loaded the script. +gdb_test "info auto-load python-scripts" "Yes.*/${testfile}-gdb.py.*" \ + "Test auto-load had loaded python scripts" + +if ![runto_main] then { + perror "couldn't run to breakpoint" + return +} +gdb_test_no_output "set python print-stack full" \ + "Set python print-stack to full" + +# Load global frame-filters +set remote_python_file [remote_download host ${srcdir}/${subdir}/${testfile}.py] +gdb_test_no_output "python execfile ('${remote_python_file}')" \ + "Load python file" + +gdb_breakpoint [gdb_get_line_number "Backtrace end breakpoint"] +gdb_breakpoint [gdb_get_line_number "Inner test breakpoint"] +gdb_continue_to_breakpoint "Inner test breakpoint" + +# Test multiple local blocks. +gdb_test "bt full no-filters" \ + ".*#0.*end_func.*h = 9.*f = 42.*g = 19.*bar = $hex \"Inside block x2\".*d = 15.*e = 14.*foo = $hex \"Inside block\".*str = $hex \"The End\".*st2 = $hex \"Is Near\".*b = 12.*c = 5.*" \ + "bt full no-filters" +gdb_test "bt full" \ + ".*#0.*cnuf_dne.*h = 9.*f = 42.*g = 19.*bar = $hex \"Inside block x2\".*d = 15.*e = 14.*foo = $hex \"Inside block\".*str = $hex \"The End\".*st2 = $hex \"Is Near\".*b = 12.*c = 5.*" \ + "bt full with filters" + +gdb_continue_to_breakpoint "Backtrace end breakpoint" + +# Test set/show +gdb_test "info frame-filter" \ + ".*900.*Yes.*Elider.*100.*Yes.*Reverse.*10.*.*No.*Object.*1.*" \ + "info frame filter before setting priority" +gdb_test "show frame-filter priority global Elider" \ + "Priority of filter 'Elider' in list 'global' is: 900" \ + "show frame-filter priority global Elider before setting" +gdb_test_no_output "set frame-filter priority global Elider 1000" \ + "set frame-filter priotiy global Elider 1000" +gdb_test "show frame-filter priority global Elider" \ + "Priority of filter 'Elider' in list 'global' is: 1000" \ + "show frame-filter priority global Elider after setting" +gdb_test "info frame-filter" \ + ".*1000.*Yes.*Elider.*100.*Yes.*Reverse.*10.*.*No.*Object.*1.*" \ + "info frame filter after setting priority" + +# Test enable/disable +gdb_test "info frame-filter" \ + ".*1000.*Yes.*Elider.*100.*Yes.*Reverse.*10.*.*No.*Object.*1.*" \ + "info frame filter before disable frame filter" +gdb_test_no_output "disable frame-filter global Elider" \ + "disable frame-filter global Elider" +gdb_test "info frame-filter" \ + ".*1000.*No.*Elider.*100.*Yes.*Reverse.*10.*.*No.*Object.*1.*" \ + "info frame filter after disable frame filter" +gdb_test_no_output "enable frame-filter global Elider" \ + "enable frame-filter global Elider" +gdb_test "info frame-filter" \ + ".*1000.*Yes.*Elider.*100.*Yes.*Reverse.*10.*.*No.*Object.*1.*" \ + "info frame filter after reenabling frame filter" + +# Test no-filters +gdb_test "bt no-filters" \ + ".*#0.*end_func.*#22.*in func1.*#27.*in main \\(\\).*" \ + "bt no-filters" + +# Test reverse +gdb_test "bt" \ + ".*#0.*cnuf_dne.*#22.*in 1cnuf.*#27.*in niam \\(\\).*" \ + "bt with frame filters" + +# Disable Reverse +gdb_test_no_output "disable frame-filter global Reverse" \ + "disable frame-filter global Reverse" +gdb_test "bt" \ + ".*#0.*end_func.*#22.*in func1.*#27.*in main \\(\\).*" \ + "bt with frame-filter Reverse disabled" +gdb_test "bt -2" \ + ".*#26.*func5.*#27.*in main \\(\\).*" \ + "bt -2 with frame-filter Reverse disabled" +gdb_test "bt 3" \ + ".*#0.*end_func.*#1.*in funca \\(\\).*#2.*in funcb \\(j=10\\).*" \ + "bt 3 with frame-filter Reverse disabled" +gdb_test "bt no-filter full" \ + ".*#0.*end_func.*str = $hex \"The End\".*st2 = $hex \"Is Near\".*b = 12.*c = 5.*#1.*in funca \\(\\).*#2.*in funcb \\(j=10\\).*bar = \{a = 42, b = 84\}.*" \ + "bt no-filters full with Reverse disabled" +gdb_test "bt full" \ + ".*#0.*end_func.*str = $hex \"The End\".*st2 = $hex \"Is Near\".*b = 12.*c = 5.*#1.*in funca \\(\\).*#2.*in funcb \\(j=10\\).*bar = \{a = 42, b = 84\}.*#22.*in func1 \\(\\).*#23.*in func2 \\(f=3\\).*elided = $hex \"Elided frame\".*fb = \{nothing = $hex \"Elided Foo Bar\", f = 84, s = 38\}.*bf = $hex.*" \ + "bt full with Reverse disabled" + +# Test set print frame-arguments +# none +gdb_test_no_output "set print frame-arguments none" \ + "turn off frame arguments" +gdb_test "bt no-filter 1" \ + "#0.*end_func \\(foo=\.\.\., bar=\.\.\., fb=\.\.\., bf=\.\.\.\\) at .*py-framefilter.c.*" \ + "bt no-filter 1 no args" +gdb_test "bt 1" \ + "#0.*end_func \\(foo=\.\.\., bar=\.\.\., fb=\.\.\., bf=\.\.\.\\) at .*py-framefilter.c.*" \ + "bt 1 no args" + +# scalars +gdb_test_no_output "set print frame-arguments scalars" \ + "turn frame arguments to scalars only" +gdb_test "bt no-filter 1" \ + "#0.*end_func \\(foo=21, bar=$hex \"Param\", fb=$hex, bf=\.\.\.\\) at .*py-framefilter.c.*" \ + "bt no-filter 1 scalars" +gdb_test "bt 1" \ + "#0.*end_func \\(foo=21, bar=$hex \"Param\", fb=$hex, bf=\.\.\.\\) at .*py-framefilter.c.*" \ + "bt 1 scalars" + +# all +gdb_test_no_output "set print frame-arguments all" \ + "turn on frame arguments" +gdb_test "bt no-filter 1" \ + "#0.*end_func \\(foo=21, bar=$hex \"Param\", fb=$hex, bf=\{nothing = $hex \"Foo Bar\", f = 42, s = 19\}\\) at .*py-framefilter.c.*" \ + "bt no-filter 1 all args" +gdb_test "bt 1" \ + "#0.*end_func \\(foo=21, bar=$hex \"Param\", fb=$hex, bf=\{nothing = $hex \"Foo Bar\", f = 42, s = 19\}\\) at .*py-framefilter.c.*" \ + "bt 1 all args" + +# set print address off +gdb_test_no_output "set print address off" \ + "Turn off address printing" +gdb_test "bt no-filter 1" \ + "#0 end_func \\(foo=21, bar=\"Param\", fb=, bf=\{nothing = \"Foo Bar\", f = 42, s = 19\}\\) at .*py-framefilter.c.*" \ + "bt no-filter 1 no address" +gdb_test "bt 1" \ + "#0 end_func \\(foo=21, bar=\"Param\", fb=, bf=\{nothing = \"Foo Bar\", f = 42, s = 19\}\\) at .*py-framefilter.c.*" \ + "bt 1 no addresss" + +remote_file host delete ${remote_python_file} + +# Test with no debuginfo + +# We cannot use prepare_for_testing as we have to set the safe-patch +# to check objfile and progspace printers. +if {[build_executable $testfile.exp $testfile $srcfile {nodebug}] == -1} { + return -1 +} + +# Start with a fresh gdb. +gdb_exit +gdb_start + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +# Make the -gdb.py 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_python_file \ + [remote_download \ + host ${srcdir}/${subdir}/${testfile}-gdb.py.in \ + ${subdir}/${testfile}-gdb.py] + +gdb_reinitialize_dir $srcdir/$subdir +gdb_test_no_output "set auto-load safe-path ${remote_obj_python_file}" \ + "set auto-load safe-path for no debug info" +gdb_load ${binfile} + +# Verify gdb loaded the script. +gdb_test "info auto-load python-scripts" "Yes.*/${testfile}-gdb.py.*" \ + "Set autoload path for no debug info tests" +if ![runto_main] then { + perror "couldn't run to breakpoint" + return +} + +gdb_test_no_output "set python print-stack full" \ + "set python print-stack full for no debuginfo tests" + +# Load global frame-filters +set remote_python_file [remote_download host ${srcdir}/${subdir}/${testfile}.py] +gdb_test_no_output "python execfile ('${remote_python_file}')" \ + "Load python file for no debuginfo tests" + +# Disable Reverse +gdb_test_no_output "disable frame-filter global Reverse" \ + "disable frame-filter gloval Reverse for no debuginfo" +gdb_test "bt" \ + ".*#0..*in main \\(\\).*" \ + "bt for no debuginfo" +gdb_test "bt full" \ + ".*#0..*in main \\(\\).*" \ + "bt full for no debuginfo" +gdb_test "bt no-filters" \ + ".*#0..*in main \\(\\).*" \ + "bt no filters for no debuginfo" +gdb_test "bt no-filters full" \ + ".*#0..*in main \\(\\).*" \ + "bt no-filters full no debuginfo" diff --git a/gdb/testsuite/gdb.python/py-framefilter.py b/gdb/testsuite/gdb.python/py-framefilter.py new file mode 100644 index 00000000000..d31bbbe168f --- /dev/null +++ b/gdb/testsuite/gdb.python/py-framefilter.py @@ -0,0 +1,117 @@ +# Copyright (C) 2013 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 Python-based +# frame-filters. +import gdb +import itertools +from gdb.FrameDecorator import FrameDecorator +import copy + +class Reverse_Function (FrameDecorator): + + def __init__(self, fobj): + super(Reverse_Function, self).__init__(fobj) + self.fobj = fobj + + def function (self): + fname = str (self.fobj.function()) + if (fname == None or fname == ""): + return None + else: + fname = fname[::-1] + return fname + +class Dummy (FrameDecorator): + + def __init__(self, fobj): + super(Dummy, self).__init__(fobj) + self.fobj = fobj + + def function (self): + return "Dummy function" + + def address (self): + return 0x123 + + def filename (self): + return "Dummy filename" + + def frame_args (self): + return [("Foo",gdb.Value(12)),("Bar","Stuff"), ("FooBar",42)] + + def frame_locals (self): + return [] + + def line (self): + return 0 + + def elided (self): + return None + +class FrameFilter (): + + def __init__ (self): + self.name = "Reverse" + self.priority = 100 + self.enabled = True + gdb.frame_filters [self.name] = self + + def filter (self, frame_iter): + frame_iter = itertools.imap (Reverse_Function, + frame_iter) + return frame_iter + +class ElidingFrameDecorator(FrameDecorator): + + def __init__(self, frame, elided_frames): + super(ElidingFrameDecorator, self).__init__(frame) + self.elided_frames = elided_frames + + def elided(self): + return iter(self.elided_frames) + +class ElidingIterator: + def __init__(self, ii): + self.input_iterator = ii + + def __iter__(self): + return self + + def next(self): + frame = next(self.input_iterator) + if str(frame.function()) != 'func1': + return frame + + # Suppose we want to return the 'func1' frame but elide the + # next frame. E.g., if call in our interpreter language takes + # two C frames to implement, and the first one we see is the + # "sentinel". + elided = next(self.input_iterator) + return ElidingFrameDecorator(frame, [elided]) + +class FrameElider (): + + def __init__ (self): + self.name = "Elider" + self.priority = 900 + self.enabled = True + gdb.frame_filters [self.name] = self + + def filter (self, frame_iter): + return ElidingIterator (frame_iter) + +FrameFilter() +FrameElider() -- 2.30.2