Home | History | Annotate | Download | only in sanitizer_common
      1 //===-- sanitizer_stackdepot.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_stackdepot.h"
     15 #include "sanitizer_common.h"
     16 #include "sanitizer_internal_defs.h"
     17 #include "sanitizer_mutex.h"
     18 #include "sanitizer_atomic.h"
     19 
     20 namespace __sanitizer {
     21 
     22 const int kTabSize = 1024 * 1024;  // Hash table size.
     23 const int kPartBits = 8;
     24 const int kPartShift = sizeof(u32) * 8 - kPartBits - 1;
     25 const int kPartCount = 1 << kPartBits;  // Number of subparts in the table.
     26 const int kPartSize = kTabSize / kPartCount;
     27 const int kMaxId = 1 << kPartShift;
     28 
     29 struct StackDesc {
     30   StackDesc *link;
     31   u32 id;
     32   u32 hash;
     33   uptr size;
     34   uptr stack[1];  // [size]
     35 };
     36 
     37 static struct {
     38   StaticSpinMutex mtx;  // Protects alloc of new blocks for region allocator.
     39   atomic_uintptr_t region_pos;  // Region allocator for StackDesc's.
     40   atomic_uintptr_t region_end;
     41   atomic_uintptr_t tab[kTabSize];  // Hash table of StackDesc's.
     42   atomic_uint32_t seq[kPartCount];  // Unique id generators.
     43 } depot;
     44 
     45 static StackDepotStats stats;
     46 
     47 StackDepotStats *StackDepotGetStats() {
     48   return &stats;
     49 }
     50 
     51 static u32 hash(const uptr *stack, uptr size) {
     52   // murmur2
     53   const u32 m = 0x5bd1e995;
     54   const u32 seed = 0x9747b28c;
     55   const u32 r = 24;
     56   u32 h = seed ^ (size * sizeof(uptr));
     57   for (uptr i = 0; i < size; i++) {
     58     u32 k = stack[i];
     59     k *= m;
     60     k ^= k >> r;
     61     k *= m;
     62     h *= m;
     63     h ^= k;
     64   }
     65   h ^= h >> 13;
     66   h *= m;
     67   h ^= h >> 15;
     68   return h;
     69 }
     70 
     71 static StackDesc *tryallocDesc(uptr memsz) {
     72   // Optimisic lock-free allocation, essentially try to bump the region ptr.
     73   for (;;) {
     74     uptr cmp = atomic_load(&depot.region_pos, memory_order_acquire);
     75     uptr end = atomic_load(&depot.region_end, memory_order_acquire);
     76     if (cmp == 0 || cmp + memsz > end)
     77       return 0;
     78     if (atomic_compare_exchange_weak(
     79         &depot.region_pos, &cmp, cmp + memsz,
     80         memory_order_acquire))
     81       return (StackDesc*)cmp;
     82   }
     83 }
     84 
     85 static StackDesc *allocDesc(uptr size) {
     86   // First, try to allocate optimisitically.
     87   uptr memsz = sizeof(StackDesc) + (size - 1) * sizeof(uptr);
     88   StackDesc *s = tryallocDesc(memsz);
     89   if (s)
     90     return s;
     91   // If failed, lock, retry and alloc new superblock.
     92   SpinMutexLock l(&depot.mtx);
     93   for (;;) {
     94     s = tryallocDesc(memsz);
     95     if (s)
     96       return s;
     97     atomic_store(&depot.region_pos, 0, memory_order_relaxed);
     98     uptr allocsz = 64 * 1024;
     99     if (allocsz < memsz)
    100       allocsz = memsz;
    101     uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");
    102     stats.mapped += allocsz;
    103     atomic_store(&depot.region_end, mem + allocsz, memory_order_release);
    104     atomic_store(&depot.region_pos, mem, memory_order_release);
    105   }
    106 }
    107 
    108 static u32 find(StackDesc *s, const uptr *stack, uptr size, u32 hash) {
    109   // Searches linked list s for the stack, returns its id.
    110   for (; s; s = s->link) {
    111     if (s->hash == hash && s->size == size) {
    112       uptr i = 0;
    113       for (; i < size; i++) {
    114         if (stack[i] != s->stack[i])
    115           break;
    116       }
    117       if (i == size)
    118         return s->id;
    119     }
    120   }
    121   return 0;
    122 }
    123 
    124 static StackDesc *lock(atomic_uintptr_t *p) {
    125   // Uses the pointer lsb as mutex.
    126   for (int i = 0;; i++) {
    127     uptr cmp = atomic_load(p, memory_order_relaxed);
    128     if ((cmp & 1) == 0
    129         && atomic_compare_exchange_weak(p, &cmp, cmp | 1,
    130                                         memory_order_acquire))
    131       return (StackDesc*)cmp;
    132     if (i < 10)
    133       proc_yield(10);
    134     else
    135       internal_sched_yield();
    136   }
    137 }
    138 
    139 static void unlock(atomic_uintptr_t *p, StackDesc *s) {
    140   DCHECK_EQ((uptr)s & 1, 0);
    141   atomic_store(p, (uptr)s, memory_order_release);
    142 }
    143 
    144 u32 StackDepotPut(const uptr *stack, uptr size) {
    145   if (stack == 0 || size == 0)
    146     return 0;
    147   uptr h = hash(stack, size);
    148   atomic_uintptr_t *p = &depot.tab[h % kTabSize];
    149   uptr v = atomic_load(p, memory_order_consume);
    150   StackDesc *s = (StackDesc*)(v & ~1);
    151   // First, try to find the existing stack.
    152   u32 id = find(s, stack, size, h);
    153   if (id)
    154     return id;
    155   // If failed, lock, retry and insert new.
    156   StackDesc *s2 = lock(p);
    157   if (s2 != s) {
    158     id = find(s2, stack, size, h);
    159     if (id) {
    160       unlock(p, s2);
    161       return id;
    162     }
    163   }
    164   uptr part = (h % kTabSize) / kPartSize;
    165   id = atomic_fetch_add(&depot.seq[part], 1, memory_order_relaxed) + 1;
    166   stats.n_uniq_ids++;
    167   CHECK_LT(id, kMaxId);
    168   id |= part << kPartShift;
    169   CHECK_NE(id, 0);
    170   CHECK_EQ(id & (1u << 31), 0);
    171   s = allocDesc(size);
    172   s->id = id;
    173   s->hash = h;
    174   s->size = size;
    175   internal_memcpy(s->stack, stack, size * sizeof(uptr));
    176   s->link = s2;
    177   unlock(p, s);
    178   return id;
    179 }
    180 
    181 const uptr *StackDepotGet(u32 id, uptr *size) {
    182   if (id == 0)
    183     return 0;
    184   CHECK_EQ(id & (1u << 31), 0);
    185   // High kPartBits contain part id, so we need to scan at most kPartSize lists.
    186   uptr part = id >> kPartShift;
    187   for (int i = 0; i != kPartSize; i++) {
    188     uptr idx = part * kPartSize + i;
    189     CHECK_LT(idx, kTabSize);
    190     atomic_uintptr_t *p = &depot.tab[idx];
    191     uptr v = atomic_load(p, memory_order_consume);
    192     StackDesc *s = (StackDesc*)(v & ~1);
    193     for (; s; s = s->link) {
    194       if (s->id == id) {
    195         *size = s->size;
    196         return s->stack;
    197       }
    198     }
    199   }
    200   *size = 0;
    201   return 0;
    202 }
    203 
    204 }  // namespace __sanitizer
    205