]>
Commit | Line | Data |
---|---|---|
edb3359d DJ |
1 | /* Inline frame unwinder for GDB. |
2 | ||
1d506c26 | 3 | Copyright (C) 2008-2024 Free Software Foundation, Inc. |
edb3359d DJ |
4 | |
5 | This file is part of GDB. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 3 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
19 | ||
ddfe970e | 20 | #include "breakpoint.h" |
e451c4a1 | 21 | #include "inline-frame.h" |
edb3359d DJ |
22 | #include "addrmap.h" |
23 | #include "block.h" | |
24 | #include "frame-unwind.h" | |
25 | #include "inferior.h" | |
00431a78 | 26 | #include "gdbthread.h" |
4e485129 | 27 | #include "regcache.h" |
edb3359d | 28 | #include "symtab.h" |
51d48146 | 29 | #include "frame.h" |
b24531ed | 30 | #include <algorithm> |
edb3359d | 31 | |
edb3359d DJ |
32 | /* We need to save a few variables for every thread stopped at the |
33 | virtual call site of an inlined function. If there was always a | |
34 | "struct thread_info", we could hang it off that; in the mean time, | |
35 | keep our own list. */ | |
36 | struct inline_state | |
37 | { | |
00431a78 | 38 | inline_state (thread_info *thread_, int skipped_frames_, CORE_ADDR saved_pc_, |
7ffa82e1 | 39 | std::vector<symbol *> &&skipped_symbols_) |
00431a78 | 40 | : thread (thread_), skipped_frames (skipped_frames_), saved_pc (saved_pc_), |
7ffa82e1 | 41 | skipped_symbols (std::move (skipped_symbols_)) |
b24531ed SM |
42 | {} |
43 | ||
edb3359d | 44 | /* The thread this data relates to. It should be a currently |
00431a78 PA |
45 | stopped thread. */ |
46 | thread_info *thread; | |
edb3359d DJ |
47 | |
48 | /* The number of inlined functions we are skipping. Each of these | |
49 | functions can be stepped in to. */ | |
50 | int skipped_frames; | |
51 | ||
52 | /* Only valid if SKIPPED_FRAMES is non-zero. This is the PC used | |
53 | when calculating SKIPPED_FRAMES; used to check whether we have | |
54 | moved to a new location by user request. If so, we invalidate | |
55 | any skipped frames. */ | |
56 | CORE_ADDR saved_pc; | |
57 | ||
7ffa82e1 AB |
58 | /* Only valid if SKIPPED_FRAMES is non-zero. This is the list of all |
59 | function symbols that have been skipped, from inner most to outer | |
60 | most. It is used to find the call site of the current frame. */ | |
61 | std::vector<struct symbol *> skipped_symbols; | |
edb3359d DJ |
62 | }; |
63 | ||
b24531ed | 64 | static std::vector<inline_state> inline_states; |
edb3359d | 65 | |
00431a78 PA |
66 | /* Locate saved inlined frame state for THREAD, if it exists and is |
67 | valid. */ | |
edb3359d DJ |
68 | |
69 | static struct inline_state * | |
00431a78 | 70 | find_inline_frame_state (thread_info *thread) |
edb3359d | 71 | { |
b24531ed | 72 | auto state_it = std::find_if (inline_states.begin (), inline_states.end (), |
00431a78 | 73 | [thread] (const inline_state &state) |
b24531ed | 74 | { |
00431a78 | 75 | return state.thread == thread; |
b24531ed | 76 | }); |
edb3359d | 77 | |
b24531ed SM |
78 | if (state_it == inline_states.end ()) |
79 | return nullptr; | |
edb3359d | 80 | |
b24531ed | 81 | inline_state &state = *state_it; |
00431a78 | 82 | struct regcache *regcache = get_thread_regcache (thread); |
b24531ed | 83 | CORE_ADDR current_pc = regcache_read_pc (regcache); |
edb3359d | 84 | |
b24531ed SM |
85 | if (current_pc != state.saved_pc) |
86 | { | |
87 | /* PC has changed - this context is invalid. Use the | |
88 | default behavior. */ | |
edb3359d | 89 | |
b24531ed SM |
90 | unordered_remove (inline_states, state_it); |
91 | return nullptr; | |
92 | } | |
edb3359d | 93 | |
b24531ed | 94 | return &state; |
edb3359d DJ |
95 | } |
96 | ||
5b6d1e4f | 97 | /* See inline-frame.h. */ |
edb3359d DJ |
98 | |
99 | void | |
5b6d1e4f | 100 | clear_inline_frame_state (process_stratum_target *target, ptid_t filter_ptid) |
edb3359d | 101 | { |
5b6d1e4f | 102 | gdb_assert (target != NULL); |
edb3359d | 103 | |
5b6d1e4f | 104 | if (filter_ptid == minus_one_ptid || filter_ptid.is_pid ()) |
edb3359d | 105 | { |
5b6d1e4f PA |
106 | auto matcher = [target, &filter_ptid] (const inline_state &state) |
107 | { | |
108 | thread_info *t = state.thread; | |
109 | return (t->inf->process_target () == target | |
110 | && t->ptid.matches (filter_ptid)); | |
111 | }; | |
112 | ||
b24531ed | 113 | auto it = std::remove_if (inline_states.begin (), inline_states.end (), |
5b6d1e4f | 114 | matcher); |
b24531ed SM |
115 | |
116 | inline_states.erase (it, inline_states.end ()); | |
117 | ||
edb3359d DJ |
118 | return; |
119 | } | |
120 | ||
5b6d1e4f PA |
121 | |
122 | auto matcher = [target, &filter_ptid] (const inline_state &state) | |
123 | { | |
124 | thread_info *t = state.thread; | |
125 | return (t->inf->process_target () == target | |
126 | && filter_ptid == t->ptid); | |
127 | }; | |
128 | ||
129 | auto it = std::find_if (inline_states.begin (), inline_states.end (), | |
130 | matcher); | |
131 | ||
132 | if (it != inline_states.end ()) | |
133 | unordered_remove (inline_states, it); | |
134 | } | |
135 | ||
136 | /* See inline-frame.h. */ | |
137 | ||
138 | void | |
139 | clear_inline_frame_state (thread_info *thread) | |
140 | { | |
b24531ed | 141 | auto it = std::find_if (inline_states.begin (), inline_states.end (), |
5b6d1e4f | 142 | [thread] (const inline_state &state) |
b24531ed | 143 | { |
5b6d1e4f | 144 | return thread == state.thread; |
b24531ed SM |
145 | }); |
146 | ||
147 | if (it != inline_states.end ()) | |
148 | unordered_remove (inline_states, it); | |
edb3359d DJ |
149 | } |
150 | ||
151 | static void | |
8480a37e | 152 | inline_frame_this_id (const frame_info_ptr &this_frame, |
edb3359d DJ |
153 | void **this_cache, |
154 | struct frame_id *this_id) | |
155 | { | |
156 | struct symbol *func; | |
157 | ||
158 | /* In order to have a stable frame ID for a given inline function, | |
159 | we must get the stack / special addresses from the underlying | |
51d48146 PA |
160 | real frame's this_id method. So we must call |
161 | get_prev_frame_always. Because we are inlined into some | |
162 | function, there must be previous frames, so this is safe - as | |
2b3cb400 PA |
163 | long as we're careful not to create any cycles. See related |
164 | comments in get_prev_frame_always_1. */ | |
bd2b40ac | 165 | frame_info_ptr prev_frame = get_prev_frame_always (this_frame); |
275ee935 AB |
166 | if (prev_frame == nullptr) |
167 | error (_("failed to find previous frame when computing inline frame id")); | |
168 | *this_id = get_frame_id (prev_frame); | |
edb3359d DJ |
169 | |
170 | /* We need a valid frame ID, so we need to be based on a valid | |
171 | frame. FSF submission NOTE: this would be a good assertion to | |
172 | apply to all frames, all the time. That would fix the ambiguity | |
173 | of null_frame_id (between "no/any frame" and "the outermost | |
174 | frame"). This will take work. */ | |
175 | gdb_assert (frame_id_p (*this_id)); | |
176 | ||
177 | /* Future work NOTE: Alexandre Oliva applied a patch to GCC 4.3 | |
178 | which generates DW_AT_entry_pc for inlined functions when | |
179 | possible. If this attribute is available, we should use it | |
180 | in the frame ID (and eventually, to set breakpoints). */ | |
181 | func = get_frame_function (this_frame); | |
182 | gdb_assert (func != NULL); | |
6395b628 | 183 | (*this_id).code_addr = func->value_block ()->entry_pc (); |
193facb3 | 184 | (*this_id).artificial_depth++; |
edb3359d DJ |
185 | } |
186 | ||
187 | static struct value * | |
8480a37e | 188 | inline_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache, |
edb3359d DJ |
189 | int regnum) |
190 | { | |
191 | /* Use get_frame_register_value instead of | |
192 | frame_unwind_got_register, to avoid requiring this frame's ID. | |
193 | This frame's ID depends on the previous frame's ID (unusual), and | |
194 | the previous frame's ID depends on this frame's unwound | |
195 | registers. If unwinding registers from this frame called | |
196 | get_frame_id, there would be a loop. | |
197 | ||
198 | Do not copy this code into any other unwinder! Inlined functions | |
199 | are special; other unwinders must not have a dependency on the | |
200 | previous frame's ID, and therefore can and should use | |
201 | frame_unwind_got_register instead. */ | |
202 | return get_frame_register_value (this_frame, regnum); | |
203 | } | |
204 | ||
205 | /* Check whether we are at an inlining site that does not already | |
206 | have an associated frame. */ | |
207 | ||
208 | static int | |
209 | inline_frame_sniffer (const struct frame_unwind *self, | |
8480a37e | 210 | const frame_info_ptr &this_frame, |
edb3359d DJ |
211 | void **this_cache) |
212 | { | |
213 | CORE_ADDR this_pc; | |
3977b71f | 214 | const struct block *frame_block, *cur_block; |
edb3359d | 215 | int depth; |
bd2b40ac | 216 | frame_info_ptr next_frame; |
00431a78 | 217 | struct inline_state *state = find_inline_frame_state (inferior_thread ()); |
edb3359d DJ |
218 | |
219 | this_pc = get_frame_address_in_block (this_frame); | |
220 | frame_block = block_for_pc (this_pc); | |
221 | if (frame_block == NULL) | |
222 | return 0; | |
223 | ||
224 | /* Calculate DEPTH, the number of inlined functions at this | |
225 | location. */ | |
226 | depth = 0; | |
227 | cur_block = frame_block; | |
f135fe72 | 228 | while (cur_block->superblock ()) |
edb3359d | 229 | { |
a4dfe747 | 230 | if (cur_block->inlined_p ()) |
edb3359d | 231 | depth++; |
6c00f721 | 232 | else if (cur_block->function () != NULL) |
0fa7fe50 | 233 | break; |
edb3359d | 234 | |
f135fe72 | 235 | cur_block = cur_block->superblock (); |
edb3359d DJ |
236 | } |
237 | ||
238 | /* Check how many inlined functions already have frames. */ | |
239 | for (next_frame = get_next_frame (this_frame); | |
240 | next_frame && get_frame_type (next_frame) == INLINE_FRAME; | |
241 | next_frame = get_next_frame (next_frame)) | |
242 | { | |
243 | gdb_assert (depth > 0); | |
244 | depth--; | |
245 | } | |
246 | ||
247 | /* If this is the topmost frame, or all frames above us are inlined, | |
248 | then check whether we were requested to skip some frames (so they | |
249 | can be stepped into later). */ | |
250 | if (state != NULL && state->skipped_frames > 0 && next_frame == NULL) | |
251 | { | |
4e485129 DJ |
252 | gdb_assert (depth >= state->skipped_frames); |
253 | depth -= state->skipped_frames; | |
edb3359d DJ |
254 | } |
255 | ||
256 | /* If all the inlined functions here already have frames, then pass | |
257 | to the normal unwinder for this PC. */ | |
258 | if (depth == 0) | |
259 | return 0; | |
260 | ||
261 | /* If the next frame is an inlined function, but not the outermost, then | |
262 | we are the next outer. If it is not an inlined function, then we | |
263 | are the innermost inlined function of a different real frame. */ | |
264 | return 1; | |
265 | } | |
266 | ||
39d7b0e2 | 267 | const struct frame_unwind inline_frame_unwind = { |
a154d838 | 268 | "inline", |
edb3359d | 269 | INLINE_FRAME, |
8fbca658 | 270 | default_frame_unwind_stop_reason, |
edb3359d DJ |
271 | inline_frame_this_id, |
272 | inline_frame_prev_register, | |
273 | NULL, | |
274 | inline_frame_sniffer | |
275 | }; | |
276 | ||
edb3359d DJ |
277 | /* Return non-zero if BLOCK, an inlined function block containing PC, |
278 | has a group of contiguous instructions starting at PC (but not | |
279 | before it). */ | |
280 | ||
281 | static int | |
3977b71f | 282 | block_starting_point_at (CORE_ADDR pc, const struct block *block) |
edb3359d | 283 | { |
346d1dfe | 284 | const struct blockvector *bv; |
582942f4 | 285 | const struct block *new_block; |
edb3359d DJ |
286 | |
287 | bv = blockvector_for_pc (pc, NULL); | |
414705d1 | 288 | if (bv->map () == nullptr) |
edb3359d DJ |
289 | return 0; |
290 | ||
769520b7 | 291 | new_block = (const struct block *) bv->map ()->find (pc - 1); |
edb3359d DJ |
292 | if (new_block == NULL) |
293 | return 1; | |
294 | ||
0d191295 | 295 | if (new_block == block || block->contains (new_block)) |
edb3359d DJ |
296 | return 0; |
297 | ||
177b42fe | 298 | /* The immediately preceding address belongs to a different block, |
edb3359d DJ |
299 | which is not a child of this one. Treat this as an entrance into |
300 | BLOCK. */ | |
301 | return 1; | |
302 | } | |
303 | ||
ddfe970e | 304 | /* Loop over the stop chain and determine if execution stopped in an |
b2b38aa4 PA |
305 | inlined frame because of a breakpoint with a user-specified location |
306 | set at FRAME_BLOCK. */ | |
ddfe970e KS |
307 | |
308 | static bool | |
313f3b21 | 309 | stopped_by_user_bp_inline_frame (const block *frame_block, bpstat *stop_chain) |
ddfe970e | 310 | { |
234f075c | 311 | for (bpstat *s = stop_chain; s != nullptr; s = s->next) |
ddfe970e KS |
312 | { |
313 | struct breakpoint *bpt = s->breakpoint_at; | |
314 | ||
b2b38aa4 PA |
315 | if (bpt != NULL |
316 | && (user_breakpoint_p (bpt) || bpt->type == bp_until)) | |
ddfe970e | 317 | { |
b6433ede | 318 | bp_location *loc = s->bp_location_at.get (); |
ddfe970e KS |
319 | enum bp_loc_type t = loc->loc_type; |
320 | ||
991ff292 PA |
321 | if (t == bp_loc_software_breakpoint |
322 | || t == bp_loc_hardware_breakpoint) | |
323 | { | |
324 | /* If the location has a function symbol, check whether | |
325 | the frame was for that inlined function. If it has | |
326 | no function symbol, then assume it is. I.e., default | |
327 | to presenting the stop at the innermost inline | |
328 | function. */ | |
329 | if (loc->symbol == nullptr | |
4aeddc50 | 330 | || frame_block == loc->symbol->value_block ()) |
991ff292 PA |
331 | return true; |
332 | } | |
ddfe970e KS |
333 | } |
334 | } | |
335 | ||
336 | return false; | |
337 | } | |
338 | ||
339 | /* See inline-frame.h. */ | |
edb3359d DJ |
340 | |
341 | void | |
313f3b21 | 342 | skip_inline_frames (thread_info *thread, bpstat *stop_chain) |
edb3359d | 343 | { |
3977b71f | 344 | const struct block *frame_block, *cur_block; |
7ffa82e1 | 345 | std::vector<struct symbol *> skipped_syms; |
edb3359d | 346 | int skip_count = 0; |
edb3359d DJ |
347 | |
348 | /* This function is called right after reinitializing the frame | |
349 | cache. We try not to do more unwinding than absolutely | |
350 | necessary, for performance. */ | |
b24531ed | 351 | CORE_ADDR this_pc = get_frame_pc (get_current_frame ()); |
edb3359d DJ |
352 | frame_block = block_for_pc (this_pc); |
353 | ||
354 | if (frame_block != NULL) | |
355 | { | |
356 | cur_block = frame_block; | |
f135fe72 | 357 | while (cur_block->superblock ()) |
edb3359d | 358 | { |
a4dfe747 | 359 | if (cur_block->inlined_p ()) |
edb3359d DJ |
360 | { |
361 | /* See comments in inline_frame_this_id about this use | |
2b1ffcfd | 362 | of BLOCK_ENTRY_PC. */ |
6395b628 | 363 | if (cur_block->entry_pc () == this_pc |
edb3359d DJ |
364 | || block_starting_point_at (this_pc, cur_block)) |
365 | { | |
ddfe970e KS |
366 | /* Do not skip the inlined frame if execution |
367 | stopped in an inlined frame because of a user | |
61b04dd0 PA |
368 | breakpoint for this inline function. */ |
369 | if (stopped_by_user_bp_inline_frame (cur_block, stop_chain)) | |
370 | break; | |
371 | ||
372 | skip_count++; | |
6c00f721 | 373 | skipped_syms.push_back (cur_block->function ()); |
edb3359d DJ |
374 | } |
375 | else | |
376 | break; | |
377 | } | |
6c00f721 | 378 | else if (cur_block->function () != NULL) |
0fa7fe50 JB |
379 | break; |
380 | ||
f135fe72 | 381 | cur_block = cur_block->superblock (); |
edb3359d DJ |
382 | } |
383 | } | |
384 | ||
00431a78 | 385 | gdb_assert (find_inline_frame_state (thread) == NULL); |
7ffa82e1 AB |
386 | inline_states.emplace_back (thread, skip_count, this_pc, |
387 | std::move (skipped_syms)); | |
edb3359d DJ |
388 | |
389 | if (skip_count != 0) | |
390 | reinit_frame_cache (); | |
391 | } | |
392 | ||
393 | /* Step into an inlined function by unhiding it. */ | |
394 | ||
395 | void | |
00431a78 | 396 | step_into_inline_frame (thread_info *thread) |
edb3359d | 397 | { |
00431a78 | 398 | inline_state *state = find_inline_frame_state (thread); |
edb3359d DJ |
399 | |
400 | gdb_assert (state != NULL && state->skipped_frames > 0); | |
401 | state->skipped_frames--; | |
402 | reinit_frame_cache (); | |
403 | } | |
404 | ||
405 | /* Return the number of hidden functions inlined into the current | |
406 | frame. */ | |
407 | ||
408 | int | |
00431a78 | 409 | inline_skipped_frames (thread_info *thread) |
edb3359d | 410 | { |
00431a78 | 411 | inline_state *state = find_inline_frame_state (thread); |
edb3359d DJ |
412 | |
413 | if (state == NULL) | |
414 | return 0; | |
415 | else | |
416 | return state->skipped_frames; | |
417 | } | |
418 | ||
419 | /* If one or more inlined functions are hidden, return the symbol for | |
420 | the function inlined into the current frame. */ | |
421 | ||
422 | struct symbol * | |
00431a78 | 423 | inline_skipped_symbol (thread_info *thread) |
edb3359d | 424 | { |
00431a78 | 425 | inline_state *state = find_inline_frame_state (thread); |
edb3359d | 426 | gdb_assert (state != NULL); |
7ffa82e1 AB |
427 | |
428 | /* This should only be called when we are skipping at least one frame, | |
429 | hence SKIPPED_FRAMES will be greater than zero when we get here. | |
430 | We initialise SKIPPED_FRAMES at the same time as we build | |
431 | SKIPPED_SYMBOLS, hence it should be true that SKIPPED_FRAMES never | |
432 | indexes outside of the SKIPPED_SYMBOLS vector. */ | |
433 | gdb_assert (state->skipped_frames > 0); | |
434 | gdb_assert (state->skipped_frames <= state->skipped_symbols.size ()); | |
435 | return state->skipped_symbols[state->skipped_frames - 1]; | |
edb3359d DJ |
436 | } |
437 | ||
438 | /* Return the number of functions inlined into THIS_FRAME. Some of | |
439 | the callees may not have associated frames (see | |
440 | skip_inline_frames). */ | |
441 | ||
442 | int | |
8480a37e | 443 | frame_inlined_callees (const frame_info_ptr &this_frame) |
edb3359d | 444 | { |
bd2b40ac | 445 | frame_info_ptr next_frame; |
edb3359d DJ |
446 | int inline_count = 0; |
447 | ||
448 | /* First count how many inlined functions at this PC have frames | |
449 | above FRAME (are inlined into FRAME). */ | |
450 | for (next_frame = get_next_frame (this_frame); | |
451 | next_frame && get_frame_type (next_frame) == INLINE_FRAME; | |
452 | next_frame = get_next_frame (next_frame)) | |
453 | inline_count++; | |
454 | ||
455 | /* Simulate some most-inner inlined frames which were suppressed, so | |
456 | they can be stepped into later. If we are unwinding already | |
457 | outer frames from some non-inlined frame this does not apply. */ | |
458 | if (next_frame == NULL) | |
00431a78 | 459 | inline_count += inline_skipped_frames (inferior_thread ()); |
edb3359d DJ |
460 | |
461 | return inline_count; | |
462 | } |