gdb/
Recognize virtual tail call frames. * Makefile.in (SFILES): Add dwarf2-frame-tailcall.c. (HFILES_NO_SRCDIR): Add dwarf2-frame-tailcall.h. (COMMON_OBS): Add dwarf2-frame-tailcall.o. * dwarf2-frame-tailcall.c: New file. * dwarf2-frame-tailcall.h: New file. * dwarf2-frame.c: Include dwarf2-frame-tailcall.h. (execute_cfa_program): New function comment. Return INSN_PTR. Reset REGS.PREV only after CIE execution. (struct dwarf2_frame_cache): New field tailcall_cache. (dwarf2_frame_cache): New variables entry_pc, entry_cfa_sp_offset, entry_cfa_sp_offset_p and instr. Execute FDE instructions in two parts, try to find entry_cfa_sp_offset. Call dwarf2_tailcall_sniffer_first. (dwarf2_frame_prev_register): Call dwarf2_tailcall_prev_register_first when appropriate. (dwarf2_frame_dealloc_cache): New function. (dwarf2_frame_sniffer): Preinitialize cache by dwarf2_frame_cache. (dwarf2_frame_unwind): Install dwarf2_frame_dealloc_cache. (dwarf2_signal_frame_unwind): Do not install dwarf2_frame_dealloc_cache. (dwarf2_append_unwinders): Add dwarf2_tailcall_frame_unwind. (dwarf2_frame_cfa): Support also dwarf2_tailcall_frame_unwind. * dwarf2loc.c (func_addr_to_tail_call_list) (tailcall_dump, call_sitep, VEC (call_sitep), chain_candidate) (call_site_find_chain_1, call_site_find_chain): New. * dwarf2loc.h (struct call_site_chain): New. (call_site_find_chain): New declaration. * frame.c (get_frame_address_in_block): Support also TAILCALL_FRAME. * frame.h (enum frame_type): New entry TAILCALL_FRAME. * python/py-frame.c (gdbpy_initialize_frames): Add TAILCALL_FRAME. * stack.c (frame_info): Support also TAILCALL_FRAME. gdb/doc/ Recognize virtual tail call frames. * gdb.texinfo (Optimized Code): Add reference to Tail Call Frames. (Tail Call Frames): New node. (Frames In Python): Add gdb.TAILCALL_FRAME. gdb/testsuite/ Recognize virtual tail call frames. * gdb.arch/amd64-entry-value.cc (c, a, b, amb_z, amb_y, amb_x, amb) (amb_b, amb_a): New. (main): Call a and b. * gdb.arch/amd64-entry-value.exp (tailcall: breakhere, tailcall: bt) (tailcall: p i, tailcall: p j, set $sp0=$sp, up, p $sp0 == $sp, frame 3) (p $sp0 + sizeof (void *) == $sp, ambiguous: breakhere, ambiguous: bt): New tests.
This commit is contained in:
parent
bb984ff154
commit
111c64899c
14 changed files with 709 additions and 12 deletions
|
@ -1,3 +1,37 @@
|
||||||
|
2011-10-09 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||||
|
|
||||||
|
Recognize virtual tail call frames.
|
||||||
|
* Makefile.in (SFILES): Add dwarf2-frame-tailcall.c.
|
||||||
|
(HFILES_NO_SRCDIR): Add dwarf2-frame-tailcall.h.
|
||||||
|
(COMMON_OBS): Add dwarf2-frame-tailcall.o.
|
||||||
|
* dwarf2-frame-tailcall.c: New file.
|
||||||
|
* dwarf2-frame-tailcall.h: New file.
|
||||||
|
* dwarf2-frame.c: Include dwarf2-frame-tailcall.h.
|
||||||
|
(execute_cfa_program): New function comment. Return INSN_PTR. Reset
|
||||||
|
REGS.PREV only after CIE execution.
|
||||||
|
(struct dwarf2_frame_cache): New field tailcall_cache.
|
||||||
|
(dwarf2_frame_cache): New variables entry_pc, entry_cfa_sp_offset,
|
||||||
|
entry_cfa_sp_offset_p and instr. Execute FDE instructions in two
|
||||||
|
parts, try to find entry_cfa_sp_offset. Call
|
||||||
|
dwarf2_tailcall_sniffer_first.
|
||||||
|
(dwarf2_frame_prev_register): Call dwarf2_tailcall_prev_register_first
|
||||||
|
when appropriate.
|
||||||
|
(dwarf2_frame_dealloc_cache): New function.
|
||||||
|
(dwarf2_frame_sniffer): Preinitialize cache by dwarf2_frame_cache.
|
||||||
|
(dwarf2_frame_unwind): Install dwarf2_frame_dealloc_cache.
|
||||||
|
(dwarf2_signal_frame_unwind): Do not install dwarf2_frame_dealloc_cache.
|
||||||
|
(dwarf2_append_unwinders): Add dwarf2_tailcall_frame_unwind.
|
||||||
|
(dwarf2_frame_cfa): Support also dwarf2_tailcall_frame_unwind.
|
||||||
|
* dwarf2loc.c (func_addr_to_tail_call_list)
|
||||||
|
(tailcall_dump, call_sitep, VEC (call_sitep), chain_candidate)
|
||||||
|
(call_site_find_chain_1, call_site_find_chain): New.
|
||||||
|
* dwarf2loc.h (struct call_site_chain): New.
|
||||||
|
(call_site_find_chain): New declaration.
|
||||||
|
* frame.c (get_frame_address_in_block): Support also TAILCALL_FRAME.
|
||||||
|
* frame.h (enum frame_type): New entry TAILCALL_FRAME.
|
||||||
|
* python/py-frame.c (gdbpy_initialize_frames): Add TAILCALL_FRAME.
|
||||||
|
* stack.c (frame_info): Support also TAILCALL_FRAME.
|
||||||
|
|
||||||
2011-10-09 Jan Kratochvil <jan.kratochvil@redhat.com>
|
2011-10-09 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||||
|
|
||||||
Tail call sites reader implementation.
|
Tail call sites reader implementation.
|
||||||
|
|
|
@ -697,6 +697,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
|
||||||
cp-name-parser.y \
|
cp-name-parser.y \
|
||||||
dbxread.c demangle.c dictionary.c disasm.c doublest.c dummy-frame.c \
|
dbxread.c demangle.c dictionary.c disasm.c doublest.c dummy-frame.c \
|
||||||
dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
|
dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
|
||||||
|
dwarf2-frame-tailcall.c \
|
||||||
elfread.c environ.c eval.c event-loop.c event-top.c \
|
elfread.c environ.c eval.c event-loop.c event-top.c \
|
||||||
exceptions.c expprint.c \
|
exceptions.c expprint.c \
|
||||||
f-exp.y f-lang.c f-typeprint.c f-valprint.c filesystem.c \
|
f-exp.y f-lang.c f-typeprint.c f-valprint.c filesystem.c \
|
||||||
|
@ -773,7 +774,7 @@ cli/cli-decode.h cli/cli-cmds.h cli/cli-dump.h cli/cli-utils.h \
|
||||||
cli/cli-script.h macrotab.h symtab.h version.h gnulib/wchar.in.h \
|
cli/cli-script.h macrotab.h symtab.h version.h gnulib/wchar.in.h \
|
||||||
gnulib/string.in.h gnulib/str-two-way.h \
|
gnulib/string.in.h gnulib/str-two-way.h \
|
||||||
gnulib/stdint.in.h remote.h gdb.h sparc-nat.h \
|
gnulib/stdint.in.h remote.h gdb.h sparc-nat.h \
|
||||||
gdbthread.h dwarf2-frame.h nbsd-nat.h dcache.h \
|
gdbthread.h dwarf2-frame.h dwarf2-frame-tailcall.h nbsd-nat.h dcache.h \
|
||||||
amd64-nat.h s390-tdep.h arm-linux-tdep.h exceptions.h macroscope.h \
|
amd64-nat.h s390-tdep.h arm-linux-tdep.h exceptions.h macroscope.h \
|
||||||
gdbarch.h bsd-uthread.h gdb_stat.h memory-map.h memrange.h \
|
gdbarch.h bsd-uthread.h gdb_stat.h memory-map.h memrange.h \
|
||||||
mdebugread.h m88k-tdep.h stabsread.h hppa-linux-offsets.h linux-fork.h \
|
mdebugread.h m88k-tdep.h stabsread.h hppa-linux-offsets.h linux-fork.h \
|
||||||
|
@ -881,7 +882,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
|
||||||
bcache.o objfiles.o observer.o minsyms.o maint.o demangle.o \
|
bcache.o objfiles.o observer.o minsyms.o maint.o demangle.o \
|
||||||
dbxread.o coffread.o coff-pe-read.o \
|
dbxread.o coffread.o coff-pe-read.o \
|
||||||
dwarf2read.o mipsread.o stabsread.o corefile.o \
|
dwarf2read.o mipsread.o stabsread.o corefile.o \
|
||||||
dwarf2expr.o dwarf2loc.o dwarf2-frame.o \
|
dwarf2expr.o dwarf2loc.o dwarf2-frame.o dwarf2-frame-tailcall.o \
|
||||||
ada-lang.o c-lang.o d-lang.o f-lang.o objc-lang.o \
|
ada-lang.o c-lang.o d-lang.o f-lang.o objc-lang.o \
|
||||||
ada-tasks.o \
|
ada-tasks.o \
|
||||||
ui-out.o cli-out.o \
|
ui-out.o cli-out.o \
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
2011-10-09 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||||
|
Eli Zaretskii <eliz@gnu.org>
|
||||||
|
|
||||||
|
Recognize virtual tail call frames.
|
||||||
|
* gdb.texinfo (Optimized Code): Add reference to Tail Call Frames.
|
||||||
|
(Tail Call Frames): New node.
|
||||||
|
(Frames In Python): Add gdb.TAILCALL_FRAME.
|
||||||
|
|
||||||
2011-10-07 Doug Evans <dje@google.com>
|
2011-10-07 Doug Evans <dje@google.com>
|
||||||
|
|
||||||
* gdb.texinfo (gdb.printing): Document new `replace' arg to
|
* gdb.texinfo (gdb.printing): Document new `replace' arg to
|
||||||
|
|
|
@ -9486,6 +9486,7 @@ please report it to us as a bug (including a test case!).
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
* Inline Functions:: How @value{GDBN} presents inlining
|
* Inline Functions:: How @value{GDBN} presents inlining
|
||||||
|
* Tail Call Frames:: @value{GDBN} analysis of jumps to functions
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
@node Inline Functions
|
@node Inline Functions
|
||||||
|
@ -9553,6 +9554,126 @@ and print a variable where your program stored the return value.
|
||||||
|
|
||||||
@end itemize
|
@end itemize
|
||||||
|
|
||||||
|
@node Tail Call Frames
|
||||||
|
@section Tail Call Frames
|
||||||
|
@cindex tail call frames, debugging
|
||||||
|
|
||||||
|
Function @code{B} can call function @code{C} in its very last statement. In
|
||||||
|
unoptimized compilation the call of @code{C} is immediately followed by return
|
||||||
|
instruction at the end of @code{B} code. Optimizing compiler may replace the
|
||||||
|
call and return in function @code{B} into one jump to function @code{C}
|
||||||
|
instead. Such use of a jump instruction is called @dfn{tail call}.
|
||||||
|
|
||||||
|
During execution of function @code{C}, there will be no indication in the
|
||||||
|
function call stack frames that it was tail-called from @code{B}. If function
|
||||||
|
@code{A} regularly calls function @code{B} which tail-calls function @code{C},
|
||||||
|
then @value{GDBN} will see @code{A} as the caller of @code{C}. However, in
|
||||||
|
some cases @value{GDBN} can determine that @code{C} was tail-called from
|
||||||
|
@code{B}, and it will then create fictitious call frame for that, with the
|
||||||
|
return address set up as if @code{B} called @code{C} normally.
|
||||||
|
|
||||||
|
This functionality is currently supported only by DWARF 2 debugging format and
|
||||||
|
the compiler has to produce @samp{DW_TAG_GNU_call_site} tags. With
|
||||||
|
@value{NGCC}, you need to specify @option{-O -g} during compilation, to get
|
||||||
|
this information.
|
||||||
|
|
||||||
|
@kbd{info frame} command (@pxref{Frame Info}) will indicate the tail call frame
|
||||||
|
kind by text @code{tail call frame} such as in this sample @value{GDBN} output:
|
||||||
|
|
||||||
|
@smallexample
|
||||||
|
(gdb) x/i $pc - 2
|
||||||
|
0x40066b <b(int, double)+11>: jmp 0x400640 <c(int, double)>
|
||||||
|
(gdb) info frame
|
||||||
|
Stack level 1, frame at 0x7fffffffda30:
|
||||||
|
rip = 0x40066d in b (amd64-entry-value.cc:59); saved rip 0x4004c5
|
||||||
|
tail call frame, caller of frame at 0x7fffffffda30
|
||||||
|
source language c++.
|
||||||
|
Arglist at unknown address.
|
||||||
|
Locals at unknown address, Previous frame's sp is 0x7fffffffda30
|
||||||
|
@end smallexample
|
||||||
|
|
||||||
|
The detection of all the possible code path executions can find them ambiguous.
|
||||||
|
There is no execution history stored (possible @ref{Reverse Execution} is never
|
||||||
|
used for this purpose) and the last known caller could have reached the known
|
||||||
|
callee by multiple different jump sequences. In such case @value{GDBN} still
|
||||||
|
tries to show at least all the unambiguous top tail callers and all the
|
||||||
|
unambiguous bottom tail calees, if any.
|
||||||
|
|
||||||
|
@table @code
|
||||||
|
@item set debug entry-values
|
||||||
|
@kindex set debug entry-values
|
||||||
|
When set to on, enables printing of analysis messages for both frame argument
|
||||||
|
values at function entry and tail calls. It will show all the possible valid
|
||||||
|
tail calls code paths it has considered. It will also print the intersection
|
||||||
|
of them with the final unambiguous (possibly partial or even empty) code path
|
||||||
|
result.
|
||||||
|
|
||||||
|
@item show debug entry-values
|
||||||
|
@kindex show debug entry-values
|
||||||
|
Show the current state of analysis messages printing for both frame argument
|
||||||
|
values at function entry and tail calls.
|
||||||
|
@end table
|
||||||
|
|
||||||
|
The analysis messages for tail calls can for example show why the virtual tail
|
||||||
|
call frame for function @code{c} has not been recognized (due to the indirect
|
||||||
|
reference by variable @code{x}):
|
||||||
|
|
||||||
|
@smallexample
|
||||||
|
static void __attribute__((noinline, noclone)) c (void);
|
||||||
|
void (*x) (void) = c;
|
||||||
|
static void __attribute__((noinline, noclone)) a (void) @{ x++; @}
|
||||||
|
static void __attribute__((noinline, noclone)) c (void) @{ a (); @}
|
||||||
|
int main (void) @{ x (); return 0; @}
|
||||||
|
|
||||||
|
Breakpoint 1, DW_OP_GNU_entry_value resolving cannot find
|
||||||
|
DW_TAG_GNU_call_site 0x40039a in main
|
||||||
|
a () at t.c:3
|
||||||
|
3 static void __attribute__((noinline, noclone)) a (void) @{ x++; @}
|
||||||
|
(gdb) bt
|
||||||
|
#0 a () at t.c:3
|
||||||
|
#1 0x000000000040039a in main () at t.c:5
|
||||||
|
@end smallexample
|
||||||
|
|
||||||
|
Another possibility is an ambiguous virtual tail call frames resolution:
|
||||||
|
|
||||||
|
@smallexample
|
||||||
|
int i;
|
||||||
|
static void __attribute__((noinline, noclone)) f (void) @{ i++; @}
|
||||||
|
static void __attribute__((noinline, noclone)) e (void) @{ f (); @}
|
||||||
|
static void __attribute__((noinline, noclone)) d (void) @{ f (); @}
|
||||||
|
static void __attribute__((noinline, noclone)) c (void) @{ d (); @}
|
||||||
|
static void __attribute__((noinline, noclone)) b (void)
|
||||||
|
@{ if (i) c (); else e (); @}
|
||||||
|
static void __attribute__((noinline, noclone)) a (void) @{ b (); @}
|
||||||
|
int main (void) @{ a (); return 0; @}
|
||||||
|
|
||||||
|
tailcall: initial: 0x4004d2(a) 0x4004ce(b) 0x4004b2(c) 0x4004a2(d)
|
||||||
|
tailcall: compare: 0x4004d2(a) 0x4004cc(b) 0x400492(e)
|
||||||
|
tailcall: reduced: 0x4004d2(a) |
|
||||||
|
(gdb) bt
|
||||||
|
#0 f () at t.c:2
|
||||||
|
#1 0x00000000004004d2 in a () at t.c:8
|
||||||
|
#2 0x0000000000400395 in main () at t.c:9
|
||||||
|
@end smallexample
|
||||||
|
|
||||||
|
Frames #0 and #2 are real, #1 is a virtual tail call frame. The code can have
|
||||||
|
possible execution paths
|
||||||
|
@code{main@arrow{}a@arrow{}b@arrow{}c@arrow{}d@arrow{}f} or
|
||||||
|
@code{main@arrow{}a@arrow{}b@arrow{}e@arrow{}f}, @value{GDBN} cannot find which
|
||||||
|
one from the inferior state.
|
||||||
|
|
||||||
|
@code{initial:} state shows some random possible calling sequence @value{GDBN}
|
||||||
|
has found. It then finds another possible calling sequcen - that one is
|
||||||
|
prefixed by @code{compare:}. The non-ambiguous intersection of these two is
|
||||||
|
printed as the @code{reduced:} calling sequence. That one could have many
|
||||||
|
futher @code{compare:} and @code{reduced:} statements as long as there remain
|
||||||
|
any non-ambiguous sequence entries.
|
||||||
|
|
||||||
|
For the frame of function @code{b} in both cases there are different possible
|
||||||
|
@code{$pc} values (@code{0x4004cc} or @code{0x4004ce}), therefore this frame is
|
||||||
|
also ambigous. The only non-ambiguous frame is the one for function @code{a},
|
||||||
|
therefore this one is displayed to the user while the ambiguous frames are
|
||||||
|
omitted.
|
||||||
|
|
||||||
@node Macros
|
@node Macros
|
||||||
@chapter C Preprocessor Macros
|
@chapter C Preprocessor Macros
|
||||||
|
@ -23099,6 +23220,9 @@ inferior function call.
|
||||||
A frame representing an inlined function. The function was inlined
|
A frame representing an inlined function. The function was inlined
|
||||||
into a @code{gdb.NORMAL_FRAME} that is older than this one.
|
into a @code{gdb.NORMAL_FRAME} that is older than this one.
|
||||||
|
|
||||||
|
@item gdb.TAILCALL_FRAME
|
||||||
|
A frame representing a tail call. @xref{Tail Call Frames}.
|
||||||
|
|
||||||
@item gdb.SIGTRAMP_FRAME
|
@item gdb.SIGTRAMP_FRAME
|
||||||
A signal trampoline frame. This is the frame created by the OS when
|
A signal trampoline frame. This is the frame created by the OS when
|
||||||
it calls into a signal handler.
|
it calls into a signal handler.
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "ax.h"
|
#include "ax.h"
|
||||||
#include "dwarf2loc.h"
|
#include "dwarf2loc.h"
|
||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
|
#include "dwarf2-frame-tailcall.h"
|
||||||
|
|
||||||
struct comp_unit;
|
struct comp_unit;
|
||||||
|
|
||||||
|
@ -399,7 +400,11 @@ Not implemented: computing unwound register using explicit value operator"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
/* Execute FDE program from INSN_PTR possibly up to INSN_END or up to inferior
|
||||||
|
PC. Modify FS state accordingly. Return current INSN_PTR where the
|
||||||
|
execution has stopped, one can resume it on the next call. */
|
||||||
|
|
||||||
|
static const gdb_byte *
|
||||||
execute_cfa_program (struct dwarf2_fde *fde, const gdb_byte *insn_ptr,
|
execute_cfa_program (struct dwarf2_fde *fde, const gdb_byte *insn_ptr,
|
||||||
const gdb_byte *insn_end, struct gdbarch *gdbarch,
|
const gdb_byte *insn_end, struct gdbarch *gdbarch,
|
||||||
CORE_ADDR pc, struct dwarf2_frame_state *fs)
|
CORE_ADDR pc, struct dwarf2_frame_state *fs)
|
||||||
|
@ -682,9 +687,14 @@ bad CFI data; mismatched DW_CFA_restore_state at %s"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't allow remember/restore between CIE and FDE programs. */
|
if (fs->initial.reg == NULL)
|
||||||
dwarf2_frame_state_free_regs (fs->regs.prev);
|
{
|
||||||
fs->regs.prev = NULL;
|
/* Don't allow remember/restore between CIE and FDE programs. */
|
||||||
|
dwarf2_frame_state_free_regs (fs->regs.prev);
|
||||||
|
fs->regs.prev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return insn_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -976,6 +986,13 @@ struct dwarf2_frame_cache
|
||||||
|
|
||||||
/* The .text offset. */
|
/* The .text offset. */
|
||||||
CORE_ADDR text_offset;
|
CORE_ADDR text_offset;
|
||||||
|
|
||||||
|
/* If not NULL then this frame is the bottom frame of a TAILCALL_FRAME
|
||||||
|
sequence. If NULL then it is a normal case with no TAILCALL_FRAME
|
||||||
|
involved. Non-bottom frames of a virtual tail call frames chain use
|
||||||
|
dwarf2_tailcall_frame_unwind unwinder so this field does not apply for
|
||||||
|
them. */
|
||||||
|
void *tailcall_cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct dwarf2_frame_cache *
|
static struct dwarf2_frame_cache *
|
||||||
|
@ -989,6 +1006,10 @@ dwarf2_frame_cache (struct frame_info *this_frame, void **this_cache)
|
||||||
struct dwarf2_frame_state *fs;
|
struct dwarf2_frame_state *fs;
|
||||||
struct dwarf2_fde *fde;
|
struct dwarf2_fde *fde;
|
||||||
volatile struct gdb_exception ex;
|
volatile struct gdb_exception ex;
|
||||||
|
CORE_ADDR entry_pc;
|
||||||
|
LONGEST entry_cfa_sp_offset;
|
||||||
|
int entry_cfa_sp_offset_p = 0;
|
||||||
|
const gdb_byte *instr;
|
||||||
|
|
||||||
if (*this_cache)
|
if (*this_cache)
|
||||||
return *this_cache;
|
return *this_cache;
|
||||||
|
@ -1040,8 +1061,25 @@ dwarf2_frame_cache (struct frame_info *this_frame, void **this_cache)
|
||||||
fs->initial = fs->regs;
|
fs->initial = fs->regs;
|
||||||
fs->initial.reg = dwarf2_frame_state_copy_regs (&fs->regs);
|
fs->initial.reg = dwarf2_frame_state_copy_regs (&fs->regs);
|
||||||
|
|
||||||
|
if (get_frame_func_if_available (this_frame, &entry_pc))
|
||||||
|
{
|
||||||
|
/* Decode the insns in the FDE up to the entry PC. */
|
||||||
|
instr = execute_cfa_program (fde, fde->instructions, fde->end, gdbarch,
|
||||||
|
entry_pc, fs);
|
||||||
|
|
||||||
|
if (fs->regs.cfa_how == CFA_REG_OFFSET
|
||||||
|
&& (gdbarch_dwarf2_reg_to_regnum (gdbarch, fs->regs.cfa_reg)
|
||||||
|
== gdbarch_sp_regnum (gdbarch)))
|
||||||
|
{
|
||||||
|
entry_cfa_sp_offset = fs->regs.cfa_offset;
|
||||||
|
entry_cfa_sp_offset_p = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
instr = fde->instructions;
|
||||||
|
|
||||||
/* Then decode the insns in the FDE up to our target PC. */
|
/* Then decode the insns in the FDE up to our target PC. */
|
||||||
execute_cfa_program (fde, fde->instructions, fde->end, gdbarch,
|
execute_cfa_program (fde, instr, fde->end, gdbarch,
|
||||||
get_frame_pc (this_frame), fs);
|
get_frame_pc (this_frame), fs);
|
||||||
|
|
||||||
TRY_CATCH (ex, RETURN_MASK_ERROR)
|
TRY_CATCH (ex, RETURN_MASK_ERROR)
|
||||||
|
@ -1182,6 +1220,12 @@ incomplete CFI data; unspecified registers (e.g., %s) at %s"),
|
||||||
|
|
||||||
do_cleanups (old_chain);
|
do_cleanups (old_chain);
|
||||||
|
|
||||||
|
/* Try to find a virtual tail call frames chain with bottom (callee) frame
|
||||||
|
starting at THIS_FRAME. */
|
||||||
|
dwarf2_tailcall_sniffer_first (this_frame, &cache->tailcall_cache,
|
||||||
|
(entry_cfa_sp_offset_p
|
||||||
|
? &entry_cfa_sp_offset : NULL));
|
||||||
|
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1227,6 +1271,22 @@ dwarf2_frame_prev_register (struct frame_info *this_frame, void **this_cache,
|
||||||
CORE_ADDR addr;
|
CORE_ADDR addr;
|
||||||
int realnum;
|
int realnum;
|
||||||
|
|
||||||
|
/* Non-bottom frames of a virtual tail call frames chain use
|
||||||
|
dwarf2_tailcall_frame_unwind unwinder so this code does not apply for
|
||||||
|
them. If dwarf2_tailcall_prev_register_first does not have specific value
|
||||||
|
unwind the register, tail call frames are assumed to have the register set
|
||||||
|
of the top caller. */
|
||||||
|
if (cache->tailcall_cache)
|
||||||
|
{
|
||||||
|
struct value *val;
|
||||||
|
|
||||||
|
val = dwarf2_tailcall_prev_register_first (this_frame,
|
||||||
|
&cache->tailcall_cache,
|
||||||
|
regnum);
|
||||||
|
if (val)
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
switch (cache->reg[regnum].how)
|
switch (cache->reg[regnum].how)
|
||||||
{
|
{
|
||||||
case DWARF2_FRAME_REG_UNDEFINED:
|
case DWARF2_FRAME_REG_UNDEFINED:
|
||||||
|
@ -1296,6 +1356,18 @@ dwarf2_frame_prev_register (struct frame_info *this_frame, void **this_cache,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Proxy for tailcall_frame_dealloc_cache for bottom frame of a virtual tail
|
||||||
|
call frames chain. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
dwarf2_frame_dealloc_cache (struct frame_info *self, void *this_cache)
|
||||||
|
{
|
||||||
|
struct dwarf2_frame_cache *cache = dwarf2_frame_cache (self, &this_cache);
|
||||||
|
|
||||||
|
if (cache->tailcall_cache)
|
||||||
|
dwarf2_tailcall_frame_unwind.dealloc_cache (self, cache->tailcall_cache);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
dwarf2_frame_sniffer (const struct frame_unwind *self,
|
dwarf2_frame_sniffer (const struct frame_unwind *self,
|
||||||
struct frame_info *this_frame, void **this_cache)
|
struct frame_info *this_frame, void **this_cache)
|
||||||
|
@ -1322,7 +1394,14 @@ dwarf2_frame_sniffer (const struct frame_unwind *self,
|
||||||
this_frame))
|
this_frame))
|
||||||
return self->type == SIGTRAMP_FRAME;
|
return self->type == SIGTRAMP_FRAME;
|
||||||
|
|
||||||
return self->type != SIGTRAMP_FRAME;
|
if (self->type != NORMAL_FRAME)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Preinitializa the cache so that TAILCALL_FRAME can find the record by
|
||||||
|
dwarf2_tailcall_sniffer_first. */
|
||||||
|
dwarf2_frame_cache (this_frame, this_cache);
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct frame_unwind dwarf2_frame_unwind =
|
static const struct frame_unwind dwarf2_frame_unwind =
|
||||||
|
@ -1332,7 +1411,8 @@ static const struct frame_unwind dwarf2_frame_unwind =
|
||||||
dwarf2_frame_this_id,
|
dwarf2_frame_this_id,
|
||||||
dwarf2_frame_prev_register,
|
dwarf2_frame_prev_register,
|
||||||
NULL,
|
NULL,
|
||||||
dwarf2_frame_sniffer
|
dwarf2_frame_sniffer,
|
||||||
|
dwarf2_frame_dealloc_cache
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct frame_unwind dwarf2_signal_frame_unwind =
|
static const struct frame_unwind dwarf2_signal_frame_unwind =
|
||||||
|
@ -1342,7 +1422,10 @@ static const struct frame_unwind dwarf2_signal_frame_unwind =
|
||||||
dwarf2_frame_this_id,
|
dwarf2_frame_this_id,
|
||||||
dwarf2_frame_prev_register,
|
dwarf2_frame_prev_register,
|
||||||
NULL,
|
NULL,
|
||||||
dwarf2_frame_sniffer
|
dwarf2_frame_sniffer,
|
||||||
|
|
||||||
|
/* TAILCALL_CACHE can never be in such frame to need dealloc_cache. */
|
||||||
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Append the DWARF-2 frame unwinders to GDBARCH's list. */
|
/* Append the DWARF-2 frame unwinders to GDBARCH's list. */
|
||||||
|
@ -1350,6 +1433,10 @@ static const struct frame_unwind dwarf2_signal_frame_unwind =
|
||||||
void
|
void
|
||||||
dwarf2_append_unwinders (struct gdbarch *gdbarch)
|
dwarf2_append_unwinders (struct gdbarch *gdbarch)
|
||||||
{
|
{
|
||||||
|
/* TAILCALL_FRAME must be first to find the record by
|
||||||
|
dwarf2_tailcall_sniffer_first. */
|
||||||
|
frame_unwind_append_unwinder (gdbarch, &dwarf2_tailcall_frame_unwind);
|
||||||
|
|
||||||
frame_unwind_append_unwinder (gdbarch, &dwarf2_frame_unwind);
|
frame_unwind_append_unwinder (gdbarch, &dwarf2_frame_unwind);
|
||||||
frame_unwind_append_unwinder (gdbarch, &dwarf2_signal_frame_unwind);
|
frame_unwind_append_unwinder (gdbarch, &dwarf2_signal_frame_unwind);
|
||||||
}
|
}
|
||||||
|
@ -1401,7 +1488,8 @@ dwarf2_frame_cfa (struct frame_info *this_frame)
|
||||||
/* This restriction could be lifted if other unwinders are known to
|
/* This restriction could be lifted if other unwinders are known to
|
||||||
compute the frame base in a way compatible with the DWARF
|
compute the frame base in a way compatible with the DWARF
|
||||||
unwinder. */
|
unwinder. */
|
||||||
if (! frame_unwinder_is (this_frame, &dwarf2_frame_unwind))
|
if (!frame_unwinder_is (this_frame, &dwarf2_frame_unwind)
|
||||||
|
&& !frame_unwinder_is (this_frame, &dwarf2_tailcall_frame_unwind))
|
||||||
error (_("can't compute CFA for this frame"));
|
error (_("can't compute CFA for this frame"));
|
||||||
return get_frame_base (this_frame);
|
return get_frame_base (this_frame);
|
||||||
}
|
}
|
||||||
|
|
315
gdb/dwarf2loc.c
315
gdb/dwarf2loc.c
|
@ -399,6 +399,321 @@ call_site_to_target_addr (struct gdbarch *call_site_gdbarch,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Convert function entry point exact address ADDR to the function which is
|
||||||
|
compliant with TAIL_CALL_LIST_COMPLETE condition. Throw
|
||||||
|
NO_ENTRY_VALUE_ERROR otherwise. */
|
||||||
|
|
||||||
|
static struct symbol *
|
||||||
|
func_addr_to_tail_call_list (struct gdbarch *gdbarch, CORE_ADDR addr)
|
||||||
|
{
|
||||||
|
struct symbol *sym = find_pc_function (addr);
|
||||||
|
struct type *type;
|
||||||
|
|
||||||
|
if (sym == NULL || BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) != addr)
|
||||||
|
throw_error (NO_ENTRY_VALUE_ERROR,
|
||||||
|
_("DW_TAG_GNU_call_site resolving failed to find function "
|
||||||
|
"name for address %s"),
|
||||||
|
paddress (gdbarch, addr));
|
||||||
|
|
||||||
|
type = SYMBOL_TYPE (sym);
|
||||||
|
gdb_assert (TYPE_CODE (type) == TYPE_CODE_FUNC);
|
||||||
|
gdb_assert (TYPE_SPECIFIC_FIELD (type) == TYPE_SPECIFIC_FUNC);
|
||||||
|
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print user readable form of CALL_SITE->PC to gdb_stdlog. Used only for
|
||||||
|
ENTRY_VALUES_DEBUG. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
tailcall_dump (struct gdbarch *gdbarch, const struct call_site *call_site)
|
||||||
|
{
|
||||||
|
CORE_ADDR addr = call_site->pc;
|
||||||
|
struct minimal_symbol *msym = lookup_minimal_symbol_by_pc (addr - 1);
|
||||||
|
|
||||||
|
fprintf_unfiltered (gdb_stdlog, " %s(%s)", paddress (gdbarch, addr),
|
||||||
|
msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vec.h needs single word type name, typedef it. */
|
||||||
|
typedef struct call_site *call_sitep;
|
||||||
|
|
||||||
|
/* Define VEC (call_sitep) functions. */
|
||||||
|
DEF_VEC_P (call_sitep);
|
||||||
|
|
||||||
|
/* Intersect RESULTP with CHAIN to keep RESULTP unambiguous, keep in RESULTP
|
||||||
|
only top callers and bottom callees which are present in both. GDBARCH is
|
||||||
|
used only for ENTRY_VALUES_DEBUG. RESULTP is NULL after return if there are
|
||||||
|
no remaining possibilities to provide unambiguous non-trivial result.
|
||||||
|
RESULTP should point to NULL on the first (initialization) call. Caller is
|
||||||
|
responsible for xfree of any RESULTP data. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
chain_candidate (struct gdbarch *gdbarch, struct call_site_chain **resultp,
|
||||||
|
VEC (call_sitep) *chain)
|
||||||
|
{
|
||||||
|
struct call_site_chain *result = *resultp;
|
||||||
|
long length = VEC_length (call_sitep, chain);
|
||||||
|
int callers, callees, idx;
|
||||||
|
|
||||||
|
if (result == NULL)
|
||||||
|
{
|
||||||
|
/* Create the initial chain containing all the passed PCs. */
|
||||||
|
|
||||||
|
result = xmalloc (sizeof (*result) + sizeof (*result->call_site)
|
||||||
|
* (length - 1));
|
||||||
|
result->length = length;
|
||||||
|
result->callers = result->callees = length;
|
||||||
|
memcpy (result->call_site, VEC_address (call_sitep, chain),
|
||||||
|
sizeof (*result->call_site) * length);
|
||||||
|
*resultp = result;
|
||||||
|
|
||||||
|
if (entry_values_debug)
|
||||||
|
{
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "tailcall: initial:");
|
||||||
|
for (idx = 0; idx < length; idx++)
|
||||||
|
tailcall_dump (gdbarch, result->call_site[idx]);
|
||||||
|
fputc_unfiltered ('\n', gdb_stdlog);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry_values_debug)
|
||||||
|
{
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "tailcall: compare:");
|
||||||
|
for (idx = 0; idx < length; idx++)
|
||||||
|
tailcall_dump (gdbarch, VEC_index (call_sitep, chain, idx));
|
||||||
|
fputc_unfiltered ('\n', gdb_stdlog);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Intersect callers. */
|
||||||
|
|
||||||
|
callers = min (result->callers, length);
|
||||||
|
for (idx = 0; idx < callers; idx++)
|
||||||
|
if (result->call_site[idx] != VEC_index (call_sitep, chain, idx))
|
||||||
|
{
|
||||||
|
result->callers = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Intersect callees. */
|
||||||
|
|
||||||
|
callees = min (result->callees, length);
|
||||||
|
for (idx = 0; idx < callees; idx++)
|
||||||
|
if (result->call_site[result->length - 1 - idx]
|
||||||
|
!= VEC_index (call_sitep, chain, length - 1 - idx))
|
||||||
|
{
|
||||||
|
result->callees = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry_values_debug)
|
||||||
|
{
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "tailcall: reduced:");
|
||||||
|
for (idx = 0; idx < result->callers; idx++)
|
||||||
|
tailcall_dump (gdbarch, result->call_site[idx]);
|
||||||
|
fputs_unfiltered (" |", gdb_stdlog);
|
||||||
|
for (idx = 0; idx < result->callees; idx++)
|
||||||
|
tailcall_dump (gdbarch, result->call_site[result->length
|
||||||
|
- result->callees + idx]);
|
||||||
|
fputc_unfiltered ('\n', gdb_stdlog);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result->callers == 0 && result->callees == 0)
|
||||||
|
{
|
||||||
|
/* There are no common callers or callees. It could be also a direct
|
||||||
|
call (which has length 0) with ambiguous possibility of an indirect
|
||||||
|
call - CALLERS == CALLEES == 0 is valid during the first allocation
|
||||||
|
but any subsequence processing of such entry means ambiguity. */
|
||||||
|
xfree (result);
|
||||||
|
*resultp = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See call_site_find_chain_1 why there is no way to reach the bottom callee
|
||||||
|
PC again. In such case there must be two different code paths to reach
|
||||||
|
it, therefore some of the former determined intermediate PCs must differ
|
||||||
|
and the unambiguous chain gets shortened. */
|
||||||
|
gdb_assert (result->callers + result->callees < result->length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create and return call_site_chain for CALLER_PC and CALLEE_PC. All the
|
||||||
|
assumed frames between them use GDBARCH. Use depth first search so we can
|
||||||
|
keep single CHAIN of call_site's back to CALLER_PC. Function recursion
|
||||||
|
would have needless GDB stack overhead. Caller is responsible for xfree of
|
||||||
|
the returned result. Any unreliability results in thrown
|
||||||
|
NO_ENTRY_VALUE_ERROR. */
|
||||||
|
|
||||||
|
static struct call_site_chain *
|
||||||
|
call_site_find_chain_1 (struct gdbarch *gdbarch, CORE_ADDR caller_pc,
|
||||||
|
CORE_ADDR callee_pc)
|
||||||
|
{
|
||||||
|
struct func_type *func_specific;
|
||||||
|
struct obstack addr_obstack;
|
||||||
|
struct cleanup *back_to_retval, *back_to_workdata;
|
||||||
|
struct call_site_chain *retval = NULL;
|
||||||
|
struct call_site *call_site;
|
||||||
|
|
||||||
|
/* Mark CALL_SITEs so we do not visit the same ones twice. */
|
||||||
|
htab_t addr_hash;
|
||||||
|
|
||||||
|
/* CHAIN contains only the intermediate CALL_SITEs. Neither CALLER_PC's
|
||||||
|
call_site nor any possible call_site at CALLEE_PC's function is there.
|
||||||
|
Any CALL_SITE in CHAIN will be iterated to its siblings - via
|
||||||
|
TAIL_CALL_NEXT. This is inappropriate for CALLER_PC's call_site. */
|
||||||
|
VEC (call_sitep) *chain = NULL;
|
||||||
|
|
||||||
|
/* We are not interested in the specific PC inside the callee function. */
|
||||||
|
callee_pc = get_pc_function_start (callee_pc);
|
||||||
|
if (callee_pc == 0)
|
||||||
|
throw_error (NO_ENTRY_VALUE_ERROR, _("Unable to find function for PC %s"),
|
||||||
|
paddress (gdbarch, callee_pc));
|
||||||
|
|
||||||
|
back_to_retval = make_cleanup (free_current_contents, &retval);
|
||||||
|
|
||||||
|
obstack_init (&addr_obstack);
|
||||||
|
back_to_workdata = make_cleanup_obstack_free (&addr_obstack);
|
||||||
|
addr_hash = htab_create_alloc_ex (64, core_addr_hash, core_addr_eq, NULL,
|
||||||
|
&addr_obstack, hashtab_obstack_allocate,
|
||||||
|
NULL);
|
||||||
|
make_cleanup_htab_delete (addr_hash);
|
||||||
|
|
||||||
|
make_cleanup (VEC_cleanup (call_sitep), &chain);
|
||||||
|
|
||||||
|
/* Do not push CALL_SITE to CHAIN. Push there only the first tail call site
|
||||||
|
at the target's function. All the possible tail call sites in the
|
||||||
|
target's function will get iterated as already pushed into CHAIN via their
|
||||||
|
TAIL_CALL_NEXT. */
|
||||||
|
call_site = call_site_for_pc (gdbarch, caller_pc);
|
||||||
|
|
||||||
|
while (call_site)
|
||||||
|
{
|
||||||
|
CORE_ADDR target_func_addr;
|
||||||
|
struct call_site *target_call_site;
|
||||||
|
|
||||||
|
/* CALLER_FRAME with registers is not available for tail-call jumped
|
||||||
|
frames. */
|
||||||
|
target_func_addr = call_site_to_target_addr (gdbarch, call_site, NULL);
|
||||||
|
|
||||||
|
if (target_func_addr == callee_pc)
|
||||||
|
{
|
||||||
|
chain_candidate (gdbarch, &retval, chain);
|
||||||
|
if (retval == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* There is no way to reach CALLEE_PC again as we would prevent
|
||||||
|
entering it twice as being already marked in ADDR_HASH. */
|
||||||
|
target_call_site = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct symbol *target_func;
|
||||||
|
|
||||||
|
target_func = func_addr_to_tail_call_list (gdbarch, target_func_addr);
|
||||||
|
target_call_site = TYPE_TAIL_CALL_LIST (SYMBOL_TYPE (target_func));
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
/* Attempt to visit TARGET_CALL_SITE. */
|
||||||
|
|
||||||
|
if (target_call_site)
|
||||||
|
{
|
||||||
|
void **slot;
|
||||||
|
|
||||||
|
slot = htab_find_slot (addr_hash, &target_call_site->pc, INSERT);
|
||||||
|
if (*slot == NULL)
|
||||||
|
{
|
||||||
|
/* Successfully entered TARGET_CALL_SITE. */
|
||||||
|
|
||||||
|
*slot = &target_call_site->pc;
|
||||||
|
VEC_safe_push (call_sitep, chain, target_call_site);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Backtrack (without revisiting the originating call_site). Try the
|
||||||
|
callers's sibling; if there isn't any try the callers's callers's
|
||||||
|
sibling etc. */
|
||||||
|
|
||||||
|
target_call_site = NULL;
|
||||||
|
while (!VEC_empty (call_sitep, chain))
|
||||||
|
{
|
||||||
|
call_site = VEC_pop (call_sitep, chain);
|
||||||
|
|
||||||
|
gdb_assert (htab_find_slot (addr_hash, &call_site->pc,
|
||||||
|
NO_INSERT) != NULL);
|
||||||
|
htab_remove_elt (addr_hash, &call_site->pc);
|
||||||
|
|
||||||
|
target_call_site = call_site->tail_call_next;
|
||||||
|
if (target_call_site)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (target_call_site);
|
||||||
|
|
||||||
|
if (VEC_empty (call_sitep, chain))
|
||||||
|
call_site = NULL;
|
||||||
|
else
|
||||||
|
call_site = VEC_last (call_sitep, chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retval == NULL)
|
||||||
|
{
|
||||||
|
struct minimal_symbol *msym_caller, *msym_callee;
|
||||||
|
|
||||||
|
msym_caller = lookup_minimal_symbol_by_pc (caller_pc);
|
||||||
|
msym_callee = lookup_minimal_symbol_by_pc (callee_pc);
|
||||||
|
throw_error (NO_ENTRY_VALUE_ERROR,
|
||||||
|
_("There are no unambiguously determinable intermediate "
|
||||||
|
"callers or callees between caller function \"%s\" at %s "
|
||||||
|
"and callee function \"%s\" at %s"),
|
||||||
|
(msym_caller == NULL
|
||||||
|
? "???" : SYMBOL_PRINT_NAME (msym_caller)),
|
||||||
|
paddress (gdbarch, caller_pc),
|
||||||
|
(msym_callee == NULL
|
||||||
|
? "???" : SYMBOL_PRINT_NAME (msym_callee)),
|
||||||
|
paddress (gdbarch, callee_pc));
|
||||||
|
}
|
||||||
|
|
||||||
|
do_cleanups (back_to_workdata);
|
||||||
|
discard_cleanups (back_to_retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create and return call_site_chain for CALLER_PC and CALLEE_PC. All the
|
||||||
|
assumed frames between them use GDBARCH. If valid call_site_chain cannot be
|
||||||
|
constructed return NULL. Caller is responsible for xfree of the returned
|
||||||
|
result. */
|
||||||
|
|
||||||
|
struct call_site_chain *
|
||||||
|
call_site_find_chain (struct gdbarch *gdbarch, CORE_ADDR caller_pc,
|
||||||
|
CORE_ADDR callee_pc)
|
||||||
|
{
|
||||||
|
volatile struct gdb_exception e;
|
||||||
|
struct call_site_chain *retval = NULL;
|
||||||
|
|
||||||
|
TRY_CATCH (e, RETURN_MASK_ERROR)
|
||||||
|
{
|
||||||
|
retval = call_site_find_chain_1 (gdbarch, caller_pc, callee_pc);
|
||||||
|
}
|
||||||
|
if (e.reason < 0)
|
||||||
|
{
|
||||||
|
if (e.error == NO_ENTRY_VALUE_ERROR)
|
||||||
|
{
|
||||||
|
if (entry_values_debug)
|
||||||
|
exception_print (gdb_stdout, e);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw_exception (e);
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/* Fetch call_site_parameter from caller matching the parameters. FRAME is for
|
/* Fetch call_site_parameter from caller matching the parameters. FRAME is for
|
||||||
callee. See DWARF_REG and FB_OFFSET description at struct
|
callee. See DWARF_REG and FB_OFFSET description at struct
|
||||||
dwarf_expr_context_funcs->push_dwarf_reg_entry_value.
|
dwarf_expr_context_funcs->push_dwarf_reg_entry_value.
|
||||||
|
|
|
@ -139,4 +139,23 @@ extern void dwarf2_compile_expr_to_ax (struct agent_expr *expr,
|
||||||
const gdb_byte *op_end,
|
const gdb_byte *op_end,
|
||||||
struct dwarf2_per_cu_data *per_cu);
|
struct dwarf2_per_cu_data *per_cu);
|
||||||
|
|
||||||
|
/* Determined tail calls for constructing virtual tail call frames. */
|
||||||
|
|
||||||
|
struct call_site_chain
|
||||||
|
{
|
||||||
|
/* Initially CALLERS == CALLEES == LENGTH. For partially ambiguous result
|
||||||
|
CALLERS + CALLEES < LENGTH. */
|
||||||
|
int callers, callees, length;
|
||||||
|
|
||||||
|
/* Variably sized array with LENGTH elements. Later [0..CALLERS-1] contain
|
||||||
|
top (GDB "prev") sites and [LENGTH-CALLEES..LENGTH-1] contain bottom
|
||||||
|
(GDB "next") sites. One is interested primarily in the PC field. */
|
||||||
|
struct call_site *call_site[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct call_site_stuff;
|
||||||
|
extern struct call_site_chain *call_site_find_chain (struct gdbarch *gdbarch,
|
||||||
|
CORE_ADDR caller_pc,
|
||||||
|
CORE_ADDR callee_pc);
|
||||||
|
|
||||||
#endif /* dwarf2loc.h */
|
#endif /* dwarf2loc.h */
|
||||||
|
|
|
@ -2035,8 +2035,10 @@ get_frame_address_in_block (struct frame_info *this_frame)
|
||||||
while (get_frame_type (next_frame) == INLINE_FRAME)
|
while (get_frame_type (next_frame) == INLINE_FRAME)
|
||||||
next_frame = next_frame->next;
|
next_frame = next_frame->next;
|
||||||
|
|
||||||
if (get_frame_type (next_frame) == NORMAL_FRAME
|
if ((get_frame_type (next_frame) == NORMAL_FRAME
|
||||||
|
|| get_frame_type (next_frame) == TAILCALL_FRAME)
|
||||||
&& (get_frame_type (this_frame) == NORMAL_FRAME
|
&& (get_frame_type (this_frame) == NORMAL_FRAME
|
||||||
|
|| get_frame_type (this_frame) == TAILCALL_FRAME
|
||||||
|| get_frame_type (this_frame) == INLINE_FRAME))
|
|| get_frame_type (this_frame) == INLINE_FRAME))
|
||||||
return pc - 1;
|
return pc - 1;
|
||||||
|
|
||||||
|
|
|
@ -206,6 +206,8 @@ enum frame_type
|
||||||
/* A frame representing an inlined function, associated with an
|
/* A frame representing an inlined function, associated with an
|
||||||
upcoming (prev, outer, older) NORMAL_FRAME. */
|
upcoming (prev, outer, older) NORMAL_FRAME. */
|
||||||
INLINE_FRAME,
|
INLINE_FRAME,
|
||||||
|
/* A virtual frame of a tail call - see dwarf2_tailcall_frame_unwind. */
|
||||||
|
TAILCALL_FRAME,
|
||||||
/* In a signal handler, various OSs handle this in various ways.
|
/* In a signal handler, various OSs handle this in various ways.
|
||||||
The main thing is that the frame may be far from normal. */
|
The main thing is that the frame may be far from normal. */
|
||||||
SIGTRAMP_FRAME,
|
SIGTRAMP_FRAME,
|
||||||
|
|
|
@ -595,6 +595,7 @@ gdbpy_initialize_frames (void)
|
||||||
PyModule_AddIntConstant (gdb_module, "NORMAL_FRAME", NORMAL_FRAME);
|
PyModule_AddIntConstant (gdb_module, "NORMAL_FRAME", NORMAL_FRAME);
|
||||||
PyModule_AddIntConstant (gdb_module, "DUMMY_FRAME", DUMMY_FRAME);
|
PyModule_AddIntConstant (gdb_module, "DUMMY_FRAME", DUMMY_FRAME);
|
||||||
PyModule_AddIntConstant (gdb_module, "INLINE_FRAME", INLINE_FRAME);
|
PyModule_AddIntConstant (gdb_module, "INLINE_FRAME", INLINE_FRAME);
|
||||||
|
PyModule_AddIntConstant (gdb_module, "TAILCALL_FRAME", TAILCALL_FRAME);
|
||||||
PyModule_AddIntConstant (gdb_module, "SIGTRAMP_FRAME", SIGTRAMP_FRAME);
|
PyModule_AddIntConstant (gdb_module, "SIGTRAMP_FRAME", SIGTRAMP_FRAME);
|
||||||
PyModule_AddIntConstant (gdb_module, "ARCH_FRAME", ARCH_FRAME);
|
PyModule_AddIntConstant (gdb_module, "ARCH_FRAME", ARCH_FRAME);
|
||||||
PyModule_AddIntConstant (gdb_module, "SENTINEL_FRAME", SENTINEL_FRAME);
|
PyModule_AddIntConstant (gdb_module, "SENTINEL_FRAME", SENTINEL_FRAME);
|
||||||
|
|
|
@ -1086,6 +1086,8 @@ frame_info (char *addr_exp, int from_tty)
|
||||||
printf_filtered (_(" Outermost frame: %s\n"),
|
printf_filtered (_(" Outermost frame: %s\n"),
|
||||||
frame_stop_reason_string (reason));
|
frame_stop_reason_string (reason));
|
||||||
}
|
}
|
||||||
|
else if (get_frame_type (fi) == TAILCALL_FRAME)
|
||||||
|
puts_filtered (" tail call frame");
|
||||||
else if (get_frame_type (fi) == INLINE_FRAME)
|
else if (get_frame_type (fi) == INLINE_FRAME)
|
||||||
printf_filtered (" inlined into frame %d",
|
printf_filtered (" inlined into frame %d",
|
||||||
frame_relative_level (get_prev_frame (fi)));
|
frame_relative_level (get_prev_frame (fi)));
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
|
2011-10-09 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||||
|
|
||||||
|
Recognize virtual tail call frames.
|
||||||
|
* gdb.arch/amd64-entry-value.cc (c, a, b, amb_z, amb_y, amb_x, amb)
|
||||||
|
(amb_b, amb_a): New.
|
||||||
|
(main): Call a and b.
|
||||||
|
* gdb.arch/amd64-entry-value.exp (tailcall: breakhere, tailcall: bt)
|
||||||
|
(tailcall: p i, tailcall: p j, set $sp0=$sp, up, p $sp0 == $sp, frame 3)
|
||||||
|
(p $sp0 + sizeof (void *) == $sp, ambiguous: breakhere, ambiguous: bt):
|
||||||
|
New tests.
|
||||||
|
|
||||||
2011-10-09 Jan Kratochvil <jan.kratochvil@redhat.com>
|
2011-10-09 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||||
|
|
||||||
Implement basic support for DW_TAG_GNU_call_site.
|
Implement basic support for DW_TAG_GNU_call_site.
|
||||||
|
|
|
@ -34,9 +34,71 @@ asm ("breakhere:");
|
||||||
e (v, v);
|
e (v, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __attribute__((noinline, noclone))
|
||||||
|
c (int i, double j)
|
||||||
|
{
|
||||||
|
d (i * 10, j * 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __attribute__((noinline, noclone))
|
||||||
|
a (int i, double j)
|
||||||
|
{
|
||||||
|
c (i + 1, j + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __attribute__((noinline, noclone))
|
||||||
|
b (int i, double j)
|
||||||
|
{
|
||||||
|
c (i + 2, j + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __attribute__((noinline, noclone))
|
||||||
|
amb_z (int i)
|
||||||
|
{
|
||||||
|
d (i + 7, i + 7.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __attribute__((noinline, noclone))
|
||||||
|
amb_y (int i)
|
||||||
|
{
|
||||||
|
amb_z (i + 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __attribute__((noinline, noclone))
|
||||||
|
amb_x (int i)
|
||||||
|
{
|
||||||
|
amb_y (i + 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __attribute__((noinline, noclone))
|
||||||
|
amb (int i)
|
||||||
|
{
|
||||||
|
if (i < 0)
|
||||||
|
amb_x (i + 3);
|
||||||
|
else
|
||||||
|
amb_x (i + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __attribute__((noinline, noclone))
|
||||||
|
amb_b (int i)
|
||||||
|
{
|
||||||
|
amb (i + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __attribute__((noinline, noclone))
|
||||||
|
amb_a (int i)
|
||||||
|
{
|
||||||
|
amb_b (i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main ()
|
main ()
|
||||||
{
|
{
|
||||||
d (30, 30.5);
|
d (30, 30.5);
|
||||||
|
if (v)
|
||||||
|
a (1, 1.25);
|
||||||
|
else
|
||||||
|
b (5, 5.25);
|
||||||
|
amb_a (100);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,3 +45,31 @@ gdb_test "bt" "^bt\r\n#0 +d *\\(i=31, j=31\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]
|
||||||
"entry: bt"
|
"entry: bt"
|
||||||
gdb_test "p i" " = 31" "entry: p i"
|
gdb_test "p i" " = 31" "entry: p i"
|
||||||
gdb_test "p j" { = 31\.5} "entry: p j"
|
gdb_test "p j" { = 31\.5} "entry: p j"
|
||||||
|
|
||||||
|
|
||||||
|
# Test virtual tail call frames.
|
||||||
|
|
||||||
|
gdb_continue_to_breakpoint "tailcall: breakhere"
|
||||||
|
|
||||||
|
gdb_test "bt" "^bt\r\n#0 +d *\\(i=71, j=73\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in c \\(i=7, j=7\\.25\\) \[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in b \\(i=5, j=5\\.25\\) \[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in main \[^\r\n\]*" \
|
||||||
|
"tailcall: bt"
|
||||||
|
gdb_test "p i" " = 71" "tailcall: p i"
|
||||||
|
gdb_test "p j" " = 73\\.5" "tailcall: p j"
|
||||||
|
|
||||||
|
# Test $sp simulation for tail call frames.
|
||||||
|
#gdb_test {p/x $sp} " = 0x.*"
|
||||||
|
#gdb_test {p/x $pc} " = 0x.*"
|
||||||
|
gdb_test_no_output {set $sp0=$sp}
|
||||||
|
gdb_test "up" "\r\n#1 .*"
|
||||||
|
#gdb_test {p/x $sp} " = 0x.*"
|
||||||
|
gdb_test {p $sp0 == $sp} " = true"
|
||||||
|
gdb_test "frame 3" "\r\n#3 .*"
|
||||||
|
gdb_test {p $sp0 + sizeof (void *) == $sp} " = true"
|
||||||
|
|
||||||
|
|
||||||
|
# Test partial-ambiguous virtual tail call frames chain.
|
||||||
|
|
||||||
|
gdb_continue_to_breakpoint "ambiguous: breakhere"
|
||||||
|
|
||||||
|
gdb_test "bt" "^bt\r\n#0 +d \\(i=<optimized out>, j=<optimized out>\\)\[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in amb_z \\(i=<optimized out>\\)\[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in amb_y \\(i=<optimized out>\\)\[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in amb_x \\(i=<optimized out>\\)\[^\r\n\]*\r\n#4 +0x\[0-9a-f\]+ in amb_b \\(i=101\\)\[^\r\n\]*\r\n#5 +0x\[0-9a-f\]+ in amb_a \\(i=100\\)\[^\r\n\]*\r\n#6 +0x\[0-9a-f\]+ in main \\(\\)\[^\r\n\]*" \
|
||||||
|
"ambiguous: bt"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue