sanitizer_stackdepotbase.h 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. //===-- sanitizer_stackdepotbase.h ------------------------------*- C++ -*-===//
  2. //
  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
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // Implementation of a mapping from arbitrary values to unique 32-bit
  10. // identifiers.
  11. //===----------------------------------------------------------------------===//
  12. #ifndef SANITIZER_STACKDEPOTBASE_H
  13. #define SANITIZER_STACKDEPOTBASE_H
  14. #include <stdio.h>
  15. #include "sanitizer_atomic.h"
  16. #include "sanitizer_flat_map.h"
  17. #include "sanitizer_internal_defs.h"
  18. #include "sanitizer_mutex.h"
  19. namespace __sanitizer {
  20. template <class Node, int kReservedBits, int kTabSizeLog>
  21. class StackDepotBase {
  22. static constexpr u32 kIdSizeLog =
  23. sizeof(u32) * 8 - Max(kReservedBits, 1 /* At least 1 bit for locking. */);
  24. static constexpr u32 kNodesSize1Log = kIdSizeLog / 2;
  25. static constexpr u32 kNodesSize2Log = kIdSizeLog - kNodesSize1Log;
  26. static constexpr int kTabSize = 1 << kTabSizeLog; // Hash table size.
  27. static constexpr u32 kUnlockMask = (1ull << kIdSizeLog) - 1;
  28. static constexpr u32 kLockMask = ~kUnlockMask;
  29. public:
  30. typedef typename Node::args_type args_type;
  31. typedef typename Node::handle_type handle_type;
  32. typedef typename Node::hash_type hash_type;
  33. static constexpr u64 kNodesSize1 = 1ull << kNodesSize1Log;
  34. static constexpr u64 kNodesSize2 = 1ull << kNodesSize2Log;
  35. // Maps stack trace to an unique id.
  36. u32 Put(args_type args, bool *inserted = nullptr);
  37. // Retrieves a stored stack trace by the id.
  38. args_type Get(u32 id);
  39. StackDepotStats GetStats() const {
  40. return {
  41. atomic_load_relaxed(&n_uniq_ids),
  42. nodes.MemoryUsage() + Node::allocated(),
  43. };
  44. }
  45. void LockAll();
  46. void UnlockAll();
  47. void PrintAll();
  48. void TestOnlyUnmap() {
  49. nodes.TestOnlyUnmap();
  50. internal_memset(this, 0, sizeof(*this));
  51. }
  52. private:
  53. friend Node;
  54. u32 find(u32 s, args_type args, hash_type hash) const;
  55. static u32 lock(atomic_uint32_t *p);
  56. static void unlock(atomic_uint32_t *p, u32 s);
  57. atomic_uint32_t tab[kTabSize]; // Hash table of Node's.
  58. atomic_uint32_t n_uniq_ids;
  59. TwoLevelMap<Node, kNodesSize1, kNodesSize2> nodes;
  60. friend class StackDepotReverseMap;
  61. };
  62. template <class Node, int kReservedBits, int kTabSizeLog>
  63. u32 StackDepotBase<Node, kReservedBits, kTabSizeLog>::find(
  64. u32 s, args_type args, hash_type hash) const {
  65. // Searches linked list s for the stack, returns its id.
  66. for (; s;) {
  67. const Node &node = nodes[s];
  68. if (node.eq(hash, args))
  69. return s;
  70. s = node.link;
  71. }
  72. return 0;
  73. }
  74. template <class Node, int kReservedBits, int kTabSizeLog>
  75. u32 StackDepotBase<Node, kReservedBits, kTabSizeLog>::lock(atomic_uint32_t *p) {
  76. // Uses the pointer lsb as mutex.
  77. for (int i = 0;; i++) {
  78. u32 cmp = atomic_load(p, memory_order_relaxed);
  79. if ((cmp & kLockMask) == 0 &&
  80. atomic_compare_exchange_weak(p, &cmp, cmp | kLockMask,
  81. memory_order_acquire))
  82. return cmp;
  83. if (i < 10)
  84. proc_yield(10);
  85. else
  86. internal_sched_yield();
  87. }
  88. }
  89. template <class Node, int kReservedBits, int kTabSizeLog>
  90. void StackDepotBase<Node, kReservedBits, kTabSizeLog>::unlock(
  91. atomic_uint32_t *p, u32 s) {
  92. DCHECK_EQ(s & kLockMask, 0);
  93. atomic_store(p, s, memory_order_release);
  94. }
  95. template <class Node, int kReservedBits, int kTabSizeLog>
  96. u32 StackDepotBase<Node, kReservedBits, kTabSizeLog>::Put(args_type args,
  97. bool *inserted) {
  98. if (inserted)
  99. *inserted = false;
  100. if (!LIKELY(Node::is_valid(args)))
  101. return 0;
  102. hash_type h = Node::hash(args);
  103. atomic_uint32_t *p = &tab[h % kTabSize];
  104. u32 v = atomic_load(p, memory_order_consume);
  105. u32 s = v & kUnlockMask;
  106. // First, try to find the existing stack.
  107. u32 node = find(s, args, h);
  108. if (LIKELY(node))
  109. return node;
  110. // If failed, lock, retry and insert new.
  111. u32 s2 = lock(p);
  112. if (s2 != s) {
  113. node = find(s2, args, h);
  114. if (node) {
  115. unlock(p, s2);
  116. return node;
  117. }
  118. }
  119. s = atomic_fetch_add(&n_uniq_ids, 1, memory_order_relaxed) + 1;
  120. CHECK_EQ(s & kUnlockMask, s);
  121. CHECK_EQ(s & (((u32)-1) >> kReservedBits), s);
  122. Node &new_node = nodes[s];
  123. new_node.store(s, args, h);
  124. new_node.link = s2;
  125. unlock(p, s);
  126. if (inserted) *inserted = true;
  127. return s;
  128. }
  129. template <class Node, int kReservedBits, int kTabSizeLog>
  130. typename StackDepotBase<Node, kReservedBits, kTabSizeLog>::args_type
  131. StackDepotBase<Node, kReservedBits, kTabSizeLog>::Get(u32 id) {
  132. if (id == 0)
  133. return args_type();
  134. CHECK_EQ(id & (((u32)-1) >> kReservedBits), id);
  135. if (!nodes.contains(id))
  136. return args_type();
  137. const Node &node = nodes[id];
  138. return node.load(id);
  139. }
  140. template <class Node, int kReservedBits, int kTabSizeLog>
  141. void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockAll() {
  142. for (int i = 0; i < kTabSize; ++i) {
  143. lock(&tab[i]);
  144. }
  145. }
  146. template <class Node, int kReservedBits, int kTabSizeLog>
  147. void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAll() {
  148. for (int i = 0; i < kTabSize; ++i) {
  149. atomic_uint32_t *p = &tab[i];
  150. uptr s = atomic_load(p, memory_order_relaxed);
  151. unlock(p, s & kUnlockMask);
  152. }
  153. }
  154. template <class Node, int kReservedBits, int kTabSizeLog>
  155. void StackDepotBase<Node, kReservedBits, kTabSizeLog>::PrintAll() {
  156. for (int i = 0; i < kTabSize; ++i) {
  157. atomic_uint32_t *p = &tab[i];
  158. u32 s = atomic_load(p, memory_order_consume) & kUnlockMask;
  159. for (; s;) {
  160. const Node &node = nodes[s];
  161. Printf("Stack for id %u:\n", s);
  162. node.load(s).Print();
  163. s = node.link;
  164. }
  165. }
  166. }
  167. } // namespace __sanitizer
  168. #endif // SANITIZER_STACKDEPOTBASE_H