blob: ae243f4f77bddea2fdd59784a836ad70cb6dcd23 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2012 The Chromium Authors
[email protected]7c7a42752012-08-09 05:14:152// 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/sparse_histogram.h"
6
danakj0c8d4aa2015-11-25 05:29:587#include <utility>
8
Hans Wennborg9f3bb63d2020-04-21 11:12:389#include "base/logging.h"
dcheng093de9b2016-04-04 21:25:5110#include "base/memory/ptr_util.h"
Gayane Petrosyan5745ac62018-03-23 01:45:2411#include "base/metrics/dummy_histogram.h"
Alexei Svitkineb49c09f2024-05-24 16:22:3512#include "base/metrics/histogram_functions.h"
bcwhiteb036e4322015-12-10 18:36:3413#include "base/metrics/metrics_hashes.h"
bcwhite3dd85c4f2016-03-17 13:21:5614#include "base/metrics/persistent_histogram_allocator.h"
15#include "base/metrics/persistent_sample_map.h"
[email protected]877ef562012-10-20 02:56:1816#include "base/metrics/sample_map.h"
[email protected]7c7a42752012-08-09 05:14:1517#include "base/metrics/statistics_recorder.h"
David Sanders83f8ae42022-04-04 23:15:3918#include "base/notreached.h"
[email protected]c50c21d2013-01-11 21:52:4419#include "base/pickle.h"
Jun Kokatsu505af9f2020-05-05 11:59:4720#include "base/strings/utf_string_conversions.h"
[email protected]b4af2ec2012-10-05 21:29:4421#include "base/synchronization/lock.h"
Jun Kokatsu505af9f2020-05-05 11:59:4722#include "base/values.h"
[email protected]7c7a42752012-08-09 05:14:1523
[email protected]7c7a42752012-08-09 05:14:1524namespace base {
25
Ramon Cano Apariciob2cba0f2025-01-22 21:26:1026typedef HistogramBase::Count32 Count32;
[email protected]b4af2ec2012-10-05 21:29:4427
[email protected]7c7a42752012-08-09 05:14:1528// static
Alexei Svitkine784ce692024-05-24 16:19:2429HistogramBase* SparseHistogram::FactoryGet(std::string_view name,
avi9b6f42932015-12-26 22:15:1430 int32_t flags) {
Ramon Cano Aparicio901ec6c2025-04-22 14:30:4531 uint64_t name_hash = HashMetricName(name);
32 HistogramBase* histogram = StatisticsRecorder::FindHistogram(name_hash, name);
[email protected]cc7dec212013-03-01 03:53:2533 if (!histogram) {
Ramon Cano Aparicio901ec6c2025-04-22 14:30:4534 bool should_record = StatisticsRecorder::ShouldRecordHistogram(
35 ParseMetricHashTo32Bits(name_hash));
Alexei Svitkineb49c09f2024-05-24 16:22:3536 if (!should_record) {
Gayane Petrosyan5745ac62018-03-23 01:45:2437 return DummyHistogram::GetInstance();
Alexei Svitkineb49c09f2024-05-24 16:22:3538 }
39 // Try to create the histogram using a "persistent" allocator. If the
40 // allocator doesn't exist or if allocating from it fails, code below will
41 // allocate the histogram from the process heap.
bcwhite3dd85c4f2016-03-17 13:21:5642 PersistentMemoryAllocator::Reference histogram_ref = 0;
dcheng093de9b2016-04-04 21:25:5143 std::unique_ptr<HistogramBase> tentative_histogram;
bcwhite5e748c62016-04-06 02:03:5344 PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
bcwhite3dd85c4f2016-03-17 13:21:5645 if (allocator) {
bcwhite3dd85c4f2016-03-17 13:21:5646 tentative_histogram = allocator->AllocateHistogram(
Ramon Cano Aparicioeb4e3152025-05-09 14:26:5747 SPARSE_HISTOGRAM, name, name_hash, /*minimum=*/0, /*maximum=*/0,
48 /*bucket_ranges=*/nullptr, flags, &histogram_ref);
bcwhite3dd85c4f2016-03-17 13:21:5649 }
50
51 // Handle the case where no persistent allocator is present or the
52 // persistent allocation fails (perhaps because it is full).
53 if (!tentative_histogram) {
54 DCHECK(!histogram_ref); // Should never have been set.
bcwhite3dd85c4f2016-03-17 13:21:5655 flags &= ~HistogramBase::kIsPersistent;
Ramon Cano Aparicioeb4e3152025-05-09 14:26:5756 tentative_histogram.reset(
57 new SparseHistogram(GetPermanentName(name), name_hash));
bcwhite3dd85c4f2016-03-17 13:21:5658 tentative_histogram->SetFlags(flags);
59 }
60
61 // Register this histogram with the StatisticsRecorder. Keep a copy of
62 // the pointer value to tell later whether the locally created histogram
63 // was registered or deleted. The type is "void" because it could point
64 // to released memory after the following line.
65 const void* tentative_histogram_ptr = tentative_histogram.get();
66 histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(
67 tentative_histogram.release());
68
69 // Persistent histograms need some follow-up processing.
70 if (histogram_ref) {
71 allocator->FinalizeHistogram(histogram_ref,
72 histogram == tentative_histogram_ptr);
73 }
[email protected]cc7dec212013-03-01 03:53:2574 }
bcwhite3dd85c4f2016-03-17 13:21:5675
Alexei Svitkineb49c09f2024-05-24 16:22:3576 if (histogram->GetHistogramType() != SPARSE_HISTOGRAM) {
77 // The type does not match the existing histogram. This can come about if an
78 // extension updates in the middle of a Chrome run or simply by bad code
79 // within Chrome itself. We can't return null since calling code does not
80 // expect it, so return a dummy instance and log the name hash.
81 //
82 // Note: Theoretically the below line could be re-entrant if something has
83 // gone very wrong, but crashing w/ an infinite recursion seems OK then.
84 UmaHistogramSparse("Histogram.MismatchedConstructionArguments",
Ramon Cano Aparicio901ec6c2025-04-22 14:30:4585 static_cast<Sample32>(name_hash));
Alexei Svitkineb49c09f2024-05-24 16:22:3586 DLOG(ERROR) << "Histogram " << name << " has a mismatched type";
87 return DummyHistogram::GetInstance();
88 }
[email protected]7c7a42752012-08-09 05:14:1589 return histogram;
90}
91
bcwhite3dd85c4f2016-03-17 13:21:5692// static
dcheng093de9b2016-04-04 21:25:5193std::unique_ptr<HistogramBase> SparseHistogram::PersistentCreate(
bcwhiteb0bb9192016-04-18 01:33:1094 PersistentHistogramAllocator* allocator,
Roger McFarlane89528592025-02-20 20:50:2795 DurableStringView durable_name,
Ramon Cano Aparicioeb4e3152025-05-09 14:26:5796 uint64_t name_hash,
bcwhite3dd85c4f2016-03-17 13:21:5697 HistogramSamples::Metadata* meta,
98 HistogramSamples::Metadata* logged_meta) {
Ramon Cano Aparicioeb4e3152025-05-09 14:26:5799 return WrapUnique(new SparseHistogram(allocator, durable_name, name_hash,
100 meta, logged_meta));
bcwhite3dd85c4f2016-03-17 13:21:56101}
102
Chris Watkinsbb7211c2017-11-29 07:16:38103SparseHistogram::~SparseHistogram() = default;
[email protected]7c7a42752012-08-09 05:14:15104
bcwhiteb036e4322015-12-10 18:36:34105uint64_t SparseHistogram::name_hash() const {
altimin498c8382017-05-12 17:49:18106 return unlogged_samples_->id();
bcwhiteb036e4322015-12-10 18:36:34107}
108
[email protected]07c02402012-10-31 06:20:25109HistogramType SparseHistogram::GetHistogramType() const {
110 return SPARSE_HISTOGRAM;
111}
112
[email protected]15ce3842013-06-27 14:38:45113bool SparseHistogram::HasConstructionArguments(
Ramon Cano Aparicio79e06642025-01-09 19:10:22114 Sample32 expected_minimum,
115 Sample32 expected_maximum,
Peter Kastingfc94f5062022-06-08 16:41:45116 size_t expected_bucket_count) const {
[email protected]abae9b022012-10-24 08:18:52117 // SparseHistogram never has min/max/bucket_count limit.
118 return false;
119}
120
Ramon Cano Aparicio79e06642025-01-09 19:10:22121void SparseHistogram::Add(Sample32 value) {
amohammadkhan6779b5c32015-08-05 20:31:11122 AddCount(value, 1);
123}
124
Ramon Cano Aparicio79e06642025-01-09 19:10:22125void SparseHistogram::AddCount(Sample32 value, int count) {
amohammadkhan6779b5c32015-08-05 20:31:11126 if (count <= 0) {
Peter Boströmde573332024-08-26 20:42:45127 NOTREACHED();
amohammadkhan6779b5c32015-08-05 20:31:11128 }
simonhatchdf5a8142015-07-15 22:22:57129 {
130 base::AutoLock auto_lock(lock_);
altimin498c8382017-05-12 17:49:18131 unlogged_samples_->Accumulate(value, count);
simonhatchdf5a8142015-07-15 22:22:57132 }
133
Peter Kastingfa488992024-08-06 07:48:14134 if (StatisticsRecorder::have_active_callbacks()) [[unlikely]] {
Shakti Sahue0e07a0e2021-06-06 00:07:52135 FindAndRunCallbacks(value);
Peter Kastingfa488992024-08-06 07:48:14136 }
[email protected]7c7a42752012-08-09 05:14:15137}
138
dcheng093de9b2016-04-04 21:25:51139std::unique_ptr<HistogramSamples> SparseHistogram::SnapshotSamples() const {
140 std::unique_ptr<SampleMap> snapshot(new SampleMap(name_hash()));
[email protected]b4af2ec2012-10-05 21:29:44141
[email protected]7c7a42752012-08-09 05:14:15142 base::AutoLock auto_lock(lock_);
altimin498c8382017-05-12 17:49:18143 snapshot->Add(*unlogged_samples_);
144 snapshot->Add(*logged_samples_);
danakj0c8d4aa2015-11-25 05:29:58145 return std::move(snapshot);
[email protected]7c7a42752012-08-09 05:14:15146}
147
Luc Nguyene01c6752022-12-01 18:40:15148std::unique_ptr<HistogramSamples> SparseHistogram::SnapshotUnloggedSamples()
149 const {
150 std::unique_ptr<SampleMap> snapshot(new SampleMap(name_hash()));
151
152 base::AutoLock auto_lock(lock_);
153 snapshot->Add(*unlogged_samples_);
154
155 return std::move(snapshot);
156}
157
158void SparseHistogram::MarkSamplesAsLogged(const HistogramSamples& samples) {
159 DCHECK(!final_delta_created_);
160
161 base::AutoLock auto_lock(lock_);
162 unlogged_samples_->Subtract(samples);
163 logged_samples_->Add(samples);
164}
165
dcheng093de9b2016-04-04 21:25:51166std::unique_ptr<HistogramSamples> SparseHistogram::SnapshotDelta() {
bcwhite65e57d02016-05-13 14:39:40167 DCHECK(!final_delta_created_);
168
Luc Nguyen257ecb02023-04-11 21:09:41169 std::unique_ptr<SampleMap> snapshot =
170 std::make_unique<SampleMap>(name_hash());
bcwhitec85a1f822016-02-18 21:22:14171 base::AutoLock auto_lock(lock_);
Luc Nguyen257ecb02023-04-11 21:09:41172 snapshot->Extract(*unlogged_samples_);
bcwhite3dd85c4f2016-03-17 13:21:56173 logged_samples_->Add(*snapshot);
bcwhitec85a1f822016-02-18 21:22:14174 return std::move(snapshot);
175}
176
bcwhite65e57d02016-05-13 14:39:40177std::unique_ptr<HistogramSamples> SparseHistogram::SnapshotFinalDelta() const {
178 DCHECK(!final_delta_created_);
179 final_delta_created_ = true;
180
181 std::unique_ptr<SampleMap> snapshot(new SampleMap(name_hash()));
182 base::AutoLock auto_lock(lock_);
altimin498c8382017-05-12 17:49:18183 snapshot->Add(*unlogged_samples_);
bcwhite65e57d02016-05-13 14:39:40184
bcwhite65e57d02016-05-13 14:39:40185 return std::move(snapshot);
186}
187
Alexei Svitkine1e86b0c892024-10-02 21:02:03188bool SparseHistogram::AddSamples(const HistogramSamples& samples) {
[email protected]c50c21d2013-01-11 21:52:44189 base::AutoLock auto_lock(lock_);
Alexei Svitkine1e86b0c892024-10-02 21:02:03190 return unlogged_samples_->Add(samples);
[email protected]c50c21d2013-01-11 21:52:44191}
192
193bool SparseHistogram::AddSamplesFromPickle(PickleIterator* iter) {
194 base::AutoLock auto_lock(lock_);
altimin498c8382017-05-12 17:49:18195 return unlogged_samples_->AddFromPickle(iter);
[email protected]c50c21d2013-01-11 21:52:44196}
197
Nathan Memmottc034fa4e2022-07-15 23:59:43198base::Value::Dict SparseHistogram::ToGraphDict() const {
Jun Kokatsu505af9f2020-05-05 11:59:47199 std::unique_ptr<HistogramSamples> snapshot = SnapshotSamples();
Quang Minh Tuan Nguyen39b1fed2021-03-16 00:49:38200 return snapshot->ToGraphDict(histogram_name(), flags());
Jun Kokatsu505af9f2020-05-05 11:59:47201}
202
Daniel Cheng0d89f9222017-09-22 05:05:07203void SparseHistogram::SerializeInfoImpl(Pickle* pickle) const {
204 pickle->WriteString(histogram_name());
205 pickle->WriteInt(flags());
[email protected]c50c21d2013-01-11 21:52:44206}
207
Roger McFarlane89528592025-02-20 20:50:27208SparseHistogram::SparseHistogram(DurableStringView durable_name)
Ramon Cano Aparicioeb4e3152025-05-09 14:26:57209 : SparseHistogram(durable_name, HashMetricName(*durable_name)) {}
210
211SparseHistogram::SparseHistogram(DurableStringView durable_name,
212 uint64_t name_hash)
Roger McFarlane89528592025-02-20 20:50:27213 : HistogramBase(durable_name),
Ramon Cano Aparicioeb4e3152025-05-09 14:26:57214 unlogged_samples_(new SampleMap(name_hash)),
215 logged_samples_(new SampleMap(unlogged_samples_->id())) {
216 DCHECK_EQ(name_hash, HashMetricName(*durable_name)) << "Name hash mismatch";
217}
bcwhite3dd85c4f2016-03-17 13:21:56218
bcwhiteb0bb9192016-04-18 01:33:10219SparseHistogram::SparseHistogram(PersistentHistogramAllocator* allocator,
Roger McFarlane89528592025-02-20 20:50:27220 DurableStringView durable_name,
Ramon Cano Aparicioeb4e3152025-05-09 14:26:57221 uint64_t name_hash,
bcwhite3dd85c4f2016-03-17 13:21:56222 HistogramSamples::Metadata* meta,
223 HistogramSamples::Metadata* logged_meta)
Roger McFarlane89528592025-02-20 20:50:27224 : HistogramBase(durable_name),
bcwhite3dd85c4f2016-03-17 13:21:56225 // While other histogram types maintain a static vector of values with
226 // sufficient space for both "active" and "logged" samples, with each
227 // SampleVector being given the appropriate half, sparse histograms
228 // have no such initial allocation. Each sample has its own record
229 // attached to a single PersistentSampleMap by a common 64-bit identifier.
230 // Since a sparse histogram has two sample maps (active and logged),
Ramon Cano Apariciob2cba0f2025-01-22 21:26:10231 // there must be two sets of sample records with different IDs. The
bcwhite3dd85c4f2016-03-17 13:21:56232 // "active" samples use, for convenience purposes, an ID matching
233 // that of the histogram while the "logged" samples use that number
234 // plus 1.
Ramon Cano Aparicioeb4e3152025-05-09 14:26:57235 unlogged_samples_(new PersistentSampleMap(name_hash, allocator, meta)),
altimin498c8382017-05-12 17:49:18236 logged_samples_(new PersistentSampleMap(unlogged_samples_->id() + 1,
237 allocator,
Ramon Cano Aparicioeb4e3152025-05-09 14:26:57238 logged_meta)) {
239 DCHECK_EQ(name_hash, HashMetricName(*durable_name)) << "Name hash mismatch";
240}
[email protected]c50c21d2013-01-11 21:52:44241
242HistogramBase* SparseHistogram::DeserializeInfoImpl(PickleIterator* iter) {
asvitkine24d3e9a2015-05-27 05:22:14243 std::string histogram_name;
[email protected]c50c21d2013-01-11 21:52:44244 int flags;
245 if (!iter->ReadString(&histogram_name) || !iter->ReadInt(&flags)) {
246 DLOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name;
Ivan Kotenkova16212a52017-11-08 12:37:33247 return nullptr;
[email protected]c50c21d2013-01-11 21:52:44248 }
249
[email protected]c50c21d2013-01-11 21:52:44250 flags &= ~HistogramBase::kIPCSerializationSourceFlag;
251
252 return SparseHistogram::FactoryGet(histogram_name, flags);
253}
[email protected]7c7a42752012-08-09 05:14:15254
Nathan Memmottc034fa4e2022-07-15 23:59:43255Value::Dict SparseHistogram::GetParameters() const {
Weilun Shif07fe532020-06-11 03:21:54256 // Unlike Histogram::GetParameters, only set the type here, and no other
257 // params. The other params do not make sense for sparse histograms.
Nathan Memmottc034fa4e2022-07-15 23:59:43258 Value::Dict params;
259 params.Set("type", HistogramTypeToString(GetHistogramType()));
Sylvain Defresne0348ecfd2021-06-23 08:27:34260 return params;
[email protected]24a7ec52012-10-08 10:31:50261}
262
[email protected]7c7a42752012-08-09 05:14:15263} // namespace base