Home | History | Annotate | Download | only in metrics
      1 // Copyright 2016 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/metrics/persistent_histogram_allocator.h"
      6 
      7 #include <memory>
      8 
      9 #include "base/atomicops.h"
     10 #include "base/files/file_path.h"
     11 #include "base/files/file_util.h"
     12 #include "base/files/important_file_writer.h"
     13 #include "base/files/memory_mapped_file.h"
     14 #include "base/lazy_instance.h"
     15 #include "base/logging.h"
     16 #include "base/memory/ptr_util.h"
     17 #include "base/metrics/histogram.h"
     18 #include "base/metrics/histogram_base.h"
     19 #include "base/metrics/histogram_samples.h"
     20 #include "base/metrics/persistent_sample_map.h"
     21 #include "base/metrics/sparse_histogram.h"
     22 #include "base/metrics/statistics_recorder.h"
     23 #include "base/pickle.h"
     24 #include "base/synchronization/lock.h"
     25 
     26 namespace base {
     27 
     28 namespace {
     29 
     30 // Name of histogram for storing results of local operations.
     31 const char kResultHistogram[] = "UMA.CreatePersistentHistogram.Result";
     32 
     33 // Type identifiers used when storing in persistent memory so they can be
     34 // identified during extraction; the first 4 bytes of the SHA1 of the name
     35 // is used as a unique integer. A "version number" is added to the base
     36 // so that, if the structure of that object changes, stored older versions
     37 // will be safely ignored.
     38 enum : uint32_t {
     39   kTypeIdRangesArray = 0xBCEA225A + 1,  // SHA1(RangesArray) v1
     40   kTypeIdCountsArray = 0x53215530 + 1,  // SHA1(CountsArray) v1
     41 };
     42 
     43 // The current globally-active persistent allocator for all new histograms.
     44 // The object held here will obviously not be destructed at process exit
     45 // but that's best since PersistentMemoryAllocator objects (that underlie
     46 // GlobalHistogramAllocator objects) are explicitly forbidden from doing
     47 // anything essential at exit anyway due to the fact that they depend on data
     48 // managed elsewhere and which could be destructed first. An AtomicWord is
     49 // used instead of std::atomic because the latter can create global ctors
     50 // and dtors.
     51 subtle::AtomicWord g_allocator = 0;
     52 
     53 // Take an array of range boundaries and create a proper BucketRanges object
     54 // which is returned to the caller. A return of nullptr indicates that the
     55 // passed boundaries are invalid.
     56 std::unique_ptr<BucketRanges> CreateRangesFromData(
     57     HistogramBase::Sample* ranges_data,
     58     uint32_t ranges_checksum,
     59     size_t count) {
     60   // To avoid racy destruction at shutdown, the following may be leaked.
     61   std::unique_ptr<BucketRanges> ranges(new BucketRanges(count));
     62   DCHECK_EQ(count, ranges->size());
     63   for (size_t i = 0; i < count; ++i) {
     64     if (i > 0 && ranges_data[i] <= ranges_data[i - 1])
     65       return nullptr;
     66     ranges->set_range(i, ranges_data[i]);
     67   }
     68 
     69   ranges->ResetChecksum();
     70   if (ranges->checksum() != ranges_checksum)
     71     return nullptr;
     72 
     73   return ranges;
     74 }
     75 
     76 // Calculate the number of bytes required to store all of a histogram's
     77 // "counts". This will return zero (0) if |bucket_count| is not valid.
     78 size_t CalculateRequiredCountsBytes(size_t bucket_count) {
     79   // 2 because each "sample count" also requires a backup "logged count"
     80   // used for calculating the delta during snapshot operations.
     81   const size_t kBytesPerBucket = 2 * sizeof(HistogramBase::AtomicCount);
     82 
     83   // If the |bucket_count| is such that it would overflow the return type,
     84   // perhaps as the result of a malicious actor, then return zero to
     85   // indicate the problem to the caller.
     86   if (bucket_count > std::numeric_limits<size_t>::max() / kBytesPerBucket)
     87     return 0;
     88 
     89   return bucket_count * kBytesPerBucket;
     90 }
     91 
     92 }  // namespace
     93 
     94 const Feature kPersistentHistogramsFeature{
     95   "PersistentHistograms", FEATURE_DISABLED_BY_DEFAULT
     96 };
     97 
     98 
     99 PersistentSparseHistogramDataManager::PersistentSparseHistogramDataManager(
    100     PersistentMemoryAllocator* allocator)
    101     : allocator_(allocator), record_iterator_(allocator) {}
    102 
    103 PersistentSparseHistogramDataManager::~PersistentSparseHistogramDataManager() {}
    104 
    105 PersistentSampleMapRecords*
    106 PersistentSparseHistogramDataManager::UseSampleMapRecords(uint64_t id,
    107                                                           const void* user) {
    108   base::AutoLock auto_lock(lock_);
    109   return GetSampleMapRecordsWhileLocked(id)->Acquire(user);
    110 }
    111 
    112 PersistentSampleMapRecords*
    113 PersistentSparseHistogramDataManager::GetSampleMapRecordsWhileLocked(
    114     uint64_t id) {
    115   lock_.AssertAcquired();
    116 
    117   auto found = sample_records_.find(id);
    118   if (found != sample_records_.end())
    119     return found->second.get();
    120 
    121   std::unique_ptr<PersistentSampleMapRecords>& samples = sample_records_[id];
    122   samples = MakeUnique<PersistentSampleMapRecords>(this, id);
    123   return samples.get();
    124 }
    125 
    126 bool PersistentSparseHistogramDataManager::LoadRecords(
    127     PersistentSampleMapRecords* sample_map_records) {
    128   // DataManager must be locked in order to access the found_ field of any
    129   // PersistentSampleMapRecords object.
    130   base::AutoLock auto_lock(lock_);
    131   bool found = false;
    132 
    133   // If there are already "found" entries for the passed object, move them.
    134   if (!sample_map_records->found_.empty()) {
    135     sample_map_records->records_.reserve(sample_map_records->records_.size() +
    136                                          sample_map_records->found_.size());
    137     sample_map_records->records_.insert(sample_map_records->records_.end(),
    138                                         sample_map_records->found_.begin(),
    139                                         sample_map_records->found_.end());
    140     sample_map_records->found_.clear();
    141     found = true;
    142   }
    143 
    144   // Acquiring a lock is a semi-expensive operation so load some records with
    145   // each call. More than this number may be loaded if it takes longer to
    146   // find at least one matching record for the passed object.
    147   const int kMinimumNumberToLoad = 10;
    148   const uint64_t match_id = sample_map_records->sample_map_id_;
    149 
    150   // Loop while no enty is found OR we haven't yet loaded the minimum number.
    151   // This will continue reading even after a match is found.
    152   for (int count = 0; !found || count < kMinimumNumberToLoad; ++count) {
    153     // Get the next sample-record. The iterator will always resume from where
    154     // it left off even if it previously had nothing further to return.
    155     uint64_t found_id;
    156     PersistentMemoryAllocator::Reference ref =
    157         PersistentSampleMap::GetNextPersistentRecord(record_iterator_,
    158                                                      &found_id);
    159 
    160     // Stop immediately if there are none.
    161     if (!ref)
    162       break;
    163 
    164     // The sample-record could be for any sparse histogram. Add the reference
    165     // to the appropriate collection for later use.
    166     if (found_id == match_id) {
    167       sample_map_records->records_.push_back(ref);
    168       found = true;
    169     } else {
    170       PersistentSampleMapRecords* samples =
    171           GetSampleMapRecordsWhileLocked(found_id);
    172       DCHECK(samples);
    173       samples->found_.push_back(ref);
    174     }
    175   }
    176 
    177   return found;
    178 }
    179 
    180 
    181 PersistentSampleMapRecords::PersistentSampleMapRecords(
    182     PersistentSparseHistogramDataManager* data_manager,
    183     uint64_t sample_map_id)
    184     : data_manager_(data_manager), sample_map_id_(sample_map_id) {}
    185 
    186 PersistentSampleMapRecords::~PersistentSampleMapRecords() {}
    187 
    188 PersistentSampleMapRecords* PersistentSampleMapRecords::Acquire(
    189     const void* user) {
    190   DCHECK(!user_);
    191   user_ = user;
    192   seen_ = 0;
    193   return this;
    194 }
    195 
    196 void PersistentSampleMapRecords::Release(const void* user) {
    197   DCHECK_EQ(user_, user);
    198   user_ = nullptr;
    199 }
    200 
    201 PersistentMemoryAllocator::Reference PersistentSampleMapRecords::GetNext() {
    202   DCHECK(user_);
    203 
    204   // If there are no unseen records, lock and swap in all the found ones.
    205   if (records_.size() == seen_) {
    206     if (!data_manager_->LoadRecords(this))
    207       return false;
    208   }
    209 
    210   // Return the next record. Records *must* be returned in the same order
    211   // they are found in the persistent memory in order to ensure that all
    212   // objects using this data always have the same state. Race conditions
    213   // can cause duplicate records so using the "first found" is the only
    214   // guarantee that all objects always access the same one.
    215   DCHECK_LT(seen_, records_.size());
    216   return records_[seen_++];
    217 }
    218 
    219 PersistentMemoryAllocator::Reference PersistentSampleMapRecords::CreateNew(
    220     HistogramBase::Sample value) {
    221   return PersistentSampleMap::CreatePersistentRecord(data_manager_->allocator_,
    222                                                      sample_map_id_, value);
    223 }
    224 
    225 
    226 // This data will be held in persistent memory in order for processes to
    227 // locate and use histograms created elsewhere.
    228 struct PersistentHistogramAllocator::PersistentHistogramData {
    229   // SHA1(Histogram): Increment this if structure changes!
    230   static constexpr uint32_t kPersistentTypeId = 0xF1645910 + 3;
    231 
    232   // Expected size for 32/64-bit check.
    233   static constexpr size_t kExpectedInstanceSize =
    234       40 + 2 * HistogramSamples::Metadata::kExpectedInstanceSize;
    235 
    236   int32_t histogram_type;
    237   int32_t flags;
    238   int32_t minimum;
    239   int32_t maximum;
    240   uint32_t bucket_count;
    241   PersistentMemoryAllocator::Reference ranges_ref;
    242   uint32_t ranges_checksum;
    243   PersistentMemoryAllocator::Reference counts_ref;
    244   HistogramSamples::Metadata samples_metadata;
    245   HistogramSamples::Metadata logged_metadata;
    246 
    247   // Space for the histogram name will be added during the actual allocation
    248   // request. This must be the last field of the structure. A zero-size array
    249   // or a "flexible" array would be preferred but is not (yet) valid C++.
    250   char name[sizeof(uint64_t)];  // Force 64-bit alignment on 32-bit builds.
    251 };
    252 
    253 PersistentHistogramAllocator::Iterator::Iterator(
    254     PersistentHistogramAllocator* allocator)
    255     : allocator_(allocator), memory_iter_(allocator->memory_allocator()) {}
    256 
    257 std::unique_ptr<HistogramBase>
    258 PersistentHistogramAllocator::Iterator::GetNextWithIgnore(Reference ignore) {
    259   PersistentMemoryAllocator::Reference ref;
    260   while ((ref = memory_iter_.GetNextOfType<PersistentHistogramData>()) != 0) {
    261     if (ref != ignore)
    262       return allocator_->GetHistogram(ref);
    263   }
    264   return nullptr;
    265 }
    266 
    267 
    268 PersistentHistogramAllocator::PersistentHistogramAllocator(
    269     std::unique_ptr<PersistentMemoryAllocator> memory)
    270     : memory_allocator_(std::move(memory)),
    271       sparse_histogram_data_manager_(memory_allocator_.get()) {}
    272 
    273 PersistentHistogramAllocator::~PersistentHistogramAllocator() {}
    274 
    275 std::unique_ptr<HistogramBase> PersistentHistogramAllocator::GetHistogram(
    276     Reference ref) {
    277   // Unfortunately, the histogram "pickle" methods cannot be used as part of
    278   // the persistance because the deserialization methods always create local
    279   // count data (while these must reference the persistent counts) and always
    280   // add it to the local list of known histograms (while these may be simple
    281   // references to histograms in other processes).
    282   PersistentHistogramData* histogram_data =
    283       memory_allocator_->GetAsObject<PersistentHistogramData>(ref);
    284   size_t length = memory_allocator_->GetAllocSize(ref);
    285 
    286   // Check that metadata is reasonable: name is NUL terminated and non-empty,
    287   // ID fields have been loaded with a hash of the name (0 is considered
    288   // unset/invalid).
    289   if (!histogram_data ||
    290       reinterpret_cast<char*>(histogram_data)[length - 1] != '\0' ||
    291       histogram_data->name[0] == '\0' ||
    292       histogram_data->samples_metadata.id == 0 ||
    293       histogram_data->logged_metadata.id == 0) {
    294     RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA);
    295     NOTREACHED();
    296     return nullptr;
    297   }
    298   return CreateHistogram(histogram_data);
    299 }
    300 
    301 std::unique_ptr<HistogramBase> PersistentHistogramAllocator::AllocateHistogram(
    302     HistogramType histogram_type,
    303     const std::string& name,
    304     int minimum,
    305     int maximum,
    306     const BucketRanges* bucket_ranges,
    307     int32_t flags,
    308     Reference* ref_ptr) {
    309   // If the allocator is corrupt, don't waste time trying anything else.
    310   // This also allows differentiating on the dashboard between allocations
    311   // failed due to a corrupt allocator and the number of process instances
    312   // with one, the latter being idicated by "newly corrupt", below.
    313   if (memory_allocator_->IsCorrupt()) {
    314     RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_CORRUPT);
    315     return nullptr;
    316   }
    317 
    318   // Create the metadata necessary for a persistent sparse histogram. This
    319   // is done first because it is a small subset of what is required for
    320   // other histograms. The type is "under construction" so that a crash
    321   // during the datafill doesn't leave a bad record around that could cause
    322   // confusion by another process trying to read it. It will be corrected
    323   // once histogram construction is complete.
    324   PersistentHistogramData* histogram_data =
    325       memory_allocator_->New<PersistentHistogramData>(
    326           offsetof(PersistentHistogramData, name) + name.length() + 1);
    327   if (histogram_data) {
    328     memcpy(histogram_data->name, name.c_str(), name.size() + 1);
    329     histogram_data->histogram_type = histogram_type;
    330     histogram_data->flags = flags | HistogramBase::kIsPersistent;
    331   }
    332 
    333   // Create the remaining metadata necessary for regular histograms.
    334   if (histogram_type != SPARSE_HISTOGRAM) {
    335     size_t bucket_count = bucket_ranges->bucket_count();
    336     size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count);
    337     if (counts_bytes == 0) {
    338       // |bucket_count| was out-of-range.
    339       NOTREACHED();
    340       return nullptr;
    341     }
    342 
    343     size_t ranges_count = bucket_count + 1;
    344     size_t ranges_bytes = ranges_count * sizeof(HistogramBase::Sample);
    345     PersistentMemoryAllocator::Reference counts_ref =
    346         memory_allocator_->Allocate(counts_bytes, kTypeIdCountsArray);
    347     PersistentMemoryAllocator::Reference ranges_ref =
    348         memory_allocator_->Allocate(ranges_bytes, kTypeIdRangesArray);
    349     HistogramBase::Sample* ranges_data =
    350         memory_allocator_->GetAsArray<HistogramBase::Sample>(
    351             ranges_ref, kTypeIdRangesArray, ranges_count);
    352 
    353     // Only continue here if all allocations were successful. If they weren't,
    354     // there is no way to free the space but that's not really a problem since
    355     // the allocations only fail because the space is full or corrupt and so
    356     // any future attempts will also fail.
    357     if (counts_ref && ranges_data && histogram_data) {
    358       for (size_t i = 0; i < bucket_ranges->size(); ++i)
    359         ranges_data[i] = bucket_ranges->range(i);
    360 
    361       histogram_data->minimum = minimum;
    362       histogram_data->maximum = maximum;
    363       // |bucket_count| must fit within 32-bits or the allocation of the counts
    364       // array would have failed for being too large; the allocator supports
    365       // less than 4GB total size.
    366       histogram_data->bucket_count = static_cast<uint32_t>(bucket_count);
    367       histogram_data->ranges_ref = ranges_ref;
    368       histogram_data->ranges_checksum = bucket_ranges->checksum();
    369       histogram_data->counts_ref = counts_ref;
    370     } else {
    371       histogram_data = nullptr;  // Clear this for proper handling below.
    372     }
    373   }
    374 
    375   if (histogram_data) {
    376     // Create the histogram using resources in persistent memory. This ends up
    377     // resolving the "ref" values stored in histogram_data instad of just
    378     // using what is already known above but avoids duplicating the switch
    379     // statement here and serves as a double-check that everything is
    380     // correct before commiting the new histogram to persistent space.
    381     std::unique_ptr<HistogramBase> histogram = CreateHistogram(histogram_data);
    382     DCHECK(histogram);
    383     DCHECK_NE(0U, histogram_data->samples_metadata.id);
    384     DCHECK_NE(0U, histogram_data->logged_metadata.id);
    385 
    386     PersistentMemoryAllocator::Reference histogram_ref =
    387         memory_allocator_->GetAsReference(histogram_data);
    388     if (ref_ptr != nullptr)
    389       *ref_ptr = histogram_ref;
    390 
    391     // By storing the reference within the allocator to this histogram, the
    392     // next import (which will happen before the next histogram creation)
    393     // will know to skip it.
    394     // See also the comment in ImportHistogramsToStatisticsRecorder().
    395     subtle::NoBarrier_Store(&last_created_, histogram_ref);
    396     return histogram;
    397   }
    398 
    399   CreateHistogramResultType result;
    400   if (memory_allocator_->IsCorrupt()) {
    401     RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_NEWLY_CORRUPT);
    402     result = CREATE_HISTOGRAM_ALLOCATOR_CORRUPT;
    403   } else if (memory_allocator_->IsFull()) {
    404     result = CREATE_HISTOGRAM_ALLOCATOR_FULL;
    405   } else {
    406     result = CREATE_HISTOGRAM_ALLOCATOR_ERROR;
    407   }
    408   RecordCreateHistogramResult(result);
    409 
    410   // Crash for failures caused by internal bugs but not "full" which is
    411   // dependent on outside code.
    412   if (result != CREATE_HISTOGRAM_ALLOCATOR_FULL)
    413     NOTREACHED() << memory_allocator_->Name() << ", error=" << result;
    414 
    415   return nullptr;
    416 }
    417 
    418 void PersistentHistogramAllocator::FinalizeHistogram(Reference ref,
    419                                                      bool registered) {
    420   if (registered) {
    421     // If the created persistent histogram was registered then it needs to
    422     // be marked as "iterable" in order to be found by other processes. This
    423     // happens only after the histogram is fully formed so it's impossible for
    424     // code iterating through the allocator to read a partially created record.
    425     memory_allocator_->MakeIterable(ref);
    426   } else {
    427     // If it wasn't registered then a race condition must have caused two to
    428     // be created. The allocator does not support releasing the acquired memory
    429     // so just change the type to be empty.
    430     memory_allocator_->ChangeType(ref, 0,
    431                                   PersistentHistogramData::kPersistentTypeId,
    432                                   /*clear=*/false);
    433   }
    434 }
    435 
    436 void PersistentHistogramAllocator::MergeHistogramDeltaToStatisticsRecorder(
    437     HistogramBase* histogram) {
    438   DCHECK(histogram);
    439 
    440   HistogramBase* existing = GetOrCreateStatisticsRecorderHistogram(histogram);
    441   if (!existing) {
    442     // The above should never fail but if it does, no real harm is done.
    443     // The data won't be merged but it also won't be recorded as merged
    444     // so a future try, if successful, will get what was missed. If it
    445     // continues to fail, some metric data will be lost but that is better
    446     // than crashing.
    447     NOTREACHED();
    448     return;
    449   }
    450 
    451   // Merge the delta from the passed object to the one in the SR.
    452   existing->AddSamples(*histogram->SnapshotDelta());
    453 }
    454 
    455 void PersistentHistogramAllocator::MergeHistogramFinalDeltaToStatisticsRecorder(
    456     const HistogramBase* histogram) {
    457   DCHECK(histogram);
    458 
    459   HistogramBase* existing = GetOrCreateStatisticsRecorderHistogram(histogram);
    460   if (!existing) {
    461     // The above should never fail but if it does, no real harm is done.
    462     // Some metric data will be lost but that is better than crashing.
    463     NOTREACHED();
    464     return;
    465   }
    466 
    467   // Merge the delta from the passed object to the one in the SR.
    468   existing->AddSamples(*histogram->SnapshotFinalDelta());
    469 }
    470 
    471 PersistentSampleMapRecords* PersistentHistogramAllocator::UseSampleMapRecords(
    472     uint64_t id,
    473     const void* user) {
    474   return sparse_histogram_data_manager_.UseSampleMapRecords(id, user);
    475 }
    476 
    477 void PersistentHistogramAllocator::CreateTrackingHistograms(StringPiece name) {
    478   memory_allocator_->CreateTrackingHistograms(name);
    479 }
    480 
    481 void PersistentHistogramAllocator::UpdateTrackingHistograms() {
    482   memory_allocator_->UpdateTrackingHistograms();
    483 }
    484 
    485 void PersistentHistogramAllocator::ClearLastCreatedReferenceForTesting() {
    486   subtle::NoBarrier_Store(&last_created_, 0);
    487 }
    488 
    489 // static
    490 HistogramBase*
    491 PersistentHistogramAllocator::GetCreateHistogramResultHistogram() {
    492   // Get the histogram in which create-results are stored. This is copied
    493   // almost exactly from the STATIC_HISTOGRAM_POINTER_BLOCK macro but with
    494   // added code to prevent recursion (a likely occurance because the creation
    495   // of a new a histogram can end up calling this.)
    496   static base::subtle::AtomicWord atomic_histogram_pointer = 0;
    497   HistogramBase* histogram_pointer =
    498       reinterpret_cast<HistogramBase*>(
    499           base::subtle::Acquire_Load(&atomic_histogram_pointer));
    500   if (!histogram_pointer) {
    501     // It's possible for multiple threads to make it here in parallel but
    502     // they'll always return the same result as there is a mutex in the Get.
    503     // The purpose of the "initialized" variable is just to ensure that
    504     // the same thread doesn't recurse which is also why it doesn't have
    505     // to be atomic.
    506     static bool initialized = false;
    507     if (!initialized) {
    508       initialized = true;
    509       if (GlobalHistogramAllocator::Get()) {
    510         DVLOG(1) << "Creating the results-histogram inside persistent"
    511                  << " memory can cause future allocations to crash if"
    512                  << " that memory is ever released (for testing).";
    513       }
    514 
    515       histogram_pointer = LinearHistogram::FactoryGet(
    516           kResultHistogram, 1, CREATE_HISTOGRAM_MAX, CREATE_HISTOGRAM_MAX + 1,
    517           HistogramBase::kUmaTargetedHistogramFlag);
    518       base::subtle::Release_Store(
    519           &atomic_histogram_pointer,
    520           reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer));
    521     }
    522   }
    523   return histogram_pointer;
    524 }
    525 
    526 std::unique_ptr<HistogramBase> PersistentHistogramAllocator::CreateHistogram(
    527     PersistentHistogramData* histogram_data_ptr) {
    528   if (!histogram_data_ptr) {
    529     RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA_POINTER);
    530     NOTREACHED();
    531     return nullptr;
    532   }
    533 
    534   // Sparse histograms are quite different so handle them as a special case.
    535   if (histogram_data_ptr->histogram_type == SPARSE_HISTOGRAM) {
    536     std::unique_ptr<HistogramBase> histogram =
    537         SparseHistogram::PersistentCreate(this, histogram_data_ptr->name,
    538                                           &histogram_data_ptr->samples_metadata,
    539                                           &histogram_data_ptr->logged_metadata);
    540     DCHECK(histogram);
    541     histogram->SetFlags(histogram_data_ptr->flags);
    542     RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS);
    543     return histogram;
    544   }
    545 
    546   // Copy the histogram_data to local storage because anything in persistent
    547   // memory cannot be trusted as it could be changed at any moment by a
    548   // malicious actor that shares access. The contents of histogram_data are
    549   // validated below; the local copy is to ensure that the contents cannot
    550   // be externally changed between validation and use.
    551   PersistentHistogramData histogram_data = *histogram_data_ptr;
    552 
    553   HistogramBase::Sample* ranges_data =
    554       memory_allocator_->GetAsArray<HistogramBase::Sample>(
    555           histogram_data.ranges_ref, kTypeIdRangesArray,
    556           PersistentMemoryAllocator::kSizeAny);
    557 
    558   const uint32_t max_buckets =
    559       std::numeric_limits<uint32_t>::max() / sizeof(HistogramBase::Sample);
    560   size_t required_bytes =
    561       (histogram_data.bucket_count + 1) * sizeof(HistogramBase::Sample);
    562   size_t allocated_bytes =
    563       memory_allocator_->GetAllocSize(histogram_data.ranges_ref);
    564   if (!ranges_data || histogram_data.bucket_count < 2 ||
    565       histogram_data.bucket_count >= max_buckets ||
    566       allocated_bytes < required_bytes) {
    567     RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY);
    568     NOTREACHED();
    569     return nullptr;
    570   }
    571 
    572   std::unique_ptr<const BucketRanges> created_ranges =
    573       CreateRangesFromData(ranges_data, histogram_data.ranges_checksum,
    574                            histogram_data.bucket_count + 1);
    575   if (!created_ranges) {
    576     RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY);
    577     NOTREACHED();
    578     return nullptr;
    579   }
    580   const BucketRanges* ranges =
    581       StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
    582           created_ranges.release());
    583 
    584   HistogramBase::AtomicCount* counts_data =
    585       memory_allocator_->GetAsArray<HistogramBase::AtomicCount>(
    586           histogram_data.counts_ref, kTypeIdCountsArray,
    587           PersistentMemoryAllocator::kSizeAny);
    588   size_t counts_bytes =
    589       CalculateRequiredCountsBytes(histogram_data.bucket_count);
    590   if (!counts_data || counts_bytes == 0 ||
    591       memory_allocator_->GetAllocSize(histogram_data.counts_ref) <
    592           counts_bytes) {
    593     RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY);
    594     NOTREACHED();
    595     return nullptr;
    596   }
    597 
    598   // After the main "counts" array is a second array using for storing what
    599   // was previously logged. This is used to calculate the "delta" during
    600   // snapshot operations.
    601   HistogramBase::AtomicCount* logged_data =
    602       counts_data + histogram_data.bucket_count;
    603 
    604   std::string name(histogram_data_ptr->name);
    605   std::unique_ptr<HistogramBase> histogram;
    606   switch (histogram_data.histogram_type) {
    607     case HISTOGRAM:
    608       histogram = Histogram::PersistentCreate(
    609           name, histogram_data.minimum, histogram_data.maximum, ranges,
    610           counts_data, logged_data, histogram_data.bucket_count,
    611           &histogram_data_ptr->samples_metadata,
    612           &histogram_data_ptr->logged_metadata);
    613       DCHECK(histogram);
    614       break;
    615     case LINEAR_HISTOGRAM:
    616       histogram = LinearHistogram::PersistentCreate(
    617           name, histogram_data.minimum, histogram_data.maximum, ranges,
    618           counts_data, logged_data, histogram_data.bucket_count,
    619           &histogram_data_ptr->samples_metadata,
    620           &histogram_data_ptr->logged_metadata);
    621       DCHECK(histogram);
    622       break;
    623     case BOOLEAN_HISTOGRAM:
    624       histogram = BooleanHistogram::PersistentCreate(
    625           name, ranges, counts_data, logged_data,
    626           &histogram_data_ptr->samples_metadata,
    627           &histogram_data_ptr->logged_metadata);
    628       DCHECK(histogram);
    629       break;
    630     case CUSTOM_HISTOGRAM:
    631       histogram = CustomHistogram::PersistentCreate(
    632           name, ranges, counts_data, logged_data, histogram_data.bucket_count,
    633           &histogram_data_ptr->samples_metadata,
    634           &histogram_data_ptr->logged_metadata);
    635       DCHECK(histogram);
    636       break;
    637     default:
    638       NOTREACHED();
    639   }
    640 
    641   if (histogram) {
    642     DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType());
    643     histogram->SetFlags(histogram_data.flags);
    644     RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS);
    645   } else {
    646     RecordCreateHistogramResult(CREATE_HISTOGRAM_UNKNOWN_TYPE);
    647   }
    648 
    649   return histogram;
    650 }
    651 
    652 HistogramBase*
    653 PersistentHistogramAllocator::GetOrCreateStatisticsRecorderHistogram(
    654     const HistogramBase* histogram) {
    655   // This should never be called on the global histogram allocator as objects
    656   // created there are already within the global statistics recorder.
    657   DCHECK_NE(GlobalHistogramAllocator::Get(), this);
    658   DCHECK(histogram);
    659 
    660   HistogramBase* existing =
    661       StatisticsRecorder::FindHistogram(histogram->histogram_name());
    662   if (existing)
    663     return existing;
    664 
    665   // Adding the passed histogram to the SR would cause a problem if the
    666   // allocator that holds it eventually goes away. Instead, create a new
    667   // one from a serialized version. Deserialization calls the appropriate
    668   // FactoryGet() which will create the histogram in the global persistent-
    669   // histogram allocator if such is set.
    670   base::Pickle pickle;
    671   if (!histogram->SerializeInfo(&pickle))
    672     return nullptr;
    673   PickleIterator iter(pickle);
    674   existing = DeserializeHistogramInfo(&iter);
    675   if (!existing)
    676     return nullptr;
    677 
    678   // Make sure there is no "serialization" flag set.
    679   DCHECK_EQ(0, existing->flags() & HistogramBase::kIPCSerializationSourceFlag);
    680   // Record the newly created histogram in the SR.
    681   return StatisticsRecorder::RegisterOrDeleteDuplicate(existing);
    682 }
    683 
    684 // static
    685 void PersistentHistogramAllocator::RecordCreateHistogramResult(
    686     CreateHistogramResultType result) {
    687   HistogramBase* result_histogram = GetCreateHistogramResultHistogram();
    688   if (result_histogram)
    689     result_histogram->Add(result);
    690 }
    691 
    692 GlobalHistogramAllocator::~GlobalHistogramAllocator() {}
    693 
    694 // static
    695 void GlobalHistogramAllocator::CreateWithPersistentMemory(
    696     void* base,
    697     size_t size,
    698     size_t page_size,
    699     uint64_t id,
    700     StringPiece name) {
    701   Set(WrapUnique(
    702       new GlobalHistogramAllocator(MakeUnique<PersistentMemoryAllocator>(
    703           base, size, page_size, id, name, false))));
    704 }
    705 
    706 // static
    707 void GlobalHistogramAllocator::CreateWithLocalMemory(
    708     size_t size,
    709     uint64_t id,
    710     StringPiece name) {
    711   Set(WrapUnique(new GlobalHistogramAllocator(
    712       MakeUnique<LocalPersistentMemoryAllocator>(size, id, name))));
    713 }
    714 
    715 #if !defined(OS_NACL)
    716 // static
    717 bool GlobalHistogramAllocator::CreateWithFile(
    718     const FilePath& file_path,
    719     size_t size,
    720     uint64_t id,
    721     StringPiece name) {
    722   bool exists = PathExists(file_path);
    723   File file(
    724       file_path, File::FLAG_OPEN_ALWAYS | File::FLAG_SHARE_DELETE |
    725                  File::FLAG_READ | File::FLAG_WRITE);
    726 
    727   std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
    728   if (exists) {
    729     mmfile->Initialize(std::move(file), MemoryMappedFile::READ_WRITE);
    730   } else {
    731     mmfile->Initialize(std::move(file), {0, static_cast<int64_t>(size)},
    732                        MemoryMappedFile::READ_WRITE_EXTEND);
    733   }
    734   if (!mmfile->IsValid() ||
    735       !FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)) {
    736     NOTREACHED();
    737     return false;
    738   }
    739 
    740   Set(WrapUnique(
    741       new GlobalHistogramAllocator(MakeUnique<FilePersistentMemoryAllocator>(
    742           std::move(mmfile), size, id, name, false))));
    743   Get()->SetPersistentLocation(file_path);
    744   return true;
    745 }
    746 
    747 // static
    748 bool GlobalHistogramAllocator::CreateWithActiveFile(const FilePath& base_path,
    749                                                     const FilePath& active_path,
    750                                                     size_t size,
    751                                                     uint64_t id,
    752                                                     StringPiece name) {
    753   if (!base::ReplaceFile(active_path, base_path, nullptr))
    754     base::DeleteFile(base_path, /*recursive=*/false);
    755 
    756   return base::GlobalHistogramAllocator::CreateWithFile(active_path, size, id,
    757                                                         name);
    758 }
    759 
    760 // static
    761 bool GlobalHistogramAllocator::CreateWithActiveFileInDir(const FilePath& dir,
    762                                                          size_t size,
    763                                                          uint64_t id,
    764                                                          StringPiece name) {
    765   FilePath base_path, active_path;
    766   ConstructFilePaths(dir, name, &base_path, &active_path);
    767   return CreateWithActiveFile(base_path, active_path, size, id, name);
    768 }
    769 
    770 // static
    771 void GlobalHistogramAllocator::ConstructFilePaths(const FilePath& dir,
    772                                                   StringPiece name,
    773                                                   FilePath* out_base_path,
    774                                                   FilePath* out_active_path) {
    775   if (out_base_path) {
    776     *out_base_path = dir.AppendASCII(name).AddExtension(
    777         PersistentMemoryAllocator::kFileExtension);
    778   }
    779   if (out_active_path) {
    780     *out_active_path =
    781         dir.AppendASCII(name.as_string() + std::string("-active"))
    782             .AddExtension(PersistentMemoryAllocator::kFileExtension);
    783   }
    784 }
    785 #endif  // !defined(OS_NACL)
    786 
    787 // static
    788 void GlobalHistogramAllocator::CreateWithSharedMemoryHandle(
    789     const SharedMemoryHandle& handle,
    790     size_t size) {
    791   std::unique_ptr<SharedMemory> shm(
    792       new SharedMemory(handle, /*readonly=*/false));
    793   if (!shm->Map(size) ||
    794       !SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(*shm)) {
    795     NOTREACHED();
    796     return;
    797   }
    798 
    799   Set(WrapUnique(
    800       new GlobalHistogramAllocator(MakeUnique<SharedPersistentMemoryAllocator>(
    801           std::move(shm), 0, StringPiece(), /*readonly=*/false))));
    802 }
    803 
    804 // static
    805 void GlobalHistogramAllocator::Set(
    806     std::unique_ptr<GlobalHistogramAllocator> allocator) {
    807   // Releasing or changing an allocator is extremely dangerous because it
    808   // likely has histograms stored within it. If the backing memory is also
    809   // also released, future accesses to those histograms will seg-fault.
    810   CHECK(!subtle::NoBarrier_Load(&g_allocator));
    811   subtle::Release_Store(&g_allocator,
    812                         reinterpret_cast<uintptr_t>(allocator.release()));
    813   size_t existing = StatisticsRecorder::GetHistogramCount();
    814 
    815   DVLOG_IF(1, existing)
    816       << existing << " histograms were created before persistence was enabled.";
    817 }
    818 
    819 // static
    820 GlobalHistogramAllocator* GlobalHistogramAllocator::Get() {
    821   return reinterpret_cast<GlobalHistogramAllocator*>(
    822       subtle::Acquire_Load(&g_allocator));
    823 }
    824 
    825 // static
    826 std::unique_ptr<GlobalHistogramAllocator>
    827 GlobalHistogramAllocator::ReleaseForTesting() {
    828   GlobalHistogramAllocator* histogram_allocator = Get();
    829   if (!histogram_allocator)
    830     return nullptr;
    831   PersistentMemoryAllocator* memory_allocator =
    832       histogram_allocator->memory_allocator();
    833 
    834   // Before releasing the memory, it's necessary to have the Statistics-
    835   // Recorder forget about the histograms contained therein; otherwise,
    836   // some operations will try to access them and the released memory.
    837   PersistentMemoryAllocator::Iterator iter(memory_allocator);
    838   const PersistentHistogramData* data;
    839   while ((data = iter.GetNextOfObject<PersistentHistogramData>()) != nullptr) {
    840     StatisticsRecorder::ForgetHistogramForTesting(data->name);
    841 
    842     // If a test breaks here then a memory region containing a histogram
    843     // actively used by this code is being released back to the test.
    844     // If that memory segment were to be deleted, future calls to create
    845     // persistent histograms would crash. To avoid this, have the test call
    846     // the method GetCreateHistogramResultHistogram() *before* setting
    847     // the (temporary) memory allocator via SetGlobalAllocator() so that
    848     // histogram is instead allocated from the process heap.
    849     DCHECK_NE(kResultHistogram, data->name);
    850   }
    851 
    852   subtle::Release_Store(&g_allocator, 0);
    853   return WrapUnique(histogram_allocator);
    854 };
    855 
    856 void GlobalHistogramAllocator::SetPersistentLocation(const FilePath& location) {
    857   persistent_location_ = location;
    858 }
    859 
    860 const FilePath& GlobalHistogramAllocator::GetPersistentLocation() const {
    861   return persistent_location_;
    862 }
    863 
    864 bool GlobalHistogramAllocator::WriteToPersistentLocation() {
    865 #if defined(OS_NACL)
    866   // NACL doesn't support file operations, including ImportantFileWriter.
    867   NOTREACHED();
    868   return false;
    869 #else
    870   // Stop if no destination is set.
    871   if (persistent_location_.empty()) {
    872     NOTREACHED() << "Could not write \"" << Name() << "\" persistent histograms"
    873                  << " to file because no location was set.";
    874     return false;
    875   }
    876 
    877   StringPiece contents(static_cast<const char*>(data()), used());
    878   if (!ImportantFileWriter::WriteFileAtomically(persistent_location_,
    879                                                 contents)) {
    880     LOG(ERROR) << "Could not write \"" << Name() << "\" persistent histograms"
    881                << " to file: " << persistent_location_.value();
    882     return false;
    883   }
    884 
    885   return true;
    886 #endif
    887 }
    888 
    889 void GlobalHistogramAllocator::DeletePersistentLocation() {
    890   memory_allocator()->SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED);
    891 
    892 #if defined(OS_NACL)
    893   NOTREACHED();
    894 #else
    895   if (persistent_location_.empty())
    896     return;
    897 
    898   // Open (with delete) and then immediately close the file by going out of
    899   // scope. This is the only cross-platform safe way to delete a file that may
    900   // be open elsewhere. Open handles will continue to operate normally but
    901   // new opens will not be possible.
    902   File file(persistent_location_,
    903             File::FLAG_OPEN | File::FLAG_READ | File::FLAG_DELETE_ON_CLOSE);
    904 #endif
    905 }
    906 
    907 GlobalHistogramAllocator::GlobalHistogramAllocator(
    908     std::unique_ptr<PersistentMemoryAllocator> memory)
    909     : PersistentHistogramAllocator(std::move(memory)),
    910       import_iterator_(this) {
    911   // Make sure the StatisticsRecorder is initialized to prevent duplicate
    912   // histograms from being created. It's safe to call this multiple times.
    913   StatisticsRecorder::Initialize();
    914 }
    915 
    916 void GlobalHistogramAllocator::ImportHistogramsToStatisticsRecorder() {
    917   // Skip the import if it's the histogram that was last created. Should a
    918   // race condition cause the "last created" to be overwritten before it
    919   // is recognized here then the histogram will be created and be ignored
    920   // when it is detected as a duplicate by the statistics-recorder. This
    921   // simple check reduces the time of creating persistent histograms by
    922   // about 40%.
    923   Reference record_to_ignore = last_created();
    924 
    925   // There is no lock on this because the iterator is lock-free while still
    926   // guaranteed to only return each entry only once. The StatisticsRecorder
    927   // has its own lock so the Register operation is safe.
    928   while (true) {
    929     std::unique_ptr<HistogramBase> histogram =
    930         import_iterator_.GetNextWithIgnore(record_to_ignore);
    931     if (!histogram)
    932       break;
    933     StatisticsRecorder::RegisterOrDeleteDuplicate(histogram.release());
    934   }
    935 }
    936 
    937 }  // namespace base
    938