#include "base/metrics/persistent_histogram_allocator.h"
#include <atomic>
#include <limits>
#include <memory>
#include <string_view>
#include <utility>
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/debug/crash_logging.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/important_file_writer.h"
#include "base/files/memory_mapped_file.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/histogram_samples.h"
#include "base/metrics/metrics_hashes.h"
#include "base/metrics/persistent_sample_map.h"
#include "base/metrics/sparse_histogram.h"
#include "base/metrics/statistics_recorder.h"
#include "base/notreached.h"
#include "base/pickle.h"
#include "base/process/process_handle.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "build/build_config.h"
namespace base {
namespace {
enum : uint32_t {
kTypeIdRangesArray = 0xBCEA225A + 1,
kTypeIdCountsArray = 0x53215530 + 1,
};
subtle::AtomicWord g_histogram_allocator = 0;
size_t CalculateRequiredCountsBytes(size_t bucket_count) {
const size_t kBytesPerBucket = 2 * sizeof(HistogramBase::AtomicCount);
if (bucket_count > std::numeric_limits<size_t>::max() / kBytesPerBucket) {
return 0;
}
return bucket_count * kBytesPerBucket;
}
bool MergeSamplesToExistingHistogram(
HistogramBase* existing,
const HistogramBase* histogram,
std::unique_ptr<HistogramSamples> samples) {
HistogramType existing_type = existing->GetHistogramType();
if (existing_type == HistogramType::DUMMY_HISTOGRAM) {
return true;
}
if (histogram->GetHistogramType() != existing_type) {
return false;
}
if (existing_type == HistogramType::HISTOGRAM ||
existing_type == HistogramType::LINEAR_HISTOGRAM ||
existing_type == HistogramType::BOOLEAN_HISTOGRAM ||
existing_type == HistogramType::CUSTOM_HISTOGRAM) {
const BucketRanges* existing_buckets =
static_cast<const Histogram*>(existing)->bucket_ranges();
const BucketRanges* histogram_buckets =
static_cast<const Histogram*>(histogram)->bucket_ranges();
DCHECK(existing_buckets->HasValidChecksum());
DCHECK(histogram_buckets->HasValidChecksum());
if (existing_buckets->checksum() != histogram_buckets->checksum()) {
return false;
}
}
return existing->AddSamples(*samples);
}
}
PersistentSparseHistogramDataManager::PersistentSparseHistogramDataManager(
PersistentMemoryAllocator* allocator)
: allocator_(allocator), record_iterator_(allocator) {}
PersistentSparseHistogramDataManager::~PersistentSparseHistogramDataManager() =
default;
std::unique_ptr<PersistentSampleMapRecords>
PersistentSparseHistogramDataManager::CreateSampleMapRecords(uint64_t id) {
base::AutoLock auto_lock(lock_);
return std::make_unique<PersistentSampleMapRecords>(
this, id, GetSampleMapRecordsWhileLocked(id));
}
std::vector<PersistentSparseHistogramDataManager::ReferenceAndSample>*
PersistentSparseHistogramDataManager::GetSampleMapRecordsWhileLocked(
uint64_t id) {
auto* samples = &sample_records_[id];
if (!samples->get()) {
*samples = std::make_unique<std::vector<ReferenceAndSample>>();
}
return samples->get();
}
std::vector<PersistentMemoryAllocator::Reference>
PersistentSparseHistogramDataManager::LoadRecords(
PersistentSampleMapRecords* sample_map_records,
std::optional<HistogramBase::Sample32> until_value) {
base::AutoLock auto_lock(lock_);
const size_t kMinimumNumberToLoad = 10;
const uint64_t match_id = sample_map_records->sample_map_id_;
auto& found_records = *sample_map_records->records_;
bool found = (found_records.size() > sample_map_records->seen_);
size_t new_records = 0;
while (!found || new_records < kMinimumNumberToLoad) {
uint64_t found_id;
HistogramBase::Sample32 value;
PersistentMemoryAllocator::Reference ref =
PersistentSampleMap::GetNextPersistentRecord(record_iterator_,
&found_id, &value);
if (!ref) {
break;
}
++new_records;
if (found_id == match_id) {
found_records.emplace_back(ref, value);
found = true;
} else {
std::vector<ReferenceAndSample>* samples =
GetSampleMapRecordsWhileLocked(found_id);
CHECK(samples);
samples->emplace_back(ref, value);
}
}
std::vector<PersistentMemoryAllocator::Reference> new_references;
CHECK_GE(found_records.size(), sample_map_records->seen_);
auto new_found_records =
span(found_records).subspan(sample_map_records->seen_);
new_references.reserve(new_found_records.size());
for (const auto& new_record : new_found_records) {
new_references.push_back(new_record.reference);
if (until_value.has_value() && new_record.value == until_value.value()) {
break;
}
}
return new_references;
}
PersistentSampleMapRecords::PersistentSampleMapRecords(
PersistentSparseHistogramDataManager* data_manager,
uint64_t sample_map_id,
std::vector<PersistentSparseHistogramDataManager::ReferenceAndSample>*
records)
: data_manager_(data_manager),
sample_map_id_(sample_map_id),
records_(records) {}
PersistentSampleMapRecords::~PersistentSampleMapRecords() = default;
std::vector<PersistentMemoryAllocator::Reference>
PersistentSampleMapRecords::GetNextRecords(
std::optional<HistogramBase::Sample32> until_value) {
auto references = data_manager_->LoadRecords(this, until_value);
seen_ += references.size();
return references;
}
PersistentMemoryAllocator::Reference PersistentSampleMapRecords::CreateNew(
HistogramBase::Sample32 value) {
return PersistentSampleMap::CreatePersistentRecord(data_manager_->allocator_,
sample_map_id_, value);
}
struct PersistentHistogramAllocator::PersistentHistogramData {
static constexpr uint32_t kPersistentTypeId = 0xF1645910 + 3;
static constexpr size_t kExpectedInstanceSize =
40 + 2 * HistogramSamples::Metadata::kExpectedInstanceSize;
int32_t histogram_type;
int32_t flags;
int32_t minimum;
int32_t maximum;
uint32_t bucket_count;
PersistentMemoryAllocator::Reference ranges_ref;
uint32_t ranges_checksum;
std::atomic<PersistentMemoryAllocator::Reference> counts_ref;
HistogramSamples::Metadata samples_metadata;
HistogramSamples::Metadata logged_metadata;
char name[sizeof(uint64_t)];
};
PersistentHistogramAllocator::Iterator::Iterator(
PersistentHistogramAllocator* allocator)
: allocator_(allocator), memory_iter_(allocator->memory_allocator()) {}
std::unique_ptr<HistogramBase>
PersistentHistogramAllocator::Iterator::GetNextWithIgnore(Reference ignore) {
PersistentMemoryAllocator::Reference ref;
while ((ref = memory_iter_.GetNextOfType<PersistentHistogramData>()) != 0) {
if (ref != ignore) {
return allocator_->GetHistogram(ref);
}
}
return nullptr;
}
PersistentHistogramAllocator::PersistentHistogramAllocator(
std::unique_ptr<PersistentMemoryAllocator> memory)
: memory_allocator_(std::move(memory)),
sparse_histogram_data_manager_(memory_allocator_.get()) {}
PersistentHistogramAllocator::~PersistentHistogramAllocator() = default;
std::unique_ptr<HistogramBase> PersistentHistogramAllocator::GetHistogram(
Reference ref) {
size_t alloc_size = 0;
PersistentHistogramData* data =
memory_allocator_->GetAsObject<PersistentHistogramData>(ref, &alloc_size);
DurableStringView durable_metric_name(PersistentMemoryAllocator::StringViewAt(
data, offsetof(PersistentHistogramData, name), alloc_size));
uint64_t name_hash = HashMetricName(*durable_metric_name);
if (durable_metric_name->empty() ||
UNSAFE_TODO(reinterpret_cast<const char*>(data)[alloc_size - 1]) !=
'\0' ||
data->samples_metadata.id == 0 || data->logged_metadata.id == 0 ||
(data->histogram_type == SPARSE_HISTOGRAM
? (data->logged_metadata.id != data->samples_metadata.id &&
data->logged_metadata.id != data->samples_metadata.id + 1)
: (data->logged_metadata.id != data->samples_metadata.id)) ||
name_hash != data->samples_metadata.id) {
return nullptr;
}
return CreateHistogram(data, durable_metric_name, name_hash);
}
std::unique_ptr<HistogramBase> PersistentHistogramAllocator::AllocateHistogram(
HistogramType histogram_type,
std::string_view name,
uint64_t name_hash,
int minimum,
int maximum,
const BucketRanges* bucket_ranges,
int32_t flags,
Reference* ref_ptr) {
if (memory_allocator_->IsCorrupt()) {
return nullptr;
}
PersistentHistogramData* histogram_data =
memory_allocator_->New<PersistentHistogramData>(
offsetof(PersistentHistogramData, name) + name.size() + 1);
if (histogram_data) {
UNSAFE_TODO(memcpy(histogram_data->name, name.data(), name.size()));
UNSAFE_TODO(histogram_data->name[name.size()]) = '\0';
histogram_data->histogram_type = histogram_type;
histogram_data->flags = flags | HistogramBase::kIsPersistent;
histogram_data->counts_ref.store(0, std::memory_order_relaxed);
}
if (histogram_type != SPARSE_HISTOGRAM) {
size_t bucket_count = bucket_ranges->bucket_count();
size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count);
if (counts_bytes == 0) {
return nullptr;
}
DCHECK_EQ(this, GlobalHistogramAllocator::Get());
PersistentMemoryAllocator::Reference ranges_ref =
bucket_ranges->persistent_reference();
if (!ranges_ref) {
size_t ranges_count = bucket_count + 1;
size_t ranges_bytes = ranges_count * sizeof(HistogramBase::Sample32);
ranges_ref =
memory_allocator_->Allocate(ranges_bytes, kTypeIdRangesArray);
if (ranges_ref) {
HistogramBase::Sample32* ranges_data =
memory_allocator_->GetAsArray<HistogramBase::Sample32>(
ranges_ref, kTypeIdRangesArray, ranges_count);
if (ranges_data) {
for (size_t i = 0; i < bucket_ranges->size(); ++i) {
UNSAFE_TODO(ranges_data[i]) = bucket_ranges->range(i);
}
bucket_ranges->set_persistent_reference(ranges_ref);
} else {
ranges_ref = PersistentMemoryAllocator::kReferenceNull;
}
}
} else {
DCHECK_EQ(kTypeIdRangesArray, memory_allocator_->GetType(ranges_ref));
}
if (ranges_ref && histogram_data) {
histogram_data->minimum = minimum;
histogram_data->maximum = maximum;
histogram_data->bucket_count = static_cast<uint32_t>(bucket_count);
histogram_data->ranges_ref = ranges_ref;
histogram_data->ranges_checksum = bucket_ranges->checksum();
} else {
histogram_data = nullptr;
}
}
if (histogram_data) {
DurableStringView durable_name(
std::string_view(histogram_data->name, name.size()));
std::unique_ptr<HistogramBase> histogram =
CreateHistogram(histogram_data, durable_name, name_hash);
DCHECK(histogram);
DCHECK_NE(0U, histogram_data->samples_metadata.id);
DCHECK_NE(0U, histogram_data->logged_metadata.id);
PersistentMemoryAllocator::Reference histogram_ref =
memory_allocator_->GetAsReference(histogram_data);
if (ref_ptr != nullptr) {
*ref_ptr = histogram_ref;
}
last_created_.store(histogram_ref, std::memory_order_relaxed);
return histogram;
}
return nullptr;
}
void PersistentHistogramAllocator::FinalizeHistogram(Reference ref,
bool registered) {
if (registered) {
memory_allocator_->MakeIterable(ref);
} else {
memory_allocator_->ChangeType(ref, 0,
PersistentHistogramData::kPersistentTypeId,
false);
}
}
bool PersistentHistogramAllocator::MergeHistogramDeltaToStatisticsRecorder(
HistogramBase* histogram) {
DCHECK(histogram);
std::unique_ptr<HistogramSamples> samples = histogram->SnapshotDelta();
if (samples->IsDefinitelyEmpty()) {
return true;
}
HistogramBase* existing = GetOrCreateStatisticsRecorderHistogram(histogram);
if (!existing) {
return false;
}
return MergeSamplesToExistingHistogram(existing, histogram,
std::move(samples));
}
bool PersistentHistogramAllocator::MergeHistogramFinalDeltaToStatisticsRecorder(
const HistogramBase* histogram) {
DCHECK(histogram);
std::unique_ptr<HistogramSamples> samples = histogram->SnapshotFinalDelta();
if (samples->IsDefinitelyEmpty()) {
return true;
}
HistogramBase* existing = GetOrCreateStatisticsRecorderHistogram(histogram);
if (!existing) {
return false;
}
return MergeSamplesToExistingHistogram(existing, histogram,
std::move(samples));
}
std::unique_ptr<PersistentSampleMapRecords>
PersistentHistogramAllocator::CreateSampleMapRecords(uint64_t id) {
return sparse_histogram_data_manager_.CreateSampleMapRecords(id);
}
void PersistentHistogramAllocator::CreateTrackingHistograms(
std::string_view name) {
memory_allocator_->CreateTrackingHistograms(name);
}
void PersistentHistogramAllocator::UpdateTrackingHistograms() {
memory_allocator_->UpdateTrackingHistograms();
}
void PersistentHistogramAllocator::SetRangesManager(
RangesManager* ranges_manager) {
ranges_manager_.reset(ranges_manager);
}
void PersistentHistogramAllocator::ClearLastCreatedReferenceForTesting() {
last_created_.store(0, std::memory_order_relaxed);
}
std::unique_ptr<HistogramBase> PersistentHistogramAllocator::CreateHistogram(
PersistentHistogramData* histogram_data_ptr,
DurableStringView durable_name,
uint64_t name_hash) {
if (!histogram_data_ptr) {
return nullptr;
}
DCHECK_EQ(durable_name->data(), histogram_data_ptr->name);
if (histogram_data_ptr->histogram_type == SPARSE_HISTOGRAM) {
std::unique_ptr<HistogramBase> histogram =
SparseHistogram::PersistentCreate(this, durable_name, name_hash,
&histogram_data_ptr->samples_metadata,
&histogram_data_ptr->logged_metadata);
DCHECK(histogram);
histogram->SetFlags(histogram_data_ptr->flags);
return histogram;
}
const int32_t histogram_type = histogram_data_ptr->histogram_type;
const int32_t histogram_flags = histogram_data_ptr->flags;
const int32_t histogram_minimum = histogram_data_ptr->minimum;
const int32_t histogram_maximum = histogram_data_ptr->maximum;
const uint32_t histogram_bucket_count = histogram_data_ptr->bucket_count;
const uint32_t histogram_ranges_ref = histogram_data_ptr->ranges_ref;
const uint32_t histogram_ranges_checksum =
histogram_data_ptr->ranges_checksum;
size_t allocated_bytes = 0;
const HistogramBase::Sample32* const ranges_data =
memory_allocator_->GetAsArray<HistogramBase::Sample32>(
histogram_ranges_ref, kTypeIdRangesArray,
PersistentMemoryAllocator::kSizeAny, &allocated_bytes);
const size_t ranges_size = histogram_bucket_count + 1;
const uint32_t max_buckets =
std::numeric_limits<uint32_t>::max() / sizeof(HistogramBase::Sample32);
const size_t required_bytes = ranges_size * sizeof(HistogramBase::Sample32);
if (!ranges_data || histogram_bucket_count < 2 ||
histogram_bucket_count >= max_buckets ||
allocated_bytes < required_bytes) {
return nullptr;
}
auto created_ranges = std::make_unique<const BucketRanges>(
UNSAFE_BUFFERS(base::span(ranges_data, ranges_size)));
if (created_ranges->size() != ranges_size ||
created_ranges->checksum() != histogram_ranges_checksum ||
created_ranges->range(1) != histogram_minimum ||
created_ranges->range(histogram_bucket_count - 1) != histogram_maximum) {
return nullptr;
}
const BucketRanges* ranges;
if (ranges_manager_) {
ranges =
ranges_manager_->GetOrRegisterCanonicalRanges(created_ranges.get());
if (ranges == created_ranges.get()) {
created_ranges.release();
}
} else {
ranges = StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
created_ranges.release());
}
size_t counts_bytes = CalculateRequiredCountsBytes(histogram_bucket_count);
if (counts_bytes == 0) {
return nullptr;
}
PersistentMemoryAllocator::Reference counts_ref =
histogram_data_ptr->counts_ref.load(std::memory_order_acquire);
if (counts_ref != 0 && !memory_allocator_->GetAsArray<uint8_t>(
counts_ref, kTypeIdCountsArray, counts_bytes)) {
return nullptr;
}
DelayedPersistentAllocation counts_data(memory_allocator_.get(),
&histogram_data_ptr->counts_ref,
kTypeIdCountsArray, counts_bytes);
DelayedPersistentAllocation logged_data(
memory_allocator_.get(), &histogram_data_ptr->counts_ref,
kTypeIdCountsArray, counts_bytes, counts_bytes / 2);
std::unique_ptr<HistogramBase> histogram;
switch (histogram_type) {
case HISTOGRAM:
histogram = Histogram::PersistentCreate(
durable_name, ranges, counts_data, logged_data,
&histogram_data_ptr->samples_metadata,
&histogram_data_ptr->logged_metadata);
DCHECK(histogram);
break;
case LINEAR_HISTOGRAM:
histogram = LinearHistogram::PersistentCreate(
durable_name, ranges, counts_data, logged_data,
&histogram_data_ptr->samples_metadata,
&histogram_data_ptr->logged_metadata);
DCHECK(histogram);
break;
case BOOLEAN_HISTOGRAM:
histogram = BooleanHistogram::PersistentCreate(
durable_name, ranges, counts_data, logged_data,
&histogram_data_ptr->samples_metadata,
&histogram_data_ptr->logged_metadata);
DCHECK(histogram);
break;
case CUSTOM_HISTOGRAM:
histogram = CustomHistogram::PersistentCreate(
durable_name, ranges, counts_data, logged_data,
&histogram_data_ptr->samples_metadata,
&histogram_data_ptr->logged_metadata);
DCHECK(histogram);
break;
default:
return nullptr;
}
if (histogram) {
DCHECK_EQ(histogram_type, histogram->GetHistogramType());
histogram->SetFlags(histogram_flags);
}
return histogram;
}
HistogramBase*
PersistentHistogramAllocator::GetOrCreateStatisticsRecorderHistogram(
const HistogramBase* histogram) {
DCHECK_NE(GlobalHistogramAllocator::Get(), this);
DCHECK(histogram);
HistogramBase* existing =
StatisticsRecorder::FindHistogram(histogram->histogram_name());
if (existing) {
return existing;
}
base::Pickle pickle;
histogram->SerializeInfo(&pickle);
PickleIterator iter(pickle);
existing = DeserializeHistogramInfo(&iter);
if (!existing) {
return nullptr;
}
DCHECK(!existing->HasFlags(HistogramBase::kIPCSerializationSourceFlag));
return StatisticsRecorder::RegisterOrDeleteDuplicate(existing);
}
GlobalHistogramAllocator::~GlobalHistogramAllocator() {
NOTREACHED();
}
void GlobalHistogramAllocator::CreateWithPersistentMemory(
void* base,
size_t size,
size_t page_size,
uint64_t id,
std::string_view name) {
Set(new GlobalHistogramAllocator(std::make_unique<PersistentMemoryAllocator>(
base, size, page_size, id, name, PersistentMemoryAllocator::kReadWrite)));
}
void GlobalHistogramAllocator::CreateWithLocalMemory(size_t size,
uint64_t id,
std::string_view name) {
Set(new GlobalHistogramAllocator(
std::make_unique<LocalPersistentMemoryAllocator>(size, id, name)));
}
bool GlobalHistogramAllocator::CreateWithFile(const FilePath& file_path,
size_t size,
uint64_t id,
std::string_view name,
bool exclusive_write) {
uint32_t flags = File::FLAG_OPEN_ALWAYS | File::FLAG_WIN_SHARE_DELETE |
File::FLAG_READ | File::FLAG_WRITE;
if (exclusive_write) {
flags |= File::FLAG_WIN_EXCLUSIVE_WRITE;
}
File file(file_path, flags);
if (!file.IsValid()) {
return false;
}
auto mmfile = std::make_unique<MemoryMappedFile>();
bool success = false;
const bool file_created = file.created();
if (file_created) {
success = mmfile->Initialize(std::move(file), {0, size},
MemoryMappedFile::READ_WRITE_EXTEND);
} else {
success = mmfile->Initialize(std::move(file), MemoryMappedFile::READ_WRITE);
}
if (!success ||
!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)) {
if (file_created) {
base::DeleteFile(file_path);
}
return false;
}
Set(new GlobalHistogramAllocator(
std::make_unique<FilePersistentMemoryAllocator>(
std::move(mmfile), 0, id, name,
PersistentMemoryAllocator::kReadWrite)));
Get()->SetPersistentLocation(file_path);
return true;
}
bool GlobalHistogramAllocator::CreateWithActiveFile(const FilePath& base_path,
const FilePath& active_path,
const FilePath& spare_path,
size_t size,
uint64_t id,
std::string_view name) {
if (!base::ReplaceFile(active_path, base_path, nullptr)) {
base::DeleteFile(base_path);
}
if (base::PathExists(active_path)) {
return false;
}
if (!spare_path.empty()) {
base::ReplaceFile(spare_path, active_path, nullptr);
}
return base::GlobalHistogramAllocator::CreateWithFile(active_path, size, id,
name);
}
bool GlobalHistogramAllocator::CreateWithActiveFileInDir(
const FilePath& dir,
size_t size,
uint64_t id,
std::string_view name) {
FilePath base_path = ConstructFilePath(dir, name);
FilePath active_path = ConstructFilePathForActiveFile(dir, name);
FilePath spare_path = ConstructFilePathForSpareFile(dir, name);
return CreateWithActiveFile(base_path, active_path, spare_path, size, id,
name);
}
FilePath GlobalHistogramAllocator::ConstructFilePath(const FilePath& dir,
std::string_view name) {
return dir.AppendASCII(name).AddExtension(
PersistentMemoryAllocator::kFileExtension);
}
FilePath GlobalHistogramAllocator::ConstructFilePathForActiveFile(
const FilePath& dir,
std::string_view name) {
return ConstructFilePath(dir, std::string(name) + "-active");
}
FilePath GlobalHistogramAllocator::ConstructFilePathForSpareFile(
const FilePath& dir,
std::string_view name) {
return ConstructFilePath(dir, std::string(name) + "-spare");
}
FilePath GlobalHistogramAllocator::ConstructFilePathForUploadDir(
const FilePath& dir,
std::string_view name,
base::Time stamp,
ProcessId pid) {
return ConstructFilePath(
dir,
StringPrintf("%.*s-%lX-%lX", static_cast<int>(name.length()), name.data(),
static_cast<long>(stamp.ToTimeT()), static_cast<long>(pid)));
}
FilePath GlobalHistogramAllocator::ConstructFilePathForUploadDir(
const FilePath& dir,
std::string_view name) {
return ConstructFilePathForUploadDir(dir, name, Time::Now(),
GetCurrentProcId());
}
bool GlobalHistogramAllocator::ParseFilePath(const FilePath& path,
std::string* out_name,
Time* out_stamp,
ProcessId* out_pid) {
std::string filename = path.BaseName().AsUTF8Unsafe();
std::vector<std::string_view> parts = base::SplitStringPiece(
filename, "-.", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
if (parts.size() != 4) {
return false;
}
if (out_name) {
*out_name = std::string(parts[0]);
}
if (out_stamp) {
int64_t stamp;
if (!HexStringToInt64(parts[1], &stamp)) {
return false;
}
*out_stamp = Time::FromTimeT(static_cast<time_t>(stamp));
}
if (out_pid) {
int64_t pid;
if (!HexStringToInt64(parts[2], &pid)) {
return false;
}
*out_pid = static_cast<ProcessId>(pid);
}
return true;
}
bool GlobalHistogramAllocator::CreateSpareFile(const FilePath& spare_path,
size_t size) {
if (base::PathExists(spare_path)) {
return false;
}
FilePath temp_spare_path = spare_path.AddExtension(FILE_PATH_LITERAL(".tmp"));
bool success;
{
File spare_file(temp_spare_path, File::FLAG_CREATE_ALWAYS |
File::FLAG_READ | File::FLAG_WRITE);
success = spare_file.IsValid();
if (success) {
MemoryMappedFile mmfile;
success = mmfile.Initialize(std::move(spare_file), {0, size},
MemoryMappedFile::READ_WRITE_EXTEND);
}
}
if (success) {
success = ReplaceFile(temp_spare_path, spare_path, nullptr);
}
if (!success) {
DeleteFile(temp_spare_path);
}
return success;
}
void GlobalHistogramAllocator::CreateWithSharedMemoryRegion(
const UnsafeSharedMemoryRegion& region) {
CHECK_EQ(Get(), nullptr) << "Histogram allocator has already been created";
base::WritableSharedMemoryMapping mapping = region.Map();
if (!mapping.IsValid() ||
!WritableSharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(
mapping)) {
DVLOG(1) << "Shared memory region is invalid or unacceptable.";
return;
}
DVLOG(1) << "Global histogram allocator initialized.";
Set(new GlobalHistogramAllocator(
std::make_unique<WritableSharedPersistentMemoryAllocator>(
std::move(mapping), 0, std::string_view())));
}
void GlobalHistogramAllocator::Set(GlobalHistogramAllocator* allocator) {
CHECK(!subtle::NoBarrier_Load(&g_histogram_allocator));
subtle::Release_Store(&g_histogram_allocator,
reinterpret_cast<intptr_t>(allocator));
size_t histogram_count = StatisticsRecorder::GetHistogramCount();
if (histogram_count != 0) {
DVLOG(1) << histogram_count
<< " histogram(s) created before persistence was enabled.";
std::string_view name = (allocator ? allocator->Name() : "");
if (!name.empty()) {
UmaHistogramCounts100(
StrCat({"UMA.PersistentAllocator.EarlyHistograms.", name}),
static_cast<int>(histogram_count));
}
}
}
GlobalHistogramAllocator* GlobalHistogramAllocator::Get() {
return reinterpret_cast<GlobalHistogramAllocator*>(
subtle::Acquire_Load(&g_histogram_allocator));
}
GlobalHistogramAllocator* GlobalHistogramAllocator::ReleaseForTesting() {
GlobalHistogramAllocator* histogram_allocator = Get();
if (!histogram_allocator) {
return nullptr;
}
PersistentMemoryAllocator* memory_allocator =
histogram_allocator->memory_allocator();
PersistentMemoryAllocator::Iterator iter(memory_allocator);
const PersistentHistogramData* data;
while ((data = iter.GetNextOfObject<PersistentHistogramData>()) != nullptr) {
StatisticsRecorder::ForgetHistogramForTesting(data->name);
}
subtle::Release_Store(&g_histogram_allocator, 0);
ANNOTATE_LEAKING_OBJECT_PTR(histogram_allocator);
return histogram_allocator;
}
void GlobalHistogramAllocator::SetPersistentLocation(const FilePath& location) {
persistent_location_ = location;
}
const FilePath& GlobalHistogramAllocator::GetPersistentLocation() const {
return persistent_location_;
}
bool GlobalHistogramAllocator::HasPersistentLocation() const {
return !persistent_location_.empty();
}
bool GlobalHistogramAllocator::MovePersistentFile(const FilePath& dir) {
DCHECK(HasPersistentLocation());
FilePath new_file_path = dir.Append(persistent_location_.BaseName());
if (!base::ReplaceFile(persistent_location_, new_file_path, nullptr)) {
return false;
}
SetPersistentLocation(new_file_path);
return true;
}
bool GlobalHistogramAllocator::WriteToPersistentLocation() {
if (!HasPersistentLocation()) {
NOTREACHED() << "Could not write \"" << Name() << "\" persistent histograms"
<< " to file because no location was set.";
}
std::string_view contents(static_cast<const char*>(data()), used());
if (!ImportantFileWriter::WriteFileAtomically(
persistent_location_, contents, "PersistentHistogramAllocator")) {
LOG(ERROR) << "Could not write \"" << Name() << "\" persistent histograms"
<< " to file: " << persistent_location_.value();
return false;
}
return true;
}
void GlobalHistogramAllocator::DeletePersistentLocation() {
memory_allocator()->SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED);
if (!HasPersistentLocation()) {
return;
}
File file(persistent_location_,
File::FLAG_OPEN | File::FLAG_READ | File::FLAG_DELETE_ON_CLOSE);
}
GlobalHistogramAllocator::GlobalHistogramAllocator(
std::unique_ptr<PersistentMemoryAllocator> memory)
: PersistentHistogramAllocator(std::move(memory)), import_iterator_(this) {}
void GlobalHistogramAllocator::ImportHistogramsToStatisticsRecorder() {
Reference record_to_ignore = last_created();
while (true) {
std::unique_ptr<HistogramBase> histogram =
import_iterator_.GetNextWithIgnore(record_to_ignore);
if (!histogram) {
break;
}
StatisticsRecorder::RegisterOrDeleteDuplicate(histogram.release());
}
}
}