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