Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3648)

Unified Diff: base/metrics/histogram_persistence.cc

Issue 1738063002: Refactor histogram_persistence to be a class. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: addressed review comments by Alexei Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: base/metrics/histogram_persistence.cc
diff --git a/base/metrics/histogram_persistence.cc b/base/metrics/histogram_persistence.cc
deleted file mode 100644
index f18d17528354329cd4995615d2451f6a52c59302..0000000000000000000000000000000000000000
--- a/base/metrics/histogram_persistence.cc
+++ /dev/null
@@ -1,516 +0,0 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/histogram_persistence.h"
-
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_samples.h"
-#include "base/metrics/statistics_recorder.h"
-#include "base/synchronization/lock.h"
-
-namespace base {
-
-namespace {
-
-// Enumerate possible creation results for reporting.
-enum CreateHistogramResultType {
- // Everything was fine.
- CREATE_HISTOGRAM_SUCCESS = 0,
-
- // Pointer to metadata was not valid.
- CREATE_HISTOGRAM_INVALID_METADATA_POINTER,
-
- // Histogram metadata was not valid.
- CREATE_HISTOGRAM_INVALID_METADATA,
-
- // Ranges information was not valid.
- CREATE_HISTOGRAM_INVALID_RANGES_ARRAY,
-
- // Counts information was not valid.
- CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY,
-
- // Could not allocate histogram memory due to corruption.
- CREATE_HISTOGRAM_ALLOCATOR_CORRUPT,
-
- // Could not allocate histogram memory due to lack of space.
- CREATE_HISTOGRAM_ALLOCATOR_FULL,
-
- // Could not allocate histogram memory due to unknown error.
- CREATE_HISTOGRAM_ALLOCATOR_ERROR,
-
- // Histogram was of unknown type.
- CREATE_HISTOGRAM_UNKNOWN_TYPE,
-
- // Instance has detected a corrupt allocator (recorded only once).
- CREATE_HISTOGRAM_ALLOCATOR_NEWLY_CORRUPT,
-
- // Always keep this at the end.
- CREATE_HISTOGRAM_MAX
-};
-
-// Name of histogram for storing results of local operations.
-const char kResultHistogram[] = "UMA.CreatePersistentHistogram.Result";
-
-// Type identifiers used when storing in persistent memory so they can be
-// identified during extraction; the first 4 bytes of the SHA1 of the name
-// is used as a unique integer. A "version number" is added to the base
-// so that, if the structure of that object changes, stored older versions
-// will be safely ignored.
-enum : uint32_t {
- kTypeIdHistogram = 0xF1645910 + 2, // SHA1(Histogram) v2
- kTypeIdRangesArray = 0xBCEA225A + 1, // SHA1(RangesArray) v1
- kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1
-};
-
-// This data must be held in persistent memory in order for processes to
-// locate and use histograms created elsewhere. All elements must be of a
-// fixed width to ensure 32/64-bit interoperability.
-struct PersistentHistogramData {
- 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;
- PersistentMemoryAllocator::Reference counts_ref;
- HistogramSamples::Metadata samples_metadata;
- HistogramSamples::Metadata logged_metadata;
-
- // Space for the histogram name will be added during the actual allocation
- // request. This must be the last field of the structure. A zero-size array
- // or a "flexible" array would be preferred but is not (yet) valid C++.
- char name[1];
-};
-
-// The object held here will obviously not be destructed at process exit
-// but that's okay since PersistentMemoryAllocator objects are explicitly
-// forbidden from doing anything essential at exit anyway due to the fact
-// that they depend on data managed elsewhere and which could be destructed
-// first.
-PersistentMemoryAllocator* g_allocator = nullptr;
-
-// Take an array of range boundaries and create a proper BucketRanges object
-// which is returned to the caller. A return of nullptr indicates that the
-// passed boundaries are invalid.
-BucketRanges* CreateRangesFromData(HistogramBase::Sample* ranges_data,
- uint32_t ranges_checksum,
- size_t count) {
- scoped_ptr<BucketRanges> ranges(new BucketRanges(count));
- DCHECK_EQ(count, ranges->size());
- for (size_t i = 0; i < count; ++i) {
- if (i > 0 && ranges_data[i] <= ranges_data[i - 1])
- return nullptr;
- ranges->set_range(i, ranges_data[i]);
- }
-
- ranges->ResetChecksum();
- if (ranges->checksum() != ranges_checksum)
- return nullptr;
-
- return ranges.release();
-}
-
-// Calculate the number of bytes required to store all of a histogram's
-// "counts". This will return zero (0) if |bucket_count| is not valid.
-size_t CalculateRequiredCountsBytes(size_t bucket_count) {
- // 2 because each "sample count" also requires a backup "logged count"
- // used for calculating the delta during snapshot operations.
- const unsigned kBytesPerBucket = 2 * sizeof(HistogramBase::AtomicCount);
-
- // If the |bucket_count| is such that it would overflow the return type,
- // perhaps as the result of a malicious actor, then return zero to
- // indicate the problem to the caller.
- if (bucket_count > std::numeric_limits<uint32_t>::max() / kBytesPerBucket)
- return 0;
-
- return bucket_count * kBytesPerBucket;
-}
-
-} // namespace
-
-const Feature kPersistentHistogramsFeature{
- "PersistentHistograms", FEATURE_DISABLED_BY_DEFAULT
-};
-
-// Get the histogram in which create results are stored. This is copied almost
-// exactly from the STATIC_HISTOGRAM_POINTER_BLOCK macro but with added code
-// to prevent recursion (a likely occurance because the creation of a new
-// histogram can end up calling this.)
-HistogramBase* GetCreateHistogramResultHistogram() {
- static base::subtle::AtomicWord atomic_histogram_pointer = 0;
- HistogramBase* histogram_pointer(
- reinterpret_cast<HistogramBase*>(
- base::subtle::Acquire_Load(&atomic_histogram_pointer)));
- if (!histogram_pointer) {
- // It's possible for multiple threads to make it here in parallel but
- // they'll always return the same result as there is a mutex in the Get.
- // The purpose of the "initialized" variable is just to ensure that
- // the same thread doesn't recurse which is also why it doesn't have
- // to be atomic.
- static bool initialized = false;
- if (!initialized) {
- initialized = true;
- if (g_allocator) {
- DLOG(WARNING) << "Creating the results-histogram inside persistent"
- << " memory can cause future allocations to crash if"
- << " that memory is ever released (for testing).";
- }
-
- histogram_pointer = LinearHistogram::FactoryGet(
- kResultHistogram, 1, CREATE_HISTOGRAM_MAX, CREATE_HISTOGRAM_MAX + 1,
- HistogramBase::kUmaTargetedHistogramFlag);
- base::subtle::Release_Store(
- &atomic_histogram_pointer,
- reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer));
- }
- }
- return histogram_pointer;
-}
-
-// Record the result of a histogram creation.
-void RecordCreateHistogramResult(CreateHistogramResultType result) {
- HistogramBase* result_histogram = GetCreateHistogramResultHistogram();
- if (result_histogram)
- result_histogram->Add(result);
-}
-
-void SetPersistentHistogramMemoryAllocator(
- PersistentMemoryAllocator* allocator) {
- // Releasing or changing an allocator is extremely dangerous because it
- // likely has histograms stored within it. If the backing memory is also
- // also released, future accesses to those histograms will seg-fault.
- CHECK(!g_allocator);
- g_allocator = allocator;
-}
-
-PersistentMemoryAllocator* GetPersistentHistogramMemoryAllocator() {
- return g_allocator;
-}
-
-PersistentMemoryAllocator*
-ReleasePersistentHistogramMemoryAllocatorForTesting() {
- PersistentMemoryAllocator* allocator = g_allocator;
- if (!allocator)
- return nullptr;
-
- // Before releasing the memory, it's necessary to have the Statistics-
- // Recorder forget about the histograms contained therein; otherwise,
- // some operations will try to access them and the released memory.
- PersistentMemoryAllocator::Iterator iter;
- PersistentMemoryAllocator::Reference ref;
- uint32_t type_id;
- allocator->CreateIterator(&iter);
- while ((ref = allocator->GetNextIterable(&iter, &type_id)) != 0) {
- if (type_id == kTypeIdHistogram) {
- PersistentHistogramData* histogram_data =
- allocator->GetAsObject<PersistentHistogramData>(
- ref, kTypeIdHistogram);
- DCHECK(histogram_data);
- StatisticsRecorder::ForgetHistogramForTesting(histogram_data->name);
-
- // If a test breaks here then a memory region containing a histogram
- // actively used by this code is being released back to the test.
- // If that memory segment were to be deleted, future calls to create
- // persistent histograms would crash. To avoid this, have the test call
- // the method GetCreateHistogramResultHistogram() *before* setting the
- // (temporary) memory allocator via SetPersistentMemoryAllocator() so
- // that the histogram is instead allocated from the process heap.
- DCHECK_NE(kResultHistogram, histogram_data->name);
- }
- }
-
- g_allocator = nullptr;
- return allocator;
-};
-
-HistogramBase* CreatePersistentHistogram(
- PersistentMemoryAllocator* allocator,
- PersistentHistogramData* histogram_data_ptr) {
- if (!histogram_data_ptr) {
- RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA_POINTER);
- NOTREACHED();
- return nullptr;
- }
-
- // Copy the histogram_data to local storage because anything in persistent
- // memory cannot be trusted as it could be changed at any moment by a
- // malicious actor that shares access. The contents of histogram_data are
- // validated below; the local copy is to ensure that the contents cannot
- // be externally changed between validation and use.
- PersistentHistogramData histogram_data = *histogram_data_ptr;
-
- HistogramBase::Sample* ranges_data =
- allocator->GetAsObject<HistogramBase::Sample>(histogram_data.ranges_ref,
- kTypeIdRangesArray);
- if (!ranges_data || histogram_data.bucket_count < 2 ||
- histogram_data.bucket_count + 1 >
- std::numeric_limits<uint32_t>::max() /
- sizeof(HistogramBase::Sample) ||
- allocator->GetAllocSize(histogram_data.ranges_ref) <
- (histogram_data.bucket_count + 1) * sizeof(HistogramBase::Sample)) {
- RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY);
- NOTREACHED();
- return nullptr;
- }
- // To avoid racy destruction at shutdown, the following will be leaked.
- const BucketRanges* ranges = CreateRangesFromData(
- ranges_data,
- histogram_data.ranges_checksum,
- histogram_data.bucket_count + 1);
- if (!ranges) {
- RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY);
- NOTREACHED();
- return nullptr;
- }
- ranges = StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
-
- HistogramBase::AtomicCount* counts_data =
- allocator->GetAsObject<HistogramBase::AtomicCount>(
- histogram_data.counts_ref, kTypeIdCountsArray);
- size_t counts_bytes =
- CalculateRequiredCountsBytes(histogram_data.bucket_count);
- if (!counts_data || !counts_bytes ||
- allocator->GetAllocSize(histogram_data.counts_ref) < counts_bytes) {
- RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY);
- NOTREACHED();
- return nullptr;
- }
-
- // After the main "counts" array is a second array using for storing what
- // was previously logged. This is used to calculate the "delta" during
- // snapshot operations.
- HistogramBase::AtomicCount* logged_data =
- counts_data + histogram_data.bucket_count;
-
- std::string name(histogram_data_ptr->name);
- HistogramBase* histogram = nullptr;
- switch (histogram_data.histogram_type) {
- case HISTOGRAM:
- histogram = Histogram::PersistentGet(
- name,
- histogram_data.minimum,
- histogram_data.maximum,
- ranges,
- counts_data,
- logged_data,
- histogram_data.bucket_count,
- &histogram_data_ptr->samples_metadata,
- &histogram_data_ptr->logged_metadata);
- DCHECK(histogram);
- break;
- case LINEAR_HISTOGRAM:
- histogram = LinearHistogram::PersistentGet(
- name,
- histogram_data.minimum,
- histogram_data.maximum,
- ranges,
- counts_data,
- logged_data,
- histogram_data.bucket_count,
- &histogram_data_ptr->samples_metadata,
- &histogram_data_ptr->logged_metadata);
- DCHECK(histogram);
- break;
- case BOOLEAN_HISTOGRAM:
- histogram = BooleanHistogram::PersistentGet(
- name,
- ranges,
- counts_data,
- logged_data,
- &histogram_data_ptr->samples_metadata,
- &histogram_data_ptr->logged_metadata);
- DCHECK(histogram);
- break;
- case CUSTOM_HISTOGRAM:
- histogram = CustomHistogram::PersistentGet(
- name,
- ranges,
- counts_data,
- logged_data,
- histogram_data.bucket_count,
- &histogram_data_ptr->samples_metadata,
- &histogram_data_ptr->logged_metadata);
- DCHECK(histogram);
- break;
- default:
- NOTREACHED();
- }
-
- if (histogram) {
- DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType());
- histogram->SetFlags(histogram_data.flags);
- RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS);
- } else {
- RecordCreateHistogramResult(CREATE_HISTOGRAM_UNKNOWN_TYPE);
- }
-
- return histogram;
-}
-
-HistogramBase* GetPersistentHistogram(
- PersistentMemoryAllocator* allocator,
- int32_t ref) {
- // Unfortunately, the above "pickle" methods cannot be used as part of the
- // persistance because the deserialization methods always create local
- // count data (these must referenced the persistent counts) and always add
- // it to the local list of known histograms (these may be simple references
- // to histograms in other processes).
- PersistentHistogramData* histogram_data =
- allocator->GetAsObject<PersistentHistogramData>(ref, kTypeIdHistogram);
- size_t length = allocator->GetAllocSize(ref);
- if (!histogram_data ||
- reinterpret_cast<char*>(histogram_data)[length - 1] != '\0') {
- RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA);
- NOTREACHED();
- return nullptr;
- }
- return CreatePersistentHistogram(allocator, histogram_data);
-}
-
-HistogramBase* GetNextPersistentHistogram(
- PersistentMemoryAllocator* allocator,
- PersistentMemoryAllocator::Iterator* iter) {
- PersistentMemoryAllocator::Reference ref;
- uint32_t type_id;
- while ((ref = allocator->GetNextIterable(iter, &type_id)) != 0) {
- if (type_id == kTypeIdHistogram)
- return GetPersistentHistogram(allocator, ref);
- }
- return nullptr;
-}
-
-void FinalizePersistentHistogram(PersistentMemoryAllocator::Reference ref,
- bool registered) {
- // If the created persistent histogram was registered then it needs to
- // be marked as "iterable" in order to be found by other processes.
- if (registered)
- GetPersistentHistogramMemoryAllocator()->MakeIterable(ref);
- // If it wasn't registered then a race condition must have caused
- // two to be created. The allocator does not support releasing the
- // acquired memory so just change the type to be empty.
- else
- GetPersistentHistogramMemoryAllocator()->SetType(ref, 0);
-}
-
-HistogramBase* AllocatePersistentHistogram(
- PersistentMemoryAllocator* allocator,
- HistogramType histogram_type,
- const std::string& name,
- int minimum,
- int maximum,
- const BucketRanges* bucket_ranges,
- int32_t flags,
- PersistentMemoryAllocator::Reference* ref_ptr) {
- if (!allocator)
- return nullptr;
-
- // If the allocator is corrupt, don't waste time trying anything else.
- // This also allows differentiating on the dashboard between allocations
- // failed due to a corrupt allocator and the number of process instances
- // with one, the latter being idicated by "newly corrupt", below.
- if (allocator->IsCorrupt()) {
- RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_CORRUPT);
- return nullptr;
- }
-
- // If CalculateRequiredCountsBytes() returns zero then the bucket_count
- // was not valid.
- size_t bucket_count = bucket_ranges->bucket_count();
- size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count);
- if (!counts_bytes) {
- NOTREACHED();
- return nullptr;
- }
-
- size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample);
- PersistentMemoryAllocator::Reference ranges_ref =
- allocator->Allocate(ranges_bytes, kTypeIdRangesArray);
- PersistentMemoryAllocator::Reference counts_ref =
- allocator->Allocate(counts_bytes, kTypeIdCountsArray);
- PersistentMemoryAllocator::Reference histogram_ref =
- allocator->Allocate(offsetof(PersistentHistogramData, name) +
- name.length() + 1, kTypeIdHistogram);
- HistogramBase::Sample* ranges_data =
- allocator->GetAsObject<HistogramBase::Sample>(ranges_ref,
- kTypeIdRangesArray);
- PersistentHistogramData* histogram_data =
- allocator->GetAsObject<PersistentHistogramData>(histogram_ref,
- kTypeIdHistogram);
-
- // Only continue here if all allocations were successful. If they weren't
- // there is no way to free the space but that's not really a problem since
- // the allocations only fail because the space is full and so any future
- // attempts will also fail.
- if (counts_ref && ranges_data && histogram_data) {
- strcpy(histogram_data->name, name.c_str());
- for (size_t i = 0; i < bucket_ranges->size(); ++i)
- ranges_data[i] = bucket_ranges->range(i);
-
- histogram_data->histogram_type = histogram_type;
- histogram_data->flags = flags;
- 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();
- histogram_data->counts_ref = counts_ref;
-
- // Create the histogram using resources in persistent memory. This ends up
- // resolving the "ref" values stored in histogram_data instad of just
- // using what is already known above but avoids duplicating the switch
- // statement here and serves as a double-check that everything is
- // correct before commiting the new histogram to persistent space.
- HistogramBase* histogram =
- CreatePersistentHistogram(allocator, histogram_data);
- DCHECK(histogram);
- if (ref_ptr != nullptr)
- *ref_ptr = histogram_ref;
- return histogram;
- }
-
- CreateHistogramResultType result;
- if (allocator->IsCorrupt()) {
- RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_NEWLY_CORRUPT);
- result = CREATE_HISTOGRAM_ALLOCATOR_CORRUPT;
- } else if (allocator->IsFull()) {
- result = CREATE_HISTOGRAM_ALLOCATOR_FULL;
- } else {
- result = CREATE_HISTOGRAM_ALLOCATOR_ERROR;
- }
- RecordCreateHistogramResult(result);
- NOTREACHED() << "error=" << result;
-
- return nullptr;
-}
-
-void ImportPersistentHistograms() {
- // The lock protects against concurrent access to the iterator and is created
- // in a thread-safe manner when needed.
- static base::LazyInstance<base::Lock>::Leaky lock = LAZY_INSTANCE_INITIALIZER;
-
- if (g_allocator) {
- base::AutoLock auto_lock(lock.Get());
-
- // Each call resumes from where it last left off so need persistant
- // iterator. This class has a constructor so even the definition has
- // to be protected by the lock in order to be thread-safe.
- static PersistentMemoryAllocator::Iterator iter;
- if (iter.is_clear())
- g_allocator->CreateIterator(&iter);
-
- while (true) {
- HistogramBase* histogram = GetNextPersistentHistogram(g_allocator, &iter);
- if (!histogram)
- break;
- StatisticsRecorder::RegisterOrDeleteDuplicate(histogram);
- }
- }
-}
-
-} // namespace base

Powered by Google App Engine
This is Rietveld 408576698