* 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 <pmuldoon@redhat.com> * 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 <pmuldoon@redhat.com> * 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.
230 lines
7.8 KiB
Python
230 lines
7.8 KiB
Python
# 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 <http://www.gnu.org/licenses/>.
|
|
|
|
"""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
|