13cab2bb3Spatrick //===-- sanitizer_stackdepotbase.h ------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // Implementation of a mapping from arbitrary values to unique 32-bit
103cab2bb3Spatrick // identifiers.
113cab2bb3Spatrick //===----------------------------------------------------------------------===//
123cab2bb3Spatrick
133cab2bb3Spatrick #ifndef SANITIZER_STACKDEPOTBASE_H
143cab2bb3Spatrick #define SANITIZER_STACKDEPOTBASE_H
153cab2bb3Spatrick
16d89ec533Spatrick #include <stdio.h>
17d89ec533Spatrick
18d89ec533Spatrick #include "sanitizer_atomic.h"
19*810390e3Srobert #include "sanitizer_flat_map.h"
203cab2bb3Spatrick #include "sanitizer_internal_defs.h"
213cab2bb3Spatrick #include "sanitizer_mutex.h"
223cab2bb3Spatrick
233cab2bb3Spatrick namespace __sanitizer {
243cab2bb3Spatrick
253cab2bb3Spatrick template <class Node, int kReservedBits, int kTabSizeLog>
263cab2bb3Spatrick class StackDepotBase {
27*810390e3Srobert static constexpr u32 kIdSizeLog =
28*810390e3Srobert sizeof(u32) * 8 - Max(kReservedBits, 1 /* At least 1 bit for locking. */);
29*810390e3Srobert static constexpr u32 kNodesSize1Log = kIdSizeLog / 2;
30*810390e3Srobert static constexpr u32 kNodesSize2Log = kIdSizeLog - kNodesSize1Log;
31*810390e3Srobert static constexpr int kTabSize = 1 << kTabSizeLog; // Hash table size.
32*810390e3Srobert static constexpr u32 kUnlockMask = (1ull << kIdSizeLog) - 1;
33*810390e3Srobert static constexpr u32 kLockMask = ~kUnlockMask;
34*810390e3Srobert
353cab2bb3Spatrick public:
363cab2bb3Spatrick typedef typename Node::args_type args_type;
373cab2bb3Spatrick typedef typename Node::handle_type handle_type;
38*810390e3Srobert typedef typename Node::hash_type hash_type;
39*810390e3Srobert
40*810390e3Srobert static constexpr u64 kNodesSize1 = 1ull << kNodesSize1Log;
41*810390e3Srobert static constexpr u64 kNodesSize2 = 1ull << kNodesSize2Log;
42*810390e3Srobert
433cab2bb3Spatrick // Maps stack trace to an unique id.
44*810390e3Srobert u32 Put(args_type args, bool *inserted = nullptr);
453cab2bb3Spatrick // Retrieves a stored stack trace by the id.
463cab2bb3Spatrick args_type Get(u32 id);
473cab2bb3Spatrick
GetStats()48*810390e3Srobert StackDepotStats GetStats() const {
49*810390e3Srobert return {
50*810390e3Srobert atomic_load_relaxed(&n_uniq_ids),
51*810390e3Srobert nodes.MemoryUsage() + Node::allocated(),
52*810390e3Srobert };
53*810390e3Srobert }
543cab2bb3Spatrick
553cab2bb3Spatrick void LockAll();
563cab2bb3Spatrick void UnlockAll();
57d89ec533Spatrick void PrintAll();
583cab2bb3Spatrick
TestOnlyUnmap()59*810390e3Srobert void TestOnlyUnmap() {
60*810390e3Srobert nodes.TestOnlyUnmap();
61*810390e3Srobert internal_memset(this, 0, sizeof(*this));
62*810390e3Srobert }
63*810390e3Srobert
643cab2bb3Spatrick private:
65*810390e3Srobert friend Node;
66*810390e3Srobert u32 find(u32 s, args_type args, hash_type hash) const;
67*810390e3Srobert static u32 lock(atomic_uint32_t *p);
68*810390e3Srobert static void unlock(atomic_uint32_t *p, u32 s);
69*810390e3Srobert atomic_uint32_t tab[kTabSize]; // Hash table of Node's.
703cab2bb3Spatrick
71*810390e3Srobert atomic_uint32_t n_uniq_ids;
723cab2bb3Spatrick
73*810390e3Srobert TwoLevelMap<Node, kNodesSize1, kNodesSize2> nodes;
743cab2bb3Spatrick
753cab2bb3Spatrick friend class StackDepotReverseMap;
763cab2bb3Spatrick };
773cab2bb3Spatrick
783cab2bb3Spatrick template <class Node, int kReservedBits, int kTabSizeLog>
find(u32 s,args_type args,hash_type hash)79*810390e3Srobert u32 StackDepotBase<Node, kReservedBits, kTabSizeLog>::find(
80*810390e3Srobert u32 s, args_type args, hash_type hash) const {
813cab2bb3Spatrick // Searches linked list s for the stack, returns its id.
82*810390e3Srobert for (; s;) {
83*810390e3Srobert const Node &node = nodes[s];
84*810390e3Srobert if (node.eq(hash, args))
853cab2bb3Spatrick return s;
86*810390e3Srobert s = node.link;
873cab2bb3Spatrick }
88*810390e3Srobert return 0;
893cab2bb3Spatrick }
903cab2bb3Spatrick
913cab2bb3Spatrick template <class Node, int kReservedBits, int kTabSizeLog>
lock(atomic_uint32_t * p)92*810390e3Srobert u32 StackDepotBase<Node, kReservedBits, kTabSizeLog>::lock(atomic_uint32_t *p) {
933cab2bb3Spatrick // Uses the pointer lsb as mutex.
943cab2bb3Spatrick for (int i = 0;; i++) {
95*810390e3Srobert u32 cmp = atomic_load(p, memory_order_relaxed);
96*810390e3Srobert if ((cmp & kLockMask) == 0 &&
97*810390e3Srobert atomic_compare_exchange_weak(p, &cmp, cmp | kLockMask,
98*810390e3Srobert memory_order_acquire))
99*810390e3Srobert return cmp;
1003cab2bb3Spatrick if (i < 10)
1013cab2bb3Spatrick proc_yield(10);
1023cab2bb3Spatrick else
1033cab2bb3Spatrick internal_sched_yield();
1043cab2bb3Spatrick }
1053cab2bb3Spatrick }
1063cab2bb3Spatrick
1073cab2bb3Spatrick template <class Node, int kReservedBits, int kTabSizeLog>
unlock(atomic_uint32_t * p,u32 s)1083cab2bb3Spatrick void StackDepotBase<Node, kReservedBits, kTabSizeLog>::unlock(
109*810390e3Srobert atomic_uint32_t *p, u32 s) {
110*810390e3Srobert DCHECK_EQ(s & kLockMask, 0);
111*810390e3Srobert atomic_store(p, s, memory_order_release);
1123cab2bb3Spatrick }
1133cab2bb3Spatrick
1143cab2bb3Spatrick template <class Node, int kReservedBits, int kTabSizeLog>
Put(args_type args,bool * inserted)115*810390e3Srobert u32 StackDepotBase<Node, kReservedBits, kTabSizeLog>::Put(args_type args,
1163cab2bb3Spatrick bool *inserted) {
117*810390e3Srobert if (inserted)
118*810390e3Srobert *inserted = false;
119*810390e3Srobert if (!LIKELY(Node::is_valid(args)))
120*810390e3Srobert return 0;
121*810390e3Srobert hash_type h = Node::hash(args);
122*810390e3Srobert atomic_uint32_t *p = &tab[h % kTabSize];
123*810390e3Srobert u32 v = atomic_load(p, memory_order_consume);
124*810390e3Srobert u32 s = v & kUnlockMask;
1253cab2bb3Spatrick // First, try to find the existing stack.
126*810390e3Srobert u32 node = find(s, args, h);
127*810390e3Srobert if (LIKELY(node))
128*810390e3Srobert return node;
129*810390e3Srobert
1303cab2bb3Spatrick // If failed, lock, retry and insert new.
131*810390e3Srobert u32 s2 = lock(p);
1323cab2bb3Spatrick if (s2 != s) {
1333cab2bb3Spatrick node = find(s2, args, h);
1343cab2bb3Spatrick if (node) {
1353cab2bb3Spatrick unlock(p, s2);
136*810390e3Srobert return node;
1373cab2bb3Spatrick }
1383cab2bb3Spatrick }
139*810390e3Srobert s = atomic_fetch_add(&n_uniq_ids, 1, memory_order_relaxed) + 1;
140*810390e3Srobert CHECK_EQ(s & kUnlockMask, s);
141*810390e3Srobert CHECK_EQ(s & (((u32)-1) >> kReservedBits), s);
142*810390e3Srobert Node &new_node = nodes[s];
143*810390e3Srobert new_node.store(s, args, h);
144*810390e3Srobert new_node.link = s2;
1453cab2bb3Spatrick unlock(p, s);
1463cab2bb3Spatrick if (inserted) *inserted = true;
147*810390e3Srobert return s;
1483cab2bb3Spatrick }
1493cab2bb3Spatrick
1503cab2bb3Spatrick template <class Node, int kReservedBits, int kTabSizeLog>
1513cab2bb3Spatrick typename StackDepotBase<Node, kReservedBits, kTabSizeLog>::args_type
Get(u32 id)1523cab2bb3Spatrick StackDepotBase<Node, kReservedBits, kTabSizeLog>::Get(u32 id) {
153*810390e3Srobert if (id == 0)
1543cab2bb3Spatrick return args_type();
1553cab2bb3Spatrick CHECK_EQ(id & (((u32)-1) >> kReservedBits), id);
156*810390e3Srobert if (!nodes.contains(id))
1573cab2bb3Spatrick return args_type();
158*810390e3Srobert const Node &node = nodes[id];
159*810390e3Srobert return node.load(id);
1603cab2bb3Spatrick }
1613cab2bb3Spatrick
1623cab2bb3Spatrick template <class Node, int kReservedBits, int kTabSizeLog>
LockAll()1633cab2bb3Spatrick void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockAll() {
1643cab2bb3Spatrick for (int i = 0; i < kTabSize; ++i) {
1653cab2bb3Spatrick lock(&tab[i]);
1663cab2bb3Spatrick }
1673cab2bb3Spatrick }
1683cab2bb3Spatrick
1693cab2bb3Spatrick template <class Node, int kReservedBits, int kTabSizeLog>
UnlockAll()1703cab2bb3Spatrick void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAll() {
1713cab2bb3Spatrick for (int i = 0; i < kTabSize; ++i) {
172*810390e3Srobert atomic_uint32_t *p = &tab[i];
1733cab2bb3Spatrick uptr s = atomic_load(p, memory_order_relaxed);
174*810390e3Srobert unlock(p, s & kUnlockMask);
1753cab2bb3Spatrick }
1763cab2bb3Spatrick }
1773cab2bb3Spatrick
178d89ec533Spatrick template <class Node, int kReservedBits, int kTabSizeLog>
PrintAll()179d89ec533Spatrick void StackDepotBase<Node, kReservedBits, kTabSizeLog>::PrintAll() {
180d89ec533Spatrick for (int i = 0; i < kTabSize; ++i) {
181*810390e3Srobert atomic_uint32_t *p = &tab[i];
182*810390e3Srobert u32 s = atomic_load(p, memory_order_consume) & kUnlockMask;
183*810390e3Srobert for (; s;) {
184*810390e3Srobert const Node &node = nodes[s];
185*810390e3Srobert Printf("Stack for id %u:\n", s);
186*810390e3Srobert node.load(s).Print();
187*810390e3Srobert s = node.link;
188d89ec533Spatrick }
189d89ec533Spatrick }
190d89ec533Spatrick }
191d89ec533Spatrick
1923cab2bb3Spatrick } // namespace __sanitizer
1933cab2bb3Spatrick
1943cab2bb3Spatrick #endif // SANITIZER_STACKDEPOTBASE_H
195