From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 5626 invoked by alias); 30 Nov 2012 14:31:35 -0000 Received: (qmail 5500 invoked by uid 22791); 30 Nov 2012 14:31:29 -0000 X-SWARE-Spam-Status: No, hits=-4.6 required=5.0 tests=AWL,BAYES_20,KAM_STOCKGEN,KHOP_RCVD_UNTRUST,RCVD_IN_DNSWL_HI,RCVD_IN_HOSTKARMA_W,RP_MATCHES_RCVD,SPF_HELO_PASS,TW_BJ,TW_JF,TW_RG X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 30 Nov 2012 14:31:02 +0000 Received: from int-mx12.intmail.prod.int.phx2.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id qAUEV1Pw026091 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Fri, 30 Nov 2012 09:31:01 -0500 Received: from localhost.localdomain (ovpn-116-27.ams2.redhat.com [10.36.116.27]) by int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id qAUEUwxr028958 for ; Fri, 30 Nov 2012 09:30:59 -0500 Message-ID: <50B8C322.8050602@redhat.com> Date: Fri, 30 Nov 2012 14:31:00 -0000 From: Phil Muldoon MIME-Version: 1.0 To: "gdb-patches@sourceware.org" Subject: [patch][python] 1 of 5 - Frame filter Python C code changes. Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2012-11/txt/msg00927.txt.bz2 This email and patch covers the python/ changes for Python Frame Filters. 2012-11-30 Phil Muldoon * 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/__init__.py: Initialize global frame_filters dictionary * python/lib/gdb/FrameWrapper.py: New file. * python/lib/gdb/FrameIterator.py: New file. * python/lib/gdb/BaseFrameWrapper.py: New file. -- diff --git a/gdb/python/lib/gdb/BaseFrameWrapper.py b/gdb/python/lib/gdb/BaseFrameWrapper.py new file mode 100644 index 0000000..c96a7df --- /dev/null +++ b/gdb/python/lib/gdb/BaseFrameWrapper.py @@ -0,0 +1,293 @@ +# Copyright (C) 2012 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 +from gdb.FrameWrapper import FrameWrapper + +class BaseFrameWrapper(FrameWrapper): + """Basic implementation of a Frame Wrapper""" + + """ This base frame wrapper wraps a frame or another frame + wrapper, and provides convenience methods. If this object is + wrapping a frame wrapper, defer to that wrapped object's method if + it has one. This allows for frame wrappers that have sub-classed + BaseFrameWrapper, but also wrap other frame wrappers 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 wrappers, all sub-classed from + BaseFrameWrapper: + + Wrapper1(Wrapper2(BaseFrameWrapper(gdb.Frame))) + + In this case we have two frame wrappers, both of which are + sub-classed from BaseFrameWrapper. If Wrapper1 just overrides the + 'function' method, then all of the other methods are carried out + by the super-class BaseFrameWrapper. But Wrapper2 may have + overriden other methods, so BaseFrameWrapper 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 filter. 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): + super(BaseFrameWrapper, self).__init__(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 == gdb.DUMMY_FRAME + or frame == 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, first determining + if it 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 not isinstance(self.base, gdb.Frame): + if hasattr(self.base, "function"): + 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() + sal = frame.find_sal() + pc = frame.pc() + + if func == None: + unknown = format(" 0x%08x in" % pc) + return unknown + + 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 iterator of frame arguments for this frame, if + any. The iterator 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 iterator of local variables for this frame, if + any. The iterator 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 wrapper.""" + + # If 'base' is a frame wrapper, 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 BaseSymValueWrapper(): + """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(): + + """Utility class to fetch and store frame local variables, or + frame arguments.""" + + def __init__(self,frame): + self.frame = frame + + @staticmethod + def fetch_b(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 { + 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 + }.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 not frame local variables, return None.""" + lvars = [] + try: + block = self.frame.block() + except: + return None + + for sym in block: + if sym.is_argument: + continue; + if self.fetch_b(sym): + lvars.append(BaseSymValueWrapper(sym, None)) + + if len(lvars) == 0: + return None + + return iter(lvars) + + def fetch_frame_args(self): + """Public utility method to fetch frame argument for the + stored frame. Frame arguments are the only type fetched. If + there are no frame arguments variables, return None.""" + + args = [] + try: + block = self.frame.block() + except: + return None + + for sym in block: + if not sym.is_argument: + continue; + args.append(BaseSymValueWrapper(sym,None)) + + if len(args) == 0: + return None + + return iter(args) + + def get_value(self, sym, block): + """Public utility method to fetch a value from a symbol.""" + if len(sym.linkage_name): + nsym, is_field_of_this = gdb.lookup_symbol(sym.linkage_name, block) + if nsym != None: + if nsym.addr_class != gdb.SYMBOL_LOC_REGISTER: + sym = nsym + + try: + val = sym.value(self.frame) + + except RuntimeError, text: + val = text + if val == None: + val = "???" + + return val diff --git a/gdb/python/lib/gdb/FrameIterator.py b/gdb/python/lib/gdb/FrameIterator.py new file mode 100644 index 0000000..ddc9087 --- /dev/null +++ b/gdb/python/lib/gdb/FrameIterator.py @@ -0,0 +1,53 @@ +# Copyright (C) 2012 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 __getitem__(self,index): + """__getitem__ implementation. + + Arguments: + index: A specific index to fetch.""" + + return next(itertools.islice(self.frame,index,index+1)) + + 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/FrameWrapper.py b/gdb/python/lib/gdb/FrameWrapper.py new file mode 100644 index 0000000..4db9a43 --- /dev/null +++ b/gdb/python/lib/gdb/FrameWrapper.py @@ -0,0 +1,155 @@ +# Copyright (C) 2012 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 FrameWrapper (object): + """Interface for a Frame Wrapper.""" + + """ A frame wrapper wraps a frame and provides additional and + convenience methods. """ + def __init__(self, frame): + super(FrameWrapper, self).__init__() + self.frame = frame + + def elided (self): + """ The elided method groups frames together in a + hierarchical system. An example would be an interpreter call + that occurs over many frames but might be better represented + as a group of frames distinct from the other frames. + + Arguments: None + + Returns: The elided function must return an iterator that + contains the frames that are being elided, or None. + Elided frames are indented from normal frames in a + backtrace, to show affinity with the frame that + elided them. Note that it is the frame filter's task + to filter out the elided frames from the source + iterator, and also to provide the iterator of elided + frames in this function. If this function returns a + None, no frames will be elided. + """ + + pass + + def function (self): + """ The name of the function in the frame. + + Arguments: None. + + Returns: A string describing the function. If this function + returns None, no data will be displayed for this + field at printing. + """ + pass + + def address (self): + """ The address of the frame. + + Arguments: None. + + Returns: A numeric integer type of sufficient size to describe + the address of the frame, or None. If this function + returns a None, no data will be displayed for this + field at printing. + """ + + pass + + def filename (self): + """ The filename associated with the function of this frame. + + Arguments: None. + + Returns: A string containing the filename, and optionally, the + path to the filename of the frame, or None. If this + function returns a None, no data will be displayed + for this field at printing. + """ + + pass + + def line (self): + """ The line number associated with the current position + within the function addressed by this frame. + + Arguments: None. + + Returns: An integer type representing the line number, or + None. If this function returns a None, no data will + be displayed for this field at printing. + """ + + pass + + def frame_args (self): + """ The arguments of the function in this frame. + + Arguments: None. + + Returns: An iterator that conforms to the Python iterator + protocol, or None. If this method returns a None, + instead of an iterator, then no data will be printed + for frame arguments. If this method returns an + iterator, it must contain objects that implement two + methods, described here. + + The object must implement an argument method which + takes no parameters and must return a gdb.Symbol or a + Python string. It must also implement a value method + which takes no parameters and which must return a + gdb.Value, a Python value, or None. If the value + method returns a None, and the argument method + returns a gdb.Symbol, GDB will look-up and print the + value of the gdb.Symbol automatically. If the + argument method contains a string, then the value + method must not return a None. + """ + pass + + def frame_locals (self): + """ The local variables of the function in this frame. + + Arguments: None. + + Returns: An iterator that conforms to the Python iterator + protocol, or None. If this method returns a None, + instead of an iterator, then no data will be printed + for frame locals. If this method returns an + iterator, it must contain objects that implement two + methods, described here. + + The object must implement an argument method which + takes no parameters and must return a gdb.Symbol or a + Python string. It must also implement a value method + which takes no parameters and which must return a + gdb.Value, a Python value, or None. If the value + method returns a None, and the argument method + returns a gdb.Symbol, GDB will look-up and print the + value of the gdb.Symbol automatically. If the + argument method contains a string, then the value + method must not return a None. + """ + pass + + def frame (self): + """ The gdb.Frame that this wrapper is wrapping. + + Arguments: None. + + Returns: The gdb.Frame that this wrapper is wrapping. + """ + pass diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py index 0671526..a1c661d 100644 --- a/gdb/python/lib/gdb/__init__.py +++ b/gdb/python/lib/gdb/__init__.py @@ -72,6 +72,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 0000000..fc115b0 --- /dev/null +++ b/gdb/python/lib/gdb/command/frame_filters.py @@ -0,0 +1,621 @@ +# Frame-filter commands. +# Copyright (C) 2012 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.BaseFrameWrapper import BaseFrameWrapper +import itertools + +def _parse_arg(cmd_name, arg): + """ Internal worker function to take an argument 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. + """ + + argv = gdb.string_to_argv(arg); + argc = len(argv) + if argc != 2: + raise gdb.GdbError(cmd_name + " takes exactly two arguments.") + + object_list = argv[0] + argument = argv[1] + + return(object_list, argument) + +def _get_sort_priority(filter_item): + """ Internal worker function to return the frame-filter's priority + from a frame filter tuple object. + + Arguments: + filter_item: A tuple, with the first element being the name, + and the second being the frame-filter object. + + Returns: + The priority of the frame filter from the "priority" + attribute. + """ + # Do not fail here, as the sort will fail. If a filter has not + # (incorrectly) set a priority, set it to zero. + if hasattr(filter_item[1], "priority"): + return filter_item[1].priority + else: + return 0 + +def _get_priority(filter_item): + """ Internal worker function to return the frame-filter's priority. + + Arguments: + filter_item: An object conforming to the frame filter + interface. + + Returns: + The priority of the frame filter from the "priority" + attribute. + + Raises: + gdb.GdbError: When the priority attribute has not been + implemented. + """ + if hasattr(filter_item, "priority"): + return filter_item.priority + else: + raise gdb.GdbError("Cannot find class attribute 'priority'") + +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. + + Raises: + gdb.GdbError: When the priority attribute has not been + implemented. + """ + + if hasattr(filter_item, "priority"): + filter_item.priority = priority + else: + raise gdb.GdbError("Cannot find class attribute 'priority'") + +def _get_enabled(filter_item): + """ Internal worker function to return the frame-filter's enabled + state. + + Arguments: + filter_item: An object conforming to the frame filter + interface. + + Returns: + The enabled state of the frame filter from the "enabled" + attribute. + + Raises: + gdb.GdbError: When the enabled attribute has not been + implemented. + """ + + if hasattr(filter_item, "enabled"): + return filter_item.enabled + else: + raise gdb.GdbError("Cannot find class attribute 'enabled'") + +def _get_filter_enabled(filter_item): + """ Internal Worker function to return the frame-filter's enabled + state for filter operations. + + Arguments: + filter_item: A tuple, with the first element being the name, + and the second being the frame-filter object. + Returns: + The enabled state of the frame filter from the "enabled" + attribute. + + """ + # If the filter class is badly implemented, do not cease filter + # operations, just set enabled to False. + try: + enabled = _get_enabled(filter_item[1]) + except gdb.GdbError as e: + enabled = False + + return enabled + +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. + + Raises: + gdb.GdbError: When the enabled attribute has not been + implemented. + """ + + if hasattr(filter_item, "enabled"): + filter_item.enabled = state + else: + raise gdb.GdbError("Cannot find class attribute 'enabled'") + +def _get_name(frame_filter): + """ Internal Worker function to return the name of the + frame-filter. + + Arguments: + filter_item: An object conforming to the frame filter + interface. + + Returns: + The name of the frame filter from the "name" attribute. + + Raises: + gdb.GdbError: When the name attribute has not been + implemented. + """ + if hasattr(frame_filter, "name"): + return frame_filter.name + raise gdb.GdbError("Cannot find class attribute 'name'") + +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 "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. + + Raises: + gdb.GdbError: A dictionary of that name cannot be found. + """ + + 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.items() + cp = gdb.current_progspace() + + all_filters = all_filters + cp.frame_filters.items() + all_filters = all_filters + gdb.frame_filters.items() + + sorted_frame_filters = copy.copy(all_filters) + sorted_frame_filters.sort(key = _get_sort_priority, + reverse = True) + sorted_frame_filters = filter(_get_filter_enabled, + sorted_frame_filters) + + return sorted_frame_filters + +def invoke(frame): + """ Public internal function that will execute the chain of frame + filters. Each filter is executed in priority order. + + Arguments: + frame: The initial frame. + + Returns: + frame_iterator: The 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 base filter to all gdb.Frames. This unifies the + # interface. + frame_iterator = itertools.imap(BaseFrameWrapper, frame_iterator) + + for ff in sorted_list: + frame_iterator = ff[1].filter(frame_iterator) + + return frame_iterator + +# GDB Commands. +class SetFilterPrefixCmd(gdb.Command): + def __init__(self): + super(SetFilterPrefixCmd, self).__init__("set python frame-filter", + gdb.COMMAND_DATA, + gdb.COMPLETE_COMMAND, True) +class ShowFilterPrefixCmd(gdb.Command): + def __init__(self): + super(ShowFilterPrefixCmd, self).__init__("show python frame-filter", + gdb.COMMAND_DATA, + gdb.COMPLETE_COMMAND, True) +class InfoFrameFilter(gdb.Command): + """GDB command to list all registered frame-filters. + + Usage: info frame-filters + """ + @staticmethod + def enabled_string(state): + """Return "Yes" if filter is enabled, otherwise "No".""" + if state: + return "Yes" + else: + return "No" + + def __init__(self): + super(InfoFrameFilter, self).__init__("info frame-filter", + gdb.COMMAND_DATA) + + 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 = frame_filters.items() + sorted_frame_filters.sort(key = _get_sort_priority, + reverse = True) + + print " Priority Enabled Name" + print " ======== ======= ====" + if len(sorted_frame_filters) == 0: + print " No frame filters registered." + else: + for frame_filter in sorted_frame_filters: + name = frame_filter[0] + try: + priority = '{:<8}'.format( + str(_get_priority(frame_filter[1]))) + enabled = '{:<7}'.format( + self.enabled_string(_get_enabled(frame_filter[1]))) + except Exception as e: + print " Error printing filter '"+name+"': ",e + else: + print " %s %s %s" % (priority, enabled, name) + + def print_list(self, title, filter_list): + """Print a list.""" + if filter_list: + print title + self.list_frame_filters(filter_list) + + def invoke(self, arg, from_tty): + """GDB calls this to perform the command.""" + self.print_list("global frame-filters:", gdb.frame_filters) + + cp = gdb.current_progspace() + self.print_list("progspace %s frame-filters:" % cp.filename, + cp.frame_filters) + + for objfile in gdb.objfiles(): + self.print_list("objfile %s frame-filters:" % objfile.filename, + objfile.frame_filters) + +# Internal enable/disable functions. + +def do_enable_frame_filter_1(frame_filters, name, flag): + """Internal worker for enabling/disabling frame_filters. + + Arguments: + frame_filters: Dictionary that this frame filter is contained + within. + name: Name of the frame filter. + flag: True for Enable, False for Disable. + + Raises: + gdb.GdbError: A frame filter cannot be found. + """ + + try: + ff = frame_filters[name] + except KeyError: + msg = "frame-filter '" + str(name) + "' not found." + raise gdb.GdbError(msg) + + _set_enabled(ff, flag) + +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] + frame_filter = command_tuple[1] + + op_list = _return_list(list_op) + do_enable_frame_filter_1(op_list, frame_filter, flag) + +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. 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(EnableFrameFilter, self).__init__("enable frame-filter", + gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + """GDB calls this to perform the command.""" + command_tuple = _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. 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(DisableFrameFilter, self).__init__("disable frame-filter", + gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + """GDB calls this to perform the command.""" + command_tuple = _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 python 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 new priority to set the frame filter. + """ + + def __init__(self): + super(SetFrameFilterPriority, self).__init__("set python " \ + "frame-filter priority", + gdb.COMMAND_DATA, + gdb.COMPLETE_COMMAND) + 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: + raise gdb.GdbError("set python frame-filter priority " \ + "takes exactly three arguments.") + + object_list = argv[0] + name = argv[1] + priority = argv[2] + return(object_list, name, priority) + + def _set_filter_priority_1(self, frame_filters, name, priority): + """Internal worker for setting priority of frame_filters. + + Arguments: + frame_filters: The frame_filter dictionary. + name: The name of the filter. + priority: Priority of filter. + + Raises: + gdb.GdbError: An error finding the frame filter. + """ + try: + ff = frame_filters[name] + except KeyError: + msg = "frame-filter '" + str(name) + "' not found." + raise gdb.GdbError(msg) + + _set_priority(ff, priority) + + def _set_filter_priority(self,command_tuple): + """Internal worker for setting priority of frame-filters, by + parsing a tuple and calling _set_filter_priority_1 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 = _return_list(list_op) + + self._set_filter_priority_1(op_list, frame_filter, priority) + + def invoke(self, arg, from_tty): + """GDB calls this to perform the command.""" + + command_tuple = self._parse_pri_arg(arg) + try: + self._set_filter_priority(command_tuple) + except gdb.GdbError as e: + # Print the error, instead of raising it. + gdb.write(e.message+"\n") + +class ShowFrameFilterPriority(gdb.Command): + """GDB command to show the priority of the specified frame-filter. + + Usage: show python frame-filter priority list name + + LIST is the name of the frame-filter list to operate. Named lists + are: "global" for the global frame-filter list, "progspace" for + the program space's file frame-filter list. If either of these + two are not specified, the list 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 python " \ + "frame-filter priority", + gdb.COMMAND_DATA, + gdb.COMPLETE_COMMAND) + 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: + raise gdb.GdbError("show python frame-filter priority " \ + "takes exactly two arguments.") + + object_list = argv[0] + name = argv[1] + return (object_list, name) + + 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 = _return_list(frame_filters) + + try: + ff = op_list[name] + except KeyError: + msg = "frame-filter '" + str(name) + "' not found." + raise gdb.GdbError(msg) + + return _get_priority(ff) + + def invoke(self, arg, from_tty): + """GDB calls this to perform the command.""" + try: + command_tuple = self._parse_pri_arg(arg) + except gdb.GdbError as e: + # Print the error instead of raising it. + gdb.write(e.message+"\n") + 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+"':",e + else: + print "Priority of filter '" + filter_name + "' in list '" \ + + list_name + "' is: " + str(priority) + +def register_frame_filter_commands(): + """Call from a top level script to install the frame-filter commands.""" + InfoFrameFilter() + SetFilterPrefixCmd() + ShowFilterPrefixCmd() + EnableFrameFilter() + DisableFrameFilter() + SetFrameFilterPriority() + ShowFrameFilterPriority() + +register_frame_filter_commands() diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c new file mode 100644 index 0000000..ad0c44a --- /dev/null +++ b/gdb/python/py-framefilter.c @@ -0,0 +1,1324 @@ +/* Python frame filters + + Copyright (C) 2012 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 "mi/mi-cmds.h" +#include "demangle.h" +#include "mi/mi-cmds.h" +#include "python-internal.h" + + +/* Helper function to extract a symbol, name and 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 0 on error with the appropriate Python exception set, and 1 + on success. */ + +static int +extract_sym (PyObject *obj, char **name, struct symbol **sym, + const struct language_defn **language) +{ + if (PyObject_HasAttrString (obj, "symbol")) + { + PyObject *result = PyObject_CallMethod (obj, "symbol", NULL); + + if (! result) + return 0; + + /* For 'symbol' callback, the function can return a symbol or a + string. */ + if (PyString_Check (result)) + { + *name = python_string_to_host_string (result); + Py_DECREF (result); + + if (! *name) + return 0; + *language = current_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) + { + PyErr_SetString (PyExc_RuntimeError, + _("Unexpected value. Expecting a " \ + "gdb.Symbol or a Python string.")); + return 0; + } + + /* Duplicate the symbol name, so the caller has consistency + in garbage collection. */ + *name = xstrdup (SYMBOL_PRINT_NAME (*sym)); + + if (language_mode == language_mode_auto) + *language = language_def (SYMBOL_LANGUAGE (*sym)); + else + *language = current_language; + } + } + else + { + PyErr_SetString (PyExc_RuntimeError, + _("Mandatory function 'symbol' not " \ + "implemented.")); + return 0; + } + + return 1; +} + +/* 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 0 on + error with the appropriate Python exception set, and 1 on + success. */ + +static int +extract_value (PyObject *obj, struct value **value) +{ + if (PyObject_HasAttrString (obj, "value")) + { + PyObject *vresult = PyObject_CallMethod (obj, "value", NULL); + + if (! vresult) + return 0; + + /* 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 1; + } + else + { + *value = convert_value_from_python (vresult); + Py_DECREF (vresult); + + if (*value == NULL) + return 0; + + return 1; + } + } + else + *value = NULL; + + return 1; +} + +static int +mi_should_print (struct symbol *sym, const char *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 (strcmp (type, "all")) + print_me = 1; + else if (strcmp (type, "locals")) + print_me = !SYMBOL_IS_ARGUMENT (sym); + else + print_me = SYMBOL_IS_ARGUMENT (sym); + } + return print_me; +} + +/* Helper function which outputs a type name to a "type" field in a + stream. 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 0 on error, with any GDB exceptions converted to a Python + exception. */ + +static int +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) + { + PyErr_SetString (PyExc_RuntimeError, + except.message); + return 0; + } + return 1; +} + +/* Helper function which outputs a value name to value field in a + stream. OUT is the ui-out structure the value will be output too, + VAL is the value that will be printed, OPTS contains the value + printing options, MI_PRINT_TYPE is the value delimiter for MI + output and LANGUAGE is the language_defn that the value will be + printed with. If the output is detected to be non-MI, + MI_PRINT_TYPE is ignored. Returns 0 on error, with any GDB + exceptions converted to a Python exception. */ + +static int +py_print_value (struct ui_out *out, struct value *val, + struct value_print_options opts, + int mi_print_type, + const struct language_defn *language) +{ + int should_print = 0; + + /* MI disallows different value types against different options the + client passes, so test type against option. For CLI print all + values. */ + if (ui_out_is_mi_like_p (out)) + { + struct type *type; + + type = check_typedef (value_type (val)); + if (mi_print_type == PRINT_ALL_VALUES) + should_print = 1; + else if (mi_print_type == 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 + should_print = 1; + + if (should_print) + { + volatile struct gdb_exception except; + + 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, 0, &opts, language); + ui_out_field_stream (out, "value", stb); + do_cleanups (cleanup); + } + if (except.reason > 0) + { + PyErr_SetString (PyExc_RuntimeError, + except.message); + return 0; + } + } + + return 1; +} + +/* Helper function to call a Python method and extract an iterator + from the result, error checking for Python exception and returns + that are not iterators. 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 None. */ + +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) + { + if (result != Py_None) + { + if (! PyIter_Check (result)) + { + PyErr_Format (PyExc_RuntimeError, + _(" %s function must " \ + "return an iterator."), func); + Py_DECREF (result); + return NULL; + } + else + { + PyObject *iterator = PyObject_GetIter (result); + + Py_DECREF (result); + + if (! iterator) + return NULL; + else + return iterator; + } + } + } + else + return NULL; + } + + Py_RETURN_NONE; +} + +/* 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, + MI_PRINT_TYPE is an enumerator to the value types that will be + printed if the output is MI. PRINT_MI_ARGS indicates whether to + output the ARGS="1" field in MI output. */ +static int +py_print_single_arg (struct ui_out *out, + char *sym_name, + struct frame_arg *fa, + struct value *fv, + struct value_print_options opts, + int mi_print_type, + int print_mi_args_flag, + const char *cli_print_frame_args_type, + const struct language_defn *language) +{ + struct value *val; + struct cleanup *inner_cleanup = + make_cleanup (null_cleanup, NULL); + + if (fa) + { + language = language_def (SYMBOL_LANGUAGE (fa->sym)); + val = fa->val; + } + else + val = fv; + + /* 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. */ + if (print_mi_args_flag || mi_print_type != PRINT_NO_VALUES) + { + inner_cleanup = + 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) + { + struct ui_file *stb; + + stb = mem_fileopen (); + + 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); + ui_file_delete (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_mi_args_flag) + ui_out_field_int (out, "arg", 1); + + /* For MI print the type. */ + if (ui_out_is_mi_like_p (out) + && mi_print_type == PRINT_SIMPLE_VALUES) + { + if (! py_print_type (out, val)) + 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) + && ! strcmp (cli_print_frame_args_type, "none")) + + { + ui_out_field_string (out, "value", "..."); + } + else + { + /* If CLI, and the first if condition above not true always + print values. For MI do not print values if the enumerator + is PRINT_NO_VALUES. */ + if (! ui_out_is_mi_like_p (out) + || (ui_out_is_mi_like_p (out) + && mi_print_type != PRINT_NO_VALUES)) + { + if (! py_print_value (out, val, opts, mi_print_type, language)) + goto error; + } + } + + do_cleanups (inner_cleanup); + + return 1; + + error: + do_cleanups (inner_cleanup); + return 0; +} + +/* 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 + iterator object, OUT is the output stream, MI_PRINT_TYPE is an + enumerator to the value types that will be printed if the output is + MI, PRINT_MI_ARGS indicates whether to output the ARGS="1" field in + MI output, CLI_PRINT_FRAME_ARGS_TYPE is an enumerator of the user + set option for frame argument printing output, and FRAME is the + backing frame. If (all) the frame argument values are provided via + the "value" API call, FRAME is not needed. */ + +static int +enumerate_args (PyObject *iter, + struct ui_out *out, + int mi_print_type, + int print_mi_args_flag, + const char *cli_print_frame_args_type, + struct frame_info *frame) +{ + PyObject *item; + struct value_print_options opts; + + get_user_print_options (&opts); + + if (! ui_out_is_mi_like_p (out)) + { + /* True in "summary" mode, false otherwise. */ + opts.summary = !strcmp (cli_print_frame_args_type, "scalars"); + } + + opts.deref_ref = 1; + + + annotate_frame_args (); + + /* 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 && PyErr_Occurred ()) + goto error; + + while (item) + { + const struct language_defn *language; + char *sym_name; + struct symbol *sym; + struct value *val; + int success = 0; + volatile struct gdb_exception except; + + success = extract_sym (item, &sym_name, &sym, &language); + if (! success) + { + Py_DECREF (item); + goto error; + } + + success = extract_value (item, &val); + if (! success) + { + 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, "args")) + continue; + + /* If the object did not provide a value, read it using + read_frame_args and account for entry values, if any. */ + if (! val) + { + struct frame_arg arg, entryarg; + + /* If there is no value, and also no symbol, set error and + exit. */ + if (! sym) + { + 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); + PyErr_SetString (PyExc_RuntimeError, + except.message); + 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) + py_print_single_arg (out, NULL, &arg, NULL, opts, + mi_print_type, + print_mi_args_flag, + cli_print_frame_args_type, NULL); + + if (entryarg.entry_kind != print_entry_values_no) + { + if (arg.entry_kind != print_entry_values_only) + { + ui_out_text (out, ", "); + ui_out_wrap_hint (out, " "); + } + + py_print_single_arg (out, NULL, &entryarg, NULL, opts, + mi_print_type, + print_mi_args_flag, + cli_print_frame_args_type, NULL); + } + + xfree (arg.error); + xfree (entryarg.error); + } + else + { + /* If the object has provided a value, we just print that. */ + if (val) + py_print_single_arg (out, sym_name, NULL, val, opts, + mi_print_type, + print_mi_args_flag, + cli_print_frame_args_type, + language); + } + + 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) + ui_out_text (out, ", "); + else + if (PyErr_Occurred ()) + goto error; + + annotate_arg_end (); + } + + return 1; + + error: + return 0; +} + + +/* Helper function to loop over variables provided by the + "frame_locals" Python API. Elements in the iterator must conform + to the "Symbol Value" interface. ITER is the Python iterator + object, OUT is the output stream, MI_PRINT_TYPE is an enumerator to + the value types that will be printed if the output is MI, + PRINT_MI_ARGS_FLAG indicates whether to output the ARGS field in MI + output, and FRAME is the backing frame. If (all) of the variables + values are provided via the "value" API call, FRAME is not + needed. */ + +static int +enumerate_locals (PyObject *iter, + struct ui_out *out, + int mi_print_type, + int indent, + int print_mi_args_flag, + 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; + int success = 0; + struct symbol *sym; + volatile struct gdb_exception except; + struct cleanup *inner_cleanup = + make_cleanup (null_cleanup, NULL); + + success = extract_sym (item, &sym_name, &sym, &language); + if (! success) + { + Py_DECREF (item); + goto error; + } + + success = extract_value (item, &val); + if (! success) + { + xfree (sym_name); + Py_DECREF (item); + goto error; + } + + Py_DECREF (item); + + if (sym && ui_out_is_mi_like_p (out) && ! mi_should_print (sym, "locals")) + continue; + + /* If the object did not provide a value, read it. */ + if (! val) + { + TRY_CATCH (except, RETURN_MASK_ALL) + { + val = read_var_value (sym, frame); + } + if (except.reason > 0) + { + xfree (sym_name); + PyErr_SetString (PyExc_RuntimeError, + except.message); + 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_mi_args_flag || mi_print_type != PRINT_NO_VALUES) + { + inner_cleanup = + make_cleanup_ui_out_tuple_begin_end (out, + NULL); + } + } + else + /* If the output is not MI we indent locals. */ + ui_out_spaces (out, (8 + (indent * 2))); + + ui_out_field_string (out, "name", sym_name); + xfree (sym_name); + + if (! ui_out_is_mi_like_p (out)) + ui_out_text (out, " = "); + + if (ui_out_is_mi_like_p (out) + && mi_print_type == PRINT_SIMPLE_VALUES) + { + if (! py_print_type (out, val)) + goto error; + } + + if (! ui_out_is_mi_like_p (out) + || (ui_out_is_mi_like_p (out) + && mi_print_type != PRINT_NO_VALUES)) + { + py_print_value (out, val, opts, mi_print_type, language); + } + + ui_out_text (out, "\n"); + do_cleanups (inner_cleanup); + } + + if (! item && PyErr_Occurred()) + goto error; + + done: + return 1; + + error: + return 0; +} + +/* Helper function for -stack-list-variables. */ + +static int +py_mi_print_variables (PyObject *filter, struct ui_out *out, + int mi_print_type, + const char *cli_print_frame_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) + goto error; + + locals_iter = get_py_iter_from_func (filter, "frame_locals"); + if (! locals_iter) + 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, mi_print_type, + 1, cli_print_frame_args_type, frame)) + goto error; + + if (locals_iter != Py_None) + if (! enumerate_locals (locals_iter, out, mi_print_type, 1, 1, + frame)) + + goto error; + + do_cleanups (old_chain); + return 1; + + error: + do_cleanups (old_chain); + return 0; +} + +/* Helper function for printing locals. This function largely just + creates the wrapping tuple, and calls enumerate_locals. */ + +static int +py_print_locals (PyObject *filter, + struct ui_out *out, + int mi_print_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) + goto locals_error; + + make_cleanup_ui_out_list_begin_end (out, "locals"); + if (locals_iter != Py_None) + if (! enumerate_locals (locals_iter, out, mi_print_type, + indent, 0, frame)) + goto locals_error; + + do_cleanups (old_chain); + return 1; + + locals_error: + do_cleanups (old_chain); + return 0; +} + +/* Helper function for printing frame arguments. This function + largely just creates the wrapping tuple, and calls + enumerate_args. */ + +static int +py_print_args (PyObject *filter, + struct ui_out *out, + int mi_print_type, + const char *cli_print_frame_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); + + if (! args_iter) + goto args_error; + + make_cleanup_ui_out_list_begin_end (out, "args"); + annotate_frame_args (); + + if (! ui_out_is_mi_like_p (out)) + ui_out_text (out, " ("); + + if (args_iter != Py_None) + if (! enumerate_args (args_iter, out, mi_print_type, + 0, cli_print_frame_args_type, + frame)) + goto args_error; + + if (! ui_out_is_mi_like_p (out)) + ui_out_text (out, ")"); + + do_cleanups (old_chain); + return 1; + + args_error: + do_cleanups (old_chain); + return 0; +} + +/* Hash function for the printed frame hash. */ + +static hashval_t +hash_printed_frame_entry (const void *data) +{ + const struct frame_info *frame = data; + + return htab_hash_pointer (frame); +} + +/* Equality function for the printed hash. */ + +static int +eq_printed_frame_entry (const void *a, const void *b) +{ + const struct frame_info *ea = a; + const struct frame_info *eb = b; + + return ea == eb; +} + + +/* 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. PRINT_LEVEL is a flag + and indicates whether to print the frame level. PRINT_FRAME_INFO + is a flag that indicates to print the frame information + (everything other than arguments or locals). PRINT_ARGS is a flag + that indicates whether to print frame arguments, and PRINT_LOCALS + is a flag that indicates whether to print frame local variables. + MI_PRINT_TYPE is an enumerator to the value types that will be + printed if the output is MI. CLI_PRINT_FRAMES_ARGS_TYPE is an + enumerator indicating what type of frame arguments to print. OUT + is the output stream to print too, 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). */ + +static int +py_print_frame (PyObject *filter, int print_level, int print_frame_info, + int print_args, int print_locals, int mi_print_type, + const char *cli_print_frame_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 *elided; + + 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. */ + if (PyObject_HasAttrString (filter, "inferior_frame")) + { + PyObject *result = PyObject_CallMethod (filter, "inferior_frame", NULL); + + if (! result) + goto error; + frame = frame_object_to_frame_info (result); + if (! frame) + { + Py_DECREF (result); + goto error; + } + + gdbarch = get_frame_arch (frame); + Py_DECREF (result); + } + else + { + PyErr_SetString (PyExc_RuntimeError, + _("'inferior_frame' API must be implemented.")); + goto error; + } + + /* stack-list-variables. */ + if (print_locals && print_args && ! print_frame_info) + { + if (! py_mi_print_variables (filter, out, + mi_print_type, + cli_print_frame_args_type, + frame)) + goto error; + else + 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) + ui_out_spaces (out, indent); + + /* 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) + { + 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; + + slot = (struct frame_info **) htab_find_slot (levels_printed, + frame, INSERT); + 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 != NULL && (*slot) == frame) + { + char buffer[10]; + sprintf (buffer, "%d", level); + ui_out_spaces (out, strlen (buffer) + 2); + } + 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 (print_frame_info) + { + /* Print address to the address field. If an address is not provided, + print nothing. */ + if (opts.addressprint && has_addr) + { + annotate_frame_address (); + ui_out_field_core_addr (out, "addr", gdbarch, address); + annotate_frame_address_end (); + ui_out_text (out, " in "); + } + + /* Print frame function. */ + if (PyObject_HasAttrString (filter, "function")) + { + PyObject *result = PyObject_CallMethod (filter, "function", NULL); + + if (result) + { + if (result != Py_None) + { + char *func = NULL; + char *dup = PyString_AsString (result); + + if (! dup) + { + Py_DECREF (result); + goto error; + } + + func = xstrdup (dup); + annotate_frame_function_name (); + ui_out_field_string (out, "func", func); + xfree (func); + + } + Py_DECREF (result); + } + else + goto error; + } + } + + + /* Frame arguments. Check the result, and error if something went + wrong. */ + if (print_args) + { + if (! py_print_args (filter, out, mi_print_type, + cli_print_frame_args_type, frame)) + goto error; + } + + /* File name/source/line number information. */ + if (print_frame_info) + { + annotate_frame_source_begin (); + + if (PyObject_HasAttrString (filter, "filename")) + { + PyObject *result = PyObject_CallMethod (filter, "filename", + NULL); + if (result) + { + if (result != Py_None) + { + char *filename = NULL; + char *dup = PyString_AsString (result); + + if (! dup) + { + Py_DECREF (result); + goto error; + } + + filename = xstrdup (dup); + 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 (); + xfree (filename); + } + Py_DECREF (result); + } + else + goto error; + } + + if (PyObject_HasAttrString (filter, "line")) + { + PyObject *result = PyObject_CallMethod (filter, "line", NULL); + int line; + + if (result) + { + if (result != Py_None) + { + line = PyLong_AsLong (result); + ui_out_text (out, ":"); + annotate_frame_source_line (); + ui_out_field_int (out, "line", line); + } + Py_DECREF (result); + } + 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)) + { + annotate_frame_end (); + ui_out_text (out, "\n"); + } + + if (print_locals) + { + if (! py_print_locals (filter, out, mi_print_type, indent, + frame)) + goto error; + } + + /* Finally recursively print elided frames, if any. */ + elided = get_py_iter_from_func (filter, "elided"); + if (! elided) + goto error; + + if (elided != Py_None) + { + PyObject *item; + + make_cleanup_py_decref (elided); + make_cleanup_ui_out_list_begin_end (out, "children"); + + if (! ui_out_is_mi_like_p (out)) + indent = indent + 4; + + while ((item = PyIter_Next (elided))) + { + int success = py_print_frame (item, print_level, + print_frame_info, + print_args, + print_locals, + mi_print_type, + cli_print_frame_args_type, + out, indent, + levels_printed); + + if (success == 0 && PyErr_Occurred ()) + { + Py_DECREF (item); + goto error; + } + + Py_DECREF (item); + } + } + + /* In MI now we can signal the end. */ + if (ui_out_is_mi_like_p (out)) + ui_out_text (out, "\n"); + + 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) +{ + + PyObject *module, *sort_func, *iterable, *frame_obj; + + frame_obj = frame_info_to_frame_object (frame); + if (! frame_obj) + return NULL; + + module = PyImport_ImportModule ("gdb.command.frame_filters"); + if (! module) + { + Py_DECREF (frame_obj); + return NULL; + } + + sort_func = PyObject_GetAttrString (module, "invoke"); + if (! sort_func) + { + Py_DECREF (frame_obj); + Py_DECREF (module); + return NULL; + } + + iterable = PyObject_CallFunctionObjArgs (sort_func, frame_obj, NULL); + + Py_DECREF (module); + Py_DECREF (sort_func); + Py_DECREF (frame_obj); + + if (! iterable) + return NULL; + + return iterable; +} + +/* Public and dispatch function for frame filters. 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 makeup 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. MI_PRINT_ARGS_TYPE is an + enum from MI that indicates which values types to print. This + parameter is ignored if the output is detected to be CLI. + CLI_PRINT_FRAME_ARGS_TYPE likewise is a an element of what value + types to print from CLI. OUT is the output stream to print, and + COUNT is a delimiter (required for MI slices). */ + +int apply_frame_filter (struct frame_info *frame, int flags, + enum print_values mi_print_args_type, + const char *cli_print_frame_args_type, + struct ui_out *out, int count) +{ + struct gdbarch *gdbarch = get_frame_arch (frame); + struct cleanup *cleanups; + int result = 0; + int print_result = 0; + int success = 0; + PyObject *iterable; + int print_level, print_frame_info, print_args, print_locals; + + /* 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; + + cleanups = ensure_python_env (gdbarch, current_language); + + iterable = bootstrap_python_frame_filters (frame); + + if (!iterable) + goto done; + + /* If iterable is None, then there are not any 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) + { + do_cleanups (cleanups); + return PY_BT_NO_FILTERS; + } + + /* Is it an iterator */ + if PyIter_Check (iterable) + { + PyObject *iterator = PyObject_GetIter (iterable); + PyObject *item; + htab_t levels_printed; + + if (! iterator) + goto done; + + make_cleanup_py_decref (iterator); + levels_printed = htab_create (20, + hash_printed_frame_entry, + eq_printed_frame_entry, + NULL); + + while ((item = PyIter_Next (iterator)) && count--) + { + success = py_print_frame (item, print_level, + print_frame_info, print_args, + print_locals, + mi_print_args_type, + cli_print_frame_args_type, + out, 0, levels_printed); + + /* Do not exit on error printing the frame, continue with + other frames. */ + if (success == PY_BT_ERROR && PyErr_Occurred ()) + gdbpy_print_stack (); + + Py_DECREF (item); + } + + htab_delete (levels_printed); + } + else + { + Py_DECREF (iterable); + error (_("Frame filter must support iteration protocol.")); + } + + done: + if (PyErr_Occurred ()) + gdbpy_print_stack (); + do_cleanups (cleanups); + return success; +} diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c index 5d2398f..228b71b 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); self->ob_type->tp_free ((PyObject *) self); } @@ -81,9 +84,19 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords) return NULL; } + self->frame_filters = PyDict_New (); + if (!self->frame_filters) + { + Py_DECREF (self->printers); + Py_DECREF (self); + return NULL; + } + self->type_printers = PyList_New (0); if (!self->type_printers) { + Py_DECREF (self->printers); + Py_DECREF (self->frame_filters); Py_DECREF (self); return NULL; } @@ -129,6 +142,44 @@ objfpy_set_printers (PyObject *o, PyObject *value, void *ignore) return 0; } +PyObject * +objfpy_get_frame_filters (PyObject *o, void *ignore) +{ + objfile_object *self = (objfile_object *) o; + + Py_INCREF (self->frame_filters); + return self->frame_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,9 +276,19 @@ objfile_to_objfile_object (struct objfile *objfile) return NULL; } + object->frame_filters = PyDict_New (); + if (!object->frame_filters) + { + Py_DECREF (object->printers); + Py_DECREF (object); + return NULL; + } + object->type_printers = PyList_New (0); if (!object->type_printers) { + Py_DECREF (object->printers); + Py_DECREF (object->frame_filters); Py_DECREF (object); return NULL; } @@ -270,6 +331,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 c1b1cac..9a26f39 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); self->ob_type->tp_free (self); } @@ -89,9 +92,19 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords) return NULL; } + self->frame_filters = PyDict_New (); + if (!self->frame_filters) + { + Py_DECREF (self->printers); + Py_DECREF (self); + return NULL; + } + self->type_printers = PyList_New (0); if (!self->type_printers) { + Py_DECREF (self->printers); + Py_DECREF (self->frame_filters); Py_DECREF (self); return NULL; } @@ -137,6 +150,44 @@ pspy_set_printers (PyObject *o, PyObject *value, void *ignore) return 0; } +PyObject * +pspy_get_frame_filters (PyObject *o, void *ignore) +{ + pspace_object *self = (pspace_object *) o; + + Py_INCREF (self->frame_filters); + return self->frame_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,13 +272,24 @@ pspace_to_pspace_object (struct program_space *pspace) return NULL; } + object->frame_filters = PyDict_New (); + if (!object->frame_filters) + { + Py_DECREF (object->printers); + Py_DECREF (object); + return NULL; + } + object->type_printers = PyList_New (0); if (!object->type_printers) { + Py_DECREF (object->printers); + Py_DECREF (object->frame_filters); Py_DECREF (object); return NULL; } + set_program_space_data (pspace, pspy_pspace_data_key, object); } } @@ -257,6 +319,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 d5d0e431..f8c2e26 100644 --- a/gdb/python/py-utils.c +++ b/gdb/python/py-utils.c @@ -48,6 +48,33 @@ 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; + + /* Note that we need the extra braces in this 'if' to avoid a + warning from gcc. */ + if (py) + { + 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, (void *) 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 ee04860..0ebb661 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -215,9 +215,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 *); thread_object *create_thread_object (struct thread_info *tp); thread_object *find_thread_object (ptid_t ptid); @@ -264,6 +266,7 @@ void gdbpy_initialize_thread_event (void); void gdbpy_initialize_new_objfile_event (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 5f6df60..e7693bd 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -1374,6 +1374,14 @@ free_type_printers (void *arg) { } +int apply_frame_filter (struct frame_info *frame, int flags, + enum print_values mi_print_args_type, + const char *cli_print_frame_args_type, + struct ui_out *out, int count) +{ + return PY_BT_NO_FILTERS; +} + #endif /* HAVE_PYTHON */ diff --git a/gdb/python/python.h b/gdb/python/python.h index 72872b0..e90f78c 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,28 @@ 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 returns constants. */ +static const int PY_BT_ERROR = 0; +static const int PY_BT_COMPLETED = 1; +static const int PY_BT_NO_FILTERS = 2; + +/* 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, + }; + extern void finish_python_initialization (void); void eval_python_from_control_command (struct command_line *); @@ -41,6 +64,11 @@ int apply_val_pretty_printer (struct type *type, const gdb_byte *valaddr, const struct value_print_options *options, const struct language_defn *language); +int apply_frame_filter (struct frame_info *frame, int flags, + enum print_values mi_print_args_type, + const char *cli_print_args_type, + struct ui_out *out, int count); + void preserve_python_values (struct objfile *objfile, htab_t copied_types); void gdbpy_load_auto_scripts_for_objfile (struct objfile *objfile);