| 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 histogram->SetFlags(histogram_data_ptr->flags); |
| 251 RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS); |
| 252 return histogram; |
| 253 } |
| 254 |
| 241 // Copy the histogram_data to local storage because anything in persistent | 255 // 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 | 256 // 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 | 257 // malicious actor that shares access. The contents of histogram_data are |
| 244 // validated below; the local copy is to ensure that the contents cannot | 258 // validated below; the local copy is to ensure that the contents cannot |
| 245 // be externally changed between validation and use. | 259 // be externally changed between validation and use. |
| 246 PersistentHistogramData histogram_data = *histogram_data_ptr; | 260 PersistentHistogramData histogram_data = *histogram_data_ptr; |
| 247 | 261 |
| 248 HistogramBase::Sample* ranges_data = | 262 HistogramBase::Sample* ranges_data = |
| 249 allocator->GetAsObject<HistogramBase::Sample>(histogram_data.ranges_ref, | 263 allocator->GetAsObject<HistogramBase::Sample>(histogram_data.ranges_ref, |
| 250 kTypeIdRangesArray); | 264 kTypeIdRangesArray); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 268 NOTREACHED(); | 282 NOTREACHED(); |
| 269 return nullptr; | 283 return nullptr; |
| 270 } | 284 } |
| 271 ranges = StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); | 285 ranges = StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); |
| 272 | 286 |
| 273 HistogramBase::AtomicCount* counts_data = | 287 HistogramBase::AtomicCount* counts_data = |
| 274 allocator->GetAsObject<HistogramBase::AtomicCount>( | 288 allocator->GetAsObject<HistogramBase::AtomicCount>( |
| 275 histogram_data.counts_ref, kTypeIdCountsArray); | 289 histogram_data.counts_ref, kTypeIdCountsArray); |
| 276 size_t counts_bytes = | 290 size_t counts_bytes = |
| 277 CalculateRequiredCountsBytes(histogram_data.bucket_count); | 291 CalculateRequiredCountsBytes(histogram_data.bucket_count); |
| 278 if (!counts_data || !counts_bytes || | 292 if (!counts_data || counts_bytes == 0 || |
| 279 allocator->GetAllocSize(histogram_data.counts_ref) < counts_bytes) { | 293 allocator->GetAllocSize(histogram_data.counts_ref) < counts_bytes) { |
| 280 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY); | 294 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY); |
| 281 NOTREACHED(); | 295 NOTREACHED(); |
| 282 return nullptr; | 296 return nullptr; |
| 283 } | 297 } |
| 284 | 298 |
| 285 // After the main "counts" array is a second array using for storing what | 299 // 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 | 300 // was previously logged. This is used to calculate the "delta" during |
| 287 // snapshot operations. | 301 // snapshot operations. |
| 288 HistogramBase::AtomicCount* logged_data = | 302 HistogramBase::AtomicCount* logged_data = |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 412 | 426 |
| 413 // If the allocator is corrupt, don't waste time trying anything else. | 427 // If the allocator is corrupt, don't waste time trying anything else. |
| 414 // This also allows differentiating on the dashboard between allocations | 428 // This also allows differentiating on the dashboard between allocations |
| 415 // failed due to a corrupt allocator and the number of process instances | 429 // failed due to a corrupt allocator and the number of process instances |
| 416 // with one, the latter being idicated by "newly corrupt", below. | 430 // with one, the latter being idicated by "newly corrupt", below. |
| 417 if (allocator->IsCorrupt()) { | 431 if (allocator->IsCorrupt()) { |
| 418 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_CORRUPT); | 432 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_CORRUPT); |
| 419 return nullptr; | 433 return nullptr; |
| 420 } | 434 } |
| 421 | 435 |
| 422 // If CalculateRequiredCountsBytes() returns zero then the bucket_count | 436 // Create the metadata necessary for a persistent sparse histogram. This |
| 423 // was not valid. | 437 // is done first because it is a small subset of what is required for |
| 424 size_t bucket_count = bucket_ranges->bucket_count(); | 438 // other histograms. |
| 425 size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count); | 439 PersistentMemoryAllocator::Reference histogram_ref = 0; |
| 426 if (!counts_bytes) { | 440 PersistentHistogramData* histogram_data = nullptr; |
| 427 NOTREACHED(); | 441 histogram_ref = allocator->Allocate( |
| 428 return nullptr; | 442 offsetof(PersistentHistogramData, name) + name.length() + 1, |
| 443 kTypeIdHistogram); |
| 444 histogram_data = allocator->GetAsObject<PersistentHistogramData>( |
| 445 histogram_ref, kTypeIdHistogram); |
| 446 if (histogram_data) { |
| 447 memcpy(histogram_data->name, name.c_str(), name.size() + 1); |
| 448 histogram_data->histogram_type = histogram_type; |
| 449 histogram_data->flags = flags | HistogramBase::kIsPersistent; |
| 429 } | 450 } |
| 430 | 451 |
| 431 size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample); | 452 // Create the remaining metadata necessary for regular histograms. |
| 432 PersistentMemoryAllocator::Reference ranges_ref = | 453 if (histogram_type != SPARSE_HISTOGRAM) { |
| 433 allocator->Allocate(ranges_bytes, kTypeIdRangesArray); | 454 size_t bucket_count = bucket_ranges->bucket_count(); |
| 434 PersistentMemoryAllocator::Reference counts_ref = | 455 size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count); |
| 435 allocator->Allocate(counts_bytes, kTypeIdCountsArray); | 456 if (counts_bytes == 0) { |
| 436 PersistentMemoryAllocator::Reference histogram_ref = | 457 // |bucket_count| was out-of-range. |
| 437 allocator->Allocate(offsetof(PersistentHistogramData, name) + | 458 NOTREACHED(); |
| 438 name.length() + 1, kTypeIdHistogram); | 459 return nullptr; |
| 439 HistogramBase::Sample* ranges_data = | 460 } |
| 440 allocator->GetAsObject<HistogramBase::Sample>(ranges_ref, | |
| 441 kTypeIdRangesArray); | |
| 442 PersistentHistogramData* histogram_data = | |
| 443 allocator->GetAsObject<PersistentHistogramData>(histogram_ref, | |
| 444 kTypeIdHistogram); | |
| 445 | 461 |
| 446 // Only continue here if all allocations were successful. If they weren't | 462 size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample); |
| 447 // there is no way to free the space but that's not really a problem since | 463 PersistentMemoryAllocator::Reference counts_ref = |
| 448 // the allocations only fail because the space is full and so any future | 464 allocator->Allocate(counts_bytes, kTypeIdCountsArray); |
| 449 // attempts will also fail. | 465 PersistentMemoryAllocator::Reference ranges_ref = |
| 450 if (counts_ref && ranges_data && histogram_data) { | 466 allocator->Allocate(ranges_bytes, kTypeIdRangesArray); |
| 451 strcpy(histogram_data->name, name.c_str()); | 467 HistogramBase::Sample* ranges_data = |
| 452 for (size_t i = 0; i < bucket_ranges->size(); ++i) | 468 allocator->GetAsObject<HistogramBase::Sample>(ranges_ref, |
| 453 ranges_data[i] = bucket_ranges->range(i); | 469 kTypeIdRangesArray); |
| 454 | 470 |
| 455 histogram_data->histogram_type = histogram_type; | 471 // Only continue here if all allocations were successful. If they weren't |
| 456 histogram_data->flags = flags; | 472 // there is no way to free the space but that's not really a problem since |
| 457 histogram_data->minimum = minimum; | 473 // the allocations only fail because the space is full and so any future |
| 458 histogram_data->maximum = maximum; | 474 // attempts will also fail. |
| 459 histogram_data->bucket_count = static_cast<uint32_t>(bucket_count); | 475 if (counts_ref && ranges_data && histogram_data) { |
| 460 histogram_data->ranges_ref = ranges_ref; | 476 for (size_t i = 0; i < bucket_ranges->size(); ++i) |
| 461 histogram_data->ranges_checksum = bucket_ranges->checksum(); | 477 ranges_data[i] = bucket_ranges->range(i); |
| 462 histogram_data->counts_ref = counts_ref; | |
| 463 | 478 |
| 479 histogram_data->minimum = minimum; |
| 480 histogram_data->maximum = maximum; |
| 481 // |bucket_count| must fit within 32-bits or the allocation of the counts |
| 482 // array would have failed for being too large; the allocator supports |
| 483 // less than 4GB total size. |
| 484 histogram_data->bucket_count = static_cast<uint32_t>(bucket_count); |
| 485 histogram_data->ranges_ref = ranges_ref; |
| 486 histogram_data->ranges_checksum = bucket_ranges->checksum(); |
| 487 histogram_data->counts_ref = counts_ref; |
| 488 } else { |
| 489 histogram_data = nullptr; // Clear this for proper handling below. |
| 490 } |
| 491 } |
| 492 |
| 493 if (histogram_data) { |
| 464 // Create the histogram using resources in persistent memory. This ends up | 494 // Create the histogram using resources in persistent memory. This ends up |
| 465 // resolving the "ref" values stored in histogram_data instad of just | 495 // resolving the "ref" values stored in histogram_data instad of just |
| 466 // using what is already known above but avoids duplicating the switch | 496 // using what is already known above but avoids duplicating the switch |
| 467 // statement here and serves as a double-check that everything is | 497 // statement here and serves as a double-check that everything is |
| 468 // correct before commiting the new histogram to persistent space. | 498 // correct before commiting the new histogram to persistent space. |
| 469 HistogramBase* histogram = | 499 HistogramBase* histogram = |
| 470 CreatePersistentHistogram(allocator, histogram_data); | 500 CreatePersistentHistogram(allocator, histogram_data); |
| 471 DCHECK(histogram); | 501 DCHECK(histogram); |
| 472 if (ref_ptr != nullptr) | 502 if (ref_ptr != nullptr) |
| 473 *ref_ptr = histogram_ref; | 503 *ref_ptr = histogram_ref; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 507 while (true) { | 537 while (true) { |
| 508 HistogramBase* histogram = GetNextPersistentHistogram(g_allocator, &iter); | 538 HistogramBase* histogram = GetNextPersistentHistogram(g_allocator, &iter); |
| 509 if (!histogram) | 539 if (!histogram) |
| 510 break; | 540 break; |
| 511 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram); | 541 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram); |
| 512 } | 542 } |
| 513 } | 543 } |
| 514 } | 544 } |
| 515 | 545 |
| 516 } // namespace base | 546 } // namespace base |
| OLD | NEW |