1 //===-- sanitizer_stacktrace.cc -------------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file is shared between AddressSanitizer and ThreadSanitizer
10 //===----------------------------------------------------------------------===//
12 #include "sanitizer_common.h"
13 #include "sanitizer_procmaps.h"
14 #include "sanitizer_stacktrace.h"
15 #include "sanitizer_symbolizer.h"
17 namespace __sanitizer
{
18 const char *StripPathPrefix(const char *filepath
,
19 const char *strip_file_prefix
) {
20 if (filepath
== 0) return 0;
21 if (filepath
== internal_strstr(filepath
, strip_file_prefix
))
22 return filepath
+ internal_strlen(strip_file_prefix
);
26 // ----------------------- StackTrace ----------------------------- {{{1
27 uptr
StackTrace::GetPreviousInstructionPc(uptr pc
) {
32 #if defined(__powerpc__) || defined(__powerpc64__)
33 // PCs are always 4 byte aligned.
35 #elif defined(__sparc__)
42 static void PrintStackFramePrefix(uptr frame_num
, uptr pc
) {
43 Printf(" #%zu 0x%zx", frame_num
, pc
);
46 static void PrintSourceLocation(const char *file
, int line
, int column
,
47 const char *strip_file_prefix
) {
49 Printf(" %s", StripPathPrefix(file
, strip_file_prefix
));
53 Printf(":%d", column
);
57 static void PrintModuleAndOffset(const char *module
, uptr offset
,
58 const char *strip_file_prefix
) {
59 Printf(" (%s+0x%zx)", StripPathPrefix(module
, strip_file_prefix
), offset
);
62 void StackTrace::PrintStack(const uptr
*addr
, uptr size
,
63 bool symbolize
, const char *strip_file_prefix
,
64 SymbolizeCallback symbolize_callback
) {
65 MemoryMappingLayout proc_maps
;
66 InternalScopedBuffer
<char> buff(GetPageSizeCached() * 2);
67 InternalScopedBuffer
<AddressInfo
> addr_frames(64);
69 for (uptr i
= 0; i
< size
&& addr
[i
]; i
++) {
70 // PCs in stack traces are actually the return addresses, that is,
71 // addresses of the next instructions after the call.
72 uptr pc
= GetPreviousInstructionPc(addr
[i
]);
73 uptr addr_frames_num
= 0; // The number of stack frames for current
74 // instruction address.
75 if (symbolize_callback
) {
76 if (symbolize_callback((void*)pc
, buff
.data(), buff
.size())) {
78 PrintStackFramePrefix(frame_num
, pc
);
79 // We can't know anything about the string returned by external
80 // symbolizer, but if it starts with filename, try to strip path prefix
82 Printf(" %s\n", StripPathPrefix(buff
.data(), strip_file_prefix
));
86 if (symbolize
&& addr_frames_num
== 0) {
87 // Use our own (online) symbolizer, if necessary.
88 addr_frames_num
= SymbolizeCode(pc
, addr_frames
.data(),
90 for (uptr j
= 0; j
< addr_frames_num
; j
++) {
91 AddressInfo
&info
= addr_frames
[j
];
92 PrintStackFramePrefix(frame_num
, pc
);
94 Printf(" in %s", info
.function
);
97 PrintSourceLocation(info
.file
, info
.line
, info
.column
,
99 } else if (info
.module
) {
100 PrintModuleAndOffset(info
.module
, info
.module_offset
,
108 if (addr_frames_num
== 0) {
109 // If online symbolization failed, try to output at least module and
110 // offset for instruction.
111 PrintStackFramePrefix(frame_num
, pc
);
113 if (proc_maps
.GetObjectNameAndOffset(pc
, &offset
,
114 buff
.data(), buff
.size())) {
115 PrintModuleAndOffset(buff
.data(), offset
, strip_file_prefix
);
123 uptr
StackTrace::GetCurrentPc() {
124 return GET_CALLER_PC();
127 void StackTrace::FastUnwindStack(uptr pc
, uptr bp
,
128 uptr stack_top
, uptr stack_bottom
) {
129 CHECK(size
== 0 && trace
[0] == pc
);
131 uhwptr
*frame
= (uhwptr
*)bp
;
132 uhwptr
*prev_frame
= frame
- 1;
133 // Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
134 while (frame
> prev_frame
&&
135 frame
< (uhwptr
*)stack_top
- 2 &&
136 frame
> (uhwptr
*)stack_bottom
&&
138 uhwptr pc1
= frame
[1];
140 trace
[size
++] = (uptr
) pc1
;
143 frame
= (uhwptr
*)frame
[0];
147 void StackTrace::PopStackFrames(uptr count
) {
148 CHECK(size
>= count
);
150 for (uptr i
= 0; i
< size
; i
++) {
151 trace
[i
] = trace
[i
+ count
];
155 // On 32-bits we don't compress stack traces.
156 // On 64-bits we compress stack traces: if a given pc differes slightly from
157 // the previous one, we record a 31-bit offset instead of the full pc.
158 SANITIZER_INTERFACE_ATTRIBUTE
159 uptr
StackTrace::CompressStack(StackTrace
*stack
, u32
*compressed
, uptr size
) {
160 #if SANITIZER_WORDSIZE == 32
161 // Don't compress, just copy.
163 for (uptr i
= 0; i
< stack
->size
&& i
< size
; i
++) {
164 compressed
[i
] = stack
->trace
[i
];
167 if (stack
->size
< size
)
168 compressed
[stack
->size
] = 0;
169 #else // 64 bits, compress.
171 const uptr kMaxOffset
= (1ULL << 30) - 1;
174 for (uptr i
= 0, n
= stack
->size
; i
< n
; i
++) {
175 uptr pc
= stack
->trace
[i
];
177 if ((s64
)pc
< 0) break;
178 // Printf("C pc[%zu] %zx\n", i, pc);
179 if (prev_pc
- pc
< kMaxOffset
|| pc
- prev_pc
< kMaxOffset
) {
180 uptr offset
= (s64
)(pc
- prev_pc
);
181 offset
|= (1U << 31);
182 if (c_index
>= size
) break;
183 // Printf("C co[%zu] offset %zx\n", i, offset);
184 compressed
[c_index
++] = offset
;
187 uptr lo
= (pc
<< 32) >> 32;
188 CHECK_EQ((hi
& (1 << 31)), 0);
189 if (c_index
+ 1 >= size
) break;
190 // Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo);
191 compressed
[c_index
++] = hi
;
192 compressed
[c_index
++] = lo
;
198 compressed
[c_index
] = 0;
199 if (c_index
+ 1 < size
)
200 compressed
[c_index
+ 1] = 0;
201 #endif // SANITIZER_WORDSIZE
205 StackTrace check_stack
;
206 UncompressStack(&check_stack
, compressed
, size
);
207 if (res
< check_stack
.size
) {
208 Printf("res %zu check_stack.size %zu; c_size %zu\n", res
,
209 check_stack
.size
, size
);
211 // |res| may be greater than check_stack.size, because
212 // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames.
213 CHECK(res
>= check_stack
.size
);
214 CHECK_EQ(0, REAL(memcmp
)(check_stack
.trace
, stack
->trace
,
215 check_stack
.size
* sizeof(uptr
)));
221 SANITIZER_INTERFACE_ATTRIBUTE
222 void StackTrace::UncompressStack(StackTrace
*stack
,
223 u32
*compressed
, uptr size
) {
224 #if SANITIZER_WORDSIZE == 32
225 // Don't uncompress, just copy.
227 for (uptr i
= 0; i
< size
&& i
< kStackTraceMax
; i
++) {
228 if (!compressed
[i
]) break;
230 stack
->trace
[i
] = compressed
[i
];
232 #else // 64 bits, uncompress
235 for (uptr i
= 0; i
< size
&& stack
->size
< kStackTraceMax
; i
++) {
236 u32 x
= compressed
[i
];
238 if (x
& (1U << 31)) {
239 // Printf("U co[%zu] offset: %x\n", i, x);
242 offset
= (offset
<< 1) >> 1; // remove the 31-byte and sign-extend.
243 pc
= prev_pc
+ offset
;
246 // CHECK(i + 1 < size);
247 if (i
+ 1 >= size
) break;
249 uptr lo
= compressed
[i
+1];
250 // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo);
252 pc
= (hi
<< 32) | lo
;
255 // Printf("U pc[%zu] %zx\n", stack->size, pc);
256 stack
->trace
[stack
->size
++] = pc
;
259 #endif // SANITIZER_WORDSIZE
262 } // namespace __sanitizer