ProfileBufferChunkManager.h (5284B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef ProfileBufferChunkManager_h 8 #define ProfileBufferChunkManager_h 9 10 #include "mozilla/ProfileBufferChunk.h" 11 #include "mozilla/ScopeExit.h" 12 13 #include <functional> 14 15 namespace mozilla { 16 17 // Manages the ProfileBufferChunks for this process. 18 // The main user of this class is the buffer that needs chunks to store its 19 // data. 20 // The main ProfileBufferChunks responsibilities are: 21 // - It can create new chunks, they are called "unreleased". 22 // - Later these chunks are returned here, and become "released". 23 // - The manager is free to destroy or recycle the oldest released chunks 24 // (usually to reclaim memory), and will inform the user through a provided 25 // callback. 26 // - The user may access still-alive released chunks. 27 class ProfileBufferChunkManager { 28 public: 29 virtual ~ProfileBufferChunkManager() 30 #ifdef DEBUG 31 { 32 MOZ_ASSERT(!mUser, "Still registered when being destroyed"); 33 } 34 #else 35 = default; 36 #endif 37 38 // Expected maximum size needed to store one stack sample. 39 // Most ChunkManager sub-classes will require chunk sizes, this can serve as 40 // a minimum recommendation to hold most backtraces. 41 constexpr static ProfileBufferChunk::Length scExpectedMaximumStackSize = 42 128 * 1024; 43 44 // Estimated maximum buffer size. 45 [[nodiscard]] virtual size_t MaxTotalSize() const = 0; 46 47 // Create or recycle a chunk right now. May return null in case of allocation 48 // failure. 49 // Note that the chunk-destroyed callback may be invoked during this call; 50 // user should be careful with reentrancy issues. 51 [[nodiscard]] virtual UniquePtr<ProfileBufferChunk> GetChunk() = 0; 52 53 // `aChunkReceiver` may be called with a new or recycled chunk, or nullptr. 54 // (See `FulfillChunkRequests()` regarding when the callback may happen.) 55 virtual void RequestChunk( 56 std::function<void(UniquePtr<ProfileBufferChunk>)>&& aChunkReceiver) = 0; 57 58 // This method may be invoked at any time on any thread (and not necessarily 59 // by the main user of this class), to do the work necessary to respond to a 60 // previous `RequestChunk()`. 61 // It is optional: If it is never called, or called too late, the user is 62 // responsible for directly calling `GetChunk()` when a chunk is really 63 // needed (or it should at least fail gracefully). 64 // The idea is to fulfill chunk request on a separate thread, and most 65 // importantly outside of profiler calls, to avoid doing expensive memory 66 // allocations during these calls. 67 virtual void FulfillChunkRequests() = 0; 68 69 // One chunk is released by the user, the ProfileBufferChunkManager should 70 // keep it as long as possible (depending on local or global memory/time 71 // limits). Note that the chunk-destroyed callback may be invoked during this 72 // call; user should be careful with reentrancy issues. 73 virtual void ReleaseChunk(UniquePtr<ProfileBufferChunk> aChunk) = 0; 74 75 // `aChunkDestroyedCallback` will be called whenever the contents of a 76 // previously-released chunk is about to be destroyed or recycled. 77 // Note that it may be called during other functions above, or at other times 78 // from the same or other threads; user should be careful with reentrancy 79 // issues. 80 virtual void SetChunkDestroyedCallback( 81 std::function<void(const ProfileBufferChunk&)>&& 82 aChunkDestroyedCallback) = 0; 83 84 // Give away all released chunks that have not yet been destroyed. 85 [[nodiscard]] virtual UniquePtr<ProfileBufferChunk> 86 GetExtantReleasedChunks() = 0; 87 88 // Let a callback see all released chunks that have not yet been destroyed, if 89 // any. Return whatever the callback returns. 90 template <typename Callback> 91 [[nodiscard]] auto PeekExtantReleasedChunks(Callback&& aCallback) { 92 const ProfileBufferChunk* chunks = PeekExtantReleasedChunksAndLock(); 93 auto unlock = 94 MakeScopeExit([&]() { UnlockAfterPeekExtantReleasedChunks(); }); 95 return std::forward<Callback>(aCallback)(chunks); 96 } 97 98 // Chunks that were still unreleased will never be released. 99 virtual void ForgetUnreleasedChunks() = 0; 100 101 [[nodiscard]] virtual size_t SizeOfExcludingThis( 102 MallocSizeOf aMallocSizeOf) const = 0; 103 [[nodiscard]] virtual size_t SizeOfIncludingThis( 104 MallocSizeOf aMallocSizeOf) const = 0; 105 106 protected: 107 // Derived classes to implement `PeekExtantReleasedChunks` through these: 108 virtual const ProfileBufferChunk* PeekExtantReleasedChunksAndLock() = 0; 109 virtual void UnlockAfterPeekExtantReleasedChunks() = 0; 110 111 #ifdef DEBUG 112 public: 113 // DEBUG checks ensuring that this manager and its users avoid UAFs. 114 // Derived classes should assert that mUser is not null in their functions. 115 116 void RegisteredWith(const void* aUser) { 117 MOZ_ASSERT(!mUser); 118 MOZ_ASSERT(aUser); 119 mUser = aUser; 120 } 121 122 void DeregisteredFrom(const void* aUser) { 123 MOZ_ASSERT(mUser == aUser); 124 mUser = nullptr; 125 } 126 127 protected: 128 const void* mUser = nullptr; 129 #endif // DEBUG 130 }; 131 132 } // namespace mozilla 133 134 #endif // ProfileBufferChunkManager_h