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