blob: d9c76135a1d71c535a9aab78a7ffa225e19e27cd [file] [log] [blame]
sewardj267100d2005-04-24 12:33:12 +00001
njnd01fef72005-03-25 23:35:48 +00002/*--------------------------------------------------------------------*/
sewardj267100d2005-04-24 12:33:12 +00003/*--- Take snapshots of client stacks. m_stacktrace.c ---*/
njnd01fef72005-03-25 23:35:48 +00004/*--------------------------------------------------------------------*/
5
6/*
7 This file is part of Valgrind, a dynamic binary instrumentation
8 framework.
9
10 Copyright (C) 2000-2005 Julian Seward
11 jseward@acm.org
12
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 02111-1307, USA.
27
28 The GNU General Public License is contained in the file COPYING.
29*/
30
31#include "core.h"
32#include "pub_core_stacktrace.h"
33
34/*------------------------------------------------------------*/
35/*--- Exported functions. ---*/
36/*------------------------------------------------------------*/
37
38/* Take a snapshot of the client's stack, putting the up to 'n_ips' IPs
39 into 'ips'. In order to be thread-safe, we pass in the thread's IP
40 and FP. Returns number of IPs put in 'ips'. */
sewardj35165532005-04-30 18:47:48 +000041UInt VG_(get_StackTrace2) ( Addr* ips, UInt n_ips,
42 Addr ip, Addr sp, Addr fp,
njnd01fef72005-03-25 23:35:48 +000043 Addr fp_min, Addr fp_max_orig )
44{
45 static const Bool debug = False;
46 Int i;
47 Addr fp_max;
48 UInt n_found = 0;
49
50 VGP_PUSHCC(VgpExeContext);
51
52 /* First snaffle IPs from the client's stack into ips[0 .. n_ips-1],
53 putting zeroes in when the trail goes cold, which we guess to be when
54 FP is not a reasonable stack location. We also assert that FP
55 increases down the chain. */
56
57 // Gives shorter stack trace for tests/badjump.c
58 // JRS 2002-aug-16: I don't think this is a big deal; looks ok for
59 // most "normal" backtraces.
60 // NJN 2002-sep-05: traces for pthreaded programs are particularly bad.
61
62 // JRS 2002-sep-17: hack, to round up fp_max to the end of the
63 // current page, at least. Dunno if it helps.
64 // NJN 2002-sep-17: seems to -- stack traces look like 1.0.X again
65 fp_max = (fp_max_orig + VKI_PAGE_SIZE - 1) & ~(VKI_PAGE_SIZE - 1);
66 fp_max -= sizeof(Addr);
67
68 if (debug)
69 VG_(printf)("n_ips=%d fp_min=%p fp_max_orig=%p, fp_max=%p ip=%p fp=%p\n",
70 n_ips, fp_min, fp_max_orig, fp_max, ip, fp);
71
72 /* Assertion broken before main() is reached in pthreaded programs; the
73 * offending stack traces only have one item. --njn, 2002-aug-16 */
74 /* vg_assert(fp_min <= fp_max);*/
75
sewardj35165532005-04-30 18:47:48 +000076 ips[0] = ip;
77 i = 1;
78
79 if (fp_min + VG_(clo_max_stackframe) <= fp_max) {
njnd01fef72005-03-25 23:35:48 +000080 /* If the stack is ridiculously big, don't poke around ... but
81 don't bomb out either. Needed to make John Regehr's
82 user-space threads package work. JRS 20021001 */
njnd01fef72005-03-25 23:35:48 +000083 } else {
sewardj35165532005-04-30 18:47:48 +000084
85 while (True) {
86
87 if (i >= n_ips)
88 break;
89
90 /* Try to derive a new (ip,sp,fp) triple from the current
91 set. */
92
93 /* First off, see if there is any CFI info to hand which can
94 be used. */
95 if ( VG_(use_CFI_info)( &ip, &sp, &fp, fp_min, fp_max ) ) {
96 ips[i++] = ip;
97 if (debug)
98 VG_(printf)(" ipsC[%d]=%08p\n", i-1, ips[i-1]);
99 continue;
100 }
101
102 /* If VG_(use_CFI_info) fails, it won't modify ip/sp/fp, so
103 we can safely try the old-fashioned method. */
104 /* This bit is supposed to deal with frames resulting from
105 functions which begin "pushl% ebp ; movl %esp, %ebp" (x86)
106 or "pushl% ebp ; movl %esp, %ebp" (amd64). Unfortunately,
107 since we can't (easily) look at the insns at the start of
108 the fn, like GDB does, there's no reliable way to tell.
109 Hence the hack of first trying out CFI, and if that fails,
110 then use this as a fallback. */
111 if (fp_min <= fp && fp <= fp_max) {
112 /* fp looks sane, so use it. */
113 ip = VGA_STACK_FRAME_RET(fp);
114 sp = fp + sizeof(Addr) /*saved %ebp/%rbp*/
115 + sizeof(Addr) /*ra*/;
116 fp = VGA_STACK_FRAME_NEXT(fp);
117 ips[i++] = ip;
118 if (debug)
119 VG_(printf)(" ipsF[%d]=%08p\n", i-1, ips[i-1]);
120 continue;
121 }
122
123 /* No luck there. We have to give up. */
124 break;
njnd01fef72005-03-25 23:35:48 +0000125 }
sewardj35165532005-04-30 18:47:48 +0000126
njnd01fef72005-03-25 23:35:48 +0000127 }
128 n_found = i;
129
130 /* Put zeroes in the rest. */
131 for (; i < n_ips; i++) {
132 ips[i] = 0;
133 }
134 VGP_POPCC(VgpExeContext);
135
136 return n_found;
137}
138
139UInt VG_(get_StackTrace) ( ThreadId tid, StackTrace ips, UInt n_ips )
140{
141 /* thread in thread table */
142 ThreadState* tst = & VG_(threads)[ tid ];
143 Addr ip = INSTR_PTR(tst->arch);
144 Addr fp = FRAME_PTR(tst->arch);
145 Addr sp = STACK_PTR(tst->arch);
njn50ba34e2005-04-04 02:41:42 +0000146 Addr stack_highest_word = tst->client_stack_highest_word;
njnd01fef72005-03-25 23:35:48 +0000147
148#ifdef __x86__
149 /* Nasty little hack to deal with sysinfo syscalls - if libc is
150 using the sysinfo page for syscalls (the TLS version does), then
151 ip will always appear to be in that page when doing a syscall,
152 not the actual libc function doing the syscall. This check sees
153 if IP is within the syscall code, and pops the return address
154 off the stack so that ip is placed within the library function
155 calling the syscall. This makes stack backtraces much more
156 useful. */
157 if (ip >= VG_(client_trampoline_code)+VG_(tramp_syscall_offset) &&
158 ip < VG_(client_trampoline_code)+VG_(trampoline_code_length) &&
159 VG_(is_addressable)(sp, sizeof(Addr), VKI_PROT_READ)) {
160 ip = *(Addr *)sp;
161 sp += sizeof(Addr);
162 }
163#endif
164 if (0)
165 VG_(printf)("tid %d: stack_highest=%p ip=%p sp=%p fp=%p\n",
166 tid, stack_highest_word, ip, sp, fp);
167
sewardj35165532005-04-30 18:47:48 +0000168 return VG_(get_StackTrace2)(ips, n_ips, ip, sp, fp, sp, stack_highest_word);
njnd01fef72005-03-25 23:35:48 +0000169}
170
171static void printIpDesc(UInt n, Addr ip)
172{
njn47b209a2005-03-25 23:47:16 +0000173 static UChar buf[VG_ERRTXT_LEN];
njnd01fef72005-03-25 23:35:48 +0000174
njn47b209a2005-03-25 23:47:16 +0000175 VG_(describe_IP)(ip, buf, VG_ERRTXT_LEN);
njnd01fef72005-03-25 23:35:48 +0000176 VG_(message)(Vg_UserMsg, " %s %s", ( n == 0 ? "at" : "by" ), buf);
177}
178
179/* Print a StackTrace. */
180void VG_(pp_StackTrace) ( StackTrace ips, UInt n_ips )
181{
182 vg_assert( n_ips > 0 );
183 VG_(apply_StackTrace)( printIpDesc, ips, n_ips );
184}
185
186/* Get and immediately print a StackTrace. */
187void VG_(get_and_pp_StackTrace) ( ThreadId tid, UInt n_ips )
188{
189 Addr ips[n_ips];
190 VG_(get_StackTrace)(tid, ips, n_ips);
191 VG_(pp_StackTrace) ( ips, n_ips);
192}
193
194
195void VG_(apply_StackTrace)( void(*action)(UInt n, Addr ip),
196 StackTrace ips, UInt n_ips )
197{
198 #define MYBUF_LEN 10 // only needs to be long enough for "main"
199
200 Bool main_done = False;
201 Char mybuf[MYBUF_LEN]; // ok to stack allocate mybuf[] -- it's tiny
202 Int i = 0;
203
204 vg_assert(n_ips > 0);
205 do {
206 Addr ip = ips[i];
207 if (i > 0)
njn9fb73db2005-03-27 01:55:21 +0000208 ip -= VGA_MIN_INSTR_SIZE; // point to calling line
njnd01fef72005-03-25 23:35:48 +0000209
210 // Stop after "main"; if main() is recursive, stop after last main().
211 if ( ! VG_(clo_show_below_main)) {
212 VG_(get_fnname_nodemangle)( ip, mybuf, MYBUF_LEN );
213 if ( VG_STREQ("main", mybuf) )
214 main_done = True;
215 else if (main_done)
216 break;
217 }
218
219 // Act on the ip
220 action(i, ip);
221
222 i++;
223 } while (i < n_ips && ips[i] != 0);
224
225 #undef MYBUF_LEN
226}
227
228
229/*--------------------------------------------------------------------*/
sewardj267100d2005-04-24 12:33:12 +0000230/*--- end m_stacktrace.c ---*/
njnd01fef72005-03-25 23:35:48 +0000231/*--------------------------------------------------------------------*/