10b57cec5SDimitry Andric //===-- sanitizer_stackdepotbase.h ------------------------------*- C++ -*-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // Implementation of a mapping from arbitrary values to unique 32-bit 100b57cec5SDimitry Andric // identifiers. 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric 130b57cec5SDimitry Andric #ifndef SANITIZER_STACKDEPOTBASE_H 140b57cec5SDimitry Andric #define SANITIZER_STACKDEPOTBASE_H 150b57cec5SDimitry Andric 16e8d8bef9SDimitry Andric #include <stdio.h> 17e8d8bef9SDimitry Andric 18e8d8bef9SDimitry Andric #include "sanitizer_atomic.h" 19349cc55cSDimitry Andric #include "sanitizer_flat_map.h" 200b57cec5SDimitry Andric #include "sanitizer_internal_defs.h" 210b57cec5SDimitry Andric #include "sanitizer_mutex.h" 220b57cec5SDimitry Andric 230b57cec5SDimitry Andric namespace __sanitizer { 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric template <class Node, int kReservedBits, int kTabSizeLog> 260b57cec5SDimitry Andric class StackDepotBase { 27349cc55cSDimitry Andric static constexpr u32 kIdSizeLog = 28349cc55cSDimitry Andric sizeof(u32) * 8 - Max(kReservedBits, 1 /* At least 1 bit for locking. */); 29349cc55cSDimitry Andric static constexpr u32 kNodesSize1Log = kIdSizeLog / 2; 30349cc55cSDimitry Andric static constexpr u32 kNodesSize2Log = kIdSizeLog - kNodesSize1Log; 31349cc55cSDimitry Andric static constexpr int kTabSize = 1 << kTabSizeLog; // Hash table size. 32349cc55cSDimitry Andric static constexpr u32 kUnlockMask = (1ull << kIdSizeLog) - 1; 33349cc55cSDimitry Andric static constexpr u32 kLockMask = ~kUnlockMask; 34349cc55cSDimitry Andric 350b57cec5SDimitry Andric public: 360b57cec5SDimitry Andric typedef typename Node::args_type args_type; 370b57cec5SDimitry Andric typedef typename Node::handle_type handle_type; 38349cc55cSDimitry Andric typedef typename Node::hash_type hash_type; 39349cc55cSDimitry Andric 40349cc55cSDimitry Andric static constexpr u64 kNodesSize1 = 1ull << kNodesSize1Log; 41349cc55cSDimitry Andric static constexpr u64 kNodesSize2 = 1ull << kNodesSize2Log; 42349cc55cSDimitry Andric 430b57cec5SDimitry Andric // Maps stack trace to an unique id. 44349cc55cSDimitry Andric u32 Put(args_type args, bool *inserted = nullptr); 450b57cec5SDimitry Andric // Retrieves a stored stack trace by the id. 460b57cec5SDimitry Andric args_type Get(u32 id); 470b57cec5SDimitry Andric 48349cc55cSDimitry Andric StackDepotStats GetStats() const { 49349cc55cSDimitry Andric return { 50349cc55cSDimitry Andric atomic_load_relaxed(&n_uniq_ids), 51349cc55cSDimitry Andric nodes.MemoryUsage() + Node::allocated(), 52349cc55cSDimitry Andric }; 53349cc55cSDimitry Andric } 540b57cec5SDimitry Andric 55cb14a3feSDimitry Andric void LockBeforeFork(); 56cb14a3feSDimitry Andric void UnlockAfterFork(bool fork_child); 57e8d8bef9SDimitry Andric void PrintAll(); 580b57cec5SDimitry Andric 59349cc55cSDimitry Andric void TestOnlyUnmap() { 60349cc55cSDimitry Andric nodes.TestOnlyUnmap(); 61349cc55cSDimitry Andric internal_memset(this, 0, sizeof(*this)); 62349cc55cSDimitry Andric } 63349cc55cSDimitry Andric 640b57cec5SDimitry Andric private: 65349cc55cSDimitry Andric friend Node; 66349cc55cSDimitry Andric u32 find(u32 s, args_type args, hash_type hash) const; 67349cc55cSDimitry Andric static u32 lock(atomic_uint32_t *p); 68349cc55cSDimitry Andric static void unlock(atomic_uint32_t *p, u32 s); 69349cc55cSDimitry Andric atomic_uint32_t tab[kTabSize]; // Hash table of Node's. 700b57cec5SDimitry Andric 71349cc55cSDimitry Andric atomic_uint32_t n_uniq_ids; 720b57cec5SDimitry Andric 73349cc55cSDimitry Andric TwoLevelMap<Node, kNodesSize1, kNodesSize2> nodes; 740b57cec5SDimitry Andric 750b57cec5SDimitry Andric friend class StackDepotReverseMap; 760b57cec5SDimitry Andric }; 770b57cec5SDimitry Andric 780b57cec5SDimitry Andric template <class Node, int kReservedBits, int kTabSizeLog> 79349cc55cSDimitry Andric u32 StackDepotBase<Node, kReservedBits, kTabSizeLog>::find( 80349cc55cSDimitry Andric u32 s, args_type args, hash_type hash) const { 810b57cec5SDimitry Andric // Searches linked list s for the stack, returns its id. 82349cc55cSDimitry Andric for (; s;) { 83349cc55cSDimitry Andric const Node &node = nodes[s]; 84349cc55cSDimitry Andric if (node.eq(hash, args)) 850b57cec5SDimitry Andric return s; 86349cc55cSDimitry Andric s = node.link; 870b57cec5SDimitry Andric } 88349cc55cSDimitry Andric return 0; 890b57cec5SDimitry Andric } 900b57cec5SDimitry Andric 910b57cec5SDimitry Andric template <class Node, int kReservedBits, int kTabSizeLog> 92349cc55cSDimitry Andric u32 StackDepotBase<Node, kReservedBits, kTabSizeLog>::lock(atomic_uint32_t *p) { 930b57cec5SDimitry Andric // Uses the pointer lsb as mutex. 940b57cec5SDimitry Andric for (int i = 0;; i++) { 95349cc55cSDimitry Andric u32 cmp = atomic_load(p, memory_order_relaxed); 96349cc55cSDimitry Andric if ((cmp & kLockMask) == 0 && 97349cc55cSDimitry Andric atomic_compare_exchange_weak(p, &cmp, cmp | kLockMask, 98349cc55cSDimitry Andric memory_order_acquire)) 99349cc55cSDimitry Andric return cmp; 1000b57cec5SDimitry Andric if (i < 10) 1010b57cec5SDimitry Andric proc_yield(10); 1020b57cec5SDimitry Andric else 1030b57cec5SDimitry Andric internal_sched_yield(); 1040b57cec5SDimitry Andric } 1050b57cec5SDimitry Andric } 1060b57cec5SDimitry Andric 1070b57cec5SDimitry Andric template <class Node, int kReservedBits, int kTabSizeLog> 1080b57cec5SDimitry Andric void StackDepotBase<Node, kReservedBits, kTabSizeLog>::unlock( 109349cc55cSDimitry Andric atomic_uint32_t *p, u32 s) { 110349cc55cSDimitry Andric DCHECK_EQ(s & kLockMask, 0); 111349cc55cSDimitry Andric atomic_store(p, s, memory_order_release); 1120b57cec5SDimitry Andric } 1130b57cec5SDimitry Andric 1140b57cec5SDimitry Andric template <class Node, int kReservedBits, int kTabSizeLog> 115349cc55cSDimitry Andric u32 StackDepotBase<Node, kReservedBits, kTabSizeLog>::Put(args_type args, 1160b57cec5SDimitry Andric bool *inserted) { 117349cc55cSDimitry Andric if (inserted) 118349cc55cSDimitry Andric *inserted = false; 119349cc55cSDimitry Andric if (!LIKELY(Node::is_valid(args))) 120349cc55cSDimitry Andric return 0; 121349cc55cSDimitry Andric hash_type h = Node::hash(args); 122349cc55cSDimitry Andric atomic_uint32_t *p = &tab[h % kTabSize]; 123349cc55cSDimitry Andric u32 v = atomic_load(p, memory_order_consume); 124349cc55cSDimitry Andric u32 s = v & kUnlockMask; 1250b57cec5SDimitry Andric // First, try to find the existing stack. 126349cc55cSDimitry Andric u32 node = find(s, args, h); 127349cc55cSDimitry Andric if (LIKELY(node)) 128349cc55cSDimitry Andric return node; 129349cc55cSDimitry Andric 1300b57cec5SDimitry Andric // If failed, lock, retry and insert new. 131349cc55cSDimitry Andric u32 s2 = lock(p); 1320b57cec5SDimitry Andric if (s2 != s) { 1330b57cec5SDimitry Andric node = find(s2, args, h); 1340b57cec5SDimitry Andric if (node) { 1350b57cec5SDimitry Andric unlock(p, s2); 136349cc55cSDimitry Andric return node; 1370b57cec5SDimitry Andric } 1380b57cec5SDimitry Andric } 139349cc55cSDimitry Andric s = atomic_fetch_add(&n_uniq_ids, 1, memory_order_relaxed) + 1; 140349cc55cSDimitry Andric CHECK_EQ(s & kUnlockMask, s); 141349cc55cSDimitry Andric CHECK_EQ(s & (((u32)-1) >> kReservedBits), s); 142349cc55cSDimitry Andric Node &new_node = nodes[s]; 143349cc55cSDimitry Andric new_node.store(s, args, h); 144349cc55cSDimitry Andric new_node.link = s2; 1450b57cec5SDimitry Andric unlock(p, s); 1460b57cec5SDimitry Andric if (inserted) *inserted = true; 147349cc55cSDimitry Andric return s; 1480b57cec5SDimitry Andric } 1490b57cec5SDimitry Andric 1500b57cec5SDimitry Andric template <class Node, int kReservedBits, int kTabSizeLog> 1510b57cec5SDimitry Andric typename StackDepotBase<Node, kReservedBits, kTabSizeLog>::args_type 1520b57cec5SDimitry Andric StackDepotBase<Node, kReservedBits, kTabSizeLog>::Get(u32 id) { 153349cc55cSDimitry Andric if (id == 0) 1540b57cec5SDimitry Andric return args_type(); 1550b57cec5SDimitry Andric CHECK_EQ(id & (((u32)-1) >> kReservedBits), id); 156349cc55cSDimitry Andric if (!nodes.contains(id)) 1570b57cec5SDimitry Andric return args_type(); 158349cc55cSDimitry Andric const Node &node = nodes[id]; 159349cc55cSDimitry Andric return node.load(id); 1600b57cec5SDimitry Andric } 1610b57cec5SDimitry Andric 1620b57cec5SDimitry Andric template <class Node, int kReservedBits, int kTabSizeLog> 163cb14a3feSDimitry Andric void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockBeforeFork() { 164*647cbc5dSDimitry Andric // Do not lock hash table. It's very expensive, but it's not rely needed. The 165*647cbc5dSDimitry Andric // parent process will neither lock nor unlock. Child process risks to be 166*647cbc5dSDimitry Andric // deadlocked on already locked buckets. To avoid deadlock we will unlock 167*647cbc5dSDimitry Andric // every locked buckets in `UnlockAfterFork`. This may affect consistency of 168*647cbc5dSDimitry Andric // the hash table, but the only issue is a few items inserted by parent 169*647cbc5dSDimitry Andric // process will be not found by child, and the child may insert them again, 170*647cbc5dSDimitry Andric // wasting some space in `stackStore`. 171*647cbc5dSDimitry Andric 172*647cbc5dSDimitry Andric // We still need to lock nodes. 173*647cbc5dSDimitry Andric nodes.Lock(); 1740b57cec5SDimitry Andric } 1750b57cec5SDimitry Andric 1760b57cec5SDimitry Andric template <class Node, int kReservedBits, int kTabSizeLog> 177cb14a3feSDimitry Andric void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAfterFork( 178cb14a3feSDimitry Andric bool fork_child) { 179*647cbc5dSDimitry Andric nodes.Unlock(); 180*647cbc5dSDimitry Andric 181*647cbc5dSDimitry Andric // Only unlock in child process to avoid deadlock. See `LockBeforeFork`. 182*647cbc5dSDimitry Andric if (!fork_child) 183*647cbc5dSDimitry Andric return; 184*647cbc5dSDimitry Andric 1850b57cec5SDimitry Andric for (int i = 0; i < kTabSize; ++i) { 186349cc55cSDimitry Andric atomic_uint32_t *p = &tab[i]; 1870b57cec5SDimitry Andric uptr s = atomic_load(p, memory_order_relaxed); 188*647cbc5dSDimitry Andric if (s & kLockMask) 189349cc55cSDimitry Andric unlock(p, s & kUnlockMask); 1900b57cec5SDimitry Andric } 1910b57cec5SDimitry Andric } 1920b57cec5SDimitry Andric 193e8d8bef9SDimitry Andric template <class Node, int kReservedBits, int kTabSizeLog> 194e8d8bef9SDimitry Andric void StackDepotBase<Node, kReservedBits, kTabSizeLog>::PrintAll() { 195e8d8bef9SDimitry Andric for (int i = 0; i < kTabSize; ++i) { 196349cc55cSDimitry Andric atomic_uint32_t *p = &tab[i]; 197349cc55cSDimitry Andric u32 s = atomic_load(p, memory_order_consume) & kUnlockMask; 198349cc55cSDimitry Andric for (; s;) { 199349cc55cSDimitry Andric const Node &node = nodes[s]; 200349cc55cSDimitry Andric Printf("Stack for id %u:\n", s); 201349cc55cSDimitry Andric node.load(s).Print(); 202349cc55cSDimitry Andric s = node.link; 203e8d8bef9SDimitry Andric } 204e8d8bef9SDimitry Andric } 205e8d8bef9SDimitry Andric } 206e8d8bef9SDimitry Andric 2070b57cec5SDimitry Andric } // namespace __sanitizer 2080b57cec5SDimitry Andric 2090b57cec5SDimitry Andric #endif // SANITIZER_STACKDEPOTBASE_H 210