[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:
Bernd Edlinger 2024-09-02 17:00:12 +02:00 committed by Andrew Burgess
parent 215ba4f398
commit cc9be6af06
5 changed files with 39 additions and 64 deletions

View File

@ -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;
}

View File

@ -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)
{
}

View File

@ -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 ();
}
}

View File

@ -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.

View File

@ -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);