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
GetStats()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
TestOnlyUnmap()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>
find(u32 s,args_type args,hash_type hash)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>
lock(atomic_uint32_t * p)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>
unlock(atomic_uint32_t * p,u32 s)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>
Put(args_type args,bool * inserted)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
Get(u32 id)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>
LockBeforeFork()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>
UnlockAfterFork(bool fork_child)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>
PrintAll()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