Home | History | Annotate | Download | only in sanitizer_common
      1 //===-- sanitizer_stackdepotbase.h ------------------------------*- C++ -*-===//
      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 // Implementation of a mapping from arbitrary values to unique 32-bit
     11 // identifiers.
     12 //===----------------------------------------------------------------------===//
     13 #ifndef SANITIZER_STACKDEPOTBASE_H
     14 #define SANITIZER_STACKDEPOTBASE_H
     15 
     16 #include "sanitizer_internal_defs.h"
     17 #include "sanitizer_mutex.h"
     18 #include "sanitizer_atomic.h"
     19 #include "sanitizer_persistent_allocator.h"
     20 
     21 namespace __sanitizer {
     22 
     23 template <class Node, int kReservedBits, int kTabSizeLog>
     24 class StackDepotBase {
     25  public:
     26   typedef typename Node::args_type args_type;
     27   typedef typename Node::handle_type handle_type;
     28   // Maps stack trace to an unique id.
     29   handle_type Put(args_type args, bool *inserted = 0);
     30   // Retrieves a stored stack trace by the id.
     31   args_type Get(u32 id);
     32 
     33   StackDepotStats *GetStats() { return &stats; }
     34 
     35  private:
     36   static Node *find(Node *s, args_type args, u32 hash);
     37   static Node *lock(atomic_uintptr_t *p);
     38   static void unlock(atomic_uintptr_t *p, Node *s);
     39 
     40   static const int kTabSize = 1 << kTabSizeLog;  // Hash table size.
     41   static const int kPartBits = 8;
     42   static const int kPartShift = sizeof(u32) * 8 - kPartBits - kReservedBits;
     43   static const int kPartCount =
     44       1 << kPartBits;  // Number of subparts in the table.
     45   static const int kPartSize = kTabSize / kPartCount;
     46   static const int kMaxId = 1 << kPartShift;
     47 
     48   atomic_uintptr_t tab[kTabSize];   // Hash table of Node's.
     49   atomic_uint32_t seq[kPartCount];  // Unique id generators.
     50 
     51   StackDepotStats stats;
     52 
     53   friend class StackDepotReverseMap;
     54 };
     55 
     56 template <class Node, int kReservedBits, int kTabSizeLog>
     57 Node *StackDepotBase<Node, kReservedBits, kTabSizeLog>::find(Node *s,
     58                                                              args_type args,
     59                                                              u32 hash) {
     60   // Searches linked list s for the stack, returns its id.
     61   for (; s; s = s->link) {
     62     if (s->eq(hash, args)) {
     63       return s;
     64     }
     65   }
     66   return 0;
     67 }
     68 
     69 template <class Node, int kReservedBits, int kTabSizeLog>
     70 Node *StackDepotBase<Node, kReservedBits, kTabSizeLog>::lock(
     71     atomic_uintptr_t *p) {
     72   // Uses the pointer lsb as mutex.
     73   for (int i = 0;; i++) {
     74     uptr cmp = atomic_load(p, memory_order_relaxed);
     75     if ((cmp & 1) == 0 &&
     76         atomic_compare_exchange_weak(p, &cmp, cmp | 1, memory_order_acquire))
     77       return (Node *)cmp;
     78     if (i < 10)
     79       proc_yield(10);
     80     else
     81       internal_sched_yield();
     82   }
     83 }
     84 
     85 template <class Node, int kReservedBits, int kTabSizeLog>
     86 void StackDepotBase<Node, kReservedBits, kTabSizeLog>::unlock(
     87     atomic_uintptr_t *p, Node *s) {
     88   DCHECK_EQ((uptr)s & 1, 0);
     89   atomic_store(p, (uptr)s, memory_order_release);
     90 }
     91 
     92 template <class Node, int kReservedBits, int kTabSizeLog>
     93 typename StackDepotBase<Node, kReservedBits, kTabSizeLog>::handle_type
     94 StackDepotBase<Node, kReservedBits, kTabSizeLog>::Put(args_type args,
     95                                                       bool *inserted) {
     96   if (inserted) *inserted = false;
     97   if (!args.is_valid()) return handle_type();
     98   uptr h = args.hash();
     99   atomic_uintptr_t *p = &tab[h % kTabSize];
    100   uptr v = atomic_load(p, memory_order_consume);
    101   Node *s = (Node *)(v & ~1);
    102   // First, try to find the existing stack.
    103   Node *node = find(s, args, h);
    104   if (node) return node->get_handle();
    105   // If failed, lock, retry and insert new.
    106   Node *s2 = lock(p);
    107   if (s2 != s) {
    108     node = find(s2, args, h);
    109     if (node) {
    110       unlock(p, s2);
    111       return node->get_handle();
    112     }
    113   }
    114   uptr part = (h % kTabSize) / kPartSize;
    115   u32 id = atomic_fetch_add(&seq[part], 1, memory_order_relaxed) + 1;
    116   stats.n_uniq_ids++;
    117   CHECK_LT(id, kMaxId);
    118   id |= part << kPartShift;
    119   CHECK_NE(id, 0);
    120   CHECK_EQ(id & (((u32)-1) >> kReservedBits), id);
    121   uptr memsz = Node::storage_size(args);
    122   s = (Node *)PersistentAlloc(memsz);
    123   stats.allocated += memsz;
    124   s->id = id;
    125   s->store(args, h);
    126   s->link = s2;
    127   unlock(p, s);
    128   if (inserted) *inserted = true;
    129   return s->get_handle();
    130 }
    131 
    132 template <class Node, int kReservedBits, int kTabSizeLog>
    133 typename StackDepotBase<Node, kReservedBits, kTabSizeLog>::args_type
    134 StackDepotBase<Node, kReservedBits, kTabSizeLog>::Get(u32 id) {
    135   if (id == 0) {
    136     return args_type();
    137   }
    138   CHECK_EQ(id & (((u32)-1) >> kReservedBits), id);
    139   // High kPartBits contain part id, so we need to scan at most kPartSize lists.
    140   uptr part = id >> kPartShift;
    141   for (int i = 0; i != kPartSize; i++) {
    142     uptr idx = part * kPartSize + i;
    143     CHECK_LT(idx, kTabSize);
    144     atomic_uintptr_t *p = &tab[idx];
    145     uptr v = atomic_load(p, memory_order_consume);
    146     Node *s = (Node *)(v & ~1);
    147     for (; s; s = s->link) {
    148       if (s->id == id) {
    149         return s->load();
    150       }
    151     }
    152   }
    153   return args_type();
    154 }
    155 
    156 }  // namespace __sanitizer
    157 #endif  // SANITIZER_STACKDEPOTBASE_H
    158