diff options
Diffstat (limited to 'coregrind/.svn/text-base/m_stacktrace.c.svn-base')
-rw-r--r-- | coregrind/.svn/text-base/m_stacktrace.c.svn-base | 580 |
1 files changed, 580 insertions, 0 deletions
diff --git a/coregrind/.svn/text-base/m_stacktrace.c.svn-base b/coregrind/.svn/text-base/m_stacktrace.c.svn-base new file mode 100644 index 0000000..aeb98ad --- /dev/null +++ b/coregrind/.svn/text-base/m_stacktrace.c.svn-base @@ -0,0 +1,580 @@ + +/*--------------------------------------------------------------------*/ +/*--- Take snapshots of client stacks. m_stacktrace.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2009 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_threadstate.h" +#include "pub_core_debuginfo.h" // XXX: circular dependency +#include "pub_core_aspacemgr.h" // For VG_(is_addressable)() +#include "pub_core_libcbase.h" +#include "pub_core_libcassert.h" +#include "pub_core_libcprint.h" +#include "pub_core_machine.h" +#include "pub_core_options.h" +#include "pub_core_stacks.h" // VG_(stack_limits) +#include "pub_core_stacktrace.h" +#include "pub_core_xarray.h" +#include "pub_core_clientstate.h" // VG_(client__dl_sysinfo_int80) +#include "pub_core_trampoline.h" + +/*------------------------------------------------------------*/ +/*--- Exported functions. ---*/ +/*------------------------------------------------------------*/ + +/* Take a snapshot of the client's stack, putting the up to 'n_ips' + IPs into 'ips'. In order to be thread-safe, we pass in the + thread's IP SP, FP if that's meaningful, and LR if that's + meaningful. Returns number of IPs put in 'ips'. + + If you know what the thread ID for this stack is, send that as the + first parameter, else send zero. This helps generate better stack + traces on ppc64-linux and has no effect on other platforms. +*/ +UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known, + /*OUT*/Addr* ips, UInt n_ips, + /*OUT*/Addr* sps, /*OUT*/Addr* fps, + Addr ip, Addr sp, Addr fp, Addr lr, + Addr fp_min, Addr fp_max_orig ) +{ +# if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux) \ + || defined(VGP_ppc32_aix5) \ + || defined(VGP_ppc64_aix5) + Bool lr_is_first_RA = False; +# endif +# if defined(VGP_ppc64_linux) || defined(VGP_ppc64_aix5) \ + || defined(VGP_ppc32_aix5) + Word redir_stack_size = 0; + Word redirs_used = 0; +# endif + + Bool debug = False; + Int i; + Addr fp_max; + UInt n_found = 0; + + vg_assert(sizeof(Addr) == sizeof(UWord)); + vg_assert(sizeof(Addr) == sizeof(void*)); + + /* Snaffle IPs from the client's stack into ips[0 .. n_ips-1], + stopping when the trail goes cold, which we guess to be + when FP is not a reasonable stack location. */ + + // JRS 2002-sep-17: hack, to round up fp_max to the end of the + // current page, at least. Dunno if it helps. + // NJN 2002-sep-17: seems to -- stack traces look like 1.0.X again + fp_max = VG_PGROUNDUP(fp_max_orig); + if (fp_max >= sizeof(Addr)) + fp_max -= sizeof(Addr); + + if (debug) + VG_(printf)("n_ips=%d fp_min=0x%lx fp_max_orig=0x%lx, " + "fp_max=0x%lx ip=0x%lx fp=0x%lx\n", + n_ips, fp_min, fp_max_orig, fp_max, ip, fp); + + /* Assertion broken before main() is reached in pthreaded programs; the + * offending stack traces only have one item. --njn, 2002-aug-16 */ + /* vg_assert(fp_min <= fp_max);*/ + if (fp_min + 512 >= fp_max) { + /* If the stack limits look bogus, don't poke around ... but + don't bomb out either. */ + if (sps) sps[0] = sp; + if (fps) fps[0] = fp; + ips[0] = ip; + return 1; + } + + /* Otherwise unwind the stack in a platform-specific way. Trying + to merge the x86, amd64, ppc32 and ppc64 logic into a single + piece of code is just too confusing and difficult to + performance-tune. */ + +# if defined(VGP_x86_linux) + + /*--------------------- x86 ---------------------*/ + + /* fp is %ebp. sp is %esp. ip is %eip. */ + + if (sps) sps[0] = sp; + if (fps) fps[0] = fp; + ips[0] = ip; + i = 1; + + /* Loop unwinding the stack. Note that the IP value we get on + * each pass (whether from CFI info or a stack frame) is a + * return address so is actually after the calling instruction + * in the calling function. + * + * Because of this we subtract one from the IP after each pass + * of the loop so that we find the right CFI block on the next + * pass - otherwise we can find the wrong CFI info if it happens + * to change after the calling instruction and that will mean + * that we will fail to unwind the next step. + * + * This most frequently happens at the end of a function when + * a tail call occurs and we wind up using the CFI info for the + * next function which is completely wrong. + */ + while (True) { + + if (i >= n_ips) + break; + + /* Try to derive a new (ip,sp,fp) triple from the current + set. */ + + /* On x86, first try the old-fashioned method of following the + %ebp-chain. Code which doesn't use this (that is, compiled + with -fomit-frame-pointer) is not ABI compliant and so + relatively rare. Besides, trying the CFI first almost always + fails, and is expensive. */ + /* Deal with frames resulting from functions which begin "pushl% + ebp ; movl %esp, %ebp" which is the ABI-mandated preamble. */ + if (fp_min <= fp && fp <= fp_max + - 1 * sizeof(UWord)/*see comment below*/) { + /* fp looks sane, so use it. */ + ip = (((UWord*)fp)[1]); + sp = fp + sizeof(Addr) /*saved %ebp*/ + + sizeof(Addr) /*ra*/; + fp = (((UWord*)fp)[0]); + if (sps) sps[i] = sp; + if (fps) fps[i] = fp; + ips[i++] = ip - 1; /* -1: refer to calling insn, not the RA */ + if (debug) + VG_(printf)(" ipsF[%d]=0x%08lx\n", i-1, ips[i-1]); + ip = ip - 1; /* as per comment at the head of this loop */ + continue; + } + + /* That didn't work out, so see if there is any CF info to hand + which can be used. */ + if ( VG_(use_CF_info)( &ip, &sp, &fp, fp_min, fp_max ) ) { + if (sps) sps[i] = sp; + if (fps) fps[i] = fp; + ips[i++] = ip - 1; /* -1: refer to calling insn, not the RA */ + if (debug) + VG_(printf)(" ipsC[%d]=0x%08lx\n", i-1, ips[i-1]); + ip = ip - 1; /* as per comment at the head of this loop */ + continue; + } + + /* And, similarly, try for MSVC FPO unwind info. */ + if ( VG_(use_FPO_info)( &ip, &sp, &fp, fp_min, fp_max ) ) { + if (sps) sps[i] = sp; + if (fps) fps[i] = fp; + ips[i++] = ip; + if (debug) + VG_(printf)(" ipsC[%d]=0x%08lx\n", i-1, ips[i-1]); + ip = ip - 1; + continue; + } + + /* No luck. We have to give up. */ + break; + } + +# elif defined(VGP_amd64_linux) + + /*--------------------- amd64 ---------------------*/ + + /* fp is %rbp. sp is %rsp. ip is %rip. */ + + ips[0] = ip; + if (sps) sps[0] = sp; + if (fps) fps[0] = fp; + i = 1; + + /* Loop unwinding the stack. Note that the IP value we get on + * each pass (whether from CFI info or a stack frame) is a + * return address so is actually after the calling instruction + * in the calling function. + * + * Because of this we subtract one from the IP after each pass + * of the loop so that we find the right CFI block on the next + * pass - otherwise we can find the wrong CFI info if it happens + * to change after the calling instruction and that will mean + * that we will fail to unwind the next step. + * + * This most frequently happens at the end of a function when + * a tail call occurs and we wind up using the CFI info for the + * next function which is completely wrong. + */ + while (True) { + + if (i >= n_ips) + break; + + /* Try to derive a new (ip,sp,fp) triple from the current + set. */ + + /* First off, see if there is any CFI info to hand which can + be used. */ + if ( VG_(use_CF_info)( &ip, &sp, &fp, fp_min, fp_max ) ) { + if (sps) sps[i] = sp; + if (fps) fps[i] = fp; + ips[i++] = ip - 1; /* -1: refer to calling insn, not the RA */ + if (debug) + VG_(printf)(" ipsC[%d]=%#08lx\n", i-1, ips[i-1]); + ip = ip - 1; /* as per comment at the head of this loop */ + continue; + } + + /* If VG_(use_CF_info) fails, it won't modify ip/sp/fp, so + we can safely try the old-fashioned method. */ + /* This bit is supposed to deal with frames resulting from + functions which begin "pushq %rbp ; movq %rsp, %rbp". + Unfortunately, since we can't (easily) look at the insns at + the start of the fn, like GDB does, there's no reliable way + to tell. Hence the hack of first trying out CFI, and if that + fails, then use this as a fallback. */ + /* Note: re "- 1 * sizeof(UWord)", need to take account of the + fact that we are prodding at & ((UWord*)fp)[1] and so need to + adjust the limit check accordingly. Omitting this has been + observed to cause segfaults on rare occasions. */ + if (fp_min <= fp && fp <= fp_max - 1 * sizeof(UWord)) { + /* fp looks sane, so use it. */ + ip = (((UWord*)fp)[1]); + sp = fp + sizeof(Addr) /*saved %rbp*/ + + sizeof(Addr) /*ra*/; + fp = (((UWord*)fp)[0]); + if (sps) sps[i] = sp; + if (fps) fps[i] = fp; + ips[i++] = ip - 1; /* -1: refer to calling insn, not the RA */ + if (debug) + VG_(printf)(" ipsF[%d]=%#08lx\n", i-1, ips[i-1]); + ip = ip - 1; /* as per comment at the head of this loop */ + continue; + } + + /* Last-ditch hack (evidently GDB does something similar). We + are in the middle of nowhere and we have a nonsense value for + the frame pointer. If the stack pointer is still valid, + assume that what it points at is a return address. Yes, + desperate measures. Could do better here: + - check that the supposed return address is in + an executable page + - check that the supposed return address is just after a call insn + - given those two checks, don't just consider *sp as the return + address; instead scan a likely section of stack (eg sp .. sp+256) + and use suitable values found there. + */ + if (fp_min <= sp && sp < fp_max) { + ip = ((UWord*)sp)[0]; + if (sps) sps[i] = sp; + if (fps) fps[i] = fp; + ips[i++] = ip == 0 + ? 0 /* sp[0] == 0 ==> stuck at the bottom of a + thread stack */ + : ip - 1; /* -1: refer to calling insn, not the RA */ + if (debug) + VG_(printf)(" ipsH[%d]=%#08lx\n", i-1, ips[i-1]); + ip = ip - 1; /* as per comment at the head of this loop */ + sp += 8; + continue; + } + + /* No luck at all. We have to give up. */ + break; + } + +# elif defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux) \ + || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) + + /*--------------------- ppc32/64 ---------------------*/ + + /* fp is %r1. ip is %cia. Note, ppc uses r1 as both the stack and + frame pointers. */ + +# if defined(VGP_ppc64_linux) || defined(VGP_ppc64_aix5) + redir_stack_size = VEX_GUEST_PPC64_REDIR_STACK_SIZE; + redirs_used = 0; +# elif defined(VGP_ppc32_aix5) + redir_stack_size = VEX_GUEST_PPC32_REDIR_STACK_SIZE; + redirs_used = 0; +# endif + +# if defined(VG_PLAT_USES_PPCTOC) + /* Deal with bogus LR values caused by function + interception/wrapping on ppc-TOC platforms; see comment on + similar code a few lines further down. */ + if (ULong_to_Ptr(lr) == (void*)&VG_(ppctoc_magic_redirect_return_stub) + && VG_(is_valid_tid)(tid_if_known)) { + Word hsp = VG_(threads)[tid_if_known].arch.vex.guest_REDIR_SP; + redirs_used++; + if (hsp >= 1 && hsp < redir_stack_size) + lr = VG_(threads)[tid_if_known] + .arch.vex.guest_REDIR_STACK[hsp-1]; + } +# endif + + /* We have to determine whether or not LR currently holds this fn + (call it F)'s return address. It might not if F has previously + called some other function, hence overwriting LR with a pointer + to some part of F. Hence if LR and IP point to the same + function then we conclude LR does not hold this function's + return address; instead the LR at entry must have been saved in + the stack by F's prologue and so we must get it from there + instead. Note all this guff only applies to the innermost + frame. */ + lr_is_first_RA = False; + { +# define M_VG_ERRTXT 1000 + UChar buf_lr[M_VG_ERRTXT], buf_ip[M_VG_ERRTXT]; + /* The following conditional looks grossly inefficient and + surely could be majorly improved, with not much effort. */ + if (VG_(get_fnname_raw) (lr, buf_lr, M_VG_ERRTXT)) + if (VG_(get_fnname_raw) (ip, buf_ip, M_VG_ERRTXT)) + if (VG_(strncmp)(buf_lr, buf_ip, M_VG_ERRTXT)) + lr_is_first_RA = True; +# undef M_VG_ERRTXT + } + + if (sps) sps[0] = fp; /* NB. not sp */ + if (fps) fps[0] = fp; + ips[0] = ip; + i = 1; + + if (fp_min <= fp && fp < fp_max-VG_WORDSIZE+1) { + + /* initial FP is sane; keep going */ + fp = (((UWord*)fp)[0]); + + while (True) { + + /* On ppc64-linux (ppc64-elf, really), and on AIX, the lr save + slot is 2 words back from sp, whereas on ppc32-elf(?) it's + only one word back. */ +# if defined(VGP_ppc64_linux) \ + || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) + const Int lr_offset = 2; +# else + const Int lr_offset = 1; +# endif + + if (i >= n_ips) + break; + + /* Try to derive a new (ip,fp) pair from the current set. */ + + if (fp_min <= fp && fp <= fp_max - lr_offset * sizeof(UWord)) { + /* fp looks sane, so use it. */ + + if (i == 1 && lr_is_first_RA) + ip = lr; + else + ip = (((UWord*)fp)[lr_offset]); + +# if defined(VG_PLAT_USES_PPCTOC) + /* Nasty hack to do with function replacement/wrapping on + ppc64-linux/ppc64-aix/ppc32-aix. If LR points to our + magic return stub, then we are in a wrapped or + intercepted function, in which LR has been messed with. + The original LR will have been pushed onto the thread's + hidden REDIR stack one down from the top (top element + is the saved R2) and so we should restore the value + from there instead. Since nested redirections can and + do happen, we keep track of the number of nested LRs + used by the unwinding so far with 'redirs_used'. */ + if (ip == (Addr)&VG_(ppctoc_magic_redirect_return_stub) + && VG_(is_valid_tid)(tid_if_known)) { + Word hsp = VG_(threads)[tid_if_known] + .arch.vex.guest_REDIR_SP; + hsp -= 2 * redirs_used; + redirs_used ++; + if (hsp >= 1 && hsp < redir_stack_size) + ip = VG_(threads)[tid_if_known] + .arch.vex.guest_REDIR_STACK[hsp-1]; + } +# endif + + fp = (((UWord*)fp)[0]); + if (sps) sps[i] = fp; /* NB. not sp */ + if (fps) fps[i] = fp; + ips[i++] = ip - 1; /* -1: refer to calling insn, not the RA */ + if (debug) + VG_(printf)(" ipsF[%d]=%#08lx\n", i-1, ips[i-1]); + ip = ip - 1; /* ip is probably dead at this point, but + play safe, a la x86/amd64 above. See + extensive comments above. */ + continue; + } + + /* No luck there. We have to give up. */ + break; + } + } + +# else +# error "Unknown platform" +# endif + + n_found = i; + return n_found; +} + +UInt VG_(get_StackTrace) ( ThreadId tid, + /*OUT*/StackTrace ips, UInt n_ips, + /*OUT*/StackTrace sps, + /*OUT*/StackTrace fps, + Word first_ip_delta ) +{ + /* thread in thread table */ + Addr ip = VG_(get_IP)(tid); + Addr fp = VG_(get_FP)(tid); + Addr sp = VG_(get_SP)(tid); + Addr lr = VG_(get_LR)(tid); + Addr stack_highest_word = VG_(threads)[tid].client_stack_highest_word; + Addr stack_lowest_word = 0; + +# if defined(VGP_x86_linux) + /* Nasty little hack to deal with syscalls - if libc is using its + _dl_sysinfo_int80 function for syscalls (the TLS version does), + then ip will always appear to be in that function when doing a + syscall, not the actual libc function doing the syscall. This + check sees if IP is within that function, and pops the return + address off the stack so that ip is placed within the library + function calling the syscall. This makes stack backtraces much + more useful. + + The function is assumed to look like this (from glibc-2.3.6 sources): + _dl_sysinfo_int80: + int $0x80 + ret + That is 3 (2+1) bytes long. We could be more thorough and check + the 3 bytes of the function are as expected, but I can't be + bothered. + */ + if (VG_(client__dl_sysinfo_int80) != 0 /* we know its address */ + && ip >= VG_(client__dl_sysinfo_int80) + && ip < VG_(client__dl_sysinfo_int80)+3 + && VG_(am_is_valid_for_client)(sp, sizeof(Addr), VKI_PROT_READ)) { + ip = *(Addr *)sp; + sp += sizeof(Addr); + } +# endif + + /* See if we can get a better idea of the stack limits */ + VG_(stack_limits)(sp, &stack_lowest_word, &stack_highest_word); + + /* Take into account the first_ip_delta. */ + vg_assert( sizeof(Addr) == sizeof(Word) ); + ip += first_ip_delta; + + if (0) + VG_(printf)("tid %d: stack_highest=0x%08lx ip=0x%08lx " + "sp=0x%08lx fp=0x%08lx\n", + tid, stack_highest_word, ip, sp, fp); + + return VG_(get_StackTrace_wrk)(tid, ips, n_ips, + sps, fps, + ip, sp, fp, lr, sp, + stack_highest_word); +} + +static void printIpDesc(UInt n, Addr ip) +{ + #define BUF_LEN 4096 + + static UChar buf[BUF_LEN]; + + VG_(describe_IP)(ip, buf, BUF_LEN); + + if (VG_(clo_xml)) { + VG_(message)(Vg_UserMsg, " %s", buf); + } else { + VG_(message)(Vg_UserMsg, " %s %s", ( n == 0 ? "at" : "by" ), buf); + } +} + +/* Print a StackTrace. */ +void VG_(pp_StackTrace) ( StackTrace ips, UInt n_ips ) +{ + vg_assert( n_ips > 0 ); + + if (VG_(clo_xml)) + VG_(message)(Vg_UserMsg, " <stack>"); + + VG_(apply_StackTrace)( printIpDesc, ips, n_ips ); + + if (VG_(clo_xml)) + VG_(message)(Vg_UserMsg, " </stack>"); +} + +/* Get and immediately print a StackTrace. */ +void VG_(get_and_pp_StackTrace) ( ThreadId tid, UInt n_ips ) +{ + Addr ips[n_ips]; + UInt n_ips_obtained + = VG_(get_StackTrace)(tid, ips, n_ips, + NULL/*array to dump SP values in*/, + NULL/*array to dump FP values in*/, + 0/*first_ip_delta*/); + VG_(pp_StackTrace)(ips, n_ips_obtained); +} + + +void VG_(apply_StackTrace)( void(*action)(UInt n, Addr ip), + StackTrace ips, UInt n_ips ) +{ + Bool main_done = False; + Int i = 0; + + vg_assert(n_ips > 0); + do { + Addr ip = ips[i]; + + // Stop after the first appearance of "main" or one of the other names + // (the appearance of which is a pretty good sign that we've gone past + // main without seeing it, for whatever reason) + if ( ! VG_(clo_show_below_main) ) { + Vg_FnNameKind kind = VG_(get_fnname_kind_from_IP)(ip); + if (Vg_FnNameMain == kind || Vg_FnNameBelowMain == kind) { + main_done = True; + } + } + + // Act on the ip + action(i, ip); + + i++; + // re 'while' condition: stop if we hit a zero value (the traditional + // end-of-stack marker) or a ~0 value. The latter because r8818 + // (in this file) changes the meaning of entries [1] and above in a + // stack trace, by subtracting 1 from them. Hence stacks that used + // to end with a zero value now end in -1 and so we must detect + // that too. + } while (i < n_ips && ips[i] != 0 && ips[i] != ~(Addr)0 && !main_done); + + #undef MYBUF_LEN +} + + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ |