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