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