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 | 8371762 | 2018-02-21 21:44:40 | [diff] [blame] | 23 | // The class implements sampling profiling of native memory heap. |
| 24 | // It hooks on base::allocator and base::PartitionAlloc. |
| 25 | // When started it selects and records allocation samples based on |
| 26 | // the sampling_interval parameter. |
| 27 | // The recorded samples can then be retrieved using GetSamples method. |
| 28 | class BASE_EXPORT SamplingHeapProfiler { |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 29 | public: |
Alexei Filippov | 8371762 | 2018-02-21 21:44:40 | [diff] [blame] | 30 | class BASE_EXPORT Sample { |
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 | Sample(const Sample&); |
| 33 | ~Sample(); |
| 34 | |
Alexei Filippov | e6633efc | 2018-03-01 19:31:58 | [diff] [blame] | 35 | size_t size; // Allocation size. |
| 36 | size_t total; // Total size attributed to the sample. |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 37 | std::vector<void*> stack; |
| 38 | |
| 39 | private: |
| 40 | friend class SamplingHeapProfiler; |
| 41 | |
Alexei Filippov | e6633efc | 2018-03-01 19:31:58 | [diff] [blame] | 42 | Sample(size_t, size_t total, uint32_t ordinal); |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 43 | |
| 44 | uint32_t ordinal; |
| 45 | }; |
| 46 | |
Alexei Filippov | 0585261 | 2018-02-10 08:22:29 | [diff] [blame] | 47 | class SamplesObserver { |
| 48 | public: |
| 49 | virtual ~SamplesObserver() = default; |
Alexei Filippov | e6633efc | 2018-03-01 19:31:58 | [diff] [blame] | 50 | virtual void SampleAdded(uint32_t id, size_t size, size_t total) = 0; |
Alexei Filippov | 0585261 | 2018-02-10 08:22:29 | [diff] [blame] | 51 | virtual void SampleRemoved(uint32_t id) = 0; |
| 52 | }; |
| 53 | |
Alexei Filippov | b956afc | 2018-03-12 22:41:08 | [diff] [blame] | 54 | // Must be called early during the process initialization. It creates and |
| 55 | // reserves a TLS slot. |
| 56 | static void InitTLSSlot(); |
| 57 | |
Alexei Filippov | 1eb85b5b | 2018-02-22 03:58:47 | [diff] [blame] | 58 | // This is an entry point for plugging in an external allocator. |
| 59 | // Profiler will invoke the provided callback upon initialization. |
| 60 | // The callback should install hooks onto the corresponding memory allocator |
| 61 | // and make them invoke SamplingHeapProfiler::RecordAlloc and |
| 62 | // SamplingHeapProfiler::RecordFree upon corresponding allocation events. |
| 63 | // |
| 64 | // If the method is called after profiler is initialized, the callback |
| 65 | // is invoked right away. |
| 66 | static void SetHooksInstallCallback(void (*hooks_install_callback)()); |
| 67 | |
Alexei Filippov | 0585261 | 2018-02-10 08:22:29 | [diff] [blame] | 68 | void AddSamplesObserver(SamplesObserver*); |
| 69 | void RemoveSamplesObserver(SamplesObserver*); |
| 70 | |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 71 | uint32_t Start(); |
| 72 | void Stop(); |
| 73 | void SetSamplingInterval(size_t sampling_interval); |
Alexei Filippov | c385768 | 2018-03-01 05:23:04 | [diff] [blame] | 74 | void SuppressRandomnessForTest(bool suppress); |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 75 | |
| 76 | std::vector<Sample> GetSamples(uint32_t profile_id); |
| 77 | |
Alexei Filippov | 1eb85b5b | 2018-02-22 03:58:47 | [diff] [blame] | 78 | static void RecordAlloc(void* address, size_t, uint32_t skip_frames = 0); |
| 79 | static void RecordFree(void* address); |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 80 | |
| 81 | static SamplingHeapProfiler* GetInstance(); |
| 82 | |
| 83 | private: |
Alexei Filippov | 0a57e5d | 2018-05-23 18:13:55 | [diff] [blame^] | 84 | using SamplesMap = std::unordered_map<void*, Sample>; |
| 85 | |
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 | 0a57e5d | 2018-05-23 18:13:55 | [diff] [blame^] | 99 | SamplesMap& EnsureNoRehashingMap(); |
| 100 | static SamplesMap& samples(); |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 101 | |
| 102 | base::ThreadLocalBoolean entered_; |
| 103 | base::Lock mutex_; |
Alexei Filippov | 0a57e5d | 2018-05-23 18:13:55 | [diff] [blame^] | 104 | std::stack<std::unique_ptr<SamplesMap>> sample_maps_; |
Alexei Filippov | 0585261 | 2018-02-10 08:22:29 | [diff] [blame] | 105 | std::vector<SamplesObserver*> observers_; |
Alexei Filippov | 0a57e5d | 2018-05-23 18:13:55 | [diff] [blame^] | 106 | uint32_t last_sample_ordinal_ = 0; |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 107 | |
Alexei Filippov | b956afc | 2018-03-12 22:41:08 | [diff] [blame] | 108 | static SamplingHeapProfiler* instance_; |
| 109 | |
Alexei Filippov | 8371762 | 2018-02-21 21:44:40 | [diff] [blame] | 110 | friend class base::NoDestructor<SamplingHeapProfiler>; |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 111 | |
| 112 | DISALLOW_COPY_AND_ASSIGN(SamplingHeapProfiler); |
| 113 | }; |
| 114 | |
Alexei Filippov | 8371762 | 2018-02-21 21:44:40 | [diff] [blame] | 115 | } // namespace base |
Alexei Filippov | 9fbb754 | 2018-02-07 09:56:57 | [diff] [blame] | 116 | |
Alexei Filippov | 0a57e5d | 2018-05-23 18:13:55 | [diff] [blame^] | 117 | #endif // BASE_SAMPLING_HEAP_PROFILER_SAMPLING_HEAP_PROFILER_H_ |