sanitizer_stacktrace.cc revision 80175018541984bdcfe6a304bb189bd30592fa8a
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. 153uptr StackTrace::CompressStack(StackTrace *stack, u32 *compressed, uptr size) { 154#if SANITIZER_WORDSIZE == 32 155 // Don't compress, just copy. 156 uptr res = 0; 157 for (uptr i = 0; i < stack->size && i < size; i++) { 158 compressed[i] = stack->trace[i]; 159 res++; 160 } 161 if (stack->size < size) 162 compressed[stack->size] = 0; 163#else // 64 bits, compress. 164 uptr prev_pc = 0; 165 const uptr kMaxOffset = (1ULL << 30) - 1; 166 uptr c_index = 0; 167 uptr res = 0; 168 for (uptr i = 0, n = stack->size; i < n; i++) { 169 uptr pc = stack->trace[i]; 170 if (!pc) break; 171 if ((s64)pc < 0) break; 172 // Printf("C pc[%zu] %zx\n", i, pc); 173 if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) { 174 uptr offset = (s64)(pc - prev_pc); 175 offset |= (1U << 31); 176 if (c_index >= size) break; 177 // Printf("C co[%zu] offset %zx\n", i, offset); 178 compressed[c_index++] = offset; 179 } else { 180 uptr hi = pc >> 32; 181 uptr lo = (pc << 32) >> 32; 182 CHECK_EQ((hi & (1 << 31)), 0); 183 if (c_index + 1 >= size) break; 184 // Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo); 185 compressed[c_index++] = hi; 186 compressed[c_index++] = lo; 187 } 188 res++; 189 prev_pc = pc; 190 } 191 if (c_index < size) 192 compressed[c_index] = 0; 193 if (c_index + 1 < size) 194 compressed[c_index + 1] = 0; 195#endif // SANITIZER_WORDSIZE 196 197 // debug-only code 198#if 0 199 StackTrace check_stack; 200 UncompressStack(&check_stack, compressed, size); 201 if (res < check_stack.size) { 202 Printf("res %zu check_stack.size %zu; c_size %zu\n", res, 203 check_stack.size, size); 204 } 205 // |res| may be greater than check_stack.size, because 206 // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames. 207 CHECK(res >= check_stack.size); 208 CHECK_EQ(0, REAL(memcmp)(check_stack.trace, stack->trace, 209 check_stack.size * sizeof(uptr))); 210#endif 211 212 return res; 213} 214 215void StackTrace::UncompressStack(StackTrace *stack, 216 u32 *compressed, uptr size) { 217#if SANITIZER_WORDSIZE == 32 218 // Don't uncompress, just copy. 219 stack->size = 0; 220 for (uptr i = 0; i < size && i < kStackTraceMax; i++) { 221 if (!compressed[i]) break; 222 stack->size++; 223 stack->trace[i] = compressed[i]; 224 } 225#else // 64 bits, uncompress 226 uptr prev_pc = 0; 227 stack->size = 0; 228 for (uptr i = 0; i < size && stack->size < kStackTraceMax; i++) { 229 u32 x = compressed[i]; 230 uptr pc = 0; 231 if (x & (1U << 31)) { 232 // Printf("U co[%zu] offset: %x\n", i, x); 233 // this is an offset 234 s32 offset = x; 235 offset = (offset << 1) >> 1; // remove the 31-byte and sign-extend. 236 pc = prev_pc + offset; 237 CHECK(pc); 238 } else { 239 // CHECK(i + 1 < size); 240 if (i + 1 >= size) break; 241 uptr hi = x; 242 uptr lo = compressed[i+1]; 243 // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo); 244 i++; 245 pc = (hi << 32) | lo; 246 if (!pc) break; 247 } 248 // Printf("U pc[%zu] %zx\n", stack->size, pc); 249 stack->trace[stack->size++] = pc; 250 prev_pc = pc; 251 } 252#endif // SANITIZER_WORDSIZE 253} 254 255} // namespace __sanitizer 256