Re: [PATCH] Python API: Add gdb.is_in_prologue and gdb.is_in_epilogue.

Pedro Alves
2014-10-24T14:57:08Z

On 10/23/2014 06:36 PM, Martin Galvan wrote:
>> > Some targets have code at address 0. Seems like we may be exposing a
>> > bad interface for these targets here?
> I used 0 because in_prologue expects it to be non-zero. If it's 0 and
> we have no debugging info, it'll always return true:
>
> /* We don't even have minsym information, so fall back to using
> func_start, if given. */
> if (! func_start)
> return 1; /* We *might* be in a prologue. */

Design mistakes in the internal APIs shouldn't be exposed to a public
API. I'd even suggest that whatever Python API we end up with, it'd
be good to make the internal API match it.

>
> Again, I did it because of the way in_prologue works, but as Eli said
> this would probably be better handled with a Python exception or a
> message of some kind.

Not sure an exception makes sense given the function's
interface. Say in the future another optional parameter is added.
What would you do then? What of old code that passed in func_start
but not that new argument? Those might not expect an exception.
So for the case of the new argument not being specified, we'd
have to return 1, which is right -- the PC _might_ be pointing
at a prologue.

But, how exactly were you planning using the gdb.is_in_prologue
function? GDB itself doesn't use this to determine whether locals
are valid, only gdbarch_in_function_epilogue_p/gdb.is_in_epilogue.

I think we can just remove the in_prologue function entirely.
Looking at the code in GDB that makes use of this, all we find is this
one caller:

if ((ecs->event_thread->control.step_over_calls == STEP_OVER_NONE)
|| ((ecs->event_thread->control.step_range_end == 1)
&& in_prologue (gdbarch, ecs->event_thread->prev_pc,
ecs->stop_func_start)))
{
/* I presume that step_over_calls is only 0 when we're
supposed to be stepping at the assembly language level
("stepi"). Just stop. */
/* Also, maybe we just did a "nexti" inside a prolog, so we
thought it was a subroutine call but it was not. Stop as
well. FENN */
/* And this works the same backward as frontward. MVS */
end_stepping_range (ecs);
return;
}

So this is only used for "nexti", and it's itself a dubious use.
It looks like old code papering over unwinder problems.

Doing some archaeology, I found the revision that code
was added:

commit 100a02e1deec2f037a15cdf232f026dc79763bf8
Author: Andrew Cagney <cagney@redhat.com>
AuthorDate: Thu Jun 28 21:48:41 2001 +0000
Commit: Andrew Cagney <cagney@redhat.com>
CommitDate: Thu Jun 28 21:48:41 2001 +0000

From Fernando Nasser:
* infrun.c (handle_inferior_event): Handle "nexti" inside function
prologues.

And the mailing list thread:

https://sourceware.org/ml/gdb-patches/2001-01/msg00047.html

Not much discussion there, and no test...

Looking at the code around what was patched in that revision, we see
that the checks that detect whether the program has just stepped into a
subroutine didn't rely on the unwinders _at all_ back then!

From 'git show 100a02e1:gdb/infrun.c':

if (stop_pc == ecs->stop_func_start /* Quick test */
|| (in_prologue (stop_pc, ecs->stop_func_start) &&
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
!IN_SOLIB_RETURN_TRAMPOLINE (stop_pc, ecs->stop_func_name))
|| IN_SOLIB_CALL_TRAMPOLINE (stop_pc, ecs->stop_func_name)
|| ecs->stop_func_name == 0)
{
/* It's a subroutine call. */

if ((step_over_calls == STEP_OVER_NONE)
|| ((step_range_end == 1)
&& in_prologue (prev_pc, ecs->stop_func_start)))
{
/* I presume that step_over_calls is only 0 when we're
supposed to be stepping at the assembly language level
("stepi"). Just stop. */
/* Also, maybe we just did a "nexti" inside a prolog,
so we thought it was a subroutine call but it was not.
Stop as well. FENN */
stop_step = 1;
print_stop_reason (END_STEPPING_RANGE, 0);
stop_stepping (ecs);
return;
}

Stripping the IN_SOLIB_RETURN_TRAMPOLINE checks for simplicity, we had:

if (stop_pc == ecs->stop_func_start /* Quick test */
|| in_prologue (stop_pc, ecs->stop_func_start)
|| ecs->stop_func_name == 0)
{
/* It's a subroutine call. */

That's it. Detecting a subroutine call was based on prologue
detection. And that's why nexti needed the fix it needed back
then. IOW, the in_prologue check in the current tree was undoing
a bad decision the in_prologue check just above did...

Compare to today's subroutine call check:

/* Check for subroutine calls. The check for the current frame
equalling the step ID is not necessary - the check of the
previous frame's ID is sufficient - but it is a common case and
cheaper than checking the previous frame's ID.

NOTE: frame_id_eq will never report two invalid frame IDs as
being equal, so to get into this block, both the current and
previous frame must have valid frame IDs. */
/* The outer_frame_id check is a heuristic to detect stepping
through startup code. If we step over an instruction which
sets the stack pointer from an invalid value to a valid value,
we may detect that as a subroutine call from the mythical
"outermost" function. This could be fixed by marking
outermost frames as !stack_p,code_p,special_p. Then the
initial outermost frame, before sp was valid, would
have code_addr == &_start. See the comment in frame_id_eq
for more. */
if (!frame_id_eq (get_stack_frame_id (frame),
ecs->event_thread->control.step_stack_frame_id)
&& (frame_id_eq (frame_unwind_caller_id (get_current_frame ()),
ecs->event_thread->control.step_stack_frame_id)
&& (!frame_id_eq (ecs->event_thread->control.step_stack_frame_id,
outer_frame_id)
|| step_start_function != find_pc_function (stop_pc))))
{

Nothing to do with prologues. Completely relying on frame ids,
which are stable throughout the function.

It seems to me we can just remove the in_prologue check for nexti,
and the whole in_prologue function along with it...

I nexti'd manually a prologue for smoke testing, and it passes
regression testing for me on x86_64 Fedora 20, FWIW...

--------
From 822267fed3994acf191071653d10caf4c9dd7247 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Fri, 24 Oct 2014 14:49:33 +0100
Subject: [PATCH] Revert nexti prologue check

commit 100a02e1deec2f037a15cdf232f026dc79763bf8
Author: Andrew Cagney <cagney@redhat.com>
AuthorDate: Thu Jun 28 21:48:41 2001 +0000
Commit: Andrew Cagney <cagney@redhat.com>
CommitDate: Thu Jun 28 21:48:41 2001 +0000

From Fernando Nasser:
* infrun.c (handle_inferior_event): Handle "nexti" inside function
prologues.
---
gdb/infrun.c | 5 +----
gdb/symtab.c | 73 ------------------------------------------------------------
gdb/symtab.h | 3 ---
3 files changed, 1 insertion(+), 80 deletions(-)

diff --git a/gdb/infrun.c b/gdb/infrun.c
index eca8eec..7a7d10d 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -4945,10 +4945,7 @@ process_event_stop_test (struct execution_control_state *ecs)
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stepped into subroutine\n");

- if ((ecs->event_thread->control.step_over_calls == STEP_OVER_NONE)
- || ((ecs->event_thread->control.step_range_end == 1)
- && in_prologue (gdbarch, ecs->event_thread->prev_pc,
- ecs->stop_func_start)))
+ if (ecs->event_thread->control.step_over_calls == STEP_OVER_NONE)
{
/* I presume that step_over_calls is only 0 when we're
supposed to be stepping at the assembly language level
diff --git a/gdb/symtab.c b/gdb/symtab.c
index c530d50..f39fc6c 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3034,79 +3034,6 @@ skip_prologue_sal (struct symtab_and_line *sal)
}
}

-/* Determine if PC is in the prologue of a function. The prologue is the area
- between the first instruction of a function, and the first executable line.
- Returns 1 if PC *might* be in prologue, 0 if definately *not* in prologue.
-
- If non-zero, func_start is where we think the prologue starts, possibly
- by previous examination of symbol table information. */
-
-int
-in_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, CORE_ADDR func_start)
-{
- struct symtab_and_line sal;
- CORE_ADDR func_addr, func_end;
-
- /* We have several sources of information we can consult to figure
- this out.
- - Compilers usually emit line number info that marks the prologue
- as its own "source line". So the ending address of that "line"
- is the end of the prologue. If available, this is the most
- reliable method.
- - The minimal symbols and partial symbols, which can usually tell
- us the starting and ending addresses of a function.
- - If we know the function's start address, we can call the
- architecture-defined gdbarch_skip_prologue function to analyze the
- instruction stream and guess where the prologue ends.
- - Our `func_start' argument; if non-zero, this is the caller's
- best guess as to the function's entry point. At the time of
- this writing, handle_inferior_event doesn't get this right, so
- it should be our last resort. */
-
- /* Consult the partial symbol table, to find which function
- the PC is in. */
- if (! find_pc_partial_function (pc, NULL, &func_addr, &func_end))
- {
- CORE_ADDR prologue_end;
-
- /* We don't even have minsym information, so fall back to using
- func_start, if given. */
- if (! func_start)
- return 1; /* We *might* be in a prologue. */
-
- prologue_end = gdbarch_skip_prologue (gdbarch, func_start);
-
- return func_start <= pc && pc < prologue_end;
- }
-
- /* If we have line number information for the function, that's
- usually pretty reliable. */
- sal = find_pc_line (func_addr, 0);
-
- /* Now sal describes the source line at the function's entry point,
- which (by convention) is the prologue. The end of that "line",
- sal.end, is the end of the prologue.
-
- Note that, for functions whose source code is all on a single
- line, the line number information doesn't always end up this way.
- So we must verify that our purported end-of-prologue address is
- *within* the function, not at its start or end. */
- if (sal.line == 0
- || sal.end <= func_addr
- || func_end <= sal.end)
- {
- /* We don't have any good line number info, so use the minsym
- information, together with the architecture-specific prologue
- scanning code. */
- CORE_ADDR prologue_end = gdbarch_skip_prologue (gdbarch, func_addr);
-
- return func_addr <= pc && pc < prologue_end;
- }
-
- /* We have line number info, and it looks good. */
- return func_addr <= pc && pc < sal.end;
-}
-
/* Given PC at the function's start address, attempt to find the
prologue end using SAL information. Return zero if the skip fails.

diff --git a/gdb/symtab.h b/gdb/symtab.h
index 4a47d48..aaef9a0 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1320,9 +1320,6 @@ extern enum language deduce_language_from_filename (const char *);

/* symtab.c */

-extern int in_prologue (struct gdbarch *gdbarch,
- CORE_ADDR pc, CORE_ADDR func_start);
-
extern CORE_ADDR skip_prologue_using_sal (struct gdbarch *gdbarch,
CORE_ADDR func_addr);

--
1.9.3