#include "base/profiler/metadata_recorder.h"
#include <optional>
#include "base/metrics/histogram_macros.h"
namespace base {
const size_t MetadataRecorder::MAX_METADATA_COUNT;
MetadataRecorder::Item::Item(uint64_t name_hash,
std::optional<int64_t> key,
std::optional<PlatformThreadId> thread_id,
int64_t value)
: name_hash(name_hash), key(key), thread_id(thread_id), value(value) {}
MetadataRecorder::Item::Item() : name_hash(0), value(0) {}
MetadataRecorder::Item::Item(const Item& other) = default;
MetadataRecorder::Item& MetadataRecorder::Item::Item::operator=(
const Item& other) = default;
MetadataRecorder::ItemInternal::ItemInternal() = default;
MetadataRecorder::ItemInternal::~ItemInternal() = default;
MetadataRecorder::MetadataRecorder() {
DCHECK(items_[0].is_active.is_lock_free());
DCHECK(items_[0].value.is_lock_free());
}
MetadataRecorder::~MetadataRecorder() = default;
void MetadataRecorder::Set(uint64_t name_hash,
std::optional<int64_t> key,
std::optional<PlatformThreadId> thread_id,
int64_t value) {
AutoLock lock(write_lock_);
size_t item_slots_used = item_slots_used_.load(std::memory_order_relaxed);
for (size_t i = 0; i < item_slots_used; ++i) {
auto& item = items_[i];
if (item.name_hash == name_hash && item.key == key &&
item.thread_id == thread_id) {
item.value.store(value, std::memory_order_relaxed);
const bool was_active =
item.is_active.exchange(true, std::memory_order_release);
if (!was_active) {
inactive_item_count_--;
}
return;
}
}
item_slots_used = TryReclaimInactiveSlots(item_slots_used);
if (item_slots_used == items_.size()) {
return;
}
auto& item = items_[item_slots_used];
item.name_hash = name_hash;
item.key = key;
item.thread_id = thread_id;
item.value.store(value, std::memory_order_relaxed);
item.is_active.store(true, std::memory_order_release);
item_slots_used_.fetch_add(1, std::memory_order_release);
}
void MetadataRecorder::Remove(uint64_t name_hash,
std::optional<int64_t> key,
std::optional<PlatformThreadId> thread_id) {
AutoLock lock(write_lock_);
size_t item_slots_used = item_slots_used_.load(std::memory_order_relaxed);
for (size_t i = 0; i < item_slots_used; ++i) {
auto& item = items_[i];
if (item.name_hash == name_hash && item.key == key &&
item.thread_id == thread_id) {
const bool was_active =
item.is_active.exchange(false, std::memory_order_relaxed);
if (was_active) {
inactive_item_count_++;
}
return;
}
}
}
MetadataRecorder::MetadataProvider::MetadataProvider(
MetadataRecorder* metadata_recorder,
PlatformThreadId thread_id)
: metadata_recorder_(metadata_recorder),
thread_id_(thread_id),
auto_lock_(metadata_recorder->read_lock_) {}
MetadataRecorder::MetadataProvider::~MetadataProvider() = default;
size_t MetadataRecorder::MetadataProvider::GetItems(
ItemArray* const items) const {
return metadata_recorder_->GetItems(items, thread_id_);
}
size_t MetadataRecorder::GetItems(ItemArray* const items,
PlatformThreadId thread_id) const {
size_t item_slots_used = item_slots_used_.load(std::memory_order_acquire);
size_t write_index = 0;
for (size_t read_index = 0; read_index < item_slots_used; ++read_index) {
const auto& item = items_[read_index];
if (item.is_active.load(std::memory_order_acquire) &&
(!item.thread_id.has_value() || item.thread_id == thread_id)) {
(*items)[write_index++] =
Item{item.name_hash, item.key, item.thread_id,
item.value.load(std::memory_order_relaxed)};
}
}
return write_index;
}
size_t MetadataRecorder::TryReclaimInactiveSlots(size_t item_slots_used) {
const size_t remaining_slots = MAX_METADATA_COUNT - item_slots_used;
if (inactive_item_count_ == 0 || inactive_item_count_ < remaining_slots) {
return item_slots_used;
}
if (read_lock_.Try()) {
item_slots_used = ReclaimInactiveSlots(item_slots_used);
read_lock_.Release();
}
return item_slots_used;
}
size_t MetadataRecorder::ReclaimInactiveSlots(size_t item_slots_used) {
size_t first_inactive_item_idx = 0;
size_t last_active_item_idx = item_slots_used - 1;
while (first_inactive_item_idx < last_active_item_idx) {
ItemInternal& inactive_item = items_[first_inactive_item_idx];
ItemInternal& active_item = items_[last_active_item_idx];
if (inactive_item.is_active.load(std::memory_order_relaxed)) {
++first_inactive_item_idx;
continue;
}
if (!active_item.is_active.load(std::memory_order_relaxed)) {
--last_active_item_idx;
item_slots_used--;
continue;
}
inactive_item.name_hash = active_item.name_hash;
inactive_item.value.store(active_item.value.load(std::memory_order_relaxed),
std::memory_order_relaxed);
inactive_item.is_active.store(true, std::memory_order_relaxed);
++first_inactive_item_idx;
--last_active_item_idx;
item_slots_used--;
}
item_slots_used_.store(item_slots_used, std::memory_order_relaxed);
return item_slots_used;
}
}