forked from OSchip/llvm-project
[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:
parent
986416251b
commit
d19470540a
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue