blob: 365b9159419e421074e31f5b2c4752cf1b5c0e17 [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"
njn04e16982005-05-31 00:23:43 +000032#include "pub_core_aspacemgr.h"
njn20242342005-05-16 23:31:24 +000033#include "pub_core_options.h"
njn31513b42005-06-01 03:09:59 +000034#include "pub_core_profile.h"
njnd01fef72005-03-25 23:35:48 +000035#include "pub_core_stacktrace.h"
36
37/*------------------------------------------------------------*/
38/*--- Exported functions. ---*/
39/*------------------------------------------------------------*/
40
njn88b5a982005-05-16 00:16:56 +000041// Stack frame layout and linkage
42#if defined(VGA_x86)
43# define FIRST_STACK_FRAME(ebp) (ebp)
44# define STACK_FRAME_RET(ebp) (((UWord*)ebp)[1])
45# define STACK_FRAME_NEXT(ebp) (((UWord*)ebp)[0])
46#elif defined(VGA_amd64)
47# define FIRST_STACK_FRAME(rbp) (rbp)
48# define STACK_FRAME_RET(rbp) (((UWord*)rbp)[1])
49# define STACK_FRAME_NEXT(rbp) (((UWord*)rbp)[0])
50#else
51# error Unknown platform
52#endif
53
njnd01fef72005-03-25 23:35:48 +000054/* Take a snapshot of the client's stack, putting the up to 'n_ips' IPs
55 into 'ips'. In order to be thread-safe, we pass in the thread's IP
56 and FP. Returns number of IPs put in 'ips'. */
sewardj35165532005-04-30 18:47:48 +000057UInt VG_(get_StackTrace2) ( Addr* ips, UInt n_ips,
58 Addr ip, Addr sp, Addr fp,
njnd01fef72005-03-25 23:35:48 +000059 Addr fp_min, Addr fp_max_orig )
60{
61 static const Bool debug = False;
62 Int i;
63 Addr fp_max;
64 UInt n_found = 0;
65
66 VGP_PUSHCC(VgpExeContext);
67
68 /* First snaffle IPs from the client's stack into ips[0 .. n_ips-1],
69 putting zeroes in when the trail goes cold, which we guess to be when
70 FP is not a reasonable stack location. We also assert that FP
71 increases down the chain. */
72
73 // Gives shorter stack trace for tests/badjump.c
74 // JRS 2002-aug-16: I don't think this is a big deal; looks ok for
75 // most "normal" backtraces.
76 // NJN 2002-sep-05: traces for pthreaded programs are particularly bad.
77
78 // JRS 2002-sep-17: hack, to round up fp_max to the end of the
79 // current page, at least. Dunno if it helps.
80 // NJN 2002-sep-17: seems to -- stack traces look like 1.0.X again
81 fp_max = (fp_max_orig + VKI_PAGE_SIZE - 1) & ~(VKI_PAGE_SIZE - 1);
82 fp_max -= sizeof(Addr);
83
84 if (debug)
85 VG_(printf)("n_ips=%d fp_min=%p fp_max_orig=%p, fp_max=%p ip=%p fp=%p\n",
86 n_ips, fp_min, fp_max_orig, fp_max, ip, fp);
87
88 /* Assertion broken before main() is reached in pthreaded programs; the
89 * offending stack traces only have one item. --njn, 2002-aug-16 */
90 /* vg_assert(fp_min <= fp_max);*/
91
sewardj35165532005-04-30 18:47:48 +000092 ips[0] = ip;
93 i = 1;
94
95 if (fp_min + VG_(clo_max_stackframe) <= fp_max) {
njnd01fef72005-03-25 23:35:48 +000096 /* If the stack is ridiculously big, don't poke around ... but
97 don't bomb out either. Needed to make John Regehr's
98 user-space threads package work. JRS 20021001 */
njnd01fef72005-03-25 23:35:48 +000099 } else {
sewardj35165532005-04-30 18:47:48 +0000100
njn88b5a982005-05-16 00:16:56 +0000101 // XXX: I think this line is needed for PPC to work, but I'm not
102 // certain, so I'm commenting it out for the moment.
103 //fp = FIRST_STACK_FRAME(fp)
104
sewardj35165532005-04-30 18:47:48 +0000105 while (True) {
106
107 if (i >= n_ips)
108 break;
109
110 /* Try to derive a new (ip,sp,fp) triple from the current
111 set. */
112
113 /* First off, see if there is any CFI info to hand which can
114 be used. */
115 if ( VG_(use_CFI_info)( &ip, &sp, &fp, fp_min, fp_max ) ) {
116 ips[i++] = ip;
117 if (debug)
118 VG_(printf)(" ipsC[%d]=%08p\n", i-1, ips[i-1]);
119 continue;
120 }
121
122 /* If VG_(use_CFI_info) fails, it won't modify ip/sp/fp, so
123 we can safely try the old-fashioned method. */
124 /* This bit is supposed to deal with frames resulting from
125 functions which begin "pushl% ebp ; movl %esp, %ebp" (x86)
sewardj0b954fd2005-05-16 11:47:17 +0000126 or "pushq %rbp ; movq %rsp, %rbp" (amd64). Unfortunately,
sewardj35165532005-04-30 18:47:48 +0000127 since we can't (easily) look at the insns at the start of
128 the fn, like GDB does, there's no reliable way to tell.
129 Hence the hack of first trying out CFI, and if that fails,
130 then use this as a fallback. */
131 if (fp_min <= fp && fp <= fp_max) {
132 /* fp looks sane, so use it. */
njn88b5a982005-05-16 00:16:56 +0000133 ip = STACK_FRAME_RET(fp);
sewardj35165532005-04-30 18:47:48 +0000134 sp = fp + sizeof(Addr) /*saved %ebp/%rbp*/
135 + sizeof(Addr) /*ra*/;
njn88b5a982005-05-16 00:16:56 +0000136 fp = STACK_FRAME_NEXT(fp);
sewardj35165532005-04-30 18:47:48 +0000137 ips[i++] = ip;
138 if (debug)
139 VG_(printf)(" ipsF[%d]=%08p\n", i-1, ips[i-1]);
140 continue;
141 }
142
143 /* No luck there. We have to give up. */
144 break;
njnd01fef72005-03-25 23:35:48 +0000145 }
sewardj35165532005-04-30 18:47:48 +0000146
njnd01fef72005-03-25 23:35:48 +0000147 }
148 n_found = i;
149
150 /* Put zeroes in the rest. */
151 for (; i < n_ips; i++) {
152 ips[i] = 0;
153 }
154 VGP_POPCC(VgpExeContext);
155
156 return n_found;
157}
158
159UInt VG_(get_StackTrace) ( ThreadId tid, StackTrace ips, UInt n_ips )
160{
161 /* thread in thread table */
162 ThreadState* tst = & VG_(threads)[ tid ];
163 Addr ip = INSTR_PTR(tst->arch);
164 Addr fp = FRAME_PTR(tst->arch);
165 Addr sp = STACK_PTR(tst->arch);
njn50ba34e2005-04-04 02:41:42 +0000166 Addr stack_highest_word = tst->client_stack_highest_word;
njnd01fef72005-03-25 23:35:48 +0000167
sewardj520e3492005-05-02 10:39:16 +0000168#if defined(VGP_x86_linux)
njnd01fef72005-03-25 23:35:48 +0000169 /* Nasty little hack to deal with sysinfo syscalls - if libc is
170 using the sysinfo page for syscalls (the TLS version does), then
171 ip will always appear to be in that page when doing a syscall,
172 not the actual libc function doing the syscall. This check sees
173 if IP is within the syscall code, and pops the return address
174 off the stack so that ip is placed within the library function
175 calling the syscall. This makes stack backtraces much more
176 useful. */
177 if (ip >= VG_(client_trampoline_code)+VG_(tramp_syscall_offset) &&
178 ip < VG_(client_trampoline_code)+VG_(trampoline_code_length) &&
179 VG_(is_addressable)(sp, sizeof(Addr), VKI_PROT_READ)) {
180 ip = *(Addr *)sp;
181 sp += sizeof(Addr);
182 }
183#endif
184 if (0)
185 VG_(printf)("tid %d: stack_highest=%p ip=%p sp=%p fp=%p\n",
186 tid, stack_highest_word, ip, sp, fp);
187
sewardj35165532005-04-30 18:47:48 +0000188 return VG_(get_StackTrace2)(ips, n_ips, ip, sp, fp, sp, stack_highest_word);
njnd01fef72005-03-25 23:35:48 +0000189}
190
191static void printIpDesc(UInt n, Addr ip)
192{
njn47b209a2005-03-25 23:47:16 +0000193 static UChar buf[VG_ERRTXT_LEN];
njnd01fef72005-03-25 23:35:48 +0000194
njn47b209a2005-03-25 23:47:16 +0000195 VG_(describe_IP)(ip, buf, VG_ERRTXT_LEN);
sewardj71bc3cb2005-05-19 00:25:45 +0000196
197 if (VG_(clo_xml)) {
198 VG_(message)(Vg_UserMsg, " %s", buf);
199 } else {
200 VG_(message)(Vg_UserMsg, " %s %s", ( n == 0 ? "at" : "by" ), buf);
201 }
njnd01fef72005-03-25 23:35:48 +0000202}
203
204/* Print a StackTrace. */
205void VG_(pp_StackTrace) ( StackTrace ips, UInt n_ips )
206{
207 vg_assert( n_ips > 0 );
sewardj71bc3cb2005-05-19 00:25:45 +0000208
209 if (VG_(clo_xml))
210 VG_(message)(Vg_UserMsg, " <stack>");
211
njnd01fef72005-03-25 23:35:48 +0000212 VG_(apply_StackTrace)( printIpDesc, ips, n_ips );
sewardj71bc3cb2005-05-19 00:25:45 +0000213
214 if (VG_(clo_xml))
215 VG_(message)(Vg_UserMsg, " </stack>");
njnd01fef72005-03-25 23:35:48 +0000216}
217
218/* Get and immediately print a StackTrace. */
219void VG_(get_and_pp_StackTrace) ( ThreadId tid, UInt n_ips )
220{
221 Addr ips[n_ips];
222 VG_(get_StackTrace)(tid, ips, n_ips);
223 VG_(pp_StackTrace) ( ips, n_ips);
224}
225
226
227void VG_(apply_StackTrace)( void(*action)(UInt n, Addr ip),
228 StackTrace ips, UInt n_ips )
229{
230 #define MYBUF_LEN 10 // only needs to be long enough for "main"
231
232 Bool main_done = False;
233 Char mybuf[MYBUF_LEN]; // ok to stack allocate mybuf[] -- it's tiny
234 Int i = 0;
235
236 vg_assert(n_ips > 0);
237 do {
238 Addr ip = ips[i];
239 if (i > 0)
njna60a7c12005-05-08 17:49:37 +0000240 ip -= VGA_MIN_INSTR_SZB; // point to calling line
njnd01fef72005-03-25 23:35:48 +0000241
242 // Stop after "main"; if main() is recursive, stop after last main().
243 if ( ! VG_(clo_show_below_main)) {
244 VG_(get_fnname_nodemangle)( ip, mybuf, MYBUF_LEN );
245 if ( VG_STREQ("main", mybuf) )
246 main_done = True;
247 else if (main_done)
248 break;
249 }
250
251 // Act on the ip
252 action(i, ip);
253
254 i++;
255 } while (i < n_ips && ips[i] != 0);
256
257 #undef MYBUF_LEN
258}
259
260
261/*--------------------------------------------------------------------*/
sewardj267100d2005-04-24 12:33:12 +0000262/*--- end m_stacktrace.c ---*/
njnd01fef72005-03-25 23:35:48 +0000263/*--------------------------------------------------------------------*/