tsan: extend MutexSet to memorize mutex address/stack_id

We currently memorize u64 id + epoch for each mutex.
The new tsan runtime will memorize address + stack_id instead.
But switching to address + stack_id requires new trace,
which in turn requires new MutexSet and some other changes.
Extend MutexSet to support both new and old info to break
the dependency cycles. The plan is to remove the old
info/methods after switching to the new runtime.

Reviewed By: vitalybuka, melver

Differential Revision: https://reviews.llvm.org/D107910
This commit is contained in:
Dmitry Vyukov 2021-08-10 20:19:58 +02:00
parent f52fc591fa
commit d9afba9000
2 changed files with 55 additions and 6 deletions

View File

@ -17,7 +17,6 @@ namespace __tsan {
const uptr MutexSet::kMaxSize;
MutexSet::MutexSet() {
size_ = 0;
internal_memset(&descs_, 0, sizeof(descs_));
}
@ -44,9 +43,12 @@ void MutexSet::Add(u64 id, bool write, u64 epoch) {
CHECK_EQ(size_, kMaxSize - 1);
}
// Add new mutex descriptor.
descs_[size_].addr = 0;
descs_[size_].stack_id = kInvalidStackID;
descs_[size_].id = id;
descs_[size_].write = write;
descs_[size_].epoch = epoch;
descs_[size_].seq = seq_++;
descs_[size_].count = 1;
size_++;
}
@ -70,6 +72,46 @@ void MutexSet::Remove(u64 id) {
}
}
void MutexSet::AddAddr(uptr addr, StackID stack_id, bool write) {
// Look up existing mutex with the same id.
for (uptr i = 0; i < size_; i++) {
if (descs_[i].addr == addr) {
descs_[i].count++;
descs_[i].seq = seq_++;
return;
}
}
// On overflow, find the oldest mutex and drop it.
if (size_ == kMaxSize) {
uptr min = 0;
for (uptr i = 0; i < size_; i++) {
if (descs_[i].seq < descs_[min].seq)
min = i;
}
RemovePos(min);
CHECK_EQ(size_, kMaxSize - 1);
}
// Add new mutex descriptor.
descs_[size_].addr = addr;
descs_[size_].stack_id = stack_id;
descs_[size_].id = 0;
descs_[size_].write = write;
descs_[size_].epoch = 0;
descs_[size_].seq = seq_++;
descs_[size_].count = 1;
size_++;
}
void MutexSet::DelAddr(uptr addr, bool destroy) {
for (uptr i = 0; i < size_; i++) {
if (descs_[i].addr == addr) {
if (destroy || --descs_[i].count == 0)
RemovePos(i);
return;
}
}
}
void MutexSet::RemovePos(uptr i) {
CHECK_LT(i, size_);
descs_[i] = descs_[size_ - 1];

View File

@ -23,9 +23,12 @@ class MutexSet {
// The oldest mutexes are discarded on overflow.
static const uptr kMaxSize = 16;
struct Desc {
uptr addr;
StackID stack_id;
u64 id;
u64 epoch;
int count;
u32 seq;
u32 count;
bool write;
};
@ -34,21 +37,24 @@ class MutexSet {
void Add(u64 id, bool write, u64 epoch);
void Del(u64 id, bool write);
void Remove(u64 id); // Removes the mutex completely (if it's destroyed).
void AddAddr(uptr addr, StackID stack_id, bool write);
void DelAddr(uptr addr, bool destroy = false);
uptr Size() const;
Desc Get(uptr i) const;
MutexSet(const MutexSet& other) { *this = other; }
void operator=(const MutexSet &other) {
internal_memcpy(this, &other, sizeof(*this));
}
private:
#if !SANITIZER_GO
uptr size_;
u32 seq_ = 0;
uptr size_ = 0;
Desc descs_[kMaxSize];
#endif
void RemovePos(uptr i);
MutexSet(const MutexSet&);
#endif
};
// Go does not have mutexes, so do not spend memory and time.
@ -59,7 +65,8 @@ MutexSet::MutexSet() {}
void MutexSet::Add(u64 id, bool write, u64 epoch) {}
void MutexSet::Del(u64 id, bool write) {}
void MutexSet::Remove(u64 id) {}
void MutexSet::RemovePos(uptr i) {}
void MutexSet::AddAddr(uptr addr, StackID stack_id, bool write) {}
void MutexSet::DelAddr(uptr addr, bool destroy) {}
uptr MutexSet::Size() const { return 0; }
MutexSet::Desc MutexSet::Get(uptr i) const { return Desc(); }
#endif