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 |