Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 1 | // Copyright 2018 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Alexei Filippov | 0a57e5d | 2018-05-23 18:13:55 | [diff] [blame] | 5 | #ifndef BASE_SAMPLING_HEAP_PROFILER_SAMPLING_HEAP_PROFILER_H_ |
| 6 | #define BASE_SAMPLING_HEAP_PROFILER_SAMPLING_HEAP_PROFILER_H_ |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 7 | |
Alexei Filippov | 0a57e5d | 2018-05-23 18:13:55 | [diff] [blame] | 8 | #include <memory> |
| 9 | #include <stack> |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 10 | #include <unordered_map> |
| 11 | #include <vector> |
| 12 | |
Alexei Filippov | 8371762 | 2018-02-21 21:44:40 | [diff] [blame] | 13 | #include "base/base_export.h" |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 14 | #include "base/macros.h" |
| 15 | #include "base/synchronization/lock.h" |
| 16 | #include "base/threading/thread_local.h" |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 17 | |
| 18 | namespace base { |
Alexei Filippov | 8371762 | 2018-02-21 21:44:40 | [diff] [blame] | 19 | |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 20 | template <typename T> |
Alexei Filippov | 8371762 | 2018-02-21 21:44:40 | [diff] [blame] | 21 | class NoDestructor; |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 22 | |
Alexei Filippov | 0bed5b24 | 2018-07-04 02:08:38 | [diff] [blame] | 23 | class LockFreeAddressHashSet; |
| 24 | |
Alexei Filippov | 8371762 | 2018-02-21 21:44:40 | [diff] [blame] | 25 | // The class implements sampling profiling of native memory heap. |
| 26 | // It hooks on base::allocator and base::PartitionAlloc. |
| 27 | // When started it selects and records allocation samples based on |
| 28 | // the sampling_interval parameter. |
| 29 | // The recorded samples can then be retrieved using GetSamples method. |
| 30 | class BASE_EXPORT SamplingHeapProfiler { |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 31 | public: |
Alexei Filippov | 8371762 | 2018-02-21 21:44:40 | [diff] [blame] | 32 | class BASE_EXPORT Sample { |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 33 | public: |
Alexei Filippov | 8371762 | 2018-02-21 21:44:40 | [diff] [blame] | 34 | Sample(const Sample&); |
| 35 | ~Sample(); |
| 36 | |
Alexei Filippov | e6633efc | 2018-03-01 19:31:58 | [diff] [blame] | 37 | size_t size; // Allocation size. |
| 38 | size_t total; // Total size attributed to the sample. |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 39 | std::vector<void*> stack; |
| 40 | |
| 41 | private: |
| 42 | friend class SamplingHeapProfiler; |
| 43 | |
Alexei Filippov | e6633efc | 2018-03-01 19:31:58 | [diff] [blame] | 44 | Sample(size_t, size_t total, uint32_t ordinal); |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 45 | |
| 46 | uint32_t ordinal; |
| 47 | }; |
| 48 | |
Alexei Filippov | 0585261 | 2018-02-10 08:22:29 | [diff] [blame] | 49 | class SamplesObserver { |
| 50 | public: |
| 51 | virtual ~SamplesObserver() = default; |
Alexei Filippov | e6633efc | 2018-03-01 19:31:58 | [diff] [blame] | 52 | virtual void SampleAdded(uint32_t id, size_t size, size_t total) = 0; |
Alexei Filippov | 0585261 | 2018-02-10 08:22:29 | [diff] [blame] | 53 | virtual void SampleRemoved(uint32_t id) = 0; |
| 54 | }; |
| 55 | |
Alexei Filippov | b956afc | 2018-03-12 22:41:08 | [diff] [blame] | 56 | // Must be called early during the process initialization. It creates and |
| 57 | // reserves a TLS slot. |
| 58 | static void InitTLSSlot(); |
| 59 | |
Alexei Filippov | 1eb85b5b | 2018-02-22 03:58:47 | [diff] [blame] | 60 | // This is an entry point for plugging in an external allocator. |
| 61 | // Profiler will invoke the provided callback upon initialization. |
| 62 | // The callback should install hooks onto the corresponding memory allocator |
| 63 | // and make them invoke SamplingHeapProfiler::RecordAlloc and |
| 64 | // SamplingHeapProfiler::RecordFree upon corresponding allocation events. |
| 65 | // |
| 66 | // If the method is called after profiler is initialized, the callback |
| 67 | // is invoked right away. |
| 68 | static void SetHooksInstallCallback(void (*hooks_install_callback)()); |
| 69 | |
Alexei Filippov | 0585261 | 2018-02-10 08:22:29 | [diff] [blame] | 70 | void AddSamplesObserver(SamplesObserver*); |
| 71 | void RemoveSamplesObserver(SamplesObserver*); |
| 72 | |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 73 | uint32_t Start(); |
| 74 | void Stop(); |
| 75 | void SetSamplingInterval(size_t sampling_interval); |
Alexei Filippov | c385768 | 2018-03-01 05:23:04 | [diff] [blame] | 76 | void SuppressRandomnessForTest(bool suppress); |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 77 | |
| 78 | std::vector<Sample> GetSamples(uint32_t profile_id); |
| 79 | |
Alexei Filippov | 1eb85b5b | 2018-02-22 03:58:47 | [diff] [blame] | 80 | static void RecordAlloc(void* address, size_t, uint32_t skip_frames = 0); |
| 81 | static void RecordFree(void* address); |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 82 | |
| 83 | static SamplingHeapProfiler* GetInstance(); |
| 84 | |
| 85 | private: |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 86 | SamplingHeapProfiler(); |
Alexei Filippov | 8371762 | 2018-02-21 21:44:40 | [diff] [blame] | 87 | ~SamplingHeapProfiler() = delete; |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 88 | |
| 89 | static void InstallAllocatorHooksOnce(); |
| 90 | static bool InstallAllocatorHooks(); |
| 91 | static size_t GetNextSampleInterval(size_t base_interval); |
| 92 | |
Alexei Filippov | 1eb85b5b | 2018-02-22 03:58:47 | [diff] [blame] | 93 | void DoRecordAlloc(size_t total_allocated, |
| 94 | size_t allocation_size, |
| 95 | void* address, |
| 96 | uint32_t skip_frames); |
| 97 | void DoRecordFree(void* address); |
Alexei Filippov | 13bf502 | 2018-02-08 05:25:59 | [diff] [blame] | 98 | void RecordStackTrace(Sample*, uint32_t skip_frames); |
Alexei Filippov | 0bed5b24 | 2018-07-04 02:08:38 | [diff] [blame] | 99 | static LockFreeAddressHashSet& sampled_addresses_set(); |
| 100 | |
| 101 | void BalanceAddressesHashSet(); |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 102 | |
| 103 | base::ThreadLocalBoolean entered_; |
| 104 | base::Lock mutex_; |
Alexei Filippov | 0bed5b24 | 2018-07-04 02:08:38 | [diff] [blame] | 105 | std::stack<std::unique_ptr<LockFreeAddressHashSet>> sampled_addresses_stack_; |
| 106 | std::unordered_map<void*, Sample> samples_; |
Alexei Filippov | 0585261 | 2018-02-10 08:22:29 | [diff] [blame] | 107 | std::vector<SamplesObserver*> observers_; |
Dirk Pranke | 0a3c049 | 2018-07-11 22:26:10 | [diff] [blame^] | 108 | uint32_t last_sample_ordinal_ = 1; |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 109 | |
Alexei Filippov | b956afc | 2018-03-12 22:41:08 | [diff] [blame] | 110 | static SamplingHeapProfiler* instance_; |
| 111 | |
Alexei Filippov | 8371762 | 2018-02-21 21:44:40 | [diff] [blame] | 112 | friend class base::NoDestructor<SamplingHeapProfiler>; |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 113 | |
| 114 | DISALLOW_COPY_AND_ASSIGN(SamplingHeapProfiler); |
| 115 | }; |
| 116 | |
Alexei Filippov | 8371762 | 2018-02-21 21:44:40 | [diff] [blame] | 117 | } // namespace base |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 118 | |
Alexei Filippov | 0a57e5d | 2018-05-23 18:13:55 | [diff] [blame] | 119 | #endif // BASE_SAMPLING_HEAP_PROFILER_SAMPLING_HEAP_PROFILER_H_ |