sanitizer_stacktrace.cc revision e89f1846d58550f919a0ef9081b11d4b0405019d
1//===-- sanitizer_stacktrace.cc -------------------------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file is shared between AddressSanitizer and ThreadSanitizer 11// run-time libraries. 12//===----------------------------------------------------------------------===// 13 14#include "sanitizer_common.h" 15#include "sanitizer_procmaps.h" 16#include "sanitizer_stacktrace.h" 17#include "sanitizer_symbolizer.h" 18 19namespace __sanitizer { 20static const char *StripPathPrefix(const char *filepath, 21 const char *strip_file_prefix) { 22 if (filepath == internal_strstr(filepath, strip_file_prefix)) 23 return filepath + internal_strlen(strip_file_prefix); 24 return filepath; 25} 26 27// ----------------------- StackTrace ----------------------------- {{{1 28// PCs in stack traces are actually the return addresses, that is, 29// addresses of the next instructions after the call. That's why we 30// decrement them. 31static uptr patch_pc(uptr pc) { 32#ifdef __arm__ 33 // Cancel Thumb bit. 34 pc = pc & (~1); 35#endif 36#if defined(__powerpc__) || defined(__powerpc64__) 37 // PCs are always 4 byte aligned. 38 return pc - 4; 39#else 40 return pc - 1; 41#endif 42} 43 44static void PrintStackFramePrefix(uptr frame_num, uptr pc) { 45 Printf(" #%zu 0x%zx", frame_num, pc); 46} 47 48static void PrintSourceLocation(const char *file, int line, int column, 49 const char *strip_file_prefix) { 50 CHECK(file); 51 Printf(" %s", StripPathPrefix(file, strip_file_prefix)); 52 if (line > 0) { 53 Printf(":%d", line); 54 if (column > 0) 55 Printf(":%d", column); 56 } 57} 58 59static void PrintModuleAndOffset(const char *module, uptr offset, 60 const char *strip_file_prefix) { 61 Printf(" (%s+0x%zx)", StripPathPrefix(module, strip_file_prefix), offset); 62} 63 64void StackTrace::PrintStack(const uptr *addr, uptr size, 65 bool symbolize, const char *strip_file_prefix, 66 SymbolizeCallback symbolize_callback ) { 67 MemoryMappingLayout proc_maps; 68 InternalScopedBuffer<char> buff(GetPageSizeCached() * 2); 69 InternalScopedBuffer<AddressInfo> addr_frames(64); 70 uptr frame_num = 0; 71 for (uptr i = 0; i < size && addr[i]; i++) { 72 uptr pc = patch_pc(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())) { 77 addr_frames_num = 1; 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 81 // from it. 82 Printf(" %s\n", StripPathPrefix(buff.data(), strip_file_prefix)); 83 frame_num++; 84 } 85 } 86 if (symbolize && addr_frames_num == 0) { 87 // Use our own (online) symbolizer, if necessary. 88 addr_frames_num = SymbolizeCode(pc, addr_frames.data(), 89 addr_frames.size()); 90 for (uptr j = 0; j < addr_frames_num; j++) { 91 AddressInfo &info = addr_frames[j]; 92 PrintStackFramePrefix(frame_num, pc); 93 if (info.function) { 94 Printf(" in %s", info.function); 95 } 96 if (info.file) { 97 PrintSourceLocation(info.file, info.line, info.column, 98 strip_file_prefix); 99 } else if (info.module) { 100 PrintModuleAndOffset(info.module, info.module_offset, 101 strip_file_prefix); 102 } 103 Printf("\n"); 104 info.Clear(); 105 frame_num++; 106 } 107 } 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); 112 uptr offset; 113 if (proc_maps.GetObjectNameAndOffset(pc, &offset, 114 buff.data(), buff.size())) { 115 PrintModuleAndOffset(buff.data(), offset, strip_file_prefix); 116 } 117 Printf("\n"); 118 frame_num++; 119 } 120 } 121} 122 123uptr StackTrace::GetCurrentPc() { 124 return GET_CALLER_PC(); 125} 126 127void StackTrace::FastUnwindStack(uptr pc, uptr bp, 128 uptr stack_top, uptr stack_bottom) { 129 CHECK(size == 0 && trace[0] == pc); 130 size = 1; 131 uhwptr *frame = (uhwptr *)bp; 132 uhwptr *prev_frame = frame; 133 while (frame >= prev_frame && 134 frame < (uhwptr *)stack_top - 2 && 135 frame > (uhwptr *)stack_bottom && 136 size < max_size) { 137 uhwptr pc1 = frame[1]; 138 if (pc1 != pc) { 139 trace[size++] = (uptr) pc1; 140 } 141 prev_frame = frame; 142 frame = (uhwptr *)frame[0]; 143 } 144} 145 146void StackTrace::PopStackFrames(uptr count) { 147 CHECK(size >= count); 148 size -= count; 149 for (uptr i = 0; i < size; i++) { 150 trace[i] = trace[i + count]; 151 } 152} 153 154// On 32-bits we don't compress stack traces. 155// On 64-bits we compress stack traces: if a given pc differes slightly from 156// the previous one, we record a 31-bit offset instead of the full pc. 157SANITIZER_INTERFACE_ATTRIBUTE 158uptr StackTrace::CompressStack(StackTrace *stack, u32 *compressed, uptr size) { 159#if SANITIZER_WORDSIZE == 32 160 // Don't compress, just copy. 161 uptr res = 0; 162 for (uptr i = 0; i < stack->size && i < size; i++) { 163 compressed[i] = stack->trace[i]; 164 res++; 165 } 166 if (stack->size < size) 167 compressed[stack->size] = 0; 168#else // 64 bits, compress. 169 uptr prev_pc = 0; 170 const uptr kMaxOffset = (1ULL << 30) - 1; 171 uptr c_index = 0; 172 uptr res = 0; 173 for (uptr i = 0, n = stack->size; i < n; i++) { 174 uptr pc = stack->trace[i]; 175 if (!pc) break; 176 if ((s64)pc < 0) break; 177 // Printf("C pc[%zu] %zx\n", i, pc); 178 if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) { 179 uptr offset = (s64)(pc - prev_pc); 180 offset |= (1U << 31); 181 if (c_index >= size) break; 182 // Printf("C co[%zu] offset %zx\n", i, offset); 183 compressed[c_index++] = offset; 184 } else { 185 uptr hi = pc >> 32; 186 uptr lo = (pc << 32) >> 32; 187 CHECK_EQ((hi & (1 << 31)), 0); 188 if (c_index + 1 >= size) break; 189 // Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo); 190 compressed[c_index++] = hi; 191 compressed[c_index++] = lo; 192 } 193 res++; 194 prev_pc = pc; 195 } 196 if (c_index < size) 197 compressed[c_index] = 0; 198 if (c_index + 1 < size) 199 compressed[c_index + 1] = 0; 200#endif // SANITIZER_WORDSIZE 201 202 // debug-only code 203#if 0 204 StackTrace check_stack; 205 UncompressStack(&check_stack, compressed, size); 206 if (res < check_stack.size) { 207 Printf("res %zu check_stack.size %zu; c_size %zu\n", res, 208 check_stack.size, size); 209 } 210 // |res| may be greater than check_stack.size, because 211 // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames. 212 CHECK(res >= check_stack.size); 213 CHECK_EQ(0, REAL(memcmp)(check_stack.trace, stack->trace, 214 check_stack.size * sizeof(uptr))); 215#endif 216 217 return res; 218} 219 220SANITIZER_INTERFACE_ATTRIBUTE 221void StackTrace::UncompressStack(StackTrace *stack, 222 u32 *compressed, uptr size) { 223#if SANITIZER_WORDSIZE == 32 224 // Don't uncompress, just copy. 225 stack->size = 0; 226 for (uptr i = 0; i < size && i < kStackTraceMax; i++) { 227 if (!compressed[i]) break; 228 stack->size++; 229 stack->trace[i] = compressed[i]; 230 } 231#else // 64 bits, uncompress 232 uptr prev_pc = 0; 233 stack->size = 0; 234 for (uptr i = 0; i < size && stack->size < kStackTraceMax; i++) { 235 u32 x = compressed[i]; 236 uptr pc = 0; 237 if (x & (1U << 31)) { 238 // Printf("U co[%zu] offset: %x\n", i, x); 239 // this is an offset 240 s32 offset = x; 241 offset = (offset << 1) >> 1; // remove the 31-byte and sign-extend. 242 pc = prev_pc + offset; 243 CHECK(pc); 244 } else { 245 // CHECK(i + 1 < size); 246 if (i + 1 >= size) break; 247 uptr hi = x; 248 uptr lo = compressed[i+1]; 249 // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo); 250 i++; 251 pc = (hi << 32) | lo; 252 if (!pc) break; 253 } 254 // Printf("U pc[%zu] %zx\n", stack->size, pc); 255 stack->trace[stack->size++] = pc; 256 prev_pc = pc; 257 } 258#endif // SANITIZER_WORDSIZE 259} 260 261} // namespace __sanitizer 262