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