Chromium Code Reviews| 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 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 66 struct PersistentHistogramData { | 66 struct PersistentHistogramData { |
| 67 int histogram_type; | 67 int histogram_type; |
| 68 int flags; | 68 int flags; |
| 69 int minimum; | 69 int minimum; |
| 70 int maximum; | 70 int maximum; |
| 71 uint32_t bucket_count; | 71 uint32_t bucket_count; |
| 72 PersistentMemoryAllocator::Reference ranges_ref; | 72 PersistentMemoryAllocator::Reference ranges_ref; |
| 73 uint32_t ranges_checksum; | 73 uint32_t ranges_checksum; |
| 74 PersistentMemoryAllocator::Reference counts_ref; | 74 PersistentMemoryAllocator::Reference counts_ref; |
| 75 HistogramSamples::Metadata samples_metadata; | 75 HistogramSamples::Metadata samples_metadata; |
| 76 HistogramSamples::Metadata logged_metadata; | |
| 76 | 77 |
| 77 // Space for the histogram name will be added during the actual allocation | 78 // Space for the histogram name will be added during the actual allocation |
| 78 // request. This must be the last field of the structure. A zero-size array | 79 // request. This must be the last field of the structure. A zero-size array |
| 79 // or a "flexible" array would be preferred but is not (yet) valid C++. | 80 // or a "flexible" array would be preferred but is not (yet) valid C++. |
| 80 char name[1]; | 81 char name[1]; |
| 81 }; | 82 }; |
| 82 | 83 |
| 83 // The object held here will obviously not be destructed at process exit | 84 // The object held here will obviously not be destructed at process exit |
| 84 // but that's okay since PersistentMemoryAllocator objects are explicitly | 85 // but that's okay since PersistentMemoryAllocator objects are explicitly |
| 85 // forbidden from doing anything essential at exit anyway due to the fact | 86 // forbidden from doing anything essential at exit anyway due to the fact |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 101 ranges->set_range(i, ranges_data[i]); | 102 ranges->set_range(i, ranges_data[i]); |
| 102 } | 103 } |
| 103 | 104 |
| 104 ranges->ResetChecksum(); | 105 ranges->ResetChecksum(); |
| 105 if (ranges->checksum() != ranges_checksum) | 106 if (ranges->checksum() != ranges_checksum) |
| 106 return nullptr; | 107 return nullptr; |
| 107 | 108 |
| 108 return ranges.release(); | 109 return ranges.release(); |
| 109 } | 110 } |
| 110 | 111 |
| 112 // Calculate the number of bytes required to store all of a histogram's | |
| 113 // "counts". | |
|
Alexei Svitkine (slow)
2016/02/17 16:21:12
Add a sentence about why this returns 0. Maybe jus
bcwhite
2016/02/17 17:58:20
Done.
| |
| 114 uint32_t RequiredCountsBytes(size_t bucket_count) { | |
|
Alexei Svitkine (slow)
2016/02/17 16:21:12
Nit: Get* or Calculate*
bcwhite
2016/02/17 17:58:20
Done.
| |
| 115 // 2 because each "sample count" also requires a backup "logged count" | |
| 116 // used for calculating the delta during snapshot operations. | |
| 117 const unsigned kBytesPerBucket = 2 * sizeof(HistogramBase::AtomicCount); | |
| 118 | |
| 119 // If the |bucket_count| is such that it would overflow the return type, | |
| 120 // perhaps as the result of a milicious actor, then return zero to | |
|
Alexei Svitkine (slow)
2016/02/17 16:21:12
Nit: malicious
bcwhite
2016/02/17 17:58:20
Done.
| |
| 121 // indicate the problem to the caller. | |
| 122 if (bucket_count > std::numeric_limits<uint32_t>::max() / kBytesPerBucket) | |
| 123 return 0; | |
| 124 | |
| 125 return static_cast<uint32_t>(bucket_count * kBytesPerBucket); | |
| 126 } | |
| 127 | |
| 111 } // namespace | 128 } // namespace |
| 112 | 129 |
| 113 const Feature kPersistentHistogramsFeature{ | 130 const Feature kPersistentHistogramsFeature{ |
| 114 "PersistentHistograms", FEATURE_DISABLED_BY_DEFAULT | 131 "PersistentHistograms", FEATURE_DISABLED_BY_DEFAULT |
| 115 }; | 132 }; |
| 116 | 133 |
| 117 // Get the histogram in which create results are stored. This is copied almost | 134 // Get the histogram in which create results are stored. This is copied almost |
| 118 // exactly from the STATIC_HISTOGRAM_POINTER_BLOCK macro but with added code | 135 // exactly from the STATIC_HISTOGRAM_POINTER_BLOCK macro but with added code |
| 119 // to prevent recursion (a likely occurance because the creation of a new | 136 // to prevent recursion (a likely occurance because the creation of a new |
| 120 // histogram can end up calling this.) | 137 // histogram can end up calling this.) |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 213 if (!ranges) { | 230 if (!ranges) { |
| 214 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY); | 231 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY); |
| 215 NOTREACHED(); | 232 NOTREACHED(); |
| 216 return nullptr; | 233 return nullptr; |
| 217 } | 234 } |
| 218 ranges = StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); | 235 ranges = StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); |
| 219 | 236 |
| 220 HistogramBase::AtomicCount* counts_data = | 237 HistogramBase::AtomicCount* counts_data = |
| 221 allocator->GetAsObject<HistogramBase::AtomicCount>( | 238 allocator->GetAsObject<HistogramBase::AtomicCount>( |
| 222 histogram_data.counts_ref, kTypeIdCountsArray); | 239 histogram_data.counts_ref, kTypeIdCountsArray); |
| 223 if (!counts_data || | 240 size_t counts_bytes = RequiredCountsBytes(histogram_data.bucket_count); |
| 224 allocator->GetAllocSize(histogram_data.counts_ref) < | 241 if (!counts_data || !counts_bytes || |
| 225 histogram_data.bucket_count * sizeof(HistogramBase::AtomicCount)) { | 242 allocator->GetAllocSize(histogram_data.counts_ref) < counts_bytes) { |
| 226 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY); | 243 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY); |
| 227 NOTREACHED(); | 244 NOTREACHED(); |
| 228 return nullptr; | 245 return nullptr; |
| 229 } | 246 } |
| 230 | 247 |
| 248 // After the main "counts" array is a second array using for storing what | |
| 249 // was previously logged. This is used to calculate the "delta" during | |
| 250 // snapshot operations. | |
| 251 HistogramBase::AtomicCount* logged_data = | |
| 252 counts_data + histogram_data.bucket_count; | |
| 253 | |
| 231 std::string name(histogram_data_ptr->name); | 254 std::string name(histogram_data_ptr->name); |
| 232 HistogramBase* histogram = nullptr; | 255 HistogramBase* histogram = nullptr; |
| 233 switch (histogram_data.histogram_type) { | 256 switch (histogram_data.histogram_type) { |
| 234 case HISTOGRAM: | 257 case HISTOGRAM: |
| 235 histogram = Histogram::PersistentGet( | 258 histogram = Histogram::PersistentGet( |
| 236 name, | 259 name, |
| 237 histogram_data.minimum, | 260 histogram_data.minimum, |
| 238 histogram_data.maximum, | 261 histogram_data.maximum, |
| 239 ranges, | 262 ranges, |
| 240 counts_data, | 263 counts_data, |
| 264 logged_data, | |
| 241 histogram_data.bucket_count, | 265 histogram_data.bucket_count, |
| 242 &histogram_data_ptr->samples_metadata); | 266 &histogram_data_ptr->samples_metadata, |
| 267 &histogram_data_ptr->logged_metadata); | |
| 243 DCHECK(histogram); | 268 DCHECK(histogram); |
| 244 break; | 269 break; |
| 245 case LINEAR_HISTOGRAM: | 270 case LINEAR_HISTOGRAM: |
| 246 histogram = LinearHistogram::PersistentGet( | 271 histogram = LinearHistogram::PersistentGet( |
| 247 name, | 272 name, |
| 248 histogram_data.minimum, | 273 histogram_data.minimum, |
| 249 histogram_data.maximum, | 274 histogram_data.maximum, |
| 250 ranges, | 275 ranges, |
| 251 counts_data, | 276 counts_data, |
| 277 logged_data, | |
| 252 histogram_data.bucket_count, | 278 histogram_data.bucket_count, |
| 253 &histogram_data_ptr->samples_metadata); | 279 &histogram_data_ptr->samples_metadata, |
| 280 &histogram_data_ptr->logged_metadata); | |
| 254 DCHECK(histogram); | 281 DCHECK(histogram); |
| 255 break; | 282 break; |
| 256 case BOOLEAN_HISTOGRAM: | 283 case BOOLEAN_HISTOGRAM: |
| 257 histogram = BooleanHistogram::PersistentGet( | 284 histogram = BooleanHistogram::PersistentGet( |
| 258 name, | 285 name, |
| 259 ranges, | 286 ranges, |
| 260 counts_data, | 287 counts_data, |
| 261 &histogram_data_ptr->samples_metadata); | 288 logged_data, |
| 289 &histogram_data_ptr->samples_metadata, | |
| 290 &histogram_data_ptr->logged_metadata); | |
| 262 DCHECK(histogram); | 291 DCHECK(histogram); |
| 263 break; | 292 break; |
| 264 case CUSTOM_HISTOGRAM: | 293 case CUSTOM_HISTOGRAM: |
| 265 histogram = CustomHistogram::PersistentGet( | 294 histogram = CustomHistogram::PersistentGet( |
| 266 name, | 295 name, |
| 267 ranges, | 296 ranges, |
| 268 counts_data, | 297 counts_data, |
| 298 logged_data, | |
| 269 histogram_data.bucket_count, | 299 histogram_data.bucket_count, |
| 270 &histogram_data_ptr->samples_metadata); | 300 &histogram_data_ptr->samples_metadata, |
| 301 &histogram_data_ptr->logged_metadata); | |
| 271 DCHECK(histogram); | 302 DCHECK(histogram); |
| 272 break; | 303 break; |
| 273 default: | 304 default: |
| 274 NOTREACHED(); | 305 NOTREACHED(); |
| 275 } | 306 } |
| 276 | 307 |
| 277 if (histogram) { | 308 if (histogram) { |
| 278 DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType()); | 309 DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType()); |
| 279 histogram->SetFlags(histogram_data.flags); | 310 histogram->SetFlags(histogram_data.flags); |
| 280 RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS); | 311 RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS); |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 335 HistogramType histogram_type, | 366 HistogramType histogram_type, |
| 336 const std::string& name, | 367 const std::string& name, |
| 337 int minimum, | 368 int minimum, |
| 338 int maximum, | 369 int maximum, |
| 339 const BucketRanges* bucket_ranges, | 370 const BucketRanges* bucket_ranges, |
| 340 int32_t flags, | 371 int32_t flags, |
| 341 PersistentMemoryAllocator::Reference* ref_ptr) { | 372 PersistentMemoryAllocator::Reference* ref_ptr) { |
| 342 if (!allocator) | 373 if (!allocator) |
| 343 return nullptr; | 374 return nullptr; |
| 344 | 375 |
| 376 // If RequiredCountsBytes() returns zero then the bucket_count was not valid. | |
| 345 size_t bucket_count = bucket_ranges->bucket_count(); | 377 size_t bucket_count = bucket_ranges->bucket_count(); |
| 346 // An overflow such as this, perhaps as the result of a milicious actor, | 378 size_t counts_bytes = RequiredCountsBytes(bucket_count); |
|
Alexei Svitkine (slow)
2016/02/17 16:21:12
Make the function return size_t instead of uint32_
bcwhite
2016/02/17 17:58:20
Done.
| |
| 347 // could lead to writing beyond the allocation boundary and into other | 379 if (!counts_bytes) { |
| 348 // memory. Just fail the allocation and let the caller deal with it. | |
| 349 if (bucket_count > std::numeric_limits<int32_t>::max() / | |
| 350 sizeof(HistogramBase::AtomicCount)) { | |
| 351 NOTREACHED(); | 380 NOTREACHED(); |
| 352 return nullptr; | 381 return nullptr; |
| 353 } | 382 } |
| 354 size_t counts_bytes = bucket_count * sizeof(HistogramBase::AtomicCount); | 383 |
| 355 size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample); | 384 size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample); |
| 356 PersistentMemoryAllocator::Reference ranges_ref = | 385 PersistentMemoryAllocator::Reference ranges_ref = |
| 357 allocator->Allocate(ranges_bytes, kTypeIdRangesArray); | 386 allocator->Allocate(ranges_bytes, kTypeIdRangesArray); |
| 358 PersistentMemoryAllocator::Reference counts_ref = | 387 PersistentMemoryAllocator::Reference counts_ref = |
| 359 allocator->Allocate(counts_bytes, kTypeIdCountsArray); | 388 allocator->Allocate(counts_bytes, kTypeIdCountsArray); |
| 360 PersistentMemoryAllocator::Reference histogram_ref = | 389 PersistentMemoryAllocator::Reference histogram_ref = |
| 361 allocator->Allocate(offsetof(PersistentHistogramData, name) + | 390 allocator->Allocate(offsetof(PersistentHistogramData, name) + |
| 362 name.length() + 1, kTypeIdHistogram); | 391 name.length() + 1, kTypeIdHistogram); |
| 363 HistogramBase::Sample* ranges_data = | 392 HistogramBase::Sample* ranges_data = |
| 364 allocator->GetAsObject<HistogramBase::Sample>(ranges_ref, | 393 allocator->GetAsObject<HistogramBase::Sample>(ranges_ref, |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 430 for (;;) { | 459 for (;;) { |
| 431 HistogramBase* histogram = GetNextPersistentHistogram(g_allocator, &iter); | 460 HistogramBase* histogram = GetNextPersistentHistogram(g_allocator, &iter); |
| 432 if (!histogram) | 461 if (!histogram) |
| 433 break; | 462 break; |
| 434 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram); | 463 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram); |
| 435 } | 464 } |
| 436 } | 465 } |
| 437 } | 466 } |
| 438 | 467 |
| 439 } // namespace base | 468 } // namespace base |
| OLD | NEW |