| /* Inline frame unwinder for GDB. | |
| Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc. | |
| This file is part of GDB. | |
| 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/>. */ | |
| #include "defs.h" | |
| #include "addrmap.h" | |
| #include "block.h" | |
| #include "frame-unwind.h" | |
| #include "inferior.h" | |
| #include "regcache.h" | |
| #include "symtab.h" | |
| #include "vec.h" | |
| #include "gdb_assert.h" | |
| /* We need to save a few variables for every thread stopped at the | |
| virtual call site of an inlined function. If there was always a | |
| "struct thread_info", we could hang it off that; in the mean time, | |
| keep our own list. */ | |
| struct inline_state | |
| { | |
| /* The thread this data relates to. It should be a currently | |
| stopped thread; we assume thread IDs never change while the | |
| thread is stopped. */ | |
| ptid_t ptid; | |
| /* The number of inlined functions we are skipping. Each of these | |
| functions can be stepped in to. */ | |
| int skipped_frames; | |
| /* Only valid if SKIPPED_FRAMES is non-zero. This is the PC used | |
| when calculating SKIPPED_FRAMES; used to check whether we have | |
| moved to a new location by user request. If so, we invalidate | |
| any skipped frames. */ | |
| CORE_ADDR saved_pc; | |
| /* Only valid if SKIPPED_FRAMES is non-zero. This is the symbol | |
| of the outermost skipped inline function. It's used to find the | |
| call site of the current frame. */ | |
| struct symbol *skipped_symbol; | |
| }; | |
| typedef struct inline_state inline_state_s; | |
| DEF_VEC_O(inline_state_s); | |
| static VEC(inline_state_s) *inline_states; | |
| /* Locate saved inlined frame state for PTID, if it exists | |
| and is valid. */ | |
| static struct inline_state * | |
| find_inline_frame_state (ptid_t ptid) | |
| { | |
| struct inline_state *state; | |
| int ix; | |
| for (ix = 0; VEC_iterate (inline_state_s, inline_states, ix, state); ix++) | |
| { | |
| if (ptid_equal (state->ptid, ptid)) | |
| { | |
| struct regcache *regcache = get_thread_regcache (ptid); | |
| CORE_ADDR current_pc = regcache_read_pc (regcache); | |
| if (current_pc != state->saved_pc) | |
| { | |
| /* PC has changed - this context is invalid. Use the | |
| default behavior. */ | |
| VEC_unordered_remove (inline_state_s, inline_states, ix); | |
| return NULL; | |
| } | |
| else | |
| return state; | |
| } | |
| } | |
| return NULL; | |
| } | |
| /* Allocate saved inlined frame state for PTID. */ | |
| static struct inline_state * | |
| allocate_inline_frame_state (ptid_t ptid) | |
| { | |
| struct inline_state *state; | |
| state = VEC_safe_push (inline_state_s, inline_states, NULL); | |
| memset (state, 0, sizeof (*state)); | |
| state->ptid = ptid; | |
| return state; | |
| } | |
| /* Forget about any hidden inlined functions in PTID, which is new or | |
| about to be resumed. PTID may be minus_one_ptid (all processes) | |
| or a PID (all threads in this process). */ | |
| void | |
| clear_inline_frame_state (ptid_t ptid) | |
| { | |
| struct inline_state *state; | |
| int ix; | |
| if (ptid_equal (ptid, minus_one_ptid)) | |
| { | |
| VEC_free (inline_state_s, inline_states); | |
| return; | |
| } | |
| if (ptid_is_pid (ptid)) | |
| { | |
| VEC (inline_state_s) *new_states = NULL; | |
| int pid = ptid_get_pid (ptid); | |
| for (ix = 0; VEC_iterate (inline_state_s, inline_states, ix, state); ix++) | |
| if (pid != ptid_get_pid (state->ptid)) | |
| VEC_safe_push (inline_state_s, new_states, state); | |
| VEC_free (inline_state_s, inline_states); | |
| inline_states = new_states; | |
| return; | |
| } | |
| for (ix = 0; VEC_iterate (inline_state_s, inline_states, ix, state); ix++) | |
| if (ptid_equal (state->ptid, ptid)) | |
| { | |
| VEC_unordered_remove (inline_state_s, inline_states, ix); | |
| return; | |
| } | |
| } | |
| static void | |
| inline_frame_this_id (struct frame_info *this_frame, | |
| void **this_cache, | |
| struct frame_id *this_id) | |
| { | |
| struct symbol *func; | |
| /* In order to have a stable frame ID for a given inline function, | |
| we must get the stack / special addresses from the underlying | |
| real frame's this_id method. So we must call get_prev_frame. | |
| Because we are inlined into some function, there must be previous | |
| frames, so this is safe - as long as we're careful not to | |
| create any cycles. */ | |
| *this_id = get_frame_id (get_prev_frame (this_frame)); | |
| /* We need a valid frame ID, so we need to be based on a valid | |
| frame. FSF submission NOTE: this would be a good assertion to | |
| apply to all frames, all the time. That would fix the ambiguity | |
| of null_frame_id (between "no/any frame" and "the outermost | |
| frame"). This will take work. */ | |
| gdb_assert (frame_id_p (*this_id)); | |
| /* For now, require we don't match outer_frame_id either (see | |
| comment above). */ | |
| gdb_assert (!frame_id_eq (*this_id, outer_frame_id)); | |
| /* Future work NOTE: Alexandre Oliva applied a patch to GCC 4.3 | |
| which generates DW_AT_entry_pc for inlined functions when | |
| possible. If this attribute is available, we should use it | |
| in the frame ID (and eventually, to set breakpoints). */ | |
| func = get_frame_function (this_frame); | |
| gdb_assert (func != NULL); | |
| (*this_id).code_addr = BLOCK_START (SYMBOL_BLOCK_VALUE (func)); | |
| (*this_id).inline_depth++; | |
| } | |
| static struct value * | |
| inline_frame_prev_register (struct frame_info *this_frame, void **this_cache, | |
| int regnum) | |
| { | |
| /* Use get_frame_register_value instead of | |
| frame_unwind_got_register, to avoid requiring this frame's ID. | |
| This frame's ID depends on the previous frame's ID (unusual), and | |
| the previous frame's ID depends on this frame's unwound | |
| registers. If unwinding registers from this frame called | |
| get_frame_id, there would be a loop. | |
| Do not copy this code into any other unwinder! Inlined functions | |
| are special; other unwinders must not have a dependency on the | |
| previous frame's ID, and therefore can and should use | |
| frame_unwind_got_register instead. */ | |
| return get_frame_register_value (this_frame, regnum); | |
| } | |
| /* Check whether we are at an inlining site that does not already | |
| have an associated frame. */ | |
| static int | |
| inline_frame_sniffer (const struct frame_unwind *self, | |
| struct frame_info *this_frame, | |
| void **this_cache) | |
| { | |
| CORE_ADDR this_pc; | |
| struct block *frame_block, *cur_block; | |
| int depth; | |
| struct frame_info *next_frame; | |
| struct inline_state *state = find_inline_frame_state (inferior_ptid); | |
| this_pc = get_frame_address_in_block (this_frame); | |
| frame_block = block_for_pc (this_pc); | |
| if (frame_block == NULL) | |
| return 0; | |
| /* Calculate DEPTH, the number of inlined functions at this | |
| location. */ | |
| depth = 0; | |
| cur_block = frame_block; | |
| while (BLOCK_SUPERBLOCK (cur_block)) | |
| { | |
| if (block_inlined_p (cur_block)) | |
| depth++; | |
| cur_block = BLOCK_SUPERBLOCK (cur_block); | |
| } | |
| /* Check how many inlined functions already have frames. */ | |
| for (next_frame = get_next_frame (this_frame); | |
| next_frame && get_frame_type (next_frame) == INLINE_FRAME; | |
| next_frame = get_next_frame (next_frame)) | |
| { | |
| gdb_assert (depth > 0); | |
| depth--; | |
| } | |
| /* If this is the topmost frame, or all frames above us are inlined, | |
| then check whether we were requested to skip some frames (so they | |
| can be stepped into later). */ | |
| if (state != NULL && state->skipped_frames > 0 && next_frame == NULL) | |
| { | |
| gdb_assert (depth >= state->skipped_frames); | |
| depth -= state->skipped_frames; | |
| } | |
| /* If all the inlined functions here already have frames, then pass | |
| to the normal unwinder for this PC. */ | |
| if (depth == 0) | |
| return 0; | |
| /* If the next frame is an inlined function, but not the outermost, then | |
| we are the next outer. If it is not an inlined function, then we | |
| are the innermost inlined function of a different real frame. */ | |
| return 1; | |
| } | |
| const struct frame_unwind inline_frame_unwinder = { | |
| INLINE_FRAME, | |
| inline_frame_this_id, | |
| inline_frame_prev_register, | |
| NULL, | |
| inline_frame_sniffer | |
| }; | |
| const struct frame_unwind *const inline_frame_unwind = &inline_frame_unwinder; | |
| /* Return non-zero if BLOCK, an inlined function block containing PC, | |
| has a group of contiguous instructions starting at PC (but not | |
| before it). */ | |
| static int | |
| block_starting_point_at (CORE_ADDR pc, struct block *block) | |
| { | |
| struct blockvector *bv; | |
| struct block *new_block; | |
| bv = blockvector_for_pc (pc, NULL); | |
| if (BLOCKVECTOR_MAP (bv) == NULL) | |
| return 0; | |
| new_block = addrmap_find (BLOCKVECTOR_MAP (bv), pc - 1); | |
| if (new_block == NULL) | |
| return 1; | |
| if (new_block == block || contained_in (new_block, block)) | |
| return 0; | |
| /* The immediately preceeding address belongs to a different block, | |
| which is not a child of this one. Treat this as an entrance into | |
| BLOCK. */ | |
| return 1; | |
| } | |
| /* Skip all inlined functions whose call sites are at the current PC. | |
| Frames for the hidden functions will not appear in the backtrace until the | |
| user steps into them. */ | |
| void | |
| skip_inline_frames (ptid_t ptid) | |
| { | |
| CORE_ADDR this_pc; | |
| struct block *frame_block, *cur_block; | |
| struct symbol *last_sym = NULL; | |
| int skip_count = 0; | |
| struct inline_state *state; | |
| /* This function is called right after reinitializing the frame | |
| cache. We try not to do more unwinding than absolutely | |
| necessary, for performance. */ | |
| this_pc = get_frame_pc (get_current_frame ()); | |
| frame_block = block_for_pc (this_pc); | |
| if (frame_block != NULL) | |
| { | |
| cur_block = frame_block; | |
| while (BLOCK_SUPERBLOCK (cur_block)) | |
| { | |
| if (block_inlined_p (cur_block)) | |
| { | |
| /* See comments in inline_frame_this_id about this use | |
| of BLOCK_START. */ | |
| if (BLOCK_START (cur_block) == this_pc | |
| || block_starting_point_at (this_pc, cur_block)) | |
| { | |
| skip_count++; | |
| last_sym = BLOCK_FUNCTION (cur_block); | |
| } | |
| else | |
| break; | |
| } | |
| cur_block = BLOCK_SUPERBLOCK (cur_block); | |
| } | |
| } | |
| gdb_assert (find_inline_frame_state (ptid) == NULL); | |
| state = allocate_inline_frame_state (ptid); | |
| state->skipped_frames = skip_count; | |
| state->saved_pc = this_pc; | |
| state->skipped_symbol = last_sym; | |
| if (skip_count != 0) | |
| reinit_frame_cache (); | |
| } | |
| /* Step into an inlined function by unhiding it. */ | |
| void | |
| step_into_inline_frame (ptid_t ptid) | |
| { | |
| struct inline_state *state = find_inline_frame_state (ptid); | |
| gdb_assert (state != NULL && state->skipped_frames > 0); | |
| state->skipped_frames--; | |
| reinit_frame_cache (); | |
| } | |
| /* Return the number of hidden functions inlined into the current | |
| frame. */ | |
| int | |
| inline_skipped_frames (ptid_t ptid) | |
| { | |
| struct inline_state *state = find_inline_frame_state (ptid); | |
| if (state == NULL) | |
| return 0; | |
| else | |
| return state->skipped_frames; | |
| } | |
| /* If one or more inlined functions are hidden, return the symbol for | |
| the function inlined into the current frame. */ | |
| struct symbol * | |
| inline_skipped_symbol (ptid_t ptid) | |
| { | |
| struct inline_state *state = find_inline_frame_state (ptid); | |
| gdb_assert (state != NULL); | |
| return state->skipped_symbol; | |
| } | |
| /* Return the number of functions inlined into THIS_FRAME. Some of | |
| the callees may not have associated frames (see | |
| skip_inline_frames). */ | |
| int | |
| frame_inlined_callees (struct frame_info *this_frame) | |
| { | |
| struct frame_info *next_frame; | |
| int inline_count = 0; | |
| /* First count how many inlined functions at this PC have frames | |
| above FRAME (are inlined into FRAME). */ | |
| for (next_frame = get_next_frame (this_frame); | |
| next_frame && get_frame_type (next_frame) == INLINE_FRAME; | |
| next_frame = get_next_frame (next_frame)) | |
| inline_count++; | |
| /* Simulate some most-inner inlined frames which were suppressed, so | |
| they can be stepped into later. If we are unwinding already | |
| outer frames from some non-inlined frame this does not apply. */ | |
| if (next_frame == NULL) | |
| inline_count += inline_skipped_frames (inferior_ptid); | |
| return inline_count; | |
| } |