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 <memory> | 7 #include <memory> |
| 8 | 8 |
| 9 #include "base/files/important_file_writer.h" | |
| 9 #include "base/lazy_instance.h" | 10 #include "base/lazy_instance.h" |
| 10 #include "base/logging.h" | 11 #include "base/logging.h" |
| 11 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
| 12 #include "base/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
| 13 #include "base/metrics/histogram_base.h" | 14 #include "base/metrics/histogram_base.h" |
| 14 #include "base/metrics/histogram_samples.h" | 15 #include "base/metrics/histogram_samples.h" |
| 15 #include "base/metrics/persistent_sample_map.h" | 16 #include "base/metrics/persistent_sample_map.h" |
| 16 #include "base/metrics/sparse_histogram.h" | 17 #include "base/metrics/sparse_histogram.h" |
| 17 #include "base/metrics/statistics_recorder.h" | 18 #include "base/metrics/statistics_recorder.h" |
| 18 #include "base/synchronization/lock.h" | 19 #include "base/synchronization/lock.h" |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 39 kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1 | 40 kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1 |
| 40 }; | 41 }; |
| 41 | 42 |
| 42 // The current globally-active persistent allocator for all new histograms. | 43 // The current globally-active persistent allocator for all new histograms. |
| 43 // The object held here will obviously not be destructed at process exit | 44 // The object held here will obviously not be destructed at process exit |
| 44 // but that's best since PersistentMemoryAllocator objects (that underlie | 45 // but that's best since PersistentMemoryAllocator objects (that underlie |
| 45 // GlobalHistogramAllocator objects) are explicitly forbidden from doing | 46 // GlobalHistogramAllocator objects) are explicitly forbidden from doing |
| 46 // anything essential at exit anyway due to the fact that they depend on data | 47 // anything essential at exit anyway due to the fact that they depend on data |
| 47 // managed elsewhere and which could be destructed first. | 48 // managed elsewhere and which could be destructed first. |
| 48 GlobalHistogramAllocator* g_allocator; | 49 GlobalHistogramAllocator* g_allocator; |
| 50 GlobalHistogramAllocator* g_allocator_disabled; | |
| 49 | 51 |
| 50 // Take an array of range boundaries and create a proper BucketRanges object | 52 // Take an array of range boundaries and create a proper BucketRanges object |
| 51 // which is returned to the caller. A return of nullptr indicates that the | 53 // which is returned to the caller. A return of nullptr indicates that the |
| 52 // passed boundaries are invalid. | 54 // passed boundaries are invalid. |
| 53 std::unique_ptr<BucketRanges> CreateRangesFromData( | 55 std::unique_ptr<BucketRanges> CreateRangesFromData( |
| 54 HistogramBase::Sample* ranges_data, | 56 HistogramBase::Sample* ranges_data, |
| 55 uint32_t ranges_checksum, | 57 uint32_t ranges_checksum, |
| 56 size_t count) { | 58 size_t count) { |
| 57 // To avoid racy destruction at shutdown, the following may be leaked. | 59 // To avoid racy destruction at shutdown, the following may be leaked. |
| 58 std::unique_ptr<BucketRanges> ranges(new BucketRanges(count)); | 60 std::unique_ptr<BucketRanges> ranges(new BucketRanges(count)); |
| (...skipping 585 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 644 NOTREACHED(); | 646 NOTREACHED(); |
| 645 return; | 647 return; |
| 646 } | 648 } |
| 647 | 649 |
| 648 Set(WrapUnique(new GlobalHistogramAllocator( | 650 Set(WrapUnique(new GlobalHistogramAllocator( |
| 649 WrapUnique(new SharedPersistentMemoryAllocator( | 651 WrapUnique(new SharedPersistentMemoryAllocator( |
| 650 std::move(shm), 0, StringPiece(), /*readonly=*/false))))); | 652 std::move(shm), 0, StringPiece(), /*readonly=*/false))))); |
| 651 } | 653 } |
| 652 | 654 |
| 653 // static | 655 // static |
| 656 void GlobalHistogramAllocator::Enable() { | |
| 657 if (g_allocator) { | |
| 658 // Already enabled. | |
| 659 DCHECK(!g_allocator_disabled); | |
| 660 return; | |
| 661 } | |
| 662 | |
| 663 DCHECK(g_allocator_disabled); | |
| 664 std::swap(g_allocator, g_allocator_disabled); | |
| 665 } | |
| 666 | |
| 667 // static | |
| 668 void GlobalHistogramAllocator::Disable() { | |
| 669 if (g_allocator_disabled) { | |
| 670 // Already disabled. | |
| 671 DCHECK(!g_allocator); | |
| 672 return; | |
| 673 } | |
| 674 | |
| 675 DCHECK(g_allocator); | |
| 676 std::swap(g_allocator, g_allocator_disabled); | |
| 677 } | |
| 678 | |
| 679 // static | |
| 680 bool GlobalHistogramAllocator::IsEnabled() { | |
| 681 return g_allocator != nullptr; | |
| 682 } | |
| 683 | |
| 684 // static | |
| 654 void GlobalHistogramAllocator::Set( | 685 void GlobalHistogramAllocator::Set( |
| 655 std::unique_ptr<GlobalHistogramAllocator> allocator) { | 686 std::unique_ptr<GlobalHistogramAllocator> allocator) { |
| 656 // Releasing or changing an allocator is extremely dangerous because it | 687 // Releasing or changing an allocator is extremely dangerous because it |
| 657 // likely has histograms stored within it. If the backing memory is also | 688 // likely has histograms stored within it. If the backing memory is also |
| 658 // also released, future accesses to those histograms will seg-fault. | 689 // also released, future accesses to those histograms will seg-fault. |
| 659 CHECK(!g_allocator); | 690 CHECK(!g_allocator && !g_allocator_disabled); |
| 660 g_allocator = allocator.release(); | 691 g_allocator = allocator.release(); |
| 661 size_t existing = StatisticsRecorder::GetHistogramCount(); | 692 size_t existing = StatisticsRecorder::GetHistogramCount(); |
| 662 | 693 |
| 663 DLOG_IF(WARNING, existing) | 694 DLOG_IF(WARNING, existing) |
| 664 << existing << " histograms were created before persistence was enabled."; | 695 << existing << " histograms were created before persistence was enabled."; |
| 665 } | 696 } |
| 666 | 697 |
| 667 // static | 698 // static |
| 668 GlobalHistogramAllocator* GlobalHistogramAllocator::Get() { | 699 GlobalHistogramAllocator* GlobalHistogramAllocator::Get() { |
| 669 return g_allocator; | 700 return g_allocator; |
| 670 } | 701 } |
| 671 | 702 |
| 672 // static | 703 // static |
| 704 GlobalHistogramAllocator* GlobalHistogramAllocator::GetEvenIfDisabled() { | |
| 705 return g_allocator ? g_allocator : g_allocator_disabled; | |
| 706 } | |
| 707 | |
| 708 // static | |
| 673 std::unique_ptr<GlobalHistogramAllocator> | 709 std::unique_ptr<GlobalHistogramAllocator> |
| 674 GlobalHistogramAllocator::ReleaseForTesting() { | 710 GlobalHistogramAllocator::ReleaseForTesting() { |
| 675 GlobalHistogramAllocator* histogram_allocator = g_allocator; | 711 GlobalHistogramAllocator* histogram_allocator = GetEvenIfDisabled(); |
| 676 if (!histogram_allocator) | 712 if (!histogram_allocator) |
| 677 return nullptr; | 713 return nullptr; |
| 678 PersistentMemoryAllocator* memory_allocator = | 714 PersistentMemoryAllocator* memory_allocator = |
| 679 histogram_allocator->memory_allocator(); | 715 histogram_allocator->memory_allocator(); |
| 680 | 716 |
| 681 // Before releasing the memory, it's necessary to have the Statistics- | 717 // Before releasing the memory, it's necessary to have the Statistics- |
| 682 // Recorder forget about the histograms contained therein; otherwise, | 718 // Recorder forget about the histograms contained therein; otherwise, |
| 683 // some operations will try to access them and the released memory. | 719 // some operations will try to access them and the released memory. |
| 684 PersistentMemoryAllocator::Iterator iter(memory_allocator); | 720 PersistentMemoryAllocator::Iterator iter(memory_allocator); |
| 685 PersistentMemoryAllocator::Reference ref; | 721 PersistentMemoryAllocator::Reference ref; |
| 686 while ((ref = iter.GetNextOfType(kTypeIdHistogram)) != 0) { | 722 while ((ref = iter.GetNextOfType(kTypeIdHistogram)) != 0) { |
| 687 PersistentHistogramData* histogram_data = | 723 PersistentHistogramData* histogram_data = |
| 688 memory_allocator->GetAsObject<PersistentHistogramData>( | 724 memory_allocator->GetAsObject<PersistentHistogramData>( |
| 689 ref, kTypeIdHistogram); | 725 ref, kTypeIdHistogram); |
| 690 DCHECK(histogram_data); | 726 DCHECK(histogram_data); |
| 691 StatisticsRecorder::ForgetHistogramForTesting(histogram_data->name); | 727 StatisticsRecorder::ForgetHistogramForTesting(histogram_data->name); |
| 692 | 728 |
| 693 // If a test breaks here then a memory region containing a histogram | 729 // If a test breaks here then a memory region containing a histogram |
| 694 // actively used by this code is being released back to the test. | 730 // actively used by this code is being released back to the test. |
| 695 // If that memory segment were to be deleted, future calls to create | 731 // If that memory segment were to be deleted, future calls to create |
| 696 // persistent histograms would crash. To avoid this, have the test call | 732 // persistent histograms would crash. To avoid this, have the test call |
| 697 // the method GetCreateHistogramResultHistogram() *before* setting | 733 // the method GetCreateHistogramResultHistogram() *before* setting |
| 698 // the (temporary) memory allocator via SetGlobalAllocator() so that | 734 // the (temporary) memory allocator via SetGlobalAllocator() so that |
| 699 // histogram is instead allocated from the process heap. | 735 // histogram is instead allocated from the process heap. |
| 700 DCHECK_NE(kResultHistogram, histogram_data->name); | 736 DCHECK_NE(kResultHistogram, histogram_data->name); |
| 701 } | 737 } |
| 702 | 738 |
| 739 DCHECK_NE(g_allocator != nullptr, g_allocator_disabled != nullptr); | |
| 703 g_allocator = nullptr; | 740 g_allocator = nullptr; |
| 741 g_allocator_disabled = nullptr; | |
| 704 return WrapUnique(histogram_allocator); | 742 return WrapUnique(histogram_allocator); |
| 705 }; | 743 }; |
| 706 | 744 |
| 745 void GlobalHistogramAllocator::SetPersistentLocation(const FilePath& location) { | |
| 746 persistent_location_ = location; | |
| 747 } | |
| 748 | |
| 749 bool GlobalHistogramAllocator::WriteToPersistentLocation() { | |
| 750 #if defined(OS_NACL) | |
| 751 // NACL doesn't support file operations, including ImportantFileWriter. | |
| 752 NOTREACHED(); | |
| 753 return false; | |
| 754 #else | |
| 755 // Stop if no destination is set, perhaps because it has not been enabled. | |
|
Ilya Sherman
2016/04/25 19:48:40
I was kind of expecting that you'd check the enabl
bcwhite
2016/04/25 20:37:17
"Enabled" has nothing to do with being able to sav
Ilya Sherman
2016/04/25 21:12:11
Why would we enable and then subsequently disable
bcwhite
2016/04/26 13:32:30
The PHA is also used in setup.exe and in subproces
Ilya Sherman
2016/04/26 20:01:24
Okay, I see your point about this code being used
| |
| 756 if (persistent_location_.empty()) { | |
| 757 LOG(WARNING) << "Could not write \"" << Name() << "\" persistent histograms" | |
| 758 << " to file because no location was set."; | |
|
Ilya Sherman
2016/04/25 19:48:40
I still think this should be an error rather than
bcwhite
2016/04/25 20:37:17
Unlike below, where there is a specific local fail
Ilya Sherman
2016/04/25 21:12:11
If I'm understanding you correctly, then I disagre
bcwhite
2016/04/26 13:32:30
I can't say I agree but I understand your point.
| |
| 759 return false; | |
| 760 } | |
| 761 | |
| 762 StringPiece contents(static_cast<const char*>(data()), used()); | |
| 763 if (!ImportantFileWriter::WriteFileAtomically(persistent_location_, | |
| 764 contents)) { | |
| 765 LOG(ERROR) << "Could not write \"" << Name() << "\" persistent histograms" | |
| 766 << " to file: " << persistent_location_.value(); | |
| 767 return false; | |
| 768 } | |
| 769 | |
| 770 return true; | |
| 771 #endif | |
| 772 } | |
| 773 | |
| 707 GlobalHistogramAllocator::GlobalHistogramAllocator( | 774 GlobalHistogramAllocator::GlobalHistogramAllocator( |
| 708 std::unique_ptr<PersistentMemoryAllocator> memory) | 775 std::unique_ptr<PersistentMemoryAllocator> memory) |
| 709 : PersistentHistogramAllocator(std::move(memory)), | 776 : PersistentHistogramAllocator(std::move(memory)), |
| 710 import_iterator_(this) {} | 777 import_iterator_(this) {} |
| 711 | 778 |
| 712 void GlobalHistogramAllocator::ImportHistogramsToStatisticsRecorder() { | 779 void GlobalHistogramAllocator::ImportHistogramsToStatisticsRecorder() { |
| 713 // Skip the import if it's the histogram that was last created. Should a | 780 // Skip the import if it's the histogram that was last created. Should a |
| 714 // race condition cause the "last created" to be overwritten before it | 781 // race condition cause the "last created" to be overwritten before it |
| 715 // is recognized here then the histogram will be created and be ignored | 782 // is recognized here then the histogram will be created and be ignored |
| 716 // when it is detected as a duplicate by the statistics-recorder. This | 783 // when it is detected as a duplicate by the statistics-recorder. This |
| 717 // simple check reduces the time of creating persistent histograms by | 784 // simple check reduces the time of creating persistent histograms by |
| 718 // about 40%. | 785 // about 40%. |
| 719 Reference record_to_ignore = last_created(); | 786 Reference record_to_ignore = last_created(); |
| 720 | 787 |
| 721 // There is no lock on this because the iterator is lock-free while still | 788 // There is no lock on this because the iterator is lock-free while still |
| 722 // guaranteed to only return each entry only once. The StatisticsRecorder | 789 // guaranteed to only return each entry only once. The StatisticsRecorder |
| 723 // has its own lock so the Register operation is safe. | 790 // has its own lock so the Register operation is safe. |
| 724 while (true) { | 791 while (true) { |
| 725 std::unique_ptr<HistogramBase> histogram = | 792 std::unique_ptr<HistogramBase> histogram = |
| 726 import_iterator_.GetNextWithIgnore(record_to_ignore); | 793 import_iterator_.GetNextWithIgnore(record_to_ignore); |
| 727 if (!histogram) | 794 if (!histogram) |
| 728 break; | 795 break; |
| 729 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram.release()); | 796 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram.release()); |
| 730 } | 797 } |
| 731 } | 798 } |
| 732 | 799 |
| 733 } // namespace base | 800 } // namespace base |
| OLD | NEW |