1 //===-- sanitizer_stackdepotbase.h ------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Implementation of a mapping from arbitrary values to unique 32-bit
11 //===----------------------------------------------------------------------===//
13 #ifndef SANITIZER_STACKDEPOTBASE_H
14 #define SANITIZER_STACKDEPOTBASE_H
16 #include "sanitizer_internal_defs.h"
17 #include "sanitizer_mutex.h"
18 #include "sanitizer_atomic.h"
19 #include "sanitizer_persistent_allocator.h"
21 namespace __sanitizer
{
23 template <class Node
, int kReservedBits
, int kTabSizeLog
>
24 class StackDepotBase
{
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
= nullptr);
30 // Retrieves a stored stack trace by the id.
31 args_type
Get(u32 id
);
33 StackDepotStats
*GetStats() { return &stats
; }
39 static Node
*find(Node
*s
, args_type args
, u32 hash
);
40 static Node
*lock(atomic_uintptr_t
*p
);
41 static void unlock(atomic_uintptr_t
*p
, Node
*s
);
43 static const int kTabSize
= 1 << kTabSizeLog
; // Hash table size.
44 static const int kPartBits
= 8;
45 static const int kPartShift
= sizeof(u32
) * 8 - kPartBits
- kReservedBits
;
46 static const int kPartCount
=
47 1 << kPartBits
; // Number of subparts in the table.
48 static const int kPartSize
= kTabSize
/ kPartCount
;
49 static const int kMaxId
= 1 << kPartShift
;
51 atomic_uintptr_t tab
[kTabSize
]; // Hash table of Node's.
52 atomic_uint32_t seq
[kPartCount
]; // Unique id generators.
54 StackDepotStats stats
;
56 friend class StackDepotReverseMap
;
59 template <class Node
, int kReservedBits
, int kTabSizeLog
>
60 Node
*StackDepotBase
<Node
, kReservedBits
, kTabSizeLog
>::find(Node
*s
,
63 // Searches linked list s for the stack, returns its id.
64 for (; s
; s
= s
->link
) {
65 if (s
->eq(hash
, args
)) {
72 template <class Node
, int kReservedBits
, int kTabSizeLog
>
73 Node
*StackDepotBase
<Node
, kReservedBits
, kTabSizeLog
>::lock(
74 atomic_uintptr_t
*p
) {
75 // Uses the pointer lsb as mutex.
76 for (int i
= 0;; i
++) {
77 uptr cmp
= atomic_load(p
, memory_order_relaxed
);
79 atomic_compare_exchange_weak(p
, &cmp
, cmp
| 1, memory_order_acquire
))
84 internal_sched_yield();
88 template <class Node
, int kReservedBits
, int kTabSizeLog
>
89 void StackDepotBase
<Node
, kReservedBits
, kTabSizeLog
>::unlock(
90 atomic_uintptr_t
*p
, Node
*s
) {
91 DCHECK_EQ((uptr
)s
& 1, 0);
92 atomic_store(p
, (uptr
)s
, memory_order_release
);
95 template <class Node
, int kReservedBits
, int kTabSizeLog
>
96 typename StackDepotBase
<Node
, kReservedBits
, kTabSizeLog
>::handle_type
97 StackDepotBase
<Node
, kReservedBits
, kTabSizeLog
>::Put(args_type args
,
99 if (inserted
) *inserted
= false;
100 if (!Node::is_valid(args
)) return handle_type();
101 uptr h
= Node::hash(args
);
102 atomic_uintptr_t
*p
= &tab
[h
% kTabSize
];
103 uptr v
= atomic_load(p
, memory_order_consume
);
104 Node
*s
= (Node
*)(v
& ~1);
105 // First, try to find the existing stack.
106 Node
*node
= find(s
, args
, h
);
107 if (node
) return node
->get_handle();
108 // If failed, lock, retry and insert new.
111 node
= find(s2
, args
, h
);
114 return node
->get_handle();
117 uptr part
= (h
% kTabSize
) / kPartSize
;
118 u32 id
= atomic_fetch_add(&seq
[part
], 1, memory_order_relaxed
) + 1;
120 CHECK_LT(id
, kMaxId
);
121 id
|= part
<< kPartShift
;
123 CHECK_EQ(id
& (((u32
)-1) >> kReservedBits
), id
);
124 uptr memsz
= Node::storage_size(args
);
125 s
= (Node
*)PersistentAlloc(memsz
);
126 stats
.allocated
+= memsz
;
131 if (inserted
) *inserted
= true;
132 return s
->get_handle();
135 template <class Node
, int kReservedBits
, int kTabSizeLog
>
136 typename StackDepotBase
<Node
, kReservedBits
, kTabSizeLog
>::args_type
137 StackDepotBase
<Node
, kReservedBits
, kTabSizeLog
>::Get(u32 id
) {
141 CHECK_EQ(id
& (((u32
)-1) >> kReservedBits
), id
);
142 // High kPartBits contain part id, so we need to scan at most kPartSize lists.
143 uptr part
= id
>> kPartShift
;
144 for (int i
= 0; i
!= kPartSize
; i
++) {
145 uptr idx
= part
* kPartSize
+ i
;
146 CHECK_LT(idx
, kTabSize
);
147 atomic_uintptr_t
*p
= &tab
[idx
];
148 uptr v
= atomic_load(p
, memory_order_consume
);
149 Node
*s
= (Node
*)(v
& ~1);
150 for (; s
; s
= s
->link
) {
159 template <class Node
, int kReservedBits
, int kTabSizeLog
>
160 void StackDepotBase
<Node
, kReservedBits
, kTabSizeLog
>::LockAll() {
161 for (int i
= 0; i
< kTabSize
; ++i
) {
166 template <class Node
, int kReservedBits
, int kTabSizeLog
>
167 void StackDepotBase
<Node
, kReservedBits
, kTabSizeLog
>::UnlockAll() {
168 for (int i
= 0; i
< kTabSize
; ++i
) {
169 atomic_uintptr_t
*p
= &tab
[i
];
170 uptr s
= atomic_load(p
, memory_order_relaxed
);
171 unlock(p
, (Node
*)(s
& ~1UL));
175 } // namespace __sanitizer
177 #endif // SANITIZER_STACKDEPOTBASE_H