Rong Xu | a7bb6f3 | 2014-11-03 13:21:25 -0800 | [diff] [blame] | 1 | # Frame-filter commands. |
| 2 | # Copyright (C) 2013-2014 Free Software Foundation, Inc. |
| 3 | |
| 4 | # This program is free software; you can redistribute it and/or modify |
| 5 | # it under the terms of the GNU General Public License as published by |
| 6 | # the Free Software Foundation; either version 3 of the License, or |
| 7 | # (at your option) any later version. |
| 8 | # |
| 9 | # This program is distributed in the hope that it will be useful, |
| 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | # GNU General Public License for more details. |
| 13 | # |
| 14 | # You should have received a copy of the GNU General Public License |
| 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 16 | |
| 17 | """Internal functions for working with frame-filters.""" |
| 18 | |
| 19 | import gdb |
| 20 | from gdb.FrameIterator import FrameIterator |
| 21 | from gdb.FrameDecorator import FrameDecorator |
| 22 | import itertools |
| 23 | import collections |
| 24 | |
| 25 | def get_priority(filter_item): |
| 26 | """ Internal worker function to return the frame-filter's priority |
| 27 | from a frame filter object. This is a fail free function as it is |
| 28 | used in sorting and filtering. If a badly implemented frame |
| 29 | filter does not implement the priority attribute, return zero |
| 30 | (otherwise sorting/filtering will fail and prevent other frame |
| 31 | filters from executing). |
| 32 | |
| 33 | Arguments: |
| 34 | filter_item: An object conforming to the frame filter |
| 35 | interface. |
| 36 | |
| 37 | Returns: |
| 38 | The priority of the frame filter from the "priority" |
| 39 | attribute, or zero. |
| 40 | """ |
| 41 | # Do not fail here, as the sort will fail. If a filter has not |
| 42 | # (incorrectly) set a priority, set it to zero. |
| 43 | return getattr(filter_item, "priority", 0) |
| 44 | |
| 45 | def set_priority(filter_item, priority): |
| 46 | """ Internal worker function to set the frame-filter's priority. |
| 47 | |
| 48 | Arguments: |
| 49 | filter_item: An object conforming to the frame filter |
| 50 | interface. |
| 51 | priority: The priority to assign as an integer. |
| 52 | """ |
| 53 | |
| 54 | filter_item.priority = priority |
| 55 | |
| 56 | def get_enabled(filter_item): |
| 57 | """ Internal worker function to return a filter's enabled state |
| 58 | from a frame filter object. This is a fail free function as it is |
| 59 | used in sorting and filtering. If a badly implemented frame |
| 60 | filter does not implement the enabled attribute, return False |
| 61 | (otherwise sorting/filtering will fail and prevent other frame |
| 62 | filters from executing). |
| 63 | |
| 64 | Arguments: |
| 65 | filter_item: An object conforming to the frame filter |
| 66 | interface. |
| 67 | |
| 68 | Returns: |
| 69 | The enabled state of the frame filter from the "enabled" |
| 70 | attribute, or False. |
| 71 | """ |
| 72 | |
| 73 | # If the filter class is badly implemented when called from the |
| 74 | # Python filter command, do not cease filter operations, just set |
| 75 | # enabled to False. |
| 76 | return getattr(filter_item, "enabled", False) |
| 77 | |
| 78 | def set_enabled(filter_item, state): |
| 79 | """ Internal Worker function to set the frame-filter's enabled |
| 80 | state. |
| 81 | |
| 82 | Arguments: |
| 83 | filter_item: An object conforming to the frame filter |
| 84 | interface. |
| 85 | state: True or False, depending on desired state. |
| 86 | """ |
| 87 | |
| 88 | filter_item.enabled = state |
| 89 | |
| 90 | def return_list(name): |
| 91 | """ Internal Worker function to return the frame filter |
| 92 | dictionary, depending on the name supplied as an argument. If the |
| 93 | name is not "all", "global" or "progspace", it is assumed to name |
| 94 | an object-file. |
| 95 | |
| 96 | Arguments: |
| 97 | name: The name of the list, as specified by GDB user commands. |
| 98 | |
| 99 | Returns: |
| 100 | A dictionary object for a single specified dictionary, or a |
| 101 | list containing all the items for "all" |
| 102 | |
| 103 | Raises: |
| 104 | gdb.GdbError: A dictionary of that name cannot be found. |
| 105 | """ |
| 106 | |
| 107 | # If all dictionaries are wanted in the case of "all" we |
| 108 | # cannot return a combined dictionary as keys() may clash in |
| 109 | # between different dictionaries. As we just want all the frame |
| 110 | # filters to enable/disable them all, just return the combined |
| 111 | # items() as a chained iterator of dictionary values. |
| 112 | if name == "all": |
| 113 | glob = gdb.frame_filters.values() |
| 114 | prog = gdb.current_progspace().frame_filters.values() |
| 115 | return_iter = itertools.chain(glob, prog) |
| 116 | for objfile in gdb.objfiles(): |
| 117 | return_iter = itertools.chain(return_iter, objfile.frame_filters.values()) |
| 118 | |
| 119 | return return_iter |
| 120 | |
| 121 | if name == "global": |
| 122 | return gdb.frame_filters |
| 123 | else: |
| 124 | if name == "progspace": |
| 125 | cp = gdb.current_progspace() |
| 126 | return cp.frame_filters |
| 127 | else: |
| 128 | for objfile in gdb.objfiles(): |
| 129 | if name == objfile.filename: |
| 130 | return objfile.frame_filters |
| 131 | |
| 132 | msg = "Cannot find frame-filter dictionary for '" + name + "'" |
| 133 | raise gdb.GdbError(msg) |
| 134 | |
| 135 | def _sort_list(): |
| 136 | """ Internal Worker function to merge all known frame-filter |
| 137 | lists, prune any filters with the state set to "disabled", and |
| 138 | sort the list on the frame-filter's "priority" attribute. |
| 139 | |
| 140 | Returns: |
| 141 | sorted_list: A sorted, pruned list of frame filters to |
| 142 | execute. |
| 143 | """ |
| 144 | |
| 145 | all_filters = return_list("all") |
| 146 | sorted_frame_filters = sorted(all_filters, key = get_priority, |
| 147 | reverse = True) |
| 148 | |
| 149 | sorted_frame_filters = filter(get_enabled, |
| 150 | sorted_frame_filters) |
| 151 | |
| 152 | return sorted_frame_filters |
| 153 | |
| 154 | def execute_frame_filters(frame, frame_low, frame_high): |
| 155 | """ Internal function called from GDB that will execute the chain |
| 156 | of frame filters. Each filter is executed in priority order. |
| 157 | After the execution completes, slice the iterator to frame_low - |
| 158 | frame_high range. |
| 159 | |
| 160 | Arguments: |
| 161 | frame: The initial frame. |
| 162 | |
| 163 | frame_low: The low range of the slice. If this is a negative |
| 164 | integer then it indicates a backward slice (ie bt -4) which |
| 165 | counts backward from the last frame in the backtrace. |
| 166 | |
| 167 | frame_high: The high range of the slice. If this is -1 then |
| 168 | it indicates all frames until the end of the stack from |
| 169 | frame_low. |
| 170 | |
| 171 | Returns: |
| 172 | frame_iterator: The sliced iterator after all frame |
| 173 | filters have had a change to execute, or None if no frame |
| 174 | filters are registered. |
| 175 | """ |
| 176 | |
| 177 | # Get a sorted list of frame filters. |
| 178 | sorted_list = list(_sort_list()) |
| 179 | |
| 180 | # Check to see if there are any frame-filters. If not, just |
| 181 | # return None and let default backtrace printing occur. |
| 182 | if len(sorted_list) == 0: |
| 183 | return None |
| 184 | |
| 185 | frame_iterator = FrameIterator(frame) |
| 186 | |
| 187 | # Apply a basic frame decorator to all gdb.Frames. This unifies |
| 188 | # the interface. Python 3.x moved the itertools.imap |
| 189 | # functionality to map(), so check if it is available. |
| 190 | if hasattr(itertools,"imap"): |
| 191 | frame_iterator = itertools.imap(FrameDecorator, frame_iterator) |
| 192 | else: |
| 193 | frame_iterator = map(FrameDecorator, frame_iterator) |
| 194 | |
| 195 | for ff in sorted_list: |
| 196 | frame_iterator = ff.filter(frame_iterator) |
| 197 | |
| 198 | # Slicing |
| 199 | |
| 200 | # Is this a slice from the end of the backtrace, ie bt -2? |
| 201 | if frame_low < 0: |
| 202 | count = 0 |
| 203 | slice_length = abs(frame_low) |
| 204 | # We cannot use MAXLEN argument for deque as it is 2.6 onwards |
| 205 | # and some GDB versions might be < 2.6. |
| 206 | sliced = collections.deque() |
| 207 | |
| 208 | for frame_item in frame_iterator: |
| 209 | if count >= slice_length: |
| 210 | sliced.popleft(); |
| 211 | count = count + 1 |
| 212 | sliced.append(frame_item) |
| 213 | |
| 214 | return iter(sliced) |
| 215 | |
| 216 | # -1 for frame_high means until the end of the backtrace. Set to |
| 217 | # None if that is the case, to indicate to itertools.islice to |
| 218 | # slice to the end of the iterator. |
| 219 | if frame_high == -1: |
| 220 | frame_high = None |
| 221 | else: |
| 222 | # As frames start from 0, add one to frame_high so islice |
| 223 | # correctly finds the end |
| 224 | frame_high = frame_high + 1; |
| 225 | |
| 226 | sliced = itertools.islice(frame_iterator, frame_low, frame_high) |
| 227 | |
| 228 | return sliced |