| 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 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 221 // (temporary) memory allocator via SetPersistentMemoryAllocator() so | 221 // (temporary) memory allocator via SetPersistentMemoryAllocator() so |
| 222 // that the histogram is instead allocated from the process heap. | 222 // that the histogram is instead allocated from the process heap. |
| 223 DCHECK_NE(kResultHistogram, histogram_data->name); | 223 DCHECK_NE(kResultHistogram, histogram_data->name); |
| 224 } | 224 } |
| 225 } | 225 } |
| 226 | 226 |
| 227 g_allocator = nullptr; | 227 g_allocator = nullptr; |
| 228 return allocator; | 228 return allocator; |
| 229 }; | 229 }; |
| 230 | 230 |
| 231 HistogramBase* CreatePersistentHistogram( | 231 scoped_ptr<HistogramBase> CreatePersistentHistogram( |
| 232 PersistentMemoryAllocator* allocator, | 232 PersistentMemoryAllocator* allocator, |
| 233 PersistentHistogramData* histogram_data_ptr) { | 233 PersistentHistogramData* histogram_data_ptr) { |
| 234 if (!histogram_data_ptr) { | 234 if (!histogram_data_ptr) { |
| 235 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA_POINTER); | 235 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA_POINTER); |
| 236 NOTREACHED(); | 236 NOTREACHED(); |
| 237 return nullptr; | 237 return nullptr; |
| 238 } | 238 } |
| 239 | 239 |
| 240 // Copy the histogram_data to local storage because anything in persistent | 240 // Copy the histogram_data to local storage because anything in persistent |
| 241 // memory cannot be trusted as it could be changed at any moment by a | 241 // memory cannot be trusted as it could be changed at any moment by a |
| 242 // malicious actor that shares access. The contents of histogram_data are | 242 // malicious actor that shares access. The contents of histogram_data are |
| 243 // validated below; the local copy is to ensure that the contents cannot | 243 // validated below; the local copy is to ensure that the contents cannot |
| 244 // be externally changed between validation and use. | 244 // be externally changed between validation and use. |
| 245 PersistentHistogramData histogram_data = *histogram_data_ptr; | 245 PersistentHistogramData histogram_data = *histogram_data_ptr; |
| 246 | 246 |
| 247 HistogramBase::Sample* ranges_data = | 247 HistogramBase::Sample* ranges_data = |
| 248 allocator->GetAsObject<HistogramBase::Sample>(histogram_data.ranges_ref, | 248 allocator->GetAsObject<HistogramBase::Sample>(histogram_data.ranges_ref, |
| 249 kTypeIdRangesArray); | 249 kTypeIdRangesArray); |
| 250 if (!ranges_data || histogram_data.bucket_count < 2 || | 250 if (!ranges_data || histogram_data.bucket_count < 2 || |
| 251 histogram_data.bucket_count + 1 > | 251 histogram_data.bucket_count + 1 > |
| 252 std::numeric_limits<uint32_t>::max() / | 252 std::numeric_limits<uint32_t>::max() / |
| 253 sizeof(HistogramBase::Sample) || | 253 sizeof(HistogramBase::Sample) || |
| 254 allocator->GetAllocSize(histogram_data.ranges_ref) < | 254 allocator->GetAllocSize(histogram_data.ranges_ref) < |
| 255 (histogram_data.bucket_count + 1) * sizeof(HistogramBase::Sample)) { | 255 (histogram_data.bucket_count + 1) * sizeof(HistogramBase::Sample)) { |
| 256 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY); | 256 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY); |
| 257 NOTREACHED(); | 257 NOTREACHED(); |
| 258 return nullptr; | 258 return nullptr; |
| 259 } | 259 } |
| 260 // To avoid racy destruction at shutdown, the following will be leaked. | 260 |
| 261 const BucketRanges* ranges = CreateRangesFromData( | 261 scoped_ptr<const BucketRanges> created_ranges(CreateRangesFromData( |
| 262 ranges_data, | 262 ranges_data, |
| 263 histogram_data.ranges_checksum, | 263 histogram_data.ranges_checksum, |
| 264 histogram_data.bucket_count + 1); | 264 histogram_data.bucket_count + 1)); |
| 265 if (!ranges) { | 265 if (!created_ranges) { |
| 266 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY); | 266 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY); |
| 267 NOTREACHED(); | 267 NOTREACHED(); |
| 268 return nullptr; | 268 return nullptr; |
| 269 } | 269 } |
| 270 ranges = StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); | 270 const BucketRanges* ranges = |
| 271 StatisticsRecorder::RegisterOrDeleteDuplicateRanges( |
| 272 std::move(created_ranges)); |
| 271 | 273 |
| 272 HistogramBase::AtomicCount* counts_data = | 274 HistogramBase::AtomicCount* counts_data = |
| 273 allocator->GetAsObject<HistogramBase::AtomicCount>( | 275 allocator->GetAsObject<HistogramBase::AtomicCount>( |
| 274 histogram_data.counts_ref, kTypeIdCountsArray); | 276 histogram_data.counts_ref, kTypeIdCountsArray); |
| 275 size_t counts_bytes = | 277 size_t counts_bytes = |
| 276 CalculateRequiredCountsBytes(histogram_data.bucket_count); | 278 CalculateRequiredCountsBytes(histogram_data.bucket_count); |
| 277 if (!counts_data || !counts_bytes || | 279 if (!counts_data || !counts_bytes || |
| 278 allocator->GetAllocSize(histogram_data.counts_ref) < counts_bytes) { | 280 allocator->GetAllocSize(histogram_data.counts_ref) < counts_bytes) { |
| 279 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY); | 281 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY); |
| 280 NOTREACHED(); | 282 NOTREACHED(); |
| 281 return nullptr; | 283 return nullptr; |
| 282 } | 284 } |
| 283 | 285 |
| 284 // After the main "counts" array is a second array using for storing what | 286 // After the main "counts" array is a second array using for storing what |
| 285 // was previously logged. This is used to calculate the "delta" during | 287 // was previously logged. This is used to calculate the "delta" during |
| 286 // snapshot operations. | 288 // snapshot operations. |
| 287 HistogramBase::AtomicCount* logged_data = | 289 HistogramBase::AtomicCount* logged_data = |
| 288 counts_data + histogram_data.bucket_count; | 290 counts_data + histogram_data.bucket_count; |
| 289 | 291 |
| 290 std::string name(histogram_data_ptr->name); | 292 std::string name(histogram_data_ptr->name); |
| 291 HistogramBase* histogram = nullptr; | 293 scoped_ptr<HistogramBase> histogram; |
| 292 switch (histogram_data.histogram_type) { | 294 switch (histogram_data.histogram_type) { |
| 293 case HISTOGRAM: | 295 case HISTOGRAM: |
| 294 histogram = Histogram::PersistentGet( | 296 histogram.reset(Histogram::PersistentGet( |
| 295 name, | 297 name, |
| 296 histogram_data.minimum, | 298 histogram_data.minimum, |
| 297 histogram_data.maximum, | 299 histogram_data.maximum, |
| 298 ranges, | 300 ranges, |
| 299 counts_data, | 301 counts_data, |
| 300 logged_data, | 302 logged_data, |
| 301 histogram_data.bucket_count, | 303 histogram_data.bucket_count, |
| 302 &histogram_data_ptr->samples_metadata, | 304 &histogram_data_ptr->samples_metadata, |
| 303 &histogram_data_ptr->logged_metadata); | 305 &histogram_data_ptr->logged_metadata)); |
| 304 DCHECK(histogram); | 306 DCHECK(histogram); |
| 305 break; | 307 break; |
| 306 case LINEAR_HISTOGRAM: | 308 case LINEAR_HISTOGRAM: |
| 307 histogram = LinearHistogram::PersistentGet( | 309 histogram.reset(LinearHistogram::PersistentGet( |
| 308 name, | 310 name, |
| 309 histogram_data.minimum, | 311 histogram_data.minimum, |
| 310 histogram_data.maximum, | 312 histogram_data.maximum, |
| 311 ranges, | 313 ranges, |
| 312 counts_data, | 314 counts_data, |
| 313 logged_data, | 315 logged_data, |
| 314 histogram_data.bucket_count, | 316 histogram_data.bucket_count, |
| 315 &histogram_data_ptr->samples_metadata, | 317 &histogram_data_ptr->samples_metadata, |
| 316 &histogram_data_ptr->logged_metadata); | 318 &histogram_data_ptr->logged_metadata)); |
| 317 DCHECK(histogram); | 319 DCHECK(histogram); |
| 318 break; | 320 break; |
| 319 case BOOLEAN_HISTOGRAM: | 321 case BOOLEAN_HISTOGRAM: |
| 320 histogram = BooleanHistogram::PersistentGet( | 322 histogram.reset(BooleanHistogram::PersistentGet( |
| 321 name, | 323 name, |
| 322 ranges, | 324 ranges, |
| 323 counts_data, | 325 counts_data, |
| 324 logged_data, | 326 logged_data, |
| 325 &histogram_data_ptr->samples_metadata, | 327 &histogram_data_ptr->samples_metadata, |
| 326 &histogram_data_ptr->logged_metadata); | 328 &histogram_data_ptr->logged_metadata)); |
| 327 DCHECK(histogram); | 329 DCHECK(histogram); |
| 328 break; | 330 break; |
| 329 case CUSTOM_HISTOGRAM: | 331 case CUSTOM_HISTOGRAM: |
| 330 histogram = CustomHistogram::PersistentGet( | 332 histogram.reset(CustomHistogram::PersistentGet( |
| 331 name, | 333 name, |
| 332 ranges, | 334 ranges, |
| 333 counts_data, | 335 counts_data, |
| 334 logged_data, | 336 logged_data, |
| 335 histogram_data.bucket_count, | 337 histogram_data.bucket_count, |
| 336 &histogram_data_ptr->samples_metadata, | 338 &histogram_data_ptr->samples_metadata, |
| 337 &histogram_data_ptr->logged_metadata); | 339 &histogram_data_ptr->logged_metadata)); |
| 338 DCHECK(histogram); | 340 DCHECK(histogram); |
| 339 break; | 341 break; |
| 340 default: | 342 default: |
| 341 NOTREACHED(); | 343 NOTREACHED(); |
| 342 } | 344 } |
| 343 | 345 |
| 344 if (histogram) { | 346 if (histogram) { |
| 345 DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType()); | 347 DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType()); |
| 346 histogram->SetFlags(histogram_data.flags); | 348 histogram->SetFlags(histogram_data.flags); |
| 347 RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS); | 349 RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS); |
| 348 } else { | 350 } else { |
| 349 RecordCreateHistogramResult(CREATE_HISTOGRAM_UNKNOWN_TYPE); | 351 RecordCreateHistogramResult(CREATE_HISTOGRAM_UNKNOWN_TYPE); |
| 350 } | 352 } |
| 351 | 353 |
| 352 return histogram; | 354 return histogram; |
| 353 } | 355 } |
| 354 | 356 |
| 355 HistogramBase* GetPersistentHistogram( | 357 scoped_ptr<HistogramBase> GetPersistentHistogram( |
| 356 PersistentMemoryAllocator* allocator, | 358 PersistentMemoryAllocator* allocator, |
| 357 int32_t ref) { | 359 int32_t ref) { |
| 358 // Unfortunately, the above "pickle" methods cannot be used as part of the | 360 // Unfortunately, the above "pickle" methods cannot be used as part of the |
| 359 // persistance because the deserialization methods always create local | 361 // persistance because the deserialization methods always create local |
| 360 // count data (these must referenced the persistent counts) and always add | 362 // count data (these must referenced the persistent counts) and always add |
| 361 // it to the local list of known histograms (these may be simple references | 363 // it to the local list of known histograms (these may be simple references |
| 362 // to histograms in other processes). | 364 // to histograms in other processes). |
| 363 PersistentHistogramData* histogram_data = | 365 PersistentHistogramData* histogram_data = |
| 364 allocator->GetAsObject<PersistentHistogramData>(ref, kTypeIdHistogram); | 366 allocator->GetAsObject<PersistentHistogramData>(ref, kTypeIdHistogram); |
| 365 size_t length = allocator->GetAllocSize(ref); | 367 size_t length = allocator->GetAllocSize(ref); |
| 366 if (!histogram_data || | 368 if (!histogram_data || |
| 367 reinterpret_cast<char*>(histogram_data)[length - 1] != '\0') { | 369 reinterpret_cast<char*>(histogram_data)[length - 1] != '\0') { |
| 368 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA); | 370 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA); |
| 369 NOTREACHED(); | 371 NOTREACHED(); |
| 370 return nullptr; | 372 return nullptr; |
| 371 } | 373 } |
| 372 return CreatePersistentHistogram(allocator, histogram_data); | 374 return CreatePersistentHistogram(allocator, histogram_data); |
| 373 } | 375 } |
| 374 | 376 |
| 375 HistogramBase* GetNextPersistentHistogram( | 377 scoped_ptr<HistogramBase> GetNextPersistentHistogram( |
| 376 PersistentMemoryAllocator* allocator, | 378 PersistentMemoryAllocator* allocator, |
| 377 PersistentMemoryAllocator::Iterator* iter) { | 379 PersistentMemoryAllocator::Iterator* iter) { |
| 378 PersistentMemoryAllocator::Reference ref; | 380 PersistentMemoryAllocator::Reference ref; |
| 379 uint32_t type_id; | 381 uint32_t type_id; |
| 380 while ((ref = allocator->GetNextIterable(iter, &type_id)) != 0) { | 382 while ((ref = allocator->GetNextIterable(iter, &type_id)) != 0) { |
| 381 if (type_id == kTypeIdHistogram) | 383 if (type_id == kTypeIdHistogram) |
| 382 return GetPersistentHistogram(allocator, ref); | 384 return GetPersistentHistogram(allocator, ref); |
| 383 } | 385 } |
| 384 return nullptr; | 386 return nullptr; |
| 385 } | 387 } |
| 386 | 388 |
| 387 void FinalizePersistentHistogram(PersistentMemoryAllocator::Reference ref, | 389 void FinalizePersistentHistogram(PersistentMemoryAllocator::Reference ref, |
| 388 bool registered) { | 390 bool registered) { |
| 389 // If the created persistent histogram was registered then it needs to | 391 // If the created persistent histogram was registered then it needs to |
| 390 // be marked as "iterable" in order to be found by other processes. | 392 // be marked as "iterable" in order to be found by other processes. |
| 391 if (registered) | 393 if (registered) |
| 392 GetPersistentHistogramMemoryAllocator()->MakeIterable(ref); | 394 GetPersistentHistogramMemoryAllocator()->MakeIterable(ref); |
| 393 // If it wasn't registered then a race condition must have caused | 395 // If it wasn't registered then a race condition must have caused |
| 394 // two to be created. The allocator does not support releasing the | 396 // two to be created. The allocator does not support releasing the |
| 395 // acquired memory so just change the type to be empty. | 397 // acquired memory so just change the type to be empty. |
| 396 else | 398 else |
| 397 GetPersistentHistogramMemoryAllocator()->SetType(ref, 0); | 399 GetPersistentHistogramMemoryAllocator()->SetType(ref, 0); |
| 398 } | 400 } |
| 399 | 401 |
| 400 HistogramBase* AllocatePersistentHistogram( | 402 scoped_ptr<HistogramBase> AllocatePersistentHistogram( |
| 401 PersistentMemoryAllocator* allocator, | 403 PersistentMemoryAllocator* allocator, |
| 402 HistogramType histogram_type, | 404 HistogramType histogram_type, |
| 403 const std::string& name, | 405 const std::string& name, |
| 404 int minimum, | 406 int minimum, |
| 405 int maximum, | 407 int maximum, |
| 406 const BucketRanges* bucket_ranges, | 408 const BucketRanges* bucket_ranges, |
| 407 int32_t flags, | 409 int32_t flags, |
| 408 PersistentMemoryAllocator::Reference* ref_ptr) { | 410 PersistentMemoryAllocator::Reference* ref_ptr) { |
| 409 if (!allocator) | 411 if (!allocator) |
| 410 return nullptr; | 412 return nullptr; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 458 histogram_data->bucket_count = static_cast<uint32_t>(bucket_count); | 460 histogram_data->bucket_count = static_cast<uint32_t>(bucket_count); |
| 459 histogram_data->ranges_ref = ranges_ref; | 461 histogram_data->ranges_ref = ranges_ref; |
| 460 histogram_data->ranges_checksum = bucket_ranges->checksum(); | 462 histogram_data->ranges_checksum = bucket_ranges->checksum(); |
| 461 histogram_data->counts_ref = counts_ref; | 463 histogram_data->counts_ref = counts_ref; |
| 462 | 464 |
| 463 // Create the histogram using resources in persistent memory. This ends up | 465 // Create the histogram using resources in persistent memory. This ends up |
| 464 // resolving the "ref" values stored in histogram_data instad of just | 466 // resolving the "ref" values stored in histogram_data instad of just |
| 465 // using what is already known above but avoids duplicating the switch | 467 // using what is already known above but avoids duplicating the switch |
| 466 // statement here and serves as a double-check that everything is | 468 // statement here and serves as a double-check that everything is |
| 467 // correct before commiting the new histogram to persistent space. | 469 // correct before commiting the new histogram to persistent space. |
| 468 HistogramBase* histogram = | 470 scoped_ptr<HistogramBase> histogram( |
| 469 CreatePersistentHistogram(allocator, histogram_data); | 471 CreatePersistentHistogram(allocator, histogram_data)); |
| 470 DCHECK(histogram); | 472 DCHECK(histogram); |
| 471 if (ref_ptr != nullptr) | 473 if (ref_ptr != nullptr) |
| 472 *ref_ptr = histogram_ref; | 474 *ref_ptr = histogram_ref; |
| 473 return histogram; | 475 return histogram; |
| 474 } | 476 } |
| 475 | 477 |
| 476 CreateHistogramResultType result; | 478 CreateHistogramResultType result; |
| 477 if (allocator->IsCorrupt()) { | 479 if (allocator->IsCorrupt()) { |
| 478 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_NEWLY_CORRUPT); | 480 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_NEWLY_CORRUPT); |
| 479 result = CREATE_HISTOGRAM_ALLOCATOR_CORRUPT; | 481 result = CREATE_HISTOGRAM_ALLOCATOR_CORRUPT; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 497 base::AutoLock auto_lock(lock.Get()); | 499 base::AutoLock auto_lock(lock.Get()); |
| 498 | 500 |
| 499 // Each call resumes from where it last left off so need persistant | 501 // Each call resumes from where it last left off so need persistant |
| 500 // iterator. This class has a constructor so even the definition has | 502 // iterator. This class has a constructor so even the definition has |
| 501 // to be protected by the lock in order to be thread-safe. | 503 // to be protected by the lock in order to be thread-safe. |
| 502 static PersistentMemoryAllocator::Iterator iter; | 504 static PersistentMemoryAllocator::Iterator iter; |
| 503 if (iter.is_clear()) | 505 if (iter.is_clear()) |
| 504 g_allocator->CreateIterator(&iter); | 506 g_allocator->CreateIterator(&iter); |
| 505 | 507 |
| 506 while (true) { | 508 while (true) { |
| 507 HistogramBase* histogram = GetNextPersistentHistogram(g_allocator, &iter); | 509 scoped_ptr<HistogramBase> histogram( |
| 510 GetNextPersistentHistogram(g_allocator, &iter)); |
| 508 if (!histogram) | 511 if (!histogram) |
| 509 break; | 512 break; |
| 510 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram); | 513 StatisticsRecorder::RegisterOrDeleteDuplicate(std::move(histogram)); |
| 511 } | 514 } |
| 512 } | 515 } |
| 513 } | 516 } |
| 514 | 517 |
| 515 } // namespace base | 518 } // namespace base |
| OLD | NEW |