OLD | NEW |
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/metrics/histogram_persistence.h" | 5 #include "base/metrics/histogram_persistence.h" |
6 | 6 |
7 #include "base/lazy_instance.h" | 7 #include "base/lazy_instance.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
69 struct PersistentHistogramData { | 69 struct PersistentHistogramData { |
70 int histogram_type; | 70 int histogram_type; |
71 int flags; | 71 int flags; |
72 int minimum; | 72 int minimum; |
73 int maximum; | 73 int maximum; |
74 uint32_t bucket_count; | 74 uint32_t bucket_count; |
75 PersistentMemoryAllocator::Reference ranges_ref; | 75 PersistentMemoryAllocator::Reference ranges_ref; |
76 uint32_t ranges_checksum; | 76 uint32_t ranges_checksum; |
77 PersistentMemoryAllocator::Reference counts_ref; | 77 PersistentMemoryAllocator::Reference counts_ref; |
78 HistogramSamples::Metadata samples_metadata; | 78 HistogramSamples::Metadata samples_metadata; |
| 79 HistogramSamples::Metadata logged_metadata; |
79 | 80 |
80 // Space for the histogram name will be added during the actual allocation | 81 // Space for the histogram name will be added during the actual allocation |
81 // request. This must be the last field of the structure. A zero-size array | 82 // request. This must be the last field of the structure. A zero-size array |
82 // or a "flexible" array would be preferred but is not (yet) valid C++. | 83 // or a "flexible" array would be preferred but is not (yet) valid C++. |
83 char name[1]; | 84 char name[1]; |
84 }; | 85 }; |
85 | 86 |
86 // The object held here will obviously not be destructed at process exit | 87 // The object held here will obviously not be destructed at process exit |
87 // but that's okay since PersistentMemoryAllocator objects are explicitly | 88 // but that's okay since PersistentMemoryAllocator objects are explicitly |
88 // forbidden from doing anything essential at exit anyway due to the fact | 89 // forbidden from doing anything essential at exit anyway due to the fact |
(...skipping 15 matching lines...) Expand all Loading... |
104 ranges->set_range(i, ranges_data[i]); | 105 ranges->set_range(i, ranges_data[i]); |
105 } | 106 } |
106 | 107 |
107 ranges->ResetChecksum(); | 108 ranges->ResetChecksum(); |
108 if (ranges->checksum() != ranges_checksum) | 109 if (ranges->checksum() != ranges_checksum) |
109 return nullptr; | 110 return nullptr; |
110 | 111 |
111 return ranges.release(); | 112 return ranges.release(); |
112 } | 113 } |
113 | 114 |
| 115 // Calculate the number of bytes required to store all of a histogram's |
| 116 // "counts". This will return zero (0) if |bucket_count| is not valid. |
| 117 size_t CalculateRequiredCountsBytes(size_t bucket_count) { |
| 118 // 2 because each "sample count" also requires a backup "logged count" |
| 119 // used for calculating the delta during snapshot operations. |
| 120 const unsigned kBytesPerBucket = 2 * sizeof(HistogramBase::AtomicCount); |
| 121 |
| 122 // If the |bucket_count| is such that it would overflow the return type, |
| 123 // perhaps as the result of a malicious actor, then return zero to |
| 124 // indicate the problem to the caller. |
| 125 if (bucket_count > std::numeric_limits<uint32_t>::max() / kBytesPerBucket) |
| 126 return 0; |
| 127 |
| 128 return bucket_count * kBytesPerBucket; |
| 129 } |
| 130 |
114 } // namespace | 131 } // namespace |
115 | 132 |
116 const Feature kPersistentHistogramsFeature{ | 133 const Feature kPersistentHistogramsFeature{ |
117 "PersistentHistograms", FEATURE_DISABLED_BY_DEFAULT | 134 "PersistentHistograms", FEATURE_DISABLED_BY_DEFAULT |
118 }; | 135 }; |
119 | 136 |
120 // Get the histogram in which create results are stored. This is copied almost | 137 // Get the histogram in which create results are stored. This is copied almost |
121 // exactly from the STATIC_HISTOGRAM_POINTER_BLOCK macro but with added code | 138 // exactly from the STATIC_HISTOGRAM_POINTER_BLOCK macro but with added code |
122 // to prevent recursion (a likely occurance because the creation of a new | 139 // to prevent recursion (a likely occurance because the creation of a new |
123 // histogram can end up calling this.) | 140 // histogram can end up calling this.) |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
237 if (!ranges) { | 254 if (!ranges) { |
238 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY); | 255 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY); |
239 NOTREACHED(); | 256 NOTREACHED(); |
240 return nullptr; | 257 return nullptr; |
241 } | 258 } |
242 ranges = StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); | 259 ranges = StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); |
243 | 260 |
244 HistogramBase::AtomicCount* counts_data = | 261 HistogramBase::AtomicCount* counts_data = |
245 allocator->GetAsObject<HistogramBase::AtomicCount>( | 262 allocator->GetAsObject<HistogramBase::AtomicCount>( |
246 histogram_data.counts_ref, kTypeIdCountsArray); | 263 histogram_data.counts_ref, kTypeIdCountsArray); |
247 if (!counts_data || | 264 size_t counts_bytes = |
248 allocator->GetAllocSize(histogram_data.counts_ref) < | 265 CalculateRequiredCountsBytes(histogram_data.bucket_count); |
249 histogram_data.bucket_count * sizeof(HistogramBase::AtomicCount)) { | 266 if (!counts_data || !counts_bytes || |
| 267 allocator->GetAllocSize(histogram_data.counts_ref) < counts_bytes) { |
250 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY); | 268 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY); |
251 NOTREACHED(); | 269 NOTREACHED(); |
252 return nullptr; | 270 return nullptr; |
253 } | 271 } |
254 | 272 |
| 273 // After the main "counts" array is a second array using for storing what |
| 274 // was previously logged. This is used to calculate the "delta" during |
| 275 // snapshot operations. |
| 276 HistogramBase::AtomicCount* logged_data = |
| 277 counts_data + histogram_data.bucket_count; |
| 278 |
255 std::string name(histogram_data_ptr->name); | 279 std::string name(histogram_data_ptr->name); |
256 HistogramBase* histogram = nullptr; | 280 HistogramBase* histogram = nullptr; |
257 switch (histogram_data.histogram_type) { | 281 switch (histogram_data.histogram_type) { |
258 case HISTOGRAM: | 282 case HISTOGRAM: |
259 histogram = Histogram::PersistentGet( | 283 histogram = Histogram::PersistentGet( |
260 name, | 284 name, |
261 histogram_data.minimum, | 285 histogram_data.minimum, |
262 histogram_data.maximum, | 286 histogram_data.maximum, |
263 ranges, | 287 ranges, |
264 counts_data, | 288 counts_data, |
| 289 logged_data, |
265 histogram_data.bucket_count, | 290 histogram_data.bucket_count, |
266 &histogram_data_ptr->samples_metadata); | 291 &histogram_data_ptr->samples_metadata, |
| 292 &histogram_data_ptr->logged_metadata); |
267 DCHECK(histogram); | 293 DCHECK(histogram); |
268 break; | 294 break; |
269 case LINEAR_HISTOGRAM: | 295 case LINEAR_HISTOGRAM: |
270 histogram = LinearHistogram::PersistentGet( | 296 histogram = LinearHistogram::PersistentGet( |
271 name, | 297 name, |
272 histogram_data.minimum, | 298 histogram_data.minimum, |
273 histogram_data.maximum, | 299 histogram_data.maximum, |
274 ranges, | 300 ranges, |
275 counts_data, | 301 counts_data, |
| 302 logged_data, |
276 histogram_data.bucket_count, | 303 histogram_data.bucket_count, |
277 &histogram_data_ptr->samples_metadata); | 304 &histogram_data_ptr->samples_metadata, |
| 305 &histogram_data_ptr->logged_metadata); |
278 DCHECK(histogram); | 306 DCHECK(histogram); |
279 break; | 307 break; |
280 case BOOLEAN_HISTOGRAM: | 308 case BOOLEAN_HISTOGRAM: |
281 histogram = BooleanHistogram::PersistentGet( | 309 histogram = BooleanHistogram::PersistentGet( |
282 name, | 310 name, |
283 ranges, | 311 ranges, |
284 counts_data, | 312 counts_data, |
285 &histogram_data_ptr->samples_metadata); | 313 logged_data, |
| 314 &histogram_data_ptr->samples_metadata, |
| 315 &histogram_data_ptr->logged_metadata); |
286 DCHECK(histogram); | 316 DCHECK(histogram); |
287 break; | 317 break; |
288 case CUSTOM_HISTOGRAM: | 318 case CUSTOM_HISTOGRAM: |
289 histogram = CustomHistogram::PersistentGet( | 319 histogram = CustomHistogram::PersistentGet( |
290 name, | 320 name, |
291 ranges, | 321 ranges, |
292 counts_data, | 322 counts_data, |
| 323 logged_data, |
293 histogram_data.bucket_count, | 324 histogram_data.bucket_count, |
294 &histogram_data_ptr->samples_metadata); | 325 &histogram_data_ptr->samples_metadata, |
| 326 &histogram_data_ptr->logged_metadata); |
295 DCHECK(histogram); | 327 DCHECK(histogram); |
296 break; | 328 break; |
297 default: | 329 default: |
298 NOTREACHED(); | 330 NOTREACHED(); |
299 } | 331 } |
300 | 332 |
301 if (histogram) { | 333 if (histogram) { |
302 DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType()); | 334 DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType()); |
303 histogram->SetFlags(histogram_data.flags); | 335 histogram->SetFlags(histogram_data.flags); |
304 RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS); | 336 RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS); |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
368 | 400 |
369 // If the allocator is corrupt, don't waste time trying anything else. | 401 // If the allocator is corrupt, don't waste time trying anything else. |
370 // This also allows differentiating on the dashboard between allocations | 402 // This also allows differentiating on the dashboard between allocations |
371 // failed due to a corrupt allocator and the number of process instances | 403 // failed due to a corrupt allocator and the number of process instances |
372 // with one, the latter being idicated by "newly corrupt", below. | 404 // with one, the latter being idicated by "newly corrupt", below. |
373 if (allocator->IsCorrupt()) { | 405 if (allocator->IsCorrupt()) { |
374 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_CORRUPT); | 406 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_CORRUPT); |
375 return nullptr; | 407 return nullptr; |
376 } | 408 } |
377 | 409 |
| 410 // If CalculateRequiredCountsBytes() returns zero then the bucket_count |
| 411 // was not valid. |
378 size_t bucket_count = bucket_ranges->bucket_count(); | 412 size_t bucket_count = bucket_ranges->bucket_count(); |
379 // An overflow such as this, perhaps as the result of a milicious actor, | 413 size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count); |
380 // could lead to writing beyond the allocation boundary and into other | 414 if (!counts_bytes) { |
381 // memory. Just fail the allocation and let the caller deal with it. | |
382 if (bucket_count > std::numeric_limits<int32_t>::max() / | |
383 sizeof(HistogramBase::AtomicCount)) { | |
384 NOTREACHED(); | 415 NOTREACHED(); |
385 return nullptr; | 416 return nullptr; |
386 } | 417 } |
387 size_t counts_bytes = bucket_count * sizeof(HistogramBase::AtomicCount); | 418 |
388 size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample); | 419 size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample); |
389 PersistentMemoryAllocator::Reference ranges_ref = | 420 PersistentMemoryAllocator::Reference ranges_ref = |
390 allocator->Allocate(ranges_bytes, kTypeIdRangesArray); | 421 allocator->Allocate(ranges_bytes, kTypeIdRangesArray); |
391 PersistentMemoryAllocator::Reference counts_ref = | 422 PersistentMemoryAllocator::Reference counts_ref = |
392 allocator->Allocate(counts_bytes, kTypeIdCountsArray); | 423 allocator->Allocate(counts_bytes, kTypeIdCountsArray); |
393 PersistentMemoryAllocator::Reference histogram_ref = | 424 PersistentMemoryAllocator::Reference histogram_ref = |
394 allocator->Allocate(offsetof(PersistentHistogramData, name) + | 425 allocator->Allocate(offsetof(PersistentHistogramData, name) + |
395 name.length() + 1, kTypeIdHistogram); | 426 name.length() + 1, kTypeIdHistogram); |
396 HistogramBase::Sample* ranges_data = | 427 HistogramBase::Sample* ranges_data = |
397 allocator->GetAsObject<HistogramBase::Sample>(ranges_ref, | 428 allocator->GetAsObject<HistogramBase::Sample>(ranges_ref, |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
464 for (;;) { | 495 for (;;) { |
465 HistogramBase* histogram = GetNextPersistentHistogram(g_allocator, &iter); | 496 HistogramBase* histogram = GetNextPersistentHistogram(g_allocator, &iter); |
466 if (!histogram) | 497 if (!histogram) |
467 break; | 498 break; |
468 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram); | 499 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram); |
469 } | 500 } |
470 } | 501 } |
471 } | 502 } |
472 | 503 |
473 } // namespace base | 504 } // namespace base |
OLD | NEW |