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