| Index: base/metrics/persistent_histogram_allocator.cc
|
| diff --git a/base/metrics/persistent_histogram_allocator.cc b/base/metrics/persistent_histogram_allocator.cc
|
| index e96433677df982570f6110447d7746b803af74ab..730116e46fbbce98db4101a10535d29f5674c7c7 100644
|
| --- a/base/metrics/persistent_histogram_allocator.cc
|
| +++ b/base/metrics/persistent_histogram_allocator.cc
|
| @@ -41,10 +41,10 @@ enum : uint32_t {
|
| // The current globally-active persistent allocator for all new histograms.
|
| // The object held here will obviously not be destructed at process exit
|
| // but that's best since PersistentMemoryAllocator objects (that underlie
|
| -// PersistentHistogramAllocator objects) are explicitly forbidden from doing
|
| +// GlobalHistogramAllocator 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.
|
| -PersistentHistogramAllocator* g_allocator;
|
| +GlobalHistogramAllocator* g_allocator;
|
|
|
| // 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
|
| @@ -175,103 +175,6 @@ void PersistentHistogramAllocator::RecordCreateHistogramResult(
|
| }
|
|
|
| // static
|
| -void PersistentHistogramAllocator::SetGlobalAllocator(
|
| - std::unique_ptr<PersistentHistogramAllocator> 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.release();
|
| -
|
| - size_t existing = StatisticsRecorder::GetHistogramCount();
|
| - DLOG_IF(WARNING, existing)
|
| - << existing
|
| - << " histograms were created before persistence was enabled.";
|
| -}
|
| -
|
| -// static
|
| -PersistentHistogramAllocator*
|
| -PersistentHistogramAllocator::GetGlobalAllocator() {
|
| - return g_allocator;
|
| -}
|
| -
|
| -// static
|
| -std::unique_ptr<PersistentHistogramAllocator>
|
| -PersistentHistogramAllocator::ReleaseGlobalAllocatorForTesting() {
|
| - PersistentHistogramAllocator* histogram_allocator = g_allocator;
|
| - if (!histogram_allocator)
|
| - return nullptr;
|
| - PersistentMemoryAllocator* memory_allocator =
|
| - histogram_allocator->memory_allocator();
|
| -
|
| - // 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;
|
| - memory_allocator->CreateIterator(&iter);
|
| - while ((ref = memory_allocator->GetNextIterable(&iter, &type_id)) != 0) {
|
| - if (type_id == kTypeIdHistogram) {
|
| - PersistentHistogramData* histogram_data =
|
| - memory_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 SetGlobalAllocator() so that
|
| - // histogram is instead allocated from the process heap.
|
| - DCHECK_NE(kResultHistogram, histogram_data->name);
|
| - }
|
| - }
|
| -
|
| - g_allocator = nullptr;
|
| - return WrapUnique(histogram_allocator);
|
| -};
|
| -
|
| -// static
|
| -void PersistentHistogramAllocator::CreateGlobalAllocatorOnPersistentMemory(
|
| - void* base,
|
| - size_t size,
|
| - size_t page_size,
|
| - uint64_t id,
|
| - StringPiece name) {
|
| - SetGlobalAllocator(WrapUnique(new PersistentHistogramAllocator(
|
| - WrapUnique(new PersistentMemoryAllocator(base, size, page_size, id,
|
| - name, false)))));
|
| -}
|
| -
|
| -// static
|
| -void PersistentHistogramAllocator::CreateGlobalAllocatorOnLocalMemory(
|
| - size_t size,
|
| - uint64_t id,
|
| - StringPiece name) {
|
| - SetGlobalAllocator(WrapUnique(new PersistentHistogramAllocator(
|
| - WrapUnique(new LocalPersistentMemoryAllocator(size, id, name)))));
|
| -}
|
| -
|
| -// static
|
| -void PersistentHistogramAllocator::CreateGlobalAllocatorOnSharedMemory(
|
| - size_t size,
|
| - const SharedMemoryHandle& handle) {
|
| - std::unique_ptr<SharedMemory> shm(
|
| - new SharedMemory(handle, /*readonly=*/false));
|
| - if (!shm->Map(size)) {
|
| - NOTREACHED();
|
| - return;
|
| - }
|
| -
|
| - SetGlobalAllocator(WrapUnique(new PersistentHistogramAllocator(
|
| - WrapUnique(new SharedPersistentMemoryAllocator(
|
| - std::move(shm), 0, StringPiece(), /*readonly=*/false)))));
|
| -}
|
| -
|
| -// static
|
| std::unique_ptr<HistogramBase> PersistentHistogramAllocator::CreateHistogram(
|
| PersistentHistogramData* histogram_data_ptr) {
|
| if (!histogram_data_ptr) {
|
| @@ -532,7 +435,8 @@ std::unique_ptr<HistogramBase> PersistentHistogramAllocator::AllocateHistogram(
|
|
|
| // By storing the reference within the allocator to this histogram, the
|
| // next import (which will happen before the next histogram creation)
|
| - // will know to skip it. See also the comment in ImportGlobalHistograms().
|
| + // will know to skip it.
|
| + // See also the comment in ImportHistogramsToStatisticsRecorder().
|
| subtle::NoBarrier_Store(&last_created_, histogram_ref);
|
| return histogram;
|
| }
|
| @@ -552,40 +456,144 @@ std::unique_ptr<HistogramBase> PersistentHistogramAllocator::AllocateHistogram(
|
| return nullptr;
|
| }
|
|
|
| +GlobalHistogramAllocator::~GlobalHistogramAllocator() {}
|
| +
|
| +// static
|
| +void GlobalHistogramAllocator::CreateWithPersistentMemory(
|
| + void* base,
|
| + size_t size,
|
| + size_t page_size,
|
| + uint64_t id,
|
| + StringPiece name) {
|
| + Set(WrapUnique(new GlobalHistogramAllocator(
|
| + WrapUnique(new PersistentMemoryAllocator(
|
| + base, size, page_size, id, name, false)))));
|
| +}
|
| +
|
| +// static
|
| +void GlobalHistogramAllocator::CreateWithLocalMemory(
|
| + size_t size,
|
| + uint64_t id,
|
| + StringPiece name) {
|
| + Set(WrapUnique(new GlobalHistogramAllocator(
|
| + WrapUnique(new LocalPersistentMemoryAllocator(size, id, name)))));
|
| +}
|
| +
|
| +// static
|
| +void GlobalHistogramAllocator::CreateWithSharedMemory(
|
| + std::unique_ptr<SharedMemory> memory,
|
| + size_t size,
|
| + uint64_t id,
|
| + StringPiece name) {
|
| + if (!memory->memory() && !memory->Map(size))
|
| + NOTREACHED();
|
| +
|
| + if (memory->memory()) {
|
| + DCHECK_LE(memory->mapped_size(), size);
|
| + Set(WrapUnique(new GlobalHistogramAllocator(
|
| + WrapUnique(new SharedPersistentMemoryAllocator(
|
| + std::move(memory), 0, StringPiece(), /*readonly=*/false)))));
|
| + }
|
| +}
|
| +
|
| // static
|
| -void PersistentHistogramAllocator::ImportGlobalHistograms() {
|
| - // 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) {
|
| - // TODO(bcwhite): Investigate a lock-free, thread-safe iterator.
|
| - base::AutoLock auto_lock(lock.Get());
|
| -
|
| - // Each call resumes from where it last left off so a persistant iterator
|
| - // is needed. This class has a constructor so even the definition has to
|
| - // be protected by the lock in order to be thread-safe.
|
| - static Iterator iter;
|
| - if (iter.is_clear())
|
| - g_allocator->CreateIterator(&iter);
|
| -
|
| - // Skip the import if it's the histogram that was last created. Should a
|
| - // race condition cause the "last created" to be overwritten before it
|
| - // is recognized here then the histogram will be created and be ignored
|
| - // when it is detected as a duplicate by the statistics-recorder. This
|
| - // simple check reduces the time of creating persistent histograms by
|
| - // about 40%.
|
| - Reference last_created =
|
| - subtle::NoBarrier_Load(&g_allocator->last_created_);
|
| -
|
| - while (true) {
|
| - std::unique_ptr<HistogramBase> histogram =
|
| - g_allocator->GetNextHistogramWithIgnore(&iter, last_created);
|
| - if (!histogram)
|
| - break;
|
| - StatisticsRecorder::RegisterOrDeleteDuplicate(histogram.release());
|
| +void GlobalHistogramAllocator::CreateWithSharedMemoryHandle(
|
| + const SharedMemoryHandle& handle,
|
| + size_t size) {
|
| + std::unique_ptr<SharedMemory> shm(
|
| + new SharedMemory(handle, /*readonly=*/false));
|
| + if (!shm->Map(size)) {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| +
|
| + Set(WrapUnique(new GlobalHistogramAllocator(
|
| + WrapUnique(new SharedPersistentMemoryAllocator(
|
| + std::move(shm), 0, StringPiece(), /*readonly=*/false)))));
|
| +}
|
| +
|
| +// static
|
| +void GlobalHistogramAllocator::Set(
|
| + std::unique_ptr<GlobalHistogramAllocator> 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.release();
|
| + size_t existing = StatisticsRecorder::GetHistogramCount();
|
| +
|
| + DLOG_IF(WARNING, existing)
|
| + << existing << " histograms were created before persistence was enabled.";
|
| +}
|
| +
|
| +// static
|
| +GlobalHistogramAllocator* GlobalHistogramAllocator::Get() {
|
| + return g_allocator;
|
| +}
|
| +
|
| +// static
|
| +std::unique_ptr<GlobalHistogramAllocator>
|
| +GlobalHistogramAllocator::ReleaseForTesting() {
|
| + GlobalHistogramAllocator* histogram_allocator = g_allocator;
|
| + if (!histogram_allocator)
|
| + return nullptr;
|
| + PersistentMemoryAllocator* memory_allocator =
|
| + histogram_allocator->memory_allocator();
|
| +
|
| + // 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;
|
| + memory_allocator->CreateIterator(&iter);
|
| + while ((ref = memory_allocator->GetNextIterable(&iter, &type_id)) != 0) {
|
| + if (type_id == kTypeIdHistogram) {
|
| + PersistentHistogramData* histogram_data =
|
| + memory_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 SetGlobalAllocator() so that
|
| + // histogram is instead allocated from the process heap.
|
| + DCHECK_NE(kResultHistogram, histogram_data->name);
|
| }
|
| }
|
| +
|
| + g_allocator = nullptr;
|
| + return WrapUnique(histogram_allocator);
|
| +};
|
| +
|
| +GlobalHistogramAllocator::GlobalHistogramAllocator(
|
| + std::unique_ptr<PersistentMemoryAllocator> memory)
|
| + : PersistentHistogramAllocator(std::move(memory)) {
|
| + CreateIterator(&import_iterator_);
|
| +}
|
| +
|
| +void GlobalHistogramAllocator::ImportHistogramsToStatisticsRecorder() {
|
| + // Skip the import if it's the histogram that was last created. Should a
|
| + // race condition cause the "last created" to be overwritten before it
|
| + // is recognized here then the histogram will be created and be ignored
|
| + // when it is detected as a duplicate by the statistics-recorder. This
|
| + // simple check reduces the time of creating persistent histograms by
|
| + // about 40%.
|
| + Reference record_to_ignore = last_created();
|
| +
|
| + // There is no lock on this because it's expected to be called only by
|
| + // the StatisticsRecorder which has its own lock.
|
| + while (true) {
|
| + std::unique_ptr<HistogramBase> histogram =
|
| + GetNextHistogramWithIgnore(&import_iterator_, record_to_ignore);
|
| + if (!histogram)
|
| + break;
|
| + StatisticsRecorder::RegisterOrDeleteDuplicate(histogram.release());
|
| + }
|
| }
|
|
|
| } // namespace base
|
|
|