| 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 |