[sanitizer] Remove StackDepotReverseMap
Now StackDepotGet can retrive the stack in O(1). Depends on D111612. Reviewed By: dvyukov Differential Revision: https://reviews.llvm.org/D111613
This commit is contained in:
		
							parent
							
								
									ce7f8c8474
								
							
						
					
					
						commit
						ca0036df7d
					
				| 
						 | 
					@ -487,7 +487,6 @@ static uptr GetCallerPC(const StackTrace &stack) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct InvalidPCParam {
 | 
					struct InvalidPCParam {
 | 
				
			||||||
  Frontier *frontier;
 | 
					  Frontier *frontier;
 | 
				
			||||||
  const StackDepotReverseMap *stack_depot;
 | 
					 | 
				
			||||||
  bool skip_linker_allocations;
 | 
					  bool skip_linker_allocations;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -502,7 +501,7 @@ static void MarkInvalidPCCb(uptr chunk, void *arg) {
 | 
				
			||||||
    u32 stack_id = m.stack_trace_id();
 | 
					    u32 stack_id = m.stack_trace_id();
 | 
				
			||||||
    uptr caller_pc = 0;
 | 
					    uptr caller_pc = 0;
 | 
				
			||||||
    if (stack_id > 0)
 | 
					    if (stack_id > 0)
 | 
				
			||||||
      caller_pc = GetCallerPC(param->stack_depot->Get(stack_id));
 | 
					      caller_pc = GetCallerPC(StackDepotGet(stack_id));
 | 
				
			||||||
    // If caller_pc is unknown, this chunk may be allocated in a coroutine. Mark
 | 
					    // If caller_pc is unknown, this chunk may be allocated in a coroutine. Mark
 | 
				
			||||||
    // it as reachable, as we can't properly report its allocation stack anyway.
 | 
					    // it as reachable, as we can't properly report its allocation stack anyway.
 | 
				
			||||||
    if (caller_pc == 0 || (param->skip_linker_allocations &&
 | 
					    if (caller_pc == 0 || (param->skip_linker_allocations &&
 | 
				
			||||||
| 
						 | 
					@ -533,11 +532,9 @@ static void MarkInvalidPCCb(uptr chunk, void *arg) {
 | 
				
			||||||
// which we don't care about).
 | 
					// which we don't care about).
 | 
				
			||||||
// On all other platforms, this simply checks to ensure that the caller pc is
 | 
					// On all other platforms, this simply checks to ensure that the caller pc is
 | 
				
			||||||
// valid before reporting chunks as leaked.
 | 
					// valid before reporting chunks as leaked.
 | 
				
			||||||
static void ProcessPC(Frontier *frontier,
 | 
					static void ProcessPC(Frontier *frontier) {
 | 
				
			||||||
                      const StackDepotReverseMap &stack_depot) {
 | 
					 | 
				
			||||||
  InvalidPCParam arg;
 | 
					  InvalidPCParam arg;
 | 
				
			||||||
  arg.frontier = frontier;
 | 
					  arg.frontier = frontier;
 | 
				
			||||||
  arg.stack_depot = &stack_depot;
 | 
					 | 
				
			||||||
  arg.skip_linker_allocations =
 | 
					  arg.skip_linker_allocations =
 | 
				
			||||||
      flags()->use_tls && flags()->use_ld_allocations && GetLinker() != nullptr;
 | 
					      flags()->use_tls && flags()->use_ld_allocations && GetLinker() != nullptr;
 | 
				
			||||||
  ForEachChunk(MarkInvalidPCCb, &arg);
 | 
					  ForEachChunk(MarkInvalidPCCb, &arg);
 | 
				
			||||||
| 
						 | 
					@ -545,7 +542,6 @@ static void ProcessPC(Frontier *frontier,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Sets the appropriate tag on each chunk.
 | 
					// Sets the appropriate tag on each chunk.
 | 
				
			||||||
static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads,
 | 
					static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads,
 | 
				
			||||||
                              const StackDepotReverseMap &stack_depot,
 | 
					 | 
				
			||||||
                              Frontier *frontier) {
 | 
					                              Frontier *frontier) {
 | 
				
			||||||
  const InternalMmapVector<u32> &suppressed_stacks =
 | 
					  const InternalMmapVector<u32> &suppressed_stacks =
 | 
				
			||||||
      GetSuppressionContext()->GetSortedSuppressedStacks();
 | 
					      GetSuppressionContext()->GetSortedSuppressedStacks();
 | 
				
			||||||
| 
						 | 
					@ -560,7 +556,7 @@ static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads,
 | 
				
			||||||
  FloodFillTag(frontier, kReachable);
 | 
					  FloodFillTag(frontier, kReachable);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  CHECK_EQ(0, frontier->size());
 | 
					  CHECK_EQ(0, frontier->size());
 | 
				
			||||||
  ProcessPC(frontier, stack_depot);
 | 
					  ProcessPC(frontier);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // The check here is relatively expensive, so we do this in a separate flood
 | 
					  // The check here is relatively expensive, so we do this in a separate flood
 | 
				
			||||||
  // fill. That way we can skip the check for chunks that are reachable
 | 
					  // fill. That way we can skip the check for chunks that are reachable
 | 
				
			||||||
| 
						 | 
					@ -654,8 +650,7 @@ static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads,
 | 
				
			||||||
  CHECK(param);
 | 
					  CHECK(param);
 | 
				
			||||||
  CHECK(!param->success);
 | 
					  CHECK(!param->success);
 | 
				
			||||||
  ReportUnsuspendedThreads(suspended_threads);
 | 
					  ReportUnsuspendedThreads(suspended_threads);
 | 
				
			||||||
  ClassifyAllChunks(suspended_threads, param->leak_report.stack_depot(),
 | 
					  ClassifyAllChunks(suspended_threads, ¶m->frontier);
 | 
				
			||||||
                    ¶m->frontier);
 | 
					 | 
				
			||||||
  ForEachChunk(CollectLeaksCb, ¶m->leak_report);
 | 
					  ForEachChunk(CollectLeaksCb, ¶m->leak_report);
 | 
				
			||||||
  // Clean up for subsequent leak checks. This assumes we did not overwrite any
 | 
					  // Clean up for subsequent leak checks. This assumes we did not overwrite any
 | 
				
			||||||
  // kIgnored tags.
 | 
					  // kIgnored tags.
 | 
				
			||||||
| 
						 | 
					@ -795,7 +790,7 @@ void LeakReport::AddLeakedChunk(uptr chunk, u32 stack_trace_id,
 | 
				
			||||||
  CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked);
 | 
					  CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (u32 resolution = flags()->resolution) {
 | 
					  if (u32 resolution = flags()->resolution) {
 | 
				
			||||||
    StackTrace stack = stack_depot_.Get(stack_trace_id);
 | 
					    StackTrace stack = StackDepotGet(stack_trace_id);
 | 
				
			||||||
    stack.size = Min(stack.size, resolution);
 | 
					    stack.size = Min(stack.size, resolution);
 | 
				
			||||||
    stack_trace_id = StackDepotPut(stack);
 | 
					    stack_trace_id = StackDepotPut(stack);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -863,7 +858,7 @@ void LeakReport::PrintReportForLeak(uptr index) {
 | 
				
			||||||
  Printf("%s", d.Default());
 | 
					  Printf("%s", d.Default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  CHECK(leaks_[index].stack_trace_id);
 | 
					  CHECK(leaks_[index].stack_trace_id);
 | 
				
			||||||
  stack_depot_.Get(leaks_[index].stack_trace_id).Print();
 | 
					  StackDepotGet(leaks_[index].stack_trace_id).Print();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (flags()->report_objects) {
 | 
					  if (flags()->report_objects) {
 | 
				
			||||||
    Printf("Objects leaked above:\n");
 | 
					    Printf("Objects leaked above:\n");
 | 
				
			||||||
| 
						 | 
					@ -900,7 +895,7 @@ uptr LeakReport::ApplySuppressions() {
 | 
				
			||||||
  uptr new_suppressions = false;
 | 
					  uptr new_suppressions = false;
 | 
				
			||||||
  for (uptr i = 0; i < leaks_.size(); i++) {
 | 
					  for (uptr i = 0; i < leaks_.size(); i++) {
 | 
				
			||||||
    Suppression *s = suppressions->GetSuppressionForStack(
 | 
					    Suppression *s = suppressions->GetSuppressionForStack(
 | 
				
			||||||
        leaks_[i].stack_trace_id, stack_depot_.Get(leaks_[i].stack_trace_id));
 | 
					        leaks_[i].stack_trace_id, StackDepotGet(leaks_[i].stack_trace_id));
 | 
				
			||||||
    if (s) {
 | 
					    if (s) {
 | 
				
			||||||
      s->weight += leaks_[i].total_size;
 | 
					      s->weight += leaks_[i].total_size;
 | 
				
			||||||
      atomic_store_relaxed(&s->hit_count, atomic_load_relaxed(&s->hit_count) +
 | 
					      atomic_store_relaxed(&s->hit_count, atomic_load_relaxed(&s->hit_count) +
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -108,14 +108,12 @@ class LeakReport {
 | 
				
			||||||
  uptr ApplySuppressions();
 | 
					  uptr ApplySuppressions();
 | 
				
			||||||
  uptr UnsuppressedLeakCount();
 | 
					  uptr UnsuppressedLeakCount();
 | 
				
			||||||
  uptr IndirectUnsuppressedLeakCount();
 | 
					  uptr IndirectUnsuppressedLeakCount();
 | 
				
			||||||
  const StackDepotReverseMap &stack_depot() { return stack_depot_; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 private:
 | 
					 private:
 | 
				
			||||||
  void PrintReportForLeak(uptr index);
 | 
					  void PrintReportForLeak(uptr index);
 | 
				
			||||||
  void PrintLeakedObjectsForLeak(uptr index);
 | 
					  void PrintLeakedObjectsForLeak(uptr index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  u32 next_id_ = 0;
 | 
					  u32 next_id_ = 0;
 | 
				
			||||||
  StackDepotReverseMap stack_depot_;
 | 
					 | 
				
			||||||
  InternalMmapVector<Leak> leaks_;
 | 
					  InternalMmapVector<Leak> leaks_;
 | 
				
			||||||
  InternalMmapVector<LeakedObject> leaked_objects_;
 | 
					  InternalMmapVector<LeakedObject> leaked_objects_;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,38 +116,4 @@ StackDepotHandle StackDepotNode::get_handle(u32 id) {
 | 
				
			||||||
  return StackDepotHandle(&theDepot.nodes[id], id);
 | 
					  return StackDepotHandle(&theDepot.nodes[id], id);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool StackDepotReverseMap::IdDescPair::IdComparator(
 | 
					 | 
				
			||||||
    const StackDepotReverseMap::IdDescPair &a,
 | 
					 | 
				
			||||||
    const StackDepotReverseMap::IdDescPair &b) {
 | 
					 | 
				
			||||||
  return a.id < b.id;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void StackDepotReverseMap::Init() const {
 | 
					 | 
				
			||||||
  if (LIKELY(map_.capacity()))
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  map_.reserve(StackDepotGetStats().n_uniq_ids + 100);
 | 
					 | 
				
			||||||
  for (int idx = 0; idx < StackDepot::kTabSize; idx++) {
 | 
					 | 
				
			||||||
    u32 s = atomic_load(&theDepot.tab[idx], memory_order_consume) &
 | 
					 | 
				
			||||||
            StackDepot::kUnlockMask;
 | 
					 | 
				
			||||||
    for (; s;) {
 | 
					 | 
				
			||||||
      const StackDepotNode &node = theDepot.nodes[s];
 | 
					 | 
				
			||||||
      IdDescPair pair = {s, &node};
 | 
					 | 
				
			||||||
      map_.push_back(pair);
 | 
					 | 
				
			||||||
      s = node.link;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  Sort(map_.data(), map_.size(), &IdDescPair::IdComparator);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
StackTrace StackDepotReverseMap::Get(u32 id) const {
 | 
					 | 
				
			||||||
  Init();
 | 
					 | 
				
			||||||
  if (!map_.size())
 | 
					 | 
				
			||||||
    return StackTrace();
 | 
					 | 
				
			||||||
  IdDescPair pair = {id, nullptr};
 | 
					 | 
				
			||||||
  uptr idx = InternalLowerBound(map_, pair, IdDescPair::IdComparator);
 | 
					 | 
				
			||||||
  if (idx > map_.size() || map_[idx].id != id)
 | 
					 | 
				
			||||||
    return StackTrace();
 | 
					 | 
				
			||||||
  return map_[idx].desc->load();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace __sanitizer
 | 
					} // namespace __sanitizer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,32 +43,6 @@ void StackDepotLockAll();
 | 
				
			||||||
void StackDepotUnlockAll();
 | 
					void StackDepotUnlockAll();
 | 
				
			||||||
void StackDepotPrintAll();
 | 
					void StackDepotPrintAll();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Instantiating this class creates a snapshot of StackDepot which can be
 | 
					 | 
				
			||||||
// efficiently queried with StackDepotGet(). You can use it concurrently with
 | 
					 | 
				
			||||||
// StackDepot, but the snapshot is only guaranteed to contain those stack traces
 | 
					 | 
				
			||||||
// which were stored before it was instantiated.
 | 
					 | 
				
			||||||
class StackDepotReverseMap {
 | 
					 | 
				
			||||||
 public:
 | 
					 | 
				
			||||||
  StackDepotReverseMap() = default;
 | 
					 | 
				
			||||||
  StackTrace Get(u32 id) const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 private:
 | 
					 | 
				
			||||||
  struct IdDescPair {
 | 
					 | 
				
			||||||
    u32 id;
 | 
					 | 
				
			||||||
    const StackDepotNode *desc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static bool IdComparator(const IdDescPair &a, const IdDescPair &b);
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  void Init() const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  mutable InternalMmapVector<IdDescPair> map_;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Disallow evil constructors.
 | 
					 | 
				
			||||||
  StackDepotReverseMap(const StackDepotReverseMap&);
 | 
					 | 
				
			||||||
  void operator=(const StackDepotReverseMap&);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace __sanitizer
 | 
					} // namespace __sanitizer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // SANITIZER_STACKDEPOT_H
 | 
					#endif // SANITIZER_STACKDEPOT_H
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,7 +58,6 @@ class StackDepotBase {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 private:
 | 
					 private:
 | 
				
			||||||
  friend Node;
 | 
					  friend Node;
 | 
				
			||||||
  friend class StackDepotReverseMap;
 | 
					 | 
				
			||||||
  u32 find(u32 s, args_type args, hash_type hash) const;
 | 
					  u32 find(u32 s, args_type args, hash_type hash) const;
 | 
				
			||||||
  static u32 lock(atomic_uint32_t *p);
 | 
					  static u32 lock(atomic_uint32_t *p);
 | 
				
			||||||
  static void unlock(atomic_uint32_t *p, u32 s);
 | 
					  static void unlock(atomic_uint32_t *p, u32 s);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,29 +106,4 @@ TEST(SanitizerCommon, StackDepotPrintNoLock) {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST(SanitizerCommon, StackDepotReverseMap) {
 | 
					 | 
				
			||||||
  uptr array1[] = {1, 2, 3, 4, 5};
 | 
					 | 
				
			||||||
  uptr array2[] = {7, 1, 3, 0};
 | 
					 | 
				
			||||||
  uptr array3[] = {10, 2, 5, 3};
 | 
					 | 
				
			||||||
  uptr array4[] = {1, 3, 2, 5};
 | 
					 | 
				
			||||||
  u32 ids[4] = {0};
 | 
					 | 
				
			||||||
  StackTrace s1(array1, ARRAY_SIZE(array1));
 | 
					 | 
				
			||||||
  StackTrace s2(array2, ARRAY_SIZE(array2));
 | 
					 | 
				
			||||||
  StackTrace s3(array3, ARRAY_SIZE(array3));
 | 
					 | 
				
			||||||
  StackTrace s4(array4, ARRAY_SIZE(array4));
 | 
					 | 
				
			||||||
  ids[0] = StackDepotPut(s1);
 | 
					 | 
				
			||||||
  ids[1] = StackDepotPut(s2);
 | 
					 | 
				
			||||||
  ids[2] = StackDepotPut(s3);
 | 
					 | 
				
			||||||
  ids[3] = StackDepotPut(s4);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  StackDepotReverseMap map;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (uptr i = 0; i < 4; i++) {
 | 
					 | 
				
			||||||
    StackTrace stack = StackDepotGet(ids[i]);
 | 
					 | 
				
			||||||
    StackTrace from_map = map.Get(ids[i]);
 | 
					 | 
				
			||||||
    EXPECT_EQ(stack.size, from_map.size);
 | 
					 | 
				
			||||||
    EXPECT_EQ(stack.trace, from_map.trace);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}  // namespace __sanitizer
 | 
					}  // namespace __sanitizer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue