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