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 229