[PATCH] [SH] Prologue skipping if there is none

Thomas Schwinge
2012-02-15T13:52:05Z

[-- Attachment #1: Type: text/plain, Size: 15261 bytes --]

Hi!

(No specific SH maintainer CCed as none is listed in MAINTAINERS.)

First, for SH GNU/Linux, when running the testsuite via gdbserver on
pristine sources, I'm getting a large number of ERRORs, in the 350s. Is
that normal or at least known? They're basically all of the kind
``ERROR: Process no longer exists'', and then follow-up errors until the
end of the specific testcase. From a very quick glance, they all seem to
be happening after the testcase has instructed GDB to invoke a function,
such as:

(gdb) PASS: gdb.cp/classes.exp: print g_D.p
call class_param.Aptr_a (&g_A)
ERROR: Process no longer exists
UNRESOLVED: gdb.cp/classes.exp: call class_param.Aptr_a (&g_A)
ERROR: Couldn't send call class_param.Aptr_x (&g_A) to GDB.

This probably suggests where to begin looking unless that's know already.

The prologue skipping issue is that GDB fails to place breakpoints
correctly at the beginning of a function -- such as for ``break main'' --
for the case that there is no prologue in that function. In such a case,
it will currently place the breakpoint *after* the first source code line
instead of at its beginning, for example ``break main'' for
gdb.base/arrayidx.c will put a breakpoint on line 25 instead of 23:

20 int
21 main (void)
22 {
23 array[0] = 5;
24
25 return 0;
26 }

Dump of assembler code for function main:
0x00400600 <+0>: mov.l 0x400610 <main+16>,r1 ! 0x41083c <array>
0x00400602 <+2>: mov #5,r2
0x00400604 <+4>: mov.l r2,@r1
0x00400606 <+6>: mov #0,r1
0x00400608 <+8>: mov r1,r0
0x0040060a <+10>: rts
0x0040060c <+12>: nop
0x0040060e <+14>: nop
0x00400610 <+16>: mov.b @(r0,r3),r8
0x00400612 <+18>: .word 0x0041
End of assembler dump.

Crude log of debugging the SH GNU/Linux GDB when advising it to ``break
main'':

Breakpoint 3, sh_skip_prologue (gdbarch=0x90043c8, start_pc=4195840)

OK, this is start_pc == 0x400600.

715 if (!find_pc_partial_function (pc, NULL, &func_addr, &func_end))
Value returned is $3 = 1
(gdb) print/x func_addr
$4 = 0x400600
(gdb) print/x func_end
$5 = 0x400614

OK.

2137 return find_pc_sect_line (pc, section, notcurrent);
Value returned is $8 = {pspace = 0x8fed340, symtab = 0x901a21c, section = 0x0, line = 23, pc = 4195840, end = 4195846, explicit_pc = 0, explicit_line = 0}

OK, still $8->pc == 0x400600, and $8->end == 0x400606, and these are
attached to line 23, ``array[0] = 5;''.

after_prologue (pc=4195840) at /scratch/tschwing/ST_sh-linux-gnu-lite/obj/gdb-src-2012.03-999999-sh-linux-gnu-i686-pc-linux-gnu/gdb/sh-tdep.c:726
726 if (sal.end < func_end)
(gdb) n
727 return sal.end;
(gdb) print/x sal.end
$9 = 0x400606

Wrong, because we now have not skipped the prologue, but the first real
instruction.

Anyway, apart from the 350 ERRORs (with and without my patch), there is a
clear tendency of improvement, and no regressions:

Progressions and removed failures:

FAIL -> PASS: default/gdb.sum:gdb.base/arrayidx.exp: Print array with array-indexes off
FAIL -> PASS: default/gdb.sum:gdb.base/arrayidx.exp: Print array with array-indexes on
FAIL -> PASS: default/gdb.sum:gdb.base/break-always.exp: continue to breakpoint: bar
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for finish; return call-sc-tc
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for finish; return call-sc-td
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for finish; return call-sc-te
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for finish; return call-sc-tf
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for finish; return call-sc-ti
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for finish; return call-sc-tl
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for finish; return call-sc-tld
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for finish; return call-sc-tll
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for finish; return call-sc-ts
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for return; return call-sc-tc
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for return; return call-sc-td
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for return; return call-sc-te
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for return; return call-sc-tf
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for return; return call-sc-ti
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for return; return call-sc-tl
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for return; return call-sc-tld
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for return; return call-sc-tll
FAIL -> PASS: default/gdb.sum:gdb.base/call-sc.exp: advance to fun for return; return call-sc-ts
FAIL -> PASS: default/gdb.sum:gdb.base/gdb11531.exp: watchpoint variable triggers at next
FAIL -> PASS: default/gdb.sum:gdb.base/reread.exp: breakpoint foo in first file
FAIL -> PASS: default/gdb.sum:gdb.base/reread.exp: run to foo()
FAIL -> PASS: default/gdb.sum:gdb.base/return.exp: continue to return of -5
FAIL -> PASS: default/gdb.sum:gdb.base/return.exp: continue to return of -5.0
FAIL -> PASS: default/gdb.sum:gdb.base/return2.exp: void function returned successfully
FAIL -> PASS: default/gdb.sum:gdb.base/scope.exp: print funclocal at bar
FAIL -> PASS: default/gdb.sum:gdb.base/scope.exp: print funclocal_bss at bar
FAIL -> PASS: default/gdb.sum:gdb.base/skip.exp: step after deleting 1 (3)
FAIL -> PASS: default/gdb.sum:gdb.base/skip.exp: step after disabling 3 (5)
FAIL -> PASS: default/gdb.sum:gdb.base/skip.exp: step after enable 3 (3)
FAIL -> PASS: default/gdb.sum:gdb.base/step-resume-infcall.exp: step
FAIL -> PASS: default/gdb.sum:gdb.base/step-test.exp: step into
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 1 structs-tc
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 1 structs-td
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 1 structs-tf
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 1 structs-ti
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 1 structs-tl
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 1 structs-tld
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 1 structs-tll
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 1 structs-ts
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 2 structs-tc-ti
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 2 structs-tc-tl
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 2 structs-tf
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 2 structs-tf-tc
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 2 structs-ti
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 2 structs-ti-tc
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 2 structs-tl
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for finish; return 2 structs-tl-tc
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 1 structs-tc
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 1 structs-td
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 1 structs-tf
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 1 structs-ti
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 1 structs-tl
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 1 structs-tld
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 1 structs-tll
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 1 structs-ts
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 2 structs-tc-ti
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 2 structs-tc-tl
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 2 structs-tf
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 2 structs-tf-tc
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 2 structs-ti
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 2 structs-ti-tc
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 2 structs-tl
FAIL -> PASS: default/gdb.sum:gdb.base/structs.exp: advance to fun<n> for return; return 2 structs-tl-tc
FAIL -> PASS: default/gdb.sum:gdb.base/value-double-free.exp: continue
FAIL -> PASS: default/gdb.sum:gdb.base/watch-cond-infcall.exp: sw: continue
FAIL -> PASS: default/gdb.sum:gdb.base/watchpoints.exp: Watchpoint hit count is 2
FAIL -> PASS: default/gdb.sum:gdb.base/watchpoints.exp: Watchpoint hit count is 3
FAIL -> PASS: default/gdb.sum:gdb.base/watchpoints.exp: Watchpoint hit count is 4
FAIL -> PASS: default/gdb.sum:gdb.base/watchpoints.exp: Watchpoint hit count is 5
FAIL -> PASS: default/gdb.sum:gdb.base/watchpoints.exp: Watchpoint ival1 hit count is 2
FAIL -> PASS: default/gdb.sum:gdb.base/watchpoints.exp: Watchpoint ival1 hit count is 3
FAIL -> PASS: default/gdb.sum:gdb.base/watchpoints.exp: watchpoint hit, fifth time
FAIL -> PASS: default/gdb.sum:gdb.base/watchpoints.exp: watchpoint hit, fourth time
FAIL -> PASS: default/gdb.sum:gdb.base/watchpoints.exp: watchpoint hit, second time
FAIL -> PASS: default/gdb.sum:gdb.base/watchpoints.exp: watchpoint hit, third time
FAIL -> PASS: default/gdb.sum:gdb.base/watchpoints.exp: watchpoint ival1 hit, second time
FAIL -> PASS: default/gdb.sum:gdb.base/watchpoints.exp: watchpoint ival1 hit, third time
FAIL -> PASS: default/gdb.sum:gdb.cp/expand-sals.exp: continue to breakpoint: func
FAIL -> PASS: default/gdb.sum:gdb.cp/expand-sals.exp: continue to breakpoint: next caller func
FAIL -> PASS: default/gdb.sum:gdb.cp/extern-c.exp: continue to breakpoint: c_func
FAIL -> PASS: default/gdb.sum:gdb.cp/extern-c.exp: continue to breakpoint: c_funcs_1
FAIL -> PASS: default/gdb.sum:gdb.cp/extern-c.exp: continue to breakpoint: c_funcs_2
FAIL -> PASS: default/gdb.sum:gdb.cp/namespace-nested-import.exp: print C::x
FAIL -> PASS: default/gdb.sum:gdb.linespec/linespec.exp: set breakpoint at lspec.cc instance of NameSpace::overload
FAIL -> PASS: default/gdb.sum:gdb.linespec/linespec.exp: set breakpoint at specific instance of NameSpace::overload
Removed FAIL: default/gdb.sum:gdb.base/chng-syms.exp: continue until exit at breakpoint first time through (the program is no longer running)
Removed FAIL: default/gdb.sum:gdb.base/chng-syms.exp: running to stop_here first time

The patch is modeled after what most of all other targets are doing for
getting past the prologue (if there is one), and have been changed
accordingly before, such as in Git commits
93afe5e97284a3d460c19f72f096c4f561673e48,
42294ab40a733dbca9258929d0a6ec72e3059fbc,
5c176a3a457b50fb047477d5e32bdde5ca2dd267, for example.

Probably the very same patch should be applied to sh64-tdep.c, but I
don't have any possibility for testing that. Shall I to do that change
``blindly''?

2012-02-15 Thomas Schwinge <thomas@codesourcery.com>

* sh-tdep.c (sh_skip_prologue): Use skip_prologue_using_sal.
(after_prologue): Remove.

Index: gdb/sh-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/sh-tdep.c,v
retrieving revision 1.236
diff -u -p -r1.236 sh-tdep.c
--- gdb/sh-tdep.c 28 Jan 2012 18:08:20 -0000 1.236
+++ gdb/sh-tdep.c 15 Feb 2012 13:08:39 -0000
@@ -697,50 +697,25 @@ sh_analyze_prologue (struct gdbarch *gdb
}

/* Skip any prologue before the guts of a function. */
-
-/* Skip the prologue using the debug information. If this fails we'll
- fall back on the 'guess' method below. */
-static CORE_ADDR
-after_prologue (CORE_ADDR pc)
-{
- struct symtab_and_line sal;
- CORE_ADDR func_addr, func_end;
-
- /* If we can not find the symbol in the partial symbol table, then
- there is no hope we can determine the function's start address
- with this code. */
- if (!find_pc_partial_function (pc, NULL, &func_addr, &func_end))
- return 0;
-
- /* Get the line associated with FUNC_ADDR. */
- sal = find_pc_line (func_addr, 0);
-
- /* There are only two cases to consider. First, the end of the source line
- is within the function bounds. In that case we return the end of the
- source line. Second is the end of the source line extends beyond the
- bounds of the current function. We need to use the slow code to
- examine instructions in that case. */
- if (sal.end < func_end)
- return sal.end;
- else
- return 0;
-}
-
static CORE_ADDR
sh_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc)
{
- CORE_ADDR pc;
+ CORE_ADDR pc, func_addr;
struct sh_frame_cache cache;

/* See if we can determine the end of the prologue via the symbol table.
If so, then return either PC, or the PC after the prologue, whichever
is greater. */
- pc = after_prologue (start_pc);
+ if (find_pc_partial_function (start_pc, NULL, &func_addr, NULL))
+ {
+ CORE_ADDR post_prologue_pc
+ = skip_prologue_using_sal (gdbarch, func_addr);
+ if (post_prologue_pc != 0)
+ return max (start_pc, post_prologue_pc);
+ }

- /* If after_prologue returned a useful address, then use it. Else
- fall back on the instruction skipping code. */
- if (pc)
- return max (pc, start_pc);
+ /* Can't determine prologue from the symbol table, need to examine
+ instructions. */

cache.sp_offset = -4;
pc = sh_analyze_prologue (gdbarch, start_pc, (CORE_ADDR) -1, &cache, 0);