Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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/persistent_histogram_allocator.h" | 5 #include "base/metrics/persistent_histogram_allocator.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 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 102 PersistentMemoryAllocator::Reference counts_ref; | 102 PersistentMemoryAllocator::Reference counts_ref; |
| 103 HistogramSamples::Metadata samples_metadata; | 103 HistogramSamples::Metadata samples_metadata; |
| 104 HistogramSamples::Metadata logged_metadata; | 104 HistogramSamples::Metadata logged_metadata; |
| 105 | 105 |
| 106 // Space for the histogram name will be added during the actual allocation | 106 // Space for the histogram name will be added during the actual allocation |
| 107 // request. This must be the last field of the structure. A zero-size array | 107 // request. This must be the last field of the structure. A zero-size array |
| 108 // or a "flexible" array would be preferred but is not (yet) valid C++. | 108 // or a "flexible" array would be preferred but is not (yet) valid C++. |
| 109 char name[1]; | 109 char name[1]; |
| 110 }; | 110 }; |
| 111 | 111 |
| 112 PersistentHistogramAllocator::Iterator::Iterator( | |
| 113 PersistentHistogramAllocator* allocator) | |
| 114 : allocator_(allocator), memory_iter_(allocator->memory_allocator()) {} | |
| 115 | |
| 116 scoped_ptr<HistogramBase> | |
| 117 PersistentHistogramAllocator::Iterator::GetNextWithIgnore(Reference ignore) { | |
| 118 PersistentMemoryAllocator::Reference ref; | |
|
Ilya Sherman
2016/03/24 02:03:58
nit: s/PersistentMemoryAllocator::Reference/Refere
bcwhite
2016/03/24 14:22:34
PHA::Reference is implemented as a PMA::Reference
| |
| 119 while ((ref = memory_iter_.GetNextOfType(kTypeIdHistogram)) != 0) { | |
| 120 if (ref != ignore) | |
| 121 return allocator_->GetHistogram(ref); | |
| 122 } | |
| 123 return nullptr; | |
| 124 } | |
| 125 | |
| 112 PersistentHistogramAllocator::PersistentHistogramAllocator( | 126 PersistentHistogramAllocator::PersistentHistogramAllocator( |
| 113 scoped_ptr<PersistentMemoryAllocator> memory) | 127 scoped_ptr<PersistentMemoryAllocator> memory) |
| 114 : memory_allocator_(std::move(memory)) {} | 128 : memory_allocator_(std::move(memory)) {} |
| 115 | 129 |
| 116 PersistentHistogramAllocator::~PersistentHistogramAllocator() {} | 130 PersistentHistogramAllocator::~PersistentHistogramAllocator() {} |
| 117 | 131 |
| 118 void PersistentHistogramAllocator::CreateIterator(Iterator* iter) { | |
| 119 memory_allocator_->CreateIterator(&iter->memory_iter); | |
| 120 } | |
| 121 | |
| 122 void PersistentHistogramAllocator::CreateTrackingHistograms(StringPiece name) { | 132 void PersistentHistogramAllocator::CreateTrackingHistograms(StringPiece name) { |
| 123 memory_allocator_->CreateTrackingHistograms(name); | 133 memory_allocator_->CreateTrackingHistograms(name); |
| 124 } | 134 } |
| 125 | 135 |
| 126 void PersistentHistogramAllocator::UpdateTrackingHistograms() { | 136 void PersistentHistogramAllocator::UpdateTrackingHistograms() { |
| 127 memory_allocator_->UpdateTrackingHistograms(); | 137 memory_allocator_->UpdateTrackingHistograms(); |
| 128 } | 138 } |
| 129 | 139 |
| 130 // static | 140 // static |
| 131 HistogramBase* | 141 HistogramBase* |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 198 PersistentHistogramAllocator::ReleaseGlobalAllocatorForTesting() { | 208 PersistentHistogramAllocator::ReleaseGlobalAllocatorForTesting() { |
| 199 PersistentHistogramAllocator* histogram_allocator = g_allocator; | 209 PersistentHistogramAllocator* histogram_allocator = g_allocator; |
| 200 if (!histogram_allocator) | 210 if (!histogram_allocator) |
| 201 return nullptr; | 211 return nullptr; |
| 202 PersistentMemoryAllocator* memory_allocator = | 212 PersistentMemoryAllocator* memory_allocator = |
| 203 histogram_allocator->memory_allocator(); | 213 histogram_allocator->memory_allocator(); |
| 204 | 214 |
| 205 // Before releasing the memory, it's necessary to have the Statistics- | 215 // Before releasing the memory, it's necessary to have the Statistics- |
| 206 // Recorder forget about the histograms contained therein; otherwise, | 216 // Recorder forget about the histograms contained therein; otherwise, |
| 207 // some operations will try to access them and the released memory. | 217 // some operations will try to access them and the released memory. |
| 208 PersistentMemoryAllocator::Iterator iter; | 218 PersistentMemoryAllocator::Iterator iter(memory_allocator); |
| 209 PersistentMemoryAllocator::Reference ref; | 219 PersistentMemoryAllocator::Reference ref; |
| 210 uint32_t type_id; | 220 while ((ref = iter.GetNextOfType(kTypeIdHistogram)) != 0) { |
| 211 memory_allocator->CreateIterator(&iter); | 221 PersistentHistogramData* histogram_data = |
| 212 while ((ref = memory_allocator->GetNextIterable(&iter, &type_id)) != 0) { | 222 memory_allocator->GetAsObject<PersistentHistogramData>( |
| 213 if (type_id == kTypeIdHistogram) { | 223 ref, kTypeIdHistogram); |
| 214 PersistentHistogramData* histogram_data = | 224 DCHECK(histogram_data); |
| 215 memory_allocator->GetAsObject<PersistentHistogramData>( | 225 StatisticsRecorder::ForgetHistogramForTesting(histogram_data->name); |
| 216 ref, kTypeIdHistogram); | |
| 217 DCHECK(histogram_data); | |
| 218 StatisticsRecorder::ForgetHistogramForTesting(histogram_data->name); | |
| 219 | 226 |
| 220 // If a test breaks here then a memory region containing a histogram | 227 // If a test breaks here then a memory region containing a histogram |
| 221 // actively used by this code is being released back to the test. | 228 // actively used by this code is being released back to the test. |
| 222 // If that memory segment were to be deleted, future calls to create | 229 // If that memory segment were to be deleted, future calls to create |
| 223 // persistent histograms would crash. To avoid this, have the test call | 230 // persistent histograms would crash. To avoid this, have the test call |
| 224 // the method GetCreateHistogramResultHistogram() *before* setting | 231 // the method GetCreateHistogramResultHistogram() *before* setting |
| 225 // the (temporary) memory allocator via SetGlobalAllocator() so that | 232 // the (temporary) memory allocator via SetGlobalAllocator() so that |
| 226 // histogram is instead allocated from the process heap. | 233 // histogram is instead allocated from the process heap. |
| 227 DCHECK_NE(kResultHistogram, histogram_data->name); | 234 DCHECK_NE(kResultHistogram, histogram_data->name); |
| 228 } | |
| 229 } | 235 } |
| 230 | 236 |
| 231 g_allocator = nullptr; | 237 g_allocator = nullptr; |
| 232 return make_scoped_ptr(histogram_allocator); | 238 return make_scoped_ptr(histogram_allocator); |
| 233 }; | 239 }; |
| 234 | 240 |
| 235 // static | 241 // static |
| 236 void PersistentHistogramAllocator::CreateGlobalAllocatorOnPersistentMemory( | 242 void PersistentHistogramAllocator::CreateGlobalAllocatorOnPersistentMemory( |
| 237 void* base, | 243 void* base, |
| 238 size_t size, | 244 size_t size, |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 406 size_t length = memory_allocator_->GetAllocSize(ref); | 412 size_t length = memory_allocator_->GetAllocSize(ref); |
| 407 if (!histogram_data || | 413 if (!histogram_data || |
| 408 reinterpret_cast<char*>(histogram_data)[length - 1] != '\0') { | 414 reinterpret_cast<char*>(histogram_data)[length - 1] != '\0') { |
| 409 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA); | 415 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA); |
| 410 NOTREACHED(); | 416 NOTREACHED(); |
| 411 return nullptr; | 417 return nullptr; |
| 412 } | 418 } |
| 413 return CreateHistogram(histogram_data); | 419 return CreateHistogram(histogram_data); |
| 414 } | 420 } |
| 415 | 421 |
| 416 scoped_ptr<HistogramBase> | |
| 417 PersistentHistogramAllocator::GetNextHistogramWithIgnore(Iterator* iter, | |
| 418 Reference ignore) { | |
| 419 PersistentMemoryAllocator::Reference ref; | |
| 420 uint32_t type_id; | |
| 421 while ((ref = memory_allocator_->GetNextIterable(&iter->memory_iter, | |
| 422 &type_id)) != 0) { | |
| 423 if (ref == ignore) | |
| 424 continue; | |
| 425 if (type_id == kTypeIdHistogram) | |
| 426 return GetHistogram(ref); | |
| 427 } | |
| 428 return nullptr; | |
| 429 } | |
| 430 | |
| 431 void PersistentHistogramAllocator::FinalizeHistogram(Reference ref, | 422 void PersistentHistogramAllocator::FinalizeHistogram(Reference ref, |
| 432 bool registered) { | 423 bool registered) { |
| 433 // If the created persistent histogram was registered then it needs to | 424 // If the created persistent histogram was registered then it needs to |
| 434 // be marked as "iterable" in order to be found by other processes. | 425 // be marked as "iterable" in order to be found by other processes. |
| 435 if (registered) | 426 if (registered) |
| 436 memory_allocator_->MakeIterable(ref); | 427 memory_allocator_->MakeIterable(ref); |
| 437 // If it wasn't registered then a race condition must have caused | 428 // If it wasn't registered then a race condition must have caused |
| 438 // two to be created. The allocator does not support releasing the | 429 // two to be created. The allocator does not support releasing the |
| 439 // acquired memory so just change the type to be empty. | 430 // acquired memory so just change the type to be empty. |
| 440 else | 431 else |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 548 return nullptr; | 539 return nullptr; |
| 549 } | 540 } |
| 550 | 541 |
| 551 // static | 542 // static |
| 552 void PersistentHistogramAllocator::ImportGlobalHistograms() { | 543 void PersistentHistogramAllocator::ImportGlobalHistograms() { |
| 553 // The lock protects against concurrent access to the iterator and is created | 544 // The lock protects against concurrent access to the iterator and is created |
| 554 // in a thread-safe manner when needed. | 545 // in a thread-safe manner when needed. |
| 555 static base::LazyInstance<base::Lock>::Leaky lock = LAZY_INSTANCE_INITIALIZER; | 546 static base::LazyInstance<base::Lock>::Leaky lock = LAZY_INSTANCE_INITIALIZER; |
| 556 | 547 |
| 557 if (g_allocator) { | 548 if (g_allocator) { |
| 558 // TODO(bcwhite): Investigate a lock-free, thread-safe iterator. | 549 // Even though the iterator is thread-safe, the rest of this code is not |
| 550 // and has to be protected by a lock. Otherwise, there is no guarantee | |
| 551 // that multiple threads would process histograms in the same order | |
| 552 // they get returned by the iterator. That is important because it | |
| 553 // guarantees all processes will always have the same state. | |
| 559 base::AutoLock auto_lock(lock.Get()); | 554 base::AutoLock auto_lock(lock.Get()); |
| 560 | 555 |
| 561 // Each call resumes from where it last left off so a persistant iterator | 556 // Each call resumes from where it last left off so a persistant iterator |
| 562 // is needed. This class has a constructor so even the definition has to | 557 // is needed. This class has a constructor so even the definition has to |
| 563 // be protected by the lock in order to be thread-safe. | 558 // be protected by the lock in order to be thread-safe. |
| 564 static Iterator iter; | 559 static Iterator iter(g_allocator); |
| 565 if (iter.is_clear()) | |
| 566 g_allocator->CreateIterator(&iter); | |
| 567 | 560 |
| 568 // Skip the import if it's the histogram that was last created. Should a | 561 // Skip the import if it's the histogram that was last created. Should a |
| 569 // race condition cause the "last created" to be overwritten before it | 562 // race condition cause the "last created" to be overwritten before it |
| 570 // is recognized here then the histogram will be created and be ignored | 563 // is recognized here then the histogram will be created and be ignored |
| 571 // when it is detected as a duplicate by the statistics-recorder. This | 564 // when it is detected as a duplicate by the statistics-recorder. This |
| 572 // simple check reduces the time of creating persistent histograms by | 565 // simple check reduces the time of creating persistent histograms by |
| 573 // about 40%. | 566 // about 40%. |
| 574 Reference last_created = | 567 Reference last_created = |
| 575 subtle::NoBarrier_Load(&g_allocator->last_created_); | 568 subtle::NoBarrier_Load(&g_allocator->last_created_); |
| 576 | 569 |
| 577 while (true) { | 570 while (true) { |
| 578 scoped_ptr<HistogramBase> histogram = | 571 scoped_ptr<HistogramBase> histogram = |
| 579 g_allocator->GetNextHistogramWithIgnore(&iter, last_created); | 572 iter.GetNextWithIgnore(last_created); |
| 580 if (!histogram) | 573 if (!histogram) |
| 581 break; | 574 break; |
| 582 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram.release()); | 575 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram.release()); |
| 583 } | 576 } |
| 584 } | 577 } |
| 585 } | 578 } |
| 586 | 579 |
| 587 } // namespace base | 580 } // namespace base |
| OLD | NEW |