sanitizer_stacktrace.cc revision ef578990fe3c9afecfcab1044d739371f4d4b459
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_flags.h" 16#include "sanitizer_libc.h" 17#include "sanitizer_procmaps.h" 18#include "sanitizer_stacktrace.h" 19#include "sanitizer_symbolizer.h" 20 21namespace __sanitizer { 22 23uptr StackTrace::GetPreviousInstructionPc(uptr pc) { 24#ifdef __arm__ 25 // Cancel Thumb bit. 26 pc = pc & (~1); 27#endif 28#if defined(__powerpc__) || defined(__powerpc64__) 29 // PCs are always 4 byte aligned. 30 return pc - 4; 31#elif defined(__sparc__) 32 return pc - 8; 33#else 34 return pc - 1; 35#endif 36} 37 38void StackTrace::CopyFrom(const uptr *src, uptr src_size) { 39 size = Min(src_size, kStackTraceMax); 40 internal_memcpy(trace, src, sizeof(trace[0]) * size); 41} 42 43static void PrintStackFramePrefix(uptr frame_num, uptr pc) { 44 Printf(" #%zu 0x%zx", frame_num, pc); 45} 46 47void StackTrace::PrintStack(const uptr *addr, uptr size, bool symbolize, 48 SymbolizeCallback symbolize_callback) { 49 MemoryMappingLayout proc_maps(/*cache_enabled*/true); 50 InternalScopedBuffer<char> buff(GetPageSizeCached() * 2); 51 InternalScopedBuffer<AddressInfo> addr_frames(64); 52 uptr frame_num = 0; 53 for (uptr i = 0; i < size && addr[i]; i++) { 54 // PCs in stack traces are actually the return addresses, that is, 55 // addresses of the next instructions after the call. 56 uptr pc = GetPreviousInstructionPc(addr[i]); 57 uptr addr_frames_num = 0; // The number of stack frames for current 58 // instruction address. 59 if (symbolize_callback) { 60 if (symbolize_callback((void*)pc, buff.data(), buff.size())) { 61 addr_frames_num = 1; 62 PrintStackFramePrefix(frame_num, pc); 63 // We can't know anything about the string returned by external 64 // symbolizer, but if it starts with filename, try to strip path prefix 65 // from it. 66 Printf(" %s\n", 67 StripPathPrefix(buff.data(), common_flags()->strip_path_prefix)); 68 frame_num++; 69 } 70 } 71 if (symbolize && addr_frames_num == 0 && &getSymbolizer) { 72 // Use our own (online) symbolizer, if necessary. 73 addr_frames_num = getSymbolizer()->SymbolizeCode( 74 pc, addr_frames.data(), addr_frames.size()); 75 for (uptr j = 0; j < addr_frames_num; j++) { 76 AddressInfo &info = addr_frames[j]; 77 PrintStackFramePrefix(frame_num, pc); 78 if (info.function) { 79 Printf(" in %s", info.function); 80 } 81 if (info.file) { 82 Printf(" "); 83 PrintSourceLocation(info.file, info.line, info.column); 84 } else if (info.module) { 85 Printf(" "); 86 PrintModuleAndOffset(info.module, info.module_offset); 87 } 88 Printf("\n"); 89 info.Clear(); 90 frame_num++; 91 } 92 } 93 if (addr_frames_num == 0) { 94 // If online symbolization failed, try to output at least module and 95 // offset for instruction. 96 PrintStackFramePrefix(frame_num, pc); 97 uptr offset; 98 if (proc_maps.GetObjectNameAndOffset(pc, &offset, 99 buff.data(), buff.size(), 100 /* protection */0)) { 101 Printf(" "); 102 PrintModuleAndOffset(buff.data(), offset); 103 } 104 Printf("\n"); 105 frame_num++; 106 } 107 } 108} 109 110uptr StackTrace::GetCurrentPc() { 111 return GET_CALLER_PC(); 112} 113 114void StackTrace::FastUnwindStack(uptr pc, uptr bp, 115 uptr stack_top, uptr stack_bottom, 116 uptr max_depth) { 117 max_size = max_depth; 118 if (max_size == 0) { 119 size = 0; 120 return; 121 } 122 trace[0] = pc; 123 size = 1; 124 uhwptr *frame = (uhwptr *)bp; 125 uhwptr *prev_frame = frame - 1; 126 if (stack_top < 4096) return; // Sanity check for stack top. 127 // Avoid infinite loop when frame == frame[0] by using frame > prev_frame. 128 while (frame > prev_frame && 129 frame < (uhwptr *)stack_top - 2 && 130 frame > (uhwptr *)stack_bottom && 131 IsAligned((uptr)frame, sizeof(*frame)) && 132 size < max_size) { 133 uhwptr pc1 = frame[1]; 134 if (pc1 != pc) { 135 trace[size++] = (uptr) pc1; 136 } 137 prev_frame = frame; 138 frame = (uhwptr *)frame[0]; 139 } 140} 141 142void StackTrace::PopStackFrames(uptr count) { 143 CHECK(size >= count); 144 size -= count; 145 for (uptr i = 0; i < size; i++) { 146 trace[i] = trace[i + count]; 147 } 148} 149 150// On 32-bits we don't compress stack traces. 151// On 64-bits we compress stack traces: if a given pc differes slightly from 152// the previous one, we record a 31-bit offset instead of the full pc. 153SANITIZER_INTERFACE_ATTRIBUTE 154uptr StackTrace::CompressStack(StackTrace *stack, u32 *compressed, uptr size) { 155#if SANITIZER_WORDSIZE == 32 156 // Don't compress, just copy. 157 uptr res = 0; 158 for (uptr i = 0; i < stack->size && i < size; i++) { 159 compressed[i] = stack->trace[i]; 160 res++; 161 } 162 if (stack->size < size) 163 compressed[stack->size] = 0; 164#else // 64 bits, compress. 165 uptr prev_pc = 0; 166 const uptr kMaxOffset = (1ULL << 30) - 1; 167 uptr c_index = 0; 168 uptr res = 0; 169 for (uptr i = 0, n = stack->size; i < n; i++) { 170 uptr pc = stack->trace[i]; 171 if (!pc) break; 172 if ((s64)pc < 0) break; 173 // Printf("C pc[%zu] %zx\n", i, pc); 174 if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) { 175 uptr offset = (s64)(pc - prev_pc); 176 offset |= (1U << 31); 177 if (c_index >= size) break; 178 // Printf("C co[%zu] offset %zx\n", i, offset); 179 compressed[c_index++] = offset; 180 } else { 181 uptr hi = pc >> 32; 182 uptr lo = (pc << 32) >> 32; 183 CHECK_EQ((hi & (1 << 31)), 0); 184 if (c_index + 1 >= size) break; 185 // Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo); 186 compressed[c_index++] = hi; 187 compressed[c_index++] = lo; 188 } 189 res++; 190 prev_pc = pc; 191 } 192 if (c_index < size) 193 compressed[c_index] = 0; 194 if (c_index + 1 < size) 195 compressed[c_index + 1] = 0; 196#endif // SANITIZER_WORDSIZE 197 198 // debug-only code 199#if 0 200 StackTrace check_stack; 201 UncompressStack(&check_stack, compressed, size); 202 if (res < check_stack.size) { 203 Printf("res %zu check_stack.size %zu; c_size %zu\n", res, 204 check_stack.size, size); 205 } 206 // |res| may be greater than check_stack.size, because 207 // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames. 208 CHECK(res >= check_stack.size); 209 CHECK_EQ(0, REAL(memcmp)(check_stack.trace, stack->trace, 210 check_stack.size * sizeof(uptr))); 211#endif 212 213 return res; 214} 215 216SANITIZER_INTERFACE_ATTRIBUTE 217void StackTrace::UncompressStack(StackTrace *stack, 218 u32 *compressed, uptr size) { 219#if SANITIZER_WORDSIZE == 32 220 // Don't uncompress, just copy. 221 stack->size = 0; 222 for (uptr i = 0; i < size && i < kStackTraceMax; i++) { 223 if (!compressed[i]) break; 224 stack->size++; 225 stack->trace[i] = compressed[i]; 226 } 227#else // 64 bits, uncompress 228 uptr prev_pc = 0; 229 stack->size = 0; 230 for (uptr i = 0; i < size && stack->size < kStackTraceMax; i++) { 231 u32 x = compressed[i]; 232 uptr pc = 0; 233 if (x & (1U << 31)) { 234 // Printf("U co[%zu] offset: %x\n", i, x); 235 // this is an offset 236 s32 offset = x; 237 offset = (offset << 1) >> 1; // remove the 31-byte and sign-extend. 238 pc = prev_pc + offset; 239 CHECK(pc); 240 } else { 241 // CHECK(i + 1 < size); 242 if (i + 1 >= size) break; 243 uptr hi = x; 244 uptr lo = compressed[i+1]; 245 // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo); 246 i++; 247 pc = (hi << 32) | lo; 248 if (!pc) break; 249 } 250 // Printf("U pc[%zu] %zx\n", stack->size, pc); 251 stack->trace[stack->size++] = pc; 252 prev_pc = pc; 253 } 254#endif // SANITIZER_WORDSIZE 255} 256 257} // namespace __sanitizer 258