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

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

Issue 1734033003: Add support for persistent sparse histograms. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: addressed review comments by Greg Created 4 years, 9 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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698