xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp (revision 68d75eff68281c1b445e3010bb975eae07aac225)
1*68d75effSDimitry Andric //===-- sanitizer_stackdepot.cpp ------------------------------------------===//
2*68d75effSDimitry Andric //
3*68d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*68d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*68d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*68d75effSDimitry Andric //
7*68d75effSDimitry Andric //===----------------------------------------------------------------------===//
8*68d75effSDimitry Andric //
9*68d75effSDimitry Andric // This file is shared between AddressSanitizer and ThreadSanitizer
10*68d75effSDimitry Andric // run-time libraries.
11*68d75effSDimitry Andric //===----------------------------------------------------------------------===//
12*68d75effSDimitry Andric 
13*68d75effSDimitry Andric #include "sanitizer_stackdepot.h"
14*68d75effSDimitry Andric 
15*68d75effSDimitry Andric #include "sanitizer_common.h"
16*68d75effSDimitry Andric #include "sanitizer_hash.h"
17*68d75effSDimitry Andric #include "sanitizer_stackdepotbase.h"
18*68d75effSDimitry Andric 
19*68d75effSDimitry Andric namespace __sanitizer {
20*68d75effSDimitry Andric 
21*68d75effSDimitry Andric struct StackDepotNode {
22*68d75effSDimitry Andric   StackDepotNode *link;
23*68d75effSDimitry Andric   u32 id;
24*68d75effSDimitry Andric   atomic_uint32_t hash_and_use_count; // hash_bits : 12; use_count : 20;
25*68d75effSDimitry Andric   u32 size;
26*68d75effSDimitry Andric   u32 tag;
27*68d75effSDimitry Andric   uptr stack[1];  // [size]
28*68d75effSDimitry Andric 
29*68d75effSDimitry Andric   static const u32 kTabSizeLog = SANITIZER_ANDROID ? 16 : 20;
30*68d75effSDimitry Andric   // Lower kTabSizeLog bits are equal for all items in one bucket.
31*68d75effSDimitry Andric   // We use these bits to store the per-stack use counter.
32*68d75effSDimitry Andric   static const u32 kUseCountBits = kTabSizeLog;
33*68d75effSDimitry Andric   static const u32 kMaxUseCount = 1 << kUseCountBits;
34*68d75effSDimitry Andric   static const u32 kUseCountMask = (1 << kUseCountBits) - 1;
35*68d75effSDimitry Andric   static const u32 kHashMask = ~kUseCountMask;
36*68d75effSDimitry Andric 
37*68d75effSDimitry Andric   typedef StackTrace args_type;
38*68d75effSDimitry Andric   bool eq(u32 hash, const args_type &args) const {
39*68d75effSDimitry Andric     u32 hash_bits =
40*68d75effSDimitry Andric         atomic_load(&hash_and_use_count, memory_order_relaxed) & kHashMask;
41*68d75effSDimitry Andric     if ((hash & kHashMask) != hash_bits || args.size != size || args.tag != tag)
42*68d75effSDimitry Andric       return false;
43*68d75effSDimitry Andric     uptr i = 0;
44*68d75effSDimitry Andric     for (; i < size; i++) {
45*68d75effSDimitry Andric       if (stack[i] != args.trace[i]) return false;
46*68d75effSDimitry Andric     }
47*68d75effSDimitry Andric     return true;
48*68d75effSDimitry Andric   }
49*68d75effSDimitry Andric   static uptr storage_size(const args_type &args) {
50*68d75effSDimitry Andric     return sizeof(StackDepotNode) + (args.size - 1) * sizeof(uptr);
51*68d75effSDimitry Andric   }
52*68d75effSDimitry Andric   static u32 hash(const args_type &args) {
53*68d75effSDimitry Andric     MurMur2HashBuilder H(args.size * sizeof(uptr));
54*68d75effSDimitry Andric     for (uptr i = 0; i < args.size; i++) H.add(args.trace[i]);
55*68d75effSDimitry Andric     return H.get();
56*68d75effSDimitry Andric   }
57*68d75effSDimitry Andric   static bool is_valid(const args_type &args) {
58*68d75effSDimitry Andric     return args.size > 0 && args.trace;
59*68d75effSDimitry Andric   }
60*68d75effSDimitry Andric   void store(const args_type &args, u32 hash) {
61*68d75effSDimitry Andric     atomic_store(&hash_and_use_count, hash & kHashMask, memory_order_relaxed);
62*68d75effSDimitry Andric     size = args.size;
63*68d75effSDimitry Andric     tag = args.tag;
64*68d75effSDimitry Andric     internal_memcpy(stack, args.trace, size * sizeof(uptr));
65*68d75effSDimitry Andric   }
66*68d75effSDimitry Andric   args_type load() const {
67*68d75effSDimitry Andric     return args_type(&stack[0], size, tag);
68*68d75effSDimitry Andric   }
69*68d75effSDimitry Andric   StackDepotHandle get_handle() { return StackDepotHandle(this); }
70*68d75effSDimitry Andric 
71*68d75effSDimitry Andric   typedef StackDepotHandle handle_type;
72*68d75effSDimitry Andric };
73*68d75effSDimitry Andric 
74*68d75effSDimitry Andric COMPILER_CHECK(StackDepotNode::kMaxUseCount == (u32)kStackDepotMaxUseCount);
75*68d75effSDimitry Andric 
76*68d75effSDimitry Andric u32 StackDepotHandle::id() { return node_->id; }
77*68d75effSDimitry Andric int StackDepotHandle::use_count() {
78*68d75effSDimitry Andric   return atomic_load(&node_->hash_and_use_count, memory_order_relaxed) &
79*68d75effSDimitry Andric          StackDepotNode::kUseCountMask;
80*68d75effSDimitry Andric }
81*68d75effSDimitry Andric void StackDepotHandle::inc_use_count_unsafe() {
82*68d75effSDimitry Andric   u32 prev =
83*68d75effSDimitry Andric       atomic_fetch_add(&node_->hash_and_use_count, 1, memory_order_relaxed) &
84*68d75effSDimitry Andric       StackDepotNode::kUseCountMask;
85*68d75effSDimitry Andric   CHECK_LT(prev + 1, StackDepotNode::kMaxUseCount);
86*68d75effSDimitry Andric }
87*68d75effSDimitry Andric 
88*68d75effSDimitry Andric // FIXME(dvyukov): this single reserved bit is used in TSan.
89*68d75effSDimitry Andric typedef StackDepotBase<StackDepotNode, 1, StackDepotNode::kTabSizeLog>
90*68d75effSDimitry Andric     StackDepot;
91*68d75effSDimitry Andric static StackDepot theDepot;
92*68d75effSDimitry Andric 
93*68d75effSDimitry Andric StackDepotStats *StackDepotGetStats() {
94*68d75effSDimitry Andric   return theDepot.GetStats();
95*68d75effSDimitry Andric }
96*68d75effSDimitry Andric 
97*68d75effSDimitry Andric u32 StackDepotPut(StackTrace stack) {
98*68d75effSDimitry Andric   StackDepotHandle h = theDepot.Put(stack);
99*68d75effSDimitry Andric   return h.valid() ? h.id() : 0;
100*68d75effSDimitry Andric }
101*68d75effSDimitry Andric 
102*68d75effSDimitry Andric StackDepotHandle StackDepotPut_WithHandle(StackTrace stack) {
103*68d75effSDimitry Andric   return theDepot.Put(stack);
104*68d75effSDimitry Andric }
105*68d75effSDimitry Andric 
106*68d75effSDimitry Andric StackTrace StackDepotGet(u32 id) {
107*68d75effSDimitry Andric   return theDepot.Get(id);
108*68d75effSDimitry Andric }
109*68d75effSDimitry Andric 
110*68d75effSDimitry Andric void StackDepotLockAll() {
111*68d75effSDimitry Andric   theDepot.LockAll();
112*68d75effSDimitry Andric }
113*68d75effSDimitry Andric 
114*68d75effSDimitry Andric void StackDepotUnlockAll() {
115*68d75effSDimitry Andric   theDepot.UnlockAll();
116*68d75effSDimitry Andric }
117*68d75effSDimitry Andric 
118*68d75effSDimitry Andric bool StackDepotReverseMap::IdDescPair::IdComparator(
119*68d75effSDimitry Andric     const StackDepotReverseMap::IdDescPair &a,
120*68d75effSDimitry Andric     const StackDepotReverseMap::IdDescPair &b) {
121*68d75effSDimitry Andric   return a.id < b.id;
122*68d75effSDimitry Andric }
123*68d75effSDimitry Andric 
124*68d75effSDimitry Andric StackDepotReverseMap::StackDepotReverseMap() {
125*68d75effSDimitry Andric   map_.reserve(StackDepotGetStats()->n_uniq_ids + 100);
126*68d75effSDimitry Andric   for (int idx = 0; idx < StackDepot::kTabSize; idx++) {
127*68d75effSDimitry Andric     atomic_uintptr_t *p = &theDepot.tab[idx];
128*68d75effSDimitry Andric     uptr v = atomic_load(p, memory_order_consume);
129*68d75effSDimitry Andric     StackDepotNode *s = (StackDepotNode*)(v & ~1);
130*68d75effSDimitry Andric     for (; s; s = s->link) {
131*68d75effSDimitry Andric       IdDescPair pair = {s->id, s};
132*68d75effSDimitry Andric       map_.push_back(pair);
133*68d75effSDimitry Andric     }
134*68d75effSDimitry Andric   }
135*68d75effSDimitry Andric   Sort(map_.data(), map_.size(), &IdDescPair::IdComparator);
136*68d75effSDimitry Andric }
137*68d75effSDimitry Andric 
138*68d75effSDimitry Andric StackTrace StackDepotReverseMap::Get(u32 id) {
139*68d75effSDimitry Andric   if (!map_.size())
140*68d75effSDimitry Andric     return StackTrace();
141*68d75effSDimitry Andric   IdDescPair pair = {id, nullptr};
142*68d75effSDimitry Andric   uptr idx =
143*68d75effSDimitry Andric       InternalLowerBound(map_, 0, map_.size(), pair, IdDescPair::IdComparator);
144*68d75effSDimitry Andric   if (idx > map_.size() || map_[idx].id != id)
145*68d75effSDimitry Andric     return StackTrace();
146*68d75effSDimitry Andric   return map_[idx].desc->load();
147*68d75effSDimitry Andric }
148*68d75effSDimitry Andric 
149*68d75effSDimitry Andric } // namespace __sanitizer
150