[sanitizer] Add a ForEach callback interface for AddrHashMap.

This change adds a ForEach method to the AddrHashMap class which can
then be used to iterate over all the key value pairs in the hash map.
I intend to use this in an upcoming change to the memprof runtime.

Added a unit test to cover basic insertion and the ForEach callback.

Differential Revision: https://reviews.llvm.org/D111368
This commit is contained in:
Snehasish Kumar 2021-10-07 16:35:11 -07:00
parent 986416251b
commit d19470540a
3 changed files with 101 additions and 0 deletions

View File

@ -39,6 +39,11 @@ namespace __sanitizer {
// the current thread has exclusive access to the data // the current thread has exclusive access to the data
// if !h.exists() then the element never existed // if !h.exists() then the element never existed
// } // }
// {
// Map::Handle h(&m, addr, false, true);
// this will create a new element or return a handle to an existing element
// if !h.created() this thread does *not* have exclusive access to the data
// }
template<typename T, uptr kSize> template<typename T, uptr kSize>
class AddrHashMap { class AddrHashMap {
private: private:
@ -89,6 +94,12 @@ class AddrHashMap {
bool create_; bool create_;
}; };
typedef void (*ForEachCallback)(const uptr key, const T &val, void *arg);
// ForEach acquires a lock on each bucket while iterating over
// elements. Note that this only ensures that the structure of the hashmap is
// unchanged, there may be a data race to the element itself.
void ForEach(ForEachCallback cb, void *arg);
private: private:
friend class Handle; friend class Handle;
Bucket *table_; Bucket *table_;
@ -98,6 +109,33 @@ class AddrHashMap {
uptr calcHash(uptr addr); uptr calcHash(uptr addr);
}; };
template <typename T, uptr kSize>
void AddrHashMap<T, kSize>::ForEach(ForEachCallback cb, void *arg) {
for (uptr n = 0; n < kSize; n++) {
Bucket *bucket = &table_[n];
ReadLock lock(&bucket->mtx);
for (uptr i = 0; i < kBucketSize; i++) {
Cell *c = &bucket->cells[i];
uptr addr1 = atomic_load(&c->addr, memory_order_acquire);
if (addr1 != 0)
cb(addr1, c->val, arg);
}
// Iterate over any additional cells.
if (AddBucket *add =
(AddBucket *)atomic_load(&bucket->add, memory_order_acquire)) {
for (uptr i = 0; i < add->size; i++) {
Cell *c = &add->cells[i];
uptr addr1 = atomic_load(&c->addr, memory_order_acquire);
if (addr1 != 0)
cb(addr1, c->val, arg);
}
}
}
}
template<typename T, uptr kSize> template<typename T, uptr kSize>
AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr) { AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr) {
map_ = map; map_ = map;

View File

@ -9,6 +9,7 @@ if(APPLE)
endif() endif()
set(SANITIZER_UNITTESTS set(SANITIZER_UNITTESTS
sanitizer_addrhashmap_test.cpp
sanitizer_allocator_test.cpp sanitizer_allocator_test.cpp
sanitizer_atomic_test.cpp sanitizer_atomic_test.cpp
sanitizer_bitvector_test.cpp sanitizer_bitvector_test.cpp

View File

@ -0,0 +1,62 @@
//===-- sanitizer_addrhashmap_test.cpp ------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_addrhashmap.h"
#include <unordered_map>
#include "gtest/gtest.h"
namespace __sanitizer {
struct Value {
int payload;
inline bool operator==(const Value& rhs) const {
return payload == rhs.payload;
}
};
using MapTy = AddrHashMap<Value, 11>;
using HandleTy = MapTy::Handle;
using RefMapTy = std::unordered_map<uptr, Value>;
static void ExistsInReferenceMap(const uptr key, const Value& val, void* arg) {
RefMapTy* ref = reinterpret_cast<RefMapTy*>(arg);
const RefMapTy::iterator iter = ref->find(key);
ASSERT_NE(iter, ref->end());
EXPECT_EQ(iter->second, val);
ref->erase(iter);
}
TEST(AddrHashMap, Basic) {
// Use a reference implementation to compare with.
RefMapTy reference_map{
{0x1000, {1}},
{0x2000, {2}},
{0x3000, {3}},
};
MapTy m;
for (const auto& key_val : reference_map) {
const uptr key = key_val.first;
const Value val = key_val.second;
// Insert all the elements.
{
HandleTy h(&m, key);
ASSERT_TRUE(h.created());
h->payload = val.payload;
}
}
// Now check that all the elements are present.
m.ForEach(ExistsInReferenceMap, &reference_map);
EXPECT_TRUE(reference_map.empty());
}
} // namespace __sanitizer