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" |
11 #include "base/metrics/histogram_base.h" | 11 #include "base/metrics/histogram_base.h" |
12 #include "base/metrics/histogram_samples.h" | 12 #include "base/metrics/histogram_samples.h" |
13 #include "base/metrics/sparse_histogram.h" | |
13 #include "base/metrics/statistics_recorder.h" | 14 #include "base/metrics/statistics_recorder.h" |
14 #include "base/synchronization/lock.h" | 15 #include "base/synchronization/lock.h" |
15 | 16 |
16 namespace base { | 17 namespace base { |
17 | 18 |
18 namespace { | 19 namespace { |
19 | 20 |
20 // Enumerate possible creation results for reporting. | 21 // Enumerate possible creation results for reporting. |
21 enum CreateHistogramResultType { | 22 enum CreateHistogramResultType { |
22 // Everything was fine. | 23 // Everything was fine. |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
114 return nullptr; | 115 return nullptr; |
115 | 116 |
116 return ranges.release(); | 117 return ranges.release(); |
117 } | 118 } |
118 | 119 |
119 // Calculate the number of bytes required to store all of a histogram's | 120 // Calculate the number of bytes required to store all of a histogram's |
120 // "counts". This will return zero (0) if |bucket_count| is not valid. | 121 // "counts". This will return zero (0) if |bucket_count| is not valid. |
121 size_t CalculateRequiredCountsBytes(size_t bucket_count) { | 122 size_t CalculateRequiredCountsBytes(size_t bucket_count) { |
122 // 2 because each "sample count" also requires a backup "logged count" | 123 // 2 because each "sample count" also requires a backup "logged count" |
123 // used for calculating the delta during snapshot operations. | 124 // used for calculating the delta during snapshot operations. |
124 const unsigned kBytesPerBucket = 2 * sizeof(HistogramBase::AtomicCount); | 125 const size_t kBytesPerBucket = 2 * sizeof(HistogramBase::AtomicCount); |
125 | 126 |
126 // If the |bucket_count| is such that it would overflow the return type, | 127 // If the |bucket_count| is such that it would overflow the return type, |
127 // perhaps as the result of a malicious actor, then return zero to | 128 // perhaps as the result of a malicious actor, then return zero to |
128 // indicate the problem to the caller. | 129 // indicate the problem to the caller. |
129 if (bucket_count > std::numeric_limits<uint32_t>::max() / kBytesPerBucket) | 130 if (bucket_count > std::numeric_limits<size_t>::max() / kBytesPerBucket) |
130 return 0; | 131 return 0; |
131 | 132 |
132 return bucket_count * kBytesPerBucket; | 133 return bucket_count * kBytesPerBucket; |
133 } | 134 } |
134 | 135 |
135 } // namespace | 136 } // namespace |
136 | 137 |
137 const Feature kPersistentHistogramsFeature{ | 138 const Feature kPersistentHistogramsFeature{ |
138 "PersistentHistograms", FEATURE_DISABLED_BY_DEFAULT | 139 "PersistentHistograms", FEATURE_DISABLED_BY_DEFAULT |
139 }; | 140 }; |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
231 | 232 |
232 HistogramBase* CreatePersistentHistogram( | 233 HistogramBase* CreatePersistentHistogram( |
233 PersistentMemoryAllocator* allocator, | 234 PersistentMemoryAllocator* allocator, |
234 PersistentHistogramData* histogram_data_ptr) { | 235 PersistentHistogramData* histogram_data_ptr) { |
235 if (!histogram_data_ptr) { | 236 if (!histogram_data_ptr) { |
236 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA_POINTER); | 237 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA_POINTER); |
237 NOTREACHED(); | 238 NOTREACHED(); |
238 return nullptr; | 239 return nullptr; |
239 } | 240 } |
240 | 241 |
242 // Sparse histograms are quite different so handle them as a special case. | |
243 if (histogram_data_ptr->histogram_type == SPARSE_HISTOGRAM) { | |
244 HistogramBase* histogram = SparseHistogram::PersistentGet( | |
245 allocator, | |
246 histogram_data_ptr->name, | |
247 &histogram_data_ptr->samples_metadata, | |
248 &histogram_data_ptr->logged_metadata); | |
249 DCHECK(histogram); | |
250 return histogram; | |
251 } | |
252 | |
241 // Copy the histogram_data to local storage because anything in persistent | 253 // Copy the histogram_data to local storage because anything in persistent |
242 // memory cannot be trusted as it could be changed at any moment by a | 254 // memory cannot be trusted as it could be changed at any moment by a |
243 // malicious actor that shares access. The contents of histogram_data are | 255 // malicious actor that shares access. The contents of histogram_data are |
244 // validated below; the local copy is to ensure that the contents cannot | 256 // validated below; the local copy is to ensure that the contents cannot |
245 // be externally changed between validation and use. | 257 // be externally changed between validation and use. |
246 PersistentHistogramData histogram_data = *histogram_data_ptr; | 258 PersistentHistogramData histogram_data = *histogram_data_ptr; |
247 | 259 |
248 HistogramBase::Sample* ranges_data = | 260 HistogramBase::Sample* ranges_data = |
249 allocator->GetAsObject<HistogramBase::Sample>(histogram_data.ranges_ref, | 261 allocator->GetAsObject<HistogramBase::Sample>(histogram_data.ranges_ref, |
250 kTypeIdRangesArray); | 262 kTypeIdRangesArray); |
(...skipping 17 matching lines...) Expand all Loading... | |
268 NOTREACHED(); | 280 NOTREACHED(); |
269 return nullptr; | 281 return nullptr; |
270 } | 282 } |
271 ranges = StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); | 283 ranges = StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); |
272 | 284 |
273 HistogramBase::AtomicCount* counts_data = | 285 HistogramBase::AtomicCount* counts_data = |
274 allocator->GetAsObject<HistogramBase::AtomicCount>( | 286 allocator->GetAsObject<HistogramBase::AtomicCount>( |
275 histogram_data.counts_ref, kTypeIdCountsArray); | 287 histogram_data.counts_ref, kTypeIdCountsArray); |
276 size_t counts_bytes = | 288 size_t counts_bytes = |
277 CalculateRequiredCountsBytes(histogram_data.bucket_count); | 289 CalculateRequiredCountsBytes(histogram_data.bucket_count); |
278 if (!counts_data || !counts_bytes || | 290 if (!counts_data || counts_bytes == 0 || |
279 allocator->GetAllocSize(histogram_data.counts_ref) < counts_bytes) { | 291 allocator->GetAllocSize(histogram_data.counts_ref) < counts_bytes) { |
280 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY); | 292 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY); |
281 NOTREACHED(); | 293 NOTREACHED(); |
282 return nullptr; | 294 return nullptr; |
283 } | 295 } |
284 | 296 |
285 // After the main "counts" array is a second array using for storing what | 297 // After the main "counts" array is a second array using for storing what |
286 // was previously logged. This is used to calculate the "delta" during | 298 // was previously logged. This is used to calculate the "delta" during |
287 // snapshot operations. | 299 // snapshot operations. |
288 HistogramBase::AtomicCount* logged_data = | 300 HistogramBase::AtomicCount* logged_data = |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
412 | 424 |
413 // If the allocator is corrupt, don't waste time trying anything else. | 425 // If the allocator is corrupt, don't waste time trying anything else. |
414 // This also allows differentiating on the dashboard between allocations | 426 // This also allows differentiating on the dashboard between allocations |
415 // failed due to a corrupt allocator and the number of process instances | 427 // failed due to a corrupt allocator and the number of process instances |
416 // with one, the latter being idicated by "newly corrupt", below. | 428 // with one, the latter being idicated by "newly corrupt", below. |
417 if (allocator->IsCorrupt()) { | 429 if (allocator->IsCorrupt()) { |
418 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_CORRUPT); | 430 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_CORRUPT); |
419 return nullptr; | 431 return nullptr; |
420 } | 432 } |
421 | 433 |
422 // If CalculateRequiredCountsBytes() returns zero then the bucket_count | 434 // Create all the metadata necessary for a persistent histogram. Sparse |
423 // was not valid. | 435 // histograms are quite different so handle them as a special case. |
424 size_t bucket_count = bucket_ranges->bucket_count(); | 436 PersistentMemoryAllocator::Reference histogram_ref = 0; |
425 size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count); | 437 PersistentHistogramData* histogram_data = nullptr; |
426 if (!counts_bytes) { | 438 if (histogram_type == SPARSE_HISTOGRAM) { |
427 NOTREACHED(); | 439 histogram_ref = allocator->Allocate( |
428 return nullptr; | 440 offsetof(PersistentHistogramData, name) + name.length() + 1, |
441 kTypeIdHistogram); | |
442 histogram_data = allocator->GetAsObject<PersistentHistogramData>( | |
443 histogram_ref, kTypeIdHistogram); | |
444 if (histogram_data) { | |
445 memcpy(histogram_data->name, name.c_str(), name.size() + 1); | |
446 histogram_data->histogram_type = histogram_type; | |
447 histogram_data->flags = flags; | |
448 } | |
Alexei Svitkine (slow)
2016/03/03 17:43:38
Everything in this block seems to be a subset of w
Alexei Svitkine (slow)
2016/03/03 17:45:19
Sorry, the above meant to say "if there's an error
bcwhite
2016/03/07 15:30:39
I looked at that but it's not clean. There would
Alexei Svitkine (slow)
2016/03/07 21:32:31
Are you sure you can't arrange the code to make th
bcwhite
2016/03/07 22:22:07
I see what you're getting at. Done.
| |
449 } else { | |
450 size_t bucket_count = bucket_ranges->bucket_count(); | |
451 size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count); | |
452 if (counts_bytes == 0) { | |
453 // |bucket_count| was out-of-range. | |
454 NOTREACHED(); | |
455 return nullptr; | |
456 } | |
457 | |
458 size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample); | |
459 PersistentMemoryAllocator::Reference counts_ref = | |
460 allocator->Allocate(counts_bytes, kTypeIdCountsArray); | |
461 PersistentMemoryAllocator::Reference ranges_ref = | |
462 allocator->Allocate(ranges_bytes, kTypeIdRangesArray); | |
463 HistogramBase::Sample* ranges_data = | |
464 allocator->GetAsObject<HistogramBase::Sample>(ranges_ref, | |
465 kTypeIdRangesArray); | |
466 histogram_ref = allocator->Allocate( | |
467 offsetof(PersistentHistogramData, name) + name.length() + 1, | |
468 kTypeIdHistogram); | |
469 histogram_data = allocator->GetAsObject<PersistentHistogramData>( | |
470 histogram_ref, kTypeIdHistogram); | |
471 | |
472 // Only continue here if all allocations were successful. If they weren't | |
473 // there is no way to free the space but that's not really a problem since | |
474 // the allocations only fail because the space is full and so any future | |
475 // attempts will also fail. | |
476 if (counts_ref && ranges_data && histogram_data) { | |
477 memcpy(histogram_data->name, name.c_str(), name.size() + 1); | |
478 for (size_t i = 0; i < bucket_ranges->size(); ++i) | |
479 ranges_data[i] = bucket_ranges->range(i); | |
480 | |
481 histogram_data->histogram_type = histogram_type; | |
482 histogram_data->flags = flags; | |
483 histogram_data->minimum = minimum; | |
484 histogram_data->maximum = maximum; | |
485 // |bucket_count| must fit within 32-bits or the allocation of the counts | |
486 // array would have failed for being too large; the allocator supports | |
487 // less than 4GB total size. | |
488 histogram_data->bucket_count = static_cast<uint32_t>(bucket_count); | |
489 histogram_data->ranges_ref = ranges_ref; | |
490 histogram_data->ranges_checksum = bucket_ranges->checksum(); | |
491 histogram_data->counts_ref = counts_ref; | |
492 } else { | |
493 histogram_data = nullptr; // Clear this for proper handling below. | |
494 } | |
429 } | 495 } |
430 | 496 |
431 size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample); | 497 if (histogram_data) { |
432 PersistentMemoryAllocator::Reference ranges_ref = | |
433 allocator->Allocate(ranges_bytes, kTypeIdRangesArray); | |
434 PersistentMemoryAllocator::Reference counts_ref = | |
435 allocator->Allocate(counts_bytes, kTypeIdCountsArray); | |
436 PersistentMemoryAllocator::Reference histogram_ref = | |
437 allocator->Allocate(offsetof(PersistentHistogramData, name) + | |
438 name.length() + 1, kTypeIdHistogram); | |
439 HistogramBase::Sample* ranges_data = | |
440 allocator->GetAsObject<HistogramBase::Sample>(ranges_ref, | |
441 kTypeIdRangesArray); | |
442 PersistentHistogramData* histogram_data = | |
443 allocator->GetAsObject<PersistentHistogramData>(histogram_ref, | |
444 kTypeIdHistogram); | |
445 | |
446 // Only continue here if all allocations were successful. If they weren't | |
447 // there is no way to free the space but that's not really a problem since | |
448 // the allocations only fail because the space is full and so any future | |
449 // attempts will also fail. | |
450 if (counts_ref && ranges_data && histogram_data) { | |
451 strcpy(histogram_data->name, name.c_str()); | |
452 for (size_t i = 0; i < bucket_ranges->size(); ++i) | |
453 ranges_data[i] = bucket_ranges->range(i); | |
454 | |
455 histogram_data->histogram_type = histogram_type; | |
456 histogram_data->flags = flags; | |
457 histogram_data->minimum = minimum; | |
458 histogram_data->maximum = maximum; | |
459 histogram_data->bucket_count = static_cast<uint32_t>(bucket_count); | |
460 histogram_data->ranges_ref = ranges_ref; | |
461 histogram_data->ranges_checksum = bucket_ranges->checksum(); | |
462 histogram_data->counts_ref = counts_ref; | |
463 | |
464 // Create the histogram using resources in persistent memory. This ends up | 498 // Create the histogram using resources in persistent memory. This ends up |
465 // resolving the "ref" values stored in histogram_data instad of just | 499 // resolving the "ref" values stored in histogram_data instad of just |
466 // using what is already known above but avoids duplicating the switch | 500 // using what is already known above but avoids duplicating the switch |
467 // statement here and serves as a double-check that everything is | 501 // statement here and serves as a double-check that everything is |
468 // correct before commiting the new histogram to persistent space. | 502 // correct before commiting the new histogram to persistent space. |
469 HistogramBase* histogram = | 503 HistogramBase* histogram = |
470 CreatePersistentHistogram(allocator, histogram_data); | 504 CreatePersistentHistogram(allocator, histogram_data); |
471 DCHECK(histogram); | 505 DCHECK(histogram); |
472 if (ref_ptr != nullptr) | 506 if (ref_ptr != nullptr) |
473 *ref_ptr = histogram_ref; | 507 *ref_ptr = histogram_ref; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
507 while (true) { | 541 while (true) { |
508 HistogramBase* histogram = GetNextPersistentHistogram(g_allocator, &iter); | 542 HistogramBase* histogram = GetNextPersistentHistogram(g_allocator, &iter); |
509 if (!histogram) | 543 if (!histogram) |
510 break; | 544 break; |
511 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram); | 545 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram); |
512 } | 546 } |
513 } | 547 } |
514 } | 548 } |
515 | 549 |
516 } // namespace base | 550 } // namespace base |
OLD | NEW |