1 // Copyright 2012 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/histogram_snapshot_manager.h"
6
7 #include <memory>
8
9 #include "base/debug/alias.h"
10 #include "base/logging.h"
11 #include "base/memory/raw_ptr.h"
12 #include "base/metrics/histogram_flattener.h"
13 #include "base/metrics/histogram_samples.h"
14 #include "base/notreached.h"
15
16 namespace base {
17
HistogramSnapshotManager(HistogramFlattener * histogram_flattener)18 HistogramSnapshotManager::HistogramSnapshotManager(
19 HistogramFlattener* histogram_flattener)
20 : histogram_flattener_(histogram_flattener) {
21 DCHECK(histogram_flattener_);
22 }
23
24 HistogramSnapshotManager::~HistogramSnapshotManager() = default;
25
PrepareDeltas(const std::vector<HistogramBase * > & histograms,HistogramBase::Flags flags_to_set,HistogramBase::Flags required_flags)26 void HistogramSnapshotManager::PrepareDeltas(
27 const std::vector<HistogramBase*>& histograms,
28 HistogramBase::Flags flags_to_set,
29 HistogramBase::Flags required_flags) {
30 for (HistogramBase* const histogram : histograms) {
31 histogram->SetFlags(flags_to_set);
32 if (histogram->HasFlags(required_flags)) {
33 PrepareDelta(histogram);
34 }
35 }
36 }
37
SnapshotUnloggedSamples(const std::vector<HistogramBase * > & histograms,HistogramBase::Flags required_flags)38 void HistogramSnapshotManager::SnapshotUnloggedSamples(
39 const std::vector<HistogramBase*>& histograms,
40 HistogramBase::Flags required_flags) {
41 DCHECK(!unlogged_samples_snapshot_taken_);
42 unlogged_samples_snapshot_taken_ = true;
43 for (HistogramBase* const histogram : histograms) {
44 if (histogram->HasFlags(required_flags)) {
45 const HistogramSnapshotPair& histogram_snapshot_pair =
46 histograms_and_snapshots_.emplace_back(
47 histogram, histogram->SnapshotUnloggedSamples());
48 PrepareSamples(histogram_snapshot_pair.first,
49 *histogram_snapshot_pair.second);
50 }
51 }
52 }
53
MarkUnloggedSamplesAsLogged()54 void HistogramSnapshotManager::MarkUnloggedSamplesAsLogged() {
55 DCHECK(unlogged_samples_snapshot_taken_);
56 unlogged_samples_snapshot_taken_ = false;
57 std::vector<HistogramSnapshotPair> histograms_and_snapshots;
58 histograms_and_snapshots.swap(histograms_and_snapshots_);
59 for (auto& [histogram, snapshot] : histograms_and_snapshots) {
60 histogram->MarkSamplesAsLogged(*snapshot);
61 }
62 }
63
PrepareDelta(HistogramBase * histogram)64 void HistogramSnapshotManager::PrepareDelta(HistogramBase* histogram) {
65 std::unique_ptr<HistogramSamples> samples = histogram->SnapshotDelta();
66 PrepareSamples(histogram, *samples);
67 }
68
PrepareFinalDelta(const HistogramBase * histogram)69 void HistogramSnapshotManager::PrepareFinalDelta(
70 const HistogramBase* histogram) {
71 std::unique_ptr<HistogramSamples> samples = histogram->SnapshotFinalDelta();
72 PrepareSamples(histogram, *samples);
73 }
74
PrepareSamples(const HistogramBase * histogram,const HistogramSamples & samples)75 void HistogramSnapshotManager::PrepareSamples(const HistogramBase* histogram,
76 const HistogramSamples& samples) {
77 DCHECK(histogram_flattener_);
78
79 // Crash if we detect that our histograms have been overwritten. This may be
80 // a fair distance from the memory smasher, but we hope to correlate these
81 // crashes with other events, such as plugins, or usage patterns, etc.
82 uint32_t corruption = histogram->FindCorruption(samples);
83 if (HistogramBase::BUCKET_ORDER_ERROR & corruption) {
84 // Extract fields useful during debug.
85 const BucketRanges* ranges =
86 static_cast<const Histogram*>(histogram)->bucket_ranges();
87 uint32_t ranges_checksum = ranges->checksum();
88 uint32_t ranges_calc_checksum = ranges->CalculateChecksum();
89 int32_t flags = histogram->flags();
90 // Ensure that compiler keeps around pointers to |histogram| and its
91 // internal |bucket_ranges_| for any minidumps.
92 base::debug::Alias(&ranges_checksum);
93 base::debug::Alias(&ranges_calc_checksum);
94 base::debug::Alias(&flags);
95 // The checksum should have caught this, so crash separately if it didn't.
96 CHECK_NE(0U, HistogramBase::RANGE_CHECKSUM_ERROR & corruption);
97 NOTREACHED(); // Crash for the bucket order corruption.
98 }
99 // Checksum corruption might not have caused order corruption.
100 CHECK_EQ(0U, HistogramBase::RANGE_CHECKSUM_ERROR & corruption);
101
102 // Note, at this point corruption can only be COUNT_HIGH_ERROR or
103 // COUNT_LOW_ERROR and they never arise together, so we don't need to extract
104 // bits from corruption.
105 if (corruption) {
106 DLOG(ERROR) << "Histogram: \"" << histogram->histogram_name()
107 << "\" has data corruption: " << corruption;
108 // Don't record corrupt data to metrics services.
109 return;
110 }
111
112 if (samples.TotalCount() > 0)
113 histogram_flattener_->RecordDelta(*histogram, samples);
114 }
115
116 } // namespace base
117