Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(262)

Side by Side Diff: base/metrics/histogram_persistence.cc

Issue 1425533011: Support "shared" histograms between processes. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@shmem-alloc
Patch Set: reorganized persistence around PersistentGet methods to fix visibility problems Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/metrics/histogram_persistence.h"
6
7 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/metrics/histogram.h"
10 #include "base/metrics/histogram_base.h"
11 #include "base/metrics/histogram_samples.h"
12 #include "base/metrics/statistics_recorder.h"
13 #include "base/synchronization/lock.h"
14
15 namespace {
16
17 // Type identifiers used when storing in persistent memory so they can be
18 // identified during extraction. A "version number" is added to the base
19 // so that, if the structure of that object changes, stored older versions
20 // will be safely ignored.
21 enum : uint32_t {
22 kTypeIdHistogram = 0xF1645910 + 1, // SHA1(Histogram) v1
Alexei Svitkine (slow) 2016/01/12 21:54:44 SHA1 identifiers are longer than uint32_t. Do you
bcwhite 2016/01/13 13:38:29 No. The number is the first 4 bytes of a sha1 has
23 kTypeIdRangesArray = 0xBCEA225A + 1, // SHA1(RangesArray) v1
24 kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1
25 };
26
27 // This data must be held in persistent memory in order for processes to
28 // locate and use histograms created elsewhere.
29 struct PersistentHistogramData {
30 int histogram_type;
31 int flags;
32 int minimum;
33 int maximum;
34 size_t bucket_count;
35 base::PersistentMemoryAllocator::Reference ranges_ref;
Alexei Svitkine (slow) 2016/01/12 21:54:45 Nit: The anon namespace can still be inside the ba
bcwhite 2016/01/13 13:38:29 Done.
36 uint32_t ranges_checksum;
37 base::PersistentMemoryAllocator::Reference counts_ref;
38 base::HistogramSamples::Metadata samples_metadata;
39
40 // Space for the histogram name will be added during the actual allocation
41 // request. This must be the last field of the structure. A zero-size array
42 // or a "flexible" array would be preferred but is not (yet) valid C++.
43 char name[1];
44 };
45
46 // The object held here will obviously not be destructed at process exit
47 // but that's okay since PersistentMemoryAllocator objects are explicitly
48 // forbidden from doing anything essential at exit anyway due to the fact
49 // that they depend on data managed elsewhere and which could be destructed
50 // first.
51 base::PersistentMemoryAllocator* g_allocator = nullptr;
52
53 } // namespace
54
55 namespace base {
56
57 const Feature kPersistentHistogramsFeature = {
Alexei Svitkine (slow) 2016/01/12 21:54:45 Nit: No = needed.
bcwhite 2016/01/13 13:38:29 Done. It's actually done both ways elsewhere, som
58 "PersistentMetrics", FEATURE_DISABLED_BY_DEFAULT
Alexei Svitkine (slow) 2016/01/12 21:54:44 Nit: PersistentHistograms
bcwhite 2016/01/13 13:38:29 Done.
59 };
60
61 void SetPersistentHistogramMemoryAllocator(
62 PersistentMemoryAllocator* allocator) {
63 // Releasing or changing an allocator is extremely dangerous because it
64 // likely has histograms stored within it. If the backing memory is also
65 // also released, future accesses to those histograms will seg-fault.
66 // It's not a fatal CHECK() because tests do this knowing that all
67 // such persistent histograms have already been forgotten.
Alexei Svitkine (slow) 2016/01/12 21:54:45 I think it should be a DCHECK here but we should h
68 if (g_allocator) {
69 LOG(WARNING) << "Active PersistentMemoryAllocator has been released."
70 << " Some existing histogram pointers may be invalid.";
71 delete g_allocator;
72 }
73 g_allocator = allocator;
74 }
75
76 PersistentMemoryAllocator* GetPersistentHistogramMemoryAllocator() {
77 return g_allocator;
78 }
79
80 PersistentMemoryAllocator* ReleasePersistentHistogramMemoryAllocator() {
81 PersistentMemoryAllocator* allocator = g_allocator;
82 g_allocator = nullptr;
83 return allocator;
84 };
85
86 HistogramBase* CreatePersistentHistogram(
87 PersistentMemoryAllocator* allocator,
88 PersistentHistogramData* histogram_data_ptr) {
89 if (!histogram_data_ptr) {
90 LOG(WARNING) << "Persistent histogram data was not valid; skipped.";
Alexei Svitkine (slow) 2016/01/12 21:54:44 Nit: You can pipe the message into the NOTREACHED(
bcwhite 2016/01/13 13:38:29 But that would omit the warning on Release builds,
Alexei Svitkine (slow) 2016/01/18 19:25:35 I think it's best practice to have release builds
bcwhite 2016/01/20 15:26:14 Should that histogram have only the errors or shou
Alexei Svitkine (slow) 2016/01/20 16:26:41 Yep, that's usually the best practice.
91 NOTREACHED();
92 return nullptr;
93 }
94
95 // Copy the histogram_data to local storage because anything in persistent
96 // memory cannot be trusted as it could be changed at any moment by a
97 // malicious actor that shares access. The contents of histogram_data are
98 // validated below; the local copy is to ensure that the contents cannot
99 // be externally changed between validation and use.
100 PersistentHistogramData histogram_data = *histogram_data_ptr;
101
102 HistogramBase::Sample* ranges_data =
103 allocator->GetAsObject<HistogramBase::Sample>(histogram_data.ranges_ref,
104 kTypeIdRangesArray);
105 if (!ranges_data || histogram_data.bucket_count < 2 ||
106 histogram_data.bucket_count + 1 >
107 std::numeric_limits<size_t>::max() / sizeof(HistogramBase::Sample) ||
108 allocator->GetAllocSize(histogram_data.ranges_ref) <
109 (histogram_data.bucket_count + 1) * sizeof(HistogramBase::Sample)) {
110 LOG(WARNING) << "Persistent histogram referenced invalid ranges array"
111 << "; skipped.";
112 NOTREACHED();
113 return nullptr;
114 }
115 // To avoid racy destruction at shutdown, the following will be leaked.
116 BucketRanges* ranges = new BucketRanges(histogram_data.bucket_count + 1);
117 bool bad_ranges = false;
118 for (size_t i = 0; i < ranges->size(); ++i) {
119 if (i > 0 && ranges_data[i] <= ranges_data[i - 1])
120 bad_ranges = true;
121 ranges->set_range(i, ranges_data[i]);
122 }
123 ranges->ResetChecksum();
124 if (bad_ranges || ranges->checksum() != histogram_data.ranges_checksum) {
125 LOG(WARNING) << "Persistent histogram referenced invalid ranges array"
126 << "; skipped.";
127 NOTREACHED();
128 return nullptr;
129 }
130 const BucketRanges* registered_ranges =
131 StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
132
133 HistogramBase::AtomicCount* counts_data =
134 allocator->GetAsObject<HistogramBase::AtomicCount>(
135 histogram_data.counts_ref, kTypeIdCountsArray);
136 if (!counts_data ||
137 allocator->GetAllocSize(histogram_data.counts_ref) <
138 histogram_data.bucket_count * sizeof(HistogramBase::AtomicCount)) {
139 LOG(WARNING) << "Persistent histogram referenced invalid counts array"
140 << "; skipped.";
141 NOTREACHED();
142 return nullptr;
143 }
144
145 HistogramBase* histogram = nullptr;
146 switch (histogram_data.histogram_type) {
147 case HISTOGRAM:
Alexei Svitkine (slow) 2016/01/12 21:54:44 Nit: indent these 2 more.
bcwhite 2016/01/13 13:38:29 Done.
148 histogram = Histogram::PersistentGet(
149 histogram_data_ptr->name,
150 histogram_data.minimum,
151 histogram_data.maximum,
152 registered_ranges,
153 counts_data,
154 histogram_data.bucket_count,
155 &histogram_data_ptr->samples_metadata);
156 break;
157 case LINEAR_HISTOGRAM:
158 histogram = LinearHistogram::PersistentGet(
159 histogram_data_ptr->name,
160 histogram_data.minimum,
161 histogram_data.maximum,
162 registered_ranges,
163 counts_data,
164 histogram_data.bucket_count,
165 &histogram_data_ptr->samples_metadata);
166 break;
167 case BOOLEAN_HISTOGRAM:
168 histogram = BooleanHistogram::PersistentGet(
169 histogram_data_ptr->name,
170 registered_ranges,
171 counts_data,
172 &histogram_data_ptr->samples_metadata);
173 break;
174 case CUSTOM_HISTOGRAM:
175 histogram = CustomHistogram::PersistentGet(
176 histogram_data_ptr->name,
177 registered_ranges,
178 counts_data,
179 histogram_data.bucket_count,
180 &histogram_data_ptr->samples_metadata);
181 break;
182 }
183
184 if (histogram) {
185 DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType());
186 histogram->SetFlags(histogram_data.flags);
187 }
188
189 return histogram;
190 }
191
192 HistogramBase* GetPersistentHistogram(
193 PersistentMemoryAllocator* allocator,
194 int32_t ref) {
195 // Unfortunately, the above "pickle" methods cannot be used as part of the
196 // persistance because the deserialization methods always create local
197 // count data (these must referenced the persistent counts) and always add
198 // it to the local list of known histograms (these may be simple references
199 // to histograms in other processes).
200 PersistentHistogramData* histogram_data =
201 allocator->GetAsObject<PersistentHistogramData>(ref, kTypeIdHistogram);
202 size_t length = allocator->GetAllocSize(ref);
203 if (!histogram_data ||
204 reinterpret_cast<char*>(histogram_data)[length - 1] != '\0') {
205 LOG(WARNING) << "Persistent histogram data was invalid or had invalid name"
206 << "; skipped.";
207 NOTREACHED();
208 return nullptr;
209 }
210 return CreatePersistentHistogram(allocator, histogram_data);
211 }
212
213 HistogramBase* GetNextPersistentHistogram(
214 PersistentMemoryAllocator* allocator,
215 PersistentMemoryAllocator::Iterator* iter) {
216 PersistentMemoryAllocator::Reference ref;
217 uint32_t type_id;
218 while ((ref = allocator->GetNextIterable(iter, &type_id)) != 0) {
219 if (type_id == kTypeIdHistogram)
220 return GetPersistentHistogram(allocator, ref);
221 }
222 return nullptr;
223 }
224
225 void FinalizePersistentHistogram(PersistentMemoryAllocator::Reference ref,
226 bool registered) {
227 // If the created persistent histogram was registered then it needs to
228 // be marked as "iterable" in order to be found by other processes.
229 if (registered)
230 GetPersistentHistogramMemoryAllocator()->MakeIterable(ref);
231 // If it wasn't registered then a race condition must have caused
232 // two to be created. The allocator does not support releasing the
233 // acquired memory so just change the type to be empty.
234 else
235 GetPersistentHistogramMemoryAllocator()->SetType(ref, 0);
236 }
237
238 HistogramBase* AllocatePersistentHistogram(
239 PersistentMemoryAllocator* allocator,
240 HistogramType histogram_type,
241 const std::string& name,
242 int minimum,
243 int maximum,
244 const BucketRanges* bucket_ranges,
245 int32_t flags,
246 PersistentMemoryAllocator::Reference* ref_ptr) {
247 if (allocator) {
248 size_t bucket_count = bucket_ranges->bucket_count();
249 CHECK(bucket_count <= std::numeric_limits<int32_t>::max() /
250 sizeof(HistogramBase::AtomicCount));
251 size_t counts_memory = bucket_count * sizeof(HistogramBase::AtomicCount);
252 size_t ranges_memory = (bucket_count + 1) * sizeof(HistogramBase::Sample);
253 PersistentMemoryAllocator::Reference ranges_ref =
254 allocator->Allocate(ranges_memory, kTypeIdRangesArray);
255 PersistentMemoryAllocator::Reference counts_ref =
256 allocator->Allocate(counts_memory, kTypeIdCountsArray);
257 PersistentMemoryAllocator::Reference histogram_ref =
258 allocator->Allocate(offsetof(PersistentHistogramData, name) +
259 name.length() + 1, kTypeIdHistogram);
260 HistogramBase::Sample* ranges_data =
261 allocator->GetAsObject<HistogramBase::Sample>(ranges_ref,
262 kTypeIdRangesArray);
263 PersistentHistogramData* histogram_data =
264 allocator->GetAsObject<PersistentHistogramData>(histogram_ref,
265 kTypeIdHistogram);
266
267 // Only continue here if all allocations were successful. If they weren't
268 // there is no way to free the space but that's not really a problem since
269 // the allocations only fail because the space is full and so any future
270 // attempts will also fail.
271 if (counts_ref && ranges_data && histogram_data) {
272 strcpy(histogram_data->name, name.c_str());
273 for (size_t i = 0; i < bucket_ranges->size(); ++i)
274 ranges_data[i] = bucket_ranges->range(i);
275
276 histogram_data->histogram_type = histogram_type;
277 histogram_data->flags = flags;
278 histogram_data->minimum = minimum;
279 histogram_data->maximum = maximum;
280 histogram_data->bucket_count = bucket_count;
281 histogram_data->ranges_ref = ranges_ref;
282 histogram_data->ranges_checksum = bucket_ranges->checksum();
283 histogram_data->counts_ref = counts_ref;
284
285 // Create the histogram using resources in persistent memory. This ends up
286 // resolving the "ref" values stored in histogram_data instad of just
287 // using what is already known above but avoids duplicating the switch
288 // statement here and serves as a double-check that everything is
289 // correct before commiting the new histogram to persistent space.
290 HistogramBase* histogram =
291 CreatePersistentHistogram(allocator, histogram_data);
292 DCHECK(histogram);
293 if (ref_ptr != nullptr)
294 *ref_ptr = histogram_ref;
295 return histogram;
296 }
297
298 LOG(WARNING) << "Could not create histogram \"" << name
299 << "\" in persistent memory (full=" << allocator->IsFull()
300 << ", corrupt=" << allocator->IsCorrupt() << ")";
301 }
302
303 return nullptr;
304 }
305
306 void ImportPersistentHistograms() {
307 // Each call resumes from where it last left off so need persistant iterator.
308 // The lock protects against concurrent access to the iterator.
309 static PersistentMemoryAllocator::Iterator iter;
310 static base::Lock lock;
311
312 if (g_allocator) {
313 base::AutoLock auto_lock(lock);
314 if (iter.is_clear())
315 g_allocator->CreateIterator(&iter);
316
317 for (;;) {
318 HistogramBase* h = GetNextPersistentHistogram(g_allocator, &iter);
319 if (!h)
320 break;
321 StatisticsRecorder::RegisterOrDeleteDuplicate(h);
322 }
323 }
324 }
325
326 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698