mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-01-18 12:24:38 +08:00
[wip] Fix range end handling of inlined subroutines
Currently there is a problem when debugging optimized code when the inferior stops at an inline sub-range end PC. It is unclear if that location is from the inline function or from the calling function. Therefore the call stack is often wrong. This patch detects the "weak" line table entries which are likely part of the previous inline block, and if we have such a location, it assumes the location belongs to the previous block. Additionally it may happen that the infrun machinery steps from one inline range to another inline range of the same inline function. That can look like jumping back and forth from the calling program to the inline function, while really the inline function just jumps from a hot to a cold section of the code, i.e. error handling. Additionally it may happen that one inline sub-range is empty or the inline is completely empty. But filtering that information away is not the right solution, since although there is no actual code from the inline, it is still possible that variables from an inline function can be inspected here. The issue with the empty ranges is also discussed here: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474 Conceptually this patch uses a heuristic to work around a deficiency in the dwarf-4 and dwarf-5 rnglists structure. There should be a location view number for each inline sub-range begin PC and end PC, similar to the DW_AT_GNU_entry_view which is the location view for the inline entry point.
This commit is contained in:
parent
215ba4f398
commit
cc9be6af06
15
gdb/block.c
15
gdb/block.c
@ -196,7 +196,20 @@ blockvector_for_pc_sect (CORE_ADDR pc, struct obj_section *section,
|
||||
return NULL;
|
||||
|
||||
if (pblock)
|
||||
*pblock = b;
|
||||
{
|
||||
struct symtab_and_line sal = find_pc_sect_line (pc, section, 0);
|
||||
if (sal.line != 0 && sal.pc == pc && sal.is_weak)
|
||||
{
|
||||
const struct block *b2 = find_block_in_blockvector (bl, pc - 1);
|
||||
const struct block *b0 = b;
|
||||
while (b0->superblock () && !b0->function ())
|
||||
b0 = b0->superblock ();
|
||||
if (b0->contains (b2))
|
||||
b = b2;
|
||||
}
|
||||
*pblock = b;
|
||||
}
|
||||
|
||||
return bl;
|
||||
}
|
||||
|
||||
|
@ -18412,21 +18412,9 @@ class lnp_state_machine
|
||||
|
||||
/* Additional bits of state we need to track. */
|
||||
|
||||
/* The last file that we called dwarf2_start_subfile for.
|
||||
This is only used for TLLs. */
|
||||
unsigned int m_last_file = 0;
|
||||
/* The last file a line number was recorded for. */
|
||||
struct subfile *m_last_subfile = NULL;
|
||||
|
||||
/* The address of the last line entry. */
|
||||
unrelocated_addr m_last_address;
|
||||
|
||||
/* Set to true when a previous line at the same address (using
|
||||
m_last_address) had LEF_IS_STMT set in m_flags. This is reset to false
|
||||
when a line entry at a new address (m_address different to
|
||||
m_last_address) is processed. */
|
||||
bool m_stmt_at_address = false;
|
||||
|
||||
/* When true, record the lines we decode. */
|
||||
bool m_currently_recording_lines = true;
|
||||
|
||||
@ -18584,7 +18572,8 @@ dwarf_record_line_1 (struct gdbarch *gdbarch, struct subfile *subfile,
|
||||
|
||||
static void
|
||||
dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
|
||||
unrelocated_addr address, struct dwarf2_cu *cu)
|
||||
unrelocated_addr address, struct dwarf2_cu *cu,
|
||||
bool end_sequence)
|
||||
{
|
||||
if (subfile == NULL)
|
||||
return;
|
||||
@ -18597,7 +18586,8 @@ dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
|
||||
paddress (gdbarch, (CORE_ADDR) address));
|
||||
}
|
||||
|
||||
dwarf_record_line_1 (gdbarch, subfile, 0, address, LEF_IS_STMT, cu);
|
||||
dwarf_record_line_1 (gdbarch, subfile, end_sequence ? 0 : -1, address,
|
||||
LEF_IS_STMT, cu);
|
||||
}
|
||||
|
||||
void
|
||||
@ -18625,38 +18615,17 @@ lnp_state_machine::record_line (bool end_sequence)
|
||||
/* For now we ignore lines not starting on an instruction boundary.
|
||||
But not when processing end_sequence for compatibility with the
|
||||
previous version of the code. */
|
||||
else if (m_op_index == 0 || end_sequence)
|
||||
else if ((m_op_index == 0 && m_line != 0) || end_sequence)
|
||||
{
|
||||
/* When we switch files we insert an end maker in the first file,
|
||||
switch to the second file and add a new line entry. The
|
||||
problem is that the end marker inserted in the first file will
|
||||
discard any previous line entries at the same address. If the
|
||||
line entries in the first file are marked as is-stmt, while
|
||||
the new line in the second file is non-stmt, then this means
|
||||
the end marker will discard is-stmt lines so we can have a
|
||||
non-stmt line. This means that there are less addresses at
|
||||
which the user can insert a breakpoint.
|
||||
|
||||
To improve this we track the last address in m_last_address,
|
||||
and whether we have seen an is-stmt at this address. Then
|
||||
when switching files, if we have seen a stmt at the current
|
||||
address, and we are switching to create a non-stmt line, then
|
||||
discard the new line. */
|
||||
bool file_changed
|
||||
= m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
|
||||
bool ignore_this_line
|
||||
= ((file_changed && !end_sequence && m_last_address == m_address
|
||||
&& ((m_flags & LEF_IS_STMT) == 0)
|
||||
&& m_stmt_at_address)
|
||||
|| (!end_sequence && m_line == 0));
|
||||
|
||||
if ((file_changed && !ignore_this_line) || end_sequence)
|
||||
if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
|
||||
|| end_sequence)
|
||||
{
|
||||
dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
|
||||
m_currently_recording_lines ? m_cu : nullptr);
|
||||
m_currently_recording_lines ? m_cu : nullptr,
|
||||
end_sequence || (m_flags & LEF_IS_STMT) != 0);
|
||||
}
|
||||
|
||||
if (!end_sequence && !ignore_this_line)
|
||||
if (!end_sequence)
|
||||
{
|
||||
linetable_entry_flags lte_flags = m_flags;
|
||||
if (producer_is_codewarrior (m_cu))
|
||||
@ -18676,15 +18645,6 @@ lnp_state_machine::record_line (bool end_sequence)
|
||||
m_last_line = m_line;
|
||||
}
|
||||
}
|
||||
|
||||
/* Track whether we have seen any IS_STMT true at m_address in case we
|
||||
have multiple line table entries all at m_address. */
|
||||
if (m_last_address != m_address)
|
||||
{
|
||||
m_stmt_at_address = false;
|
||||
m_last_address = m_address;
|
||||
}
|
||||
m_stmt_at_address |= (m_flags & LEF_IS_STMT) != 0;
|
||||
}
|
||||
|
||||
lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
|
||||
@ -18698,8 +18658,7 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
|
||||
This is currently used by MIPS code,
|
||||
cf. `mips_adjust_dwarf2_line'. */
|
||||
m_address ((unrelocated_addr) gdbarch_adjust_dwarf2_line (arch, 0, 0)),
|
||||
m_flags (lh->default_is_stmt ? LEF_IS_STMT : (linetable_entry_flags) 0),
|
||||
m_last_address (m_address)
|
||||
m_flags (lh->default_is_stmt ? LEF_IS_STMT : (linetable_entry_flags) 0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -996,7 +996,8 @@ prepare_one_step (thread_info *tp, struct step_command_fsm *sm)
|
||||
if (sym->aclass () == LOC_BLOCK)
|
||||
{
|
||||
const block *block = sym->value_block ();
|
||||
if (block->end () < tp->control.step_range_end)
|
||||
if (block->end () < tp->control.step_range_end
|
||||
&& block->end () > tp->control.step_range_start)
|
||||
tp->control.step_range_end = block->end ();
|
||||
}
|
||||
}
|
||||
|
@ -8200,6 +8200,8 @@ process_event_stop_test (struct execution_control_state *ecs)
|
||||
infrun_debug_printf ("stepping through inlined function");
|
||||
|
||||
if (ecs->event_thread->control.step_over_calls == STEP_OVER_ALL
|
||||
|| ecs->event_thread->stop_pc () != stop_pc_sal.pc
|
||||
|| !stop_pc_sal.is_stmt
|
||||
|| inline_frame_is_marked_for_skip (false, ecs->event_thread))
|
||||
keep_going (ecs);
|
||||
else
|
||||
@ -8248,7 +8250,8 @@ process_event_stop_test (struct execution_control_state *ecs)
|
||||
end_stepping_range (ecs);
|
||||
return;
|
||||
}
|
||||
else if (*curr_frame_id == original_frame_id)
|
||||
else if (get_stack_frame_id (frame)
|
||||
== ecs->event_thread->control.step_stack_frame_id)
|
||||
{
|
||||
/* We are not at the start of a statement, and we have not changed
|
||||
frame.
|
||||
|
17
gdb/symtab.c
17
gdb/symtab.c
@ -3294,7 +3294,10 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
|
||||
0) instead of a real line. */
|
||||
|
||||
if (prev && prev->line
|
||||
&& (!best || prev->unrelocated_pc () > best->unrelocated_pc ()))
|
||||
&& (!best || prev->unrelocated_pc () > best->unrelocated_pc ()
|
||||
|| (prev->unrelocated_pc () == best->unrelocated_pc ()
|
||||
&& (best->pc (objfile) == pc
|
||||
? !best->is_stmt : best->is_weak))))
|
||||
{
|
||||
best = prev;
|
||||
best_symtab = iter_s;
|
||||
@ -3322,7 +3325,7 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
|
||||
&& (tmp - 1)->unrelocated_pc () == tmp->unrelocated_pc ()
|
||||
&& (tmp - 1)->line != 0 && !tmp->is_stmt)
|
||||
--tmp;
|
||||
if (tmp->is_stmt)
|
||||
if (tmp->is_stmt && (tmp->pc (objfile) == pc || !tmp->is_weak))
|
||||
best = tmp;
|
||||
}
|
||||
|
||||
@ -3346,18 +3349,14 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
|
||||
We used to return alt->line - 1 here, but that could be
|
||||
anywhere; if we don't have line number info for this PC,
|
||||
don't make some up. */
|
||||
val.pc = pc;
|
||||
}
|
||||
else if (best->line == 0)
|
||||
{
|
||||
/* If our best fit is in a range of PC's for which no line
|
||||
number info is available (line number is zero) then we didn't
|
||||
find any valid line information. */
|
||||
if (notcurrent)
|
||||
pc++;
|
||||
val.pc = pc;
|
||||
}
|
||||
else
|
||||
{
|
||||
val.is_stmt = best->is_stmt;
|
||||
val.is_weak = best->is_weak;
|
||||
val.symtab = best_symtab;
|
||||
val.line = best->line;
|
||||
val.pc = best->pc (objfile);
|
||||
|
Loading…
Reference in New Issue
Block a user