xref: /aosp_15_r20/external/perfetto/src/profiling/memory/bookkeeping.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker /*
2*6dbdd20aSAndroid Build Coastguard Worker  * Copyright (C) 2018 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker  *
4*6dbdd20aSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker  *
8*6dbdd20aSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker  *
10*6dbdd20aSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker  * limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker  */
16*6dbdd20aSAndroid Build Coastguard Worker 
17*6dbdd20aSAndroid Build Coastguard Worker #ifndef SRC_PROFILING_MEMORY_BOOKKEEPING_H_
18*6dbdd20aSAndroid Build Coastguard Worker #define SRC_PROFILING_MEMORY_BOOKKEEPING_H_
19*6dbdd20aSAndroid Build Coastguard Worker 
20*6dbdd20aSAndroid Build Coastguard Worker #include <map>
21*6dbdd20aSAndroid Build Coastguard Worker #include <vector>
22*6dbdd20aSAndroid Build Coastguard Worker 
23*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/base/time.h"
24*6dbdd20aSAndroid Build Coastguard Worker #include "src/profiling/common/callstack_trie.h"
25*6dbdd20aSAndroid Build Coastguard Worker #include "src/profiling/common/interner.h"
26*6dbdd20aSAndroid Build Coastguard Worker #include "src/profiling/memory/unwound_messages.h"
27*6dbdd20aSAndroid Build Coastguard Worker 
28*6dbdd20aSAndroid Build Coastguard Worker // Below is an illustration of the bookkeeping system state where
29*6dbdd20aSAndroid Build Coastguard Worker // PID 1 does the following allocations:
30*6dbdd20aSAndroid Build Coastguard Worker // 0x123: 128 bytes at [bar main]
31*6dbdd20aSAndroid Build Coastguard Worker // 0x234: 128 bytes at [bar main]
32*6dbdd20aSAndroid Build Coastguard Worker // 0xf00: 512 bytes at [foo main]
33*6dbdd20aSAndroid Build Coastguard Worker // PID 1 allocated but previously freed 1024 bytes at [bar main]
34*6dbdd20aSAndroid Build Coastguard Worker //
35*6dbdd20aSAndroid Build Coastguard Worker // PID 2 does the following allocations:
36*6dbdd20aSAndroid Build Coastguard Worker // 0x345: 512 bytes at [foo main]
37*6dbdd20aSAndroid Build Coastguard Worker // 0x456:  32 bytes at [foo main]
38*6dbdd20aSAndroid Build Coastguard Worker // PID 2 allocated but already freed 1235 bytes at [foo main]
39*6dbdd20aSAndroid Build Coastguard Worker // PID 2 allocated and freed 2048 bytes in main.
40*6dbdd20aSAndroid Build Coastguard Worker //
41*6dbdd20aSAndroid Build Coastguard Worker // +---------------------------------+   +-------------------+
42*6dbdd20aSAndroid Build Coastguard Worker // | +---------+    HeapTracker PID 1|   | GlobalCallstackTri|
43*6dbdd20aSAndroid Build Coastguard Worker // | |0x123 128+---+    +----------+ |   |           +---+   |
44*6dbdd20aSAndroid Build Coastguard Worker // | |         |   +---->alloc:1280+----------------->bar|   |
45*6dbdd20aSAndroid Build Coastguard Worker // | |0x234 128+---+    |free: 1024| |   |           +-^-+   |
46*6dbdd20aSAndroid Build Coastguard Worker // | |         |        +----------+ |   |   +---+     ^     |
47*6dbdd20aSAndroid Build Coastguard Worker // | |0xf00 512+---+                 | +----->foo|     |     |
48*6dbdd20aSAndroid Build Coastguard Worker // | +--------+|   |    +----------+ | | |   +-^-+     |     |
49*6dbdd20aSAndroid Build Coastguard Worker // |               +---->alloc: 512+---+ |     |       |     |
50*6dbdd20aSAndroid Build Coastguard Worker // |                    |free:    0| | | |     +--+----+     |
51*6dbdd20aSAndroid Build Coastguard Worker // |                    +----------+ | | |        |          |
52*6dbdd20aSAndroid Build Coastguard Worker // |                                 | | |      +-+--+       |
53*6dbdd20aSAndroid Build Coastguard Worker // +---------------------------------+ | |      |main|       |
54*6dbdd20aSAndroid Build Coastguard Worker //                                     | |      +--+-+       |
55*6dbdd20aSAndroid Build Coastguard Worker // +---------------------------------+ | |         ^         |
56*6dbdd20aSAndroid Build Coastguard Worker // | +---------+    HeapTracker PID 2| | +-------------------+
57*6dbdd20aSAndroid Build Coastguard Worker // | |0x345 512+---+    +----------+ | |           |
58*6dbdd20aSAndroid Build Coastguard Worker // | |         |   +---->alloc:1779+---+           |
59*6dbdd20aSAndroid Build Coastguard Worker // | |0x456  32+---+    |free: 1235| |             |
60*6dbdd20aSAndroid Build Coastguard Worker // | +---------+        +----------+ |             |
61*6dbdd20aSAndroid Build Coastguard Worker // |                                 |             |
62*6dbdd20aSAndroid Build Coastguard Worker // |                    +----------+ |             |
63*6dbdd20aSAndroid Build Coastguard Worker // |                    |alloc:2048+---------------+
64*6dbdd20aSAndroid Build Coastguard Worker // |                    |free: 2048| |
65*6dbdd20aSAndroid Build Coastguard Worker // |                    +----------+ |
66*6dbdd20aSAndroid Build Coastguard Worker // |                                 |
67*6dbdd20aSAndroid Build Coastguard Worker // +---------------------------------+
68*6dbdd20aSAndroid Build Coastguard Worker //   Allocation    CallstackAllocations        Node
69*6dbdd20aSAndroid Build Coastguard Worker //
70*6dbdd20aSAndroid Build Coastguard Worker // The active allocations are on the leftmost side, modeled as the class
71*6dbdd20aSAndroid Build Coastguard Worker // HeapTracker::Allocation.
72*6dbdd20aSAndroid Build Coastguard Worker //
73*6dbdd20aSAndroid Build Coastguard Worker // The total allocated and freed bytes per callsite are in the middle, modeled
74*6dbdd20aSAndroid Build Coastguard Worker // as the HeapTracker::CallstackAllocations class.
75*6dbdd20aSAndroid Build Coastguard Worker // Note that (1280 - 1024) = 256, so alloc - free is equal to the total of the
76*6dbdd20aSAndroid Build Coastguard Worker // currently active allocations.
77*6dbdd20aSAndroid Build Coastguard Worker // Note in PID 2 there is a CallstackAllocations with 2048 allocated and 2048
78*6dbdd20aSAndroid Build Coastguard Worker // freed bytes. This is not currently referenced by any Allocations (as it
79*6dbdd20aSAndroid Build Coastguard Worker // should, as 2048 - 2048 = 0, which would mean that the total size of the
80*6dbdd20aSAndroid Build Coastguard Worker // allocations referencing it should be 0). This is because we haven't dumped
81*6dbdd20aSAndroid Build Coastguard Worker // this state yet, so the CallstackAllocations will be kept around until the
82*6dbdd20aSAndroid Build Coastguard Worker // next dump, written to the trace, and then destroyed.
83*6dbdd20aSAndroid Build Coastguard Worker //
84*6dbdd20aSAndroid Build Coastguard Worker // On the right hand side is the GlobalCallstackTrie, with nodes representing
85*6dbdd20aSAndroid Build Coastguard Worker // distinct callstacks. They have no information about the currently allocated
86*6dbdd20aSAndroid Build Coastguard Worker // or freed bytes, they only contain a reference count to destroy them as
87*6dbdd20aSAndroid Build Coastguard Worker // soon as they are no longer referenced by a HeapTracker.
88*6dbdd20aSAndroid Build Coastguard Worker 
89*6dbdd20aSAndroid Build Coastguard Worker namespace perfetto {
90*6dbdd20aSAndroid Build Coastguard Worker namespace profiling {
91*6dbdd20aSAndroid Build Coastguard Worker 
92*6dbdd20aSAndroid Build Coastguard Worker // Snapshot for memory allocations of a particular process. Shares callsites
93*6dbdd20aSAndroid Build Coastguard Worker // with other processes.
94*6dbdd20aSAndroid Build Coastguard Worker class HeapTracker {
95*6dbdd20aSAndroid Build Coastguard Worker  public:
96*6dbdd20aSAndroid Build Coastguard Worker   struct CallstackMaxAllocations {
97*6dbdd20aSAndroid Build Coastguard Worker     uint64_t max;
98*6dbdd20aSAndroid Build Coastguard Worker     uint64_t cur;
99*6dbdd20aSAndroid Build Coastguard Worker     uint64_t max_count;
100*6dbdd20aSAndroid Build Coastguard Worker     uint64_t cur_count;
101*6dbdd20aSAndroid Build Coastguard Worker   };
102*6dbdd20aSAndroid Build Coastguard Worker   struct CallstackTotalAllocations {
103*6dbdd20aSAndroid Build Coastguard Worker     uint64_t allocated;
104*6dbdd20aSAndroid Build Coastguard Worker     uint64_t freed;
105*6dbdd20aSAndroid Build Coastguard Worker     uint64_t allocation_count;
106*6dbdd20aSAndroid Build Coastguard Worker     uint64_t free_count;
107*6dbdd20aSAndroid Build Coastguard Worker   };
108*6dbdd20aSAndroid Build Coastguard Worker 
109*6dbdd20aSAndroid Build Coastguard Worker   // Sum of all the allocations for a given callstack.
110*6dbdd20aSAndroid Build Coastguard Worker   struct CallstackAllocations {
CallstackAllocationsCallstackAllocations111*6dbdd20aSAndroid Build Coastguard Worker     explicit CallstackAllocations(GlobalCallstackTrie::Node* n) : node(n) {}
112*6dbdd20aSAndroid Build Coastguard Worker 
113*6dbdd20aSAndroid Build Coastguard Worker     uint64_t allocs = 0;
114*6dbdd20aSAndroid Build Coastguard Worker 
115*6dbdd20aSAndroid Build Coastguard Worker     union {
116*6dbdd20aSAndroid Build Coastguard Worker       CallstackMaxAllocations retain_max;
117*6dbdd20aSAndroid Build Coastguard Worker       CallstackTotalAllocations totals;
118*6dbdd20aSAndroid Build Coastguard Worker     } value = {};
119*6dbdd20aSAndroid Build Coastguard Worker 
120*6dbdd20aSAndroid Build Coastguard Worker     GlobalCallstackTrie::Node* const node;
121*6dbdd20aSAndroid Build Coastguard Worker 
~CallstackAllocationsCallstackAllocations122*6dbdd20aSAndroid Build Coastguard Worker     ~CallstackAllocations() { GlobalCallstackTrie::DecrementNode(node); }
123*6dbdd20aSAndroid Build Coastguard Worker 
124*6dbdd20aSAndroid Build Coastguard Worker     bool operator<(const CallstackAllocations& other) const {
125*6dbdd20aSAndroid Build Coastguard Worker       return node < other.node;
126*6dbdd20aSAndroid Build Coastguard Worker     }
127*6dbdd20aSAndroid Build Coastguard Worker   };
128*6dbdd20aSAndroid Build Coastguard Worker 
129*6dbdd20aSAndroid Build Coastguard Worker   // Caller needs to ensure that callsites outlives the HeapTracker.
HeapTracker(GlobalCallstackTrie * callsites,bool dump_at_max_mode)130*6dbdd20aSAndroid Build Coastguard Worker   explicit HeapTracker(GlobalCallstackTrie* callsites, bool dump_at_max_mode)
131*6dbdd20aSAndroid Build Coastguard Worker       : callsites_(callsites), dump_at_max_mode_(dump_at_max_mode) {}
132*6dbdd20aSAndroid Build Coastguard Worker 
133*6dbdd20aSAndroid Build Coastguard Worker   void RecordMalloc(const std::vector<unwindstack::FrameData>& callstack,
134*6dbdd20aSAndroid Build Coastguard Worker                     const std::vector<std::string>& build_ids,
135*6dbdd20aSAndroid Build Coastguard Worker                     uint64_t address,
136*6dbdd20aSAndroid Build Coastguard Worker                     uint64_t sample_size,
137*6dbdd20aSAndroid Build Coastguard Worker                     uint64_t alloc_size,
138*6dbdd20aSAndroid Build Coastguard Worker                     uint64_t sequence_number,
139*6dbdd20aSAndroid Build Coastguard Worker                     uint64_t timestamp);
140*6dbdd20aSAndroid Build Coastguard Worker 
141*6dbdd20aSAndroid Build Coastguard Worker   template <typename F>
GetCallstackAllocations(F fn)142*6dbdd20aSAndroid Build Coastguard Worker   void GetCallstackAllocations(F fn) {
143*6dbdd20aSAndroid Build Coastguard Worker     // There are two reasons we remove the unused callstack allocations on the
144*6dbdd20aSAndroid Build Coastguard Worker     // next iteration of Dump:
145*6dbdd20aSAndroid Build Coastguard Worker     // * We need to remove them after the callstacks were dumped, which
146*6dbdd20aSAndroid Build Coastguard Worker     //   currently happens after the allocations are dumped.
147*6dbdd20aSAndroid Build Coastguard Worker     // * This way, we do not destroy and recreate callstacks as frequently.
148*6dbdd20aSAndroid Build Coastguard Worker     for (auto it_and_alloc : dead_callstack_allocations_) {
149*6dbdd20aSAndroid Build Coastguard Worker       auto& it = it_and_alloc.first;
150*6dbdd20aSAndroid Build Coastguard Worker       uint64_t allocated = it_and_alloc.second;
151*6dbdd20aSAndroid Build Coastguard Worker       const CallstackAllocations& alloc = it->second;
152*6dbdd20aSAndroid Build Coastguard Worker       // For non-dump-at-max, we need to check, even if there are still no
153*6dbdd20aSAndroid Build Coastguard Worker       // allocations referencing this callstack, whether there were any
154*6dbdd20aSAndroid Build Coastguard Worker       // allocations that happened but were freed again. If that was the case,
155*6dbdd20aSAndroid Build Coastguard Worker       // we need to keep the callsite, because the next dump will indicate a
156*6dbdd20aSAndroid Build Coastguard Worker       // different self_alloc and self_freed.
157*6dbdd20aSAndroid Build Coastguard Worker       if (alloc.allocs == 0 &&
158*6dbdd20aSAndroid Build Coastguard Worker           (dump_at_max_mode_ ||
159*6dbdd20aSAndroid Build Coastguard Worker            alloc.value.totals.allocation_count == allocated)) {
160*6dbdd20aSAndroid Build Coastguard Worker         // TODO(fmayer): We could probably be smarter than throw away
161*6dbdd20aSAndroid Build Coastguard Worker         // our whole frames cache.
162*6dbdd20aSAndroid Build Coastguard Worker         ClearFrameCache();
163*6dbdd20aSAndroid Build Coastguard Worker         callstack_allocations_.erase(it);
164*6dbdd20aSAndroid Build Coastguard Worker       }
165*6dbdd20aSAndroid Build Coastguard Worker     }
166*6dbdd20aSAndroid Build Coastguard Worker     dead_callstack_allocations_.clear();
167*6dbdd20aSAndroid Build Coastguard Worker 
168*6dbdd20aSAndroid Build Coastguard Worker     for (auto it = callstack_allocations_.begin();
169*6dbdd20aSAndroid Build Coastguard Worker          it != callstack_allocations_.end(); ++it) {
170*6dbdd20aSAndroid Build Coastguard Worker       const CallstackAllocations& alloc = it->second;
171*6dbdd20aSAndroid Build Coastguard Worker       fn(alloc);
172*6dbdd20aSAndroid Build Coastguard Worker 
173*6dbdd20aSAndroid Build Coastguard Worker       if (alloc.allocs == 0)
174*6dbdd20aSAndroid Build Coastguard Worker         dead_callstack_allocations_.emplace_back(
175*6dbdd20aSAndroid Build Coastguard Worker             it, !dump_at_max_mode_ ? alloc.value.totals.allocation_count : 0);
176*6dbdd20aSAndroid Build Coastguard Worker     }
177*6dbdd20aSAndroid Build Coastguard Worker   }
178*6dbdd20aSAndroid Build Coastguard Worker 
179*6dbdd20aSAndroid Build Coastguard Worker   template <typename F>
GetAllocations(F fn)180*6dbdd20aSAndroid Build Coastguard Worker   void GetAllocations(F fn) {
181*6dbdd20aSAndroid Build Coastguard Worker     for (const auto& addr_and_allocation : allocations_) {
182*6dbdd20aSAndroid Build Coastguard Worker       const Allocation& alloc = addr_and_allocation.second;
183*6dbdd20aSAndroid Build Coastguard Worker       fn(addr_and_allocation.first, alloc.sample_size, alloc.alloc_size,
184*6dbdd20aSAndroid Build Coastguard Worker          alloc.callstack_allocations()->node->id());
185*6dbdd20aSAndroid Build Coastguard Worker     }
186*6dbdd20aSAndroid Build Coastguard Worker   }
187*6dbdd20aSAndroid Build Coastguard Worker 
RecordFree(uint64_t address,uint64_t sequence_number,uint64_t timestamp)188*6dbdd20aSAndroid Build Coastguard Worker   void RecordFree(uint64_t address,
189*6dbdd20aSAndroid Build Coastguard Worker                   uint64_t sequence_number,
190*6dbdd20aSAndroid Build Coastguard Worker                   uint64_t timestamp) {
191*6dbdd20aSAndroid Build Coastguard Worker     RecordOperation(sequence_number, {address, timestamp});
192*6dbdd20aSAndroid Build Coastguard Worker   }
193*6dbdd20aSAndroid Build Coastguard Worker 
ClearFrameCache()194*6dbdd20aSAndroid Build Coastguard Worker   void ClearFrameCache() { frame_cache_.clear(); }
195*6dbdd20aSAndroid Build Coastguard Worker 
dump_timestamp()196*6dbdd20aSAndroid Build Coastguard Worker   uint64_t dump_timestamp() {
197*6dbdd20aSAndroid Build Coastguard Worker     return dump_at_max_mode_ ? max_timestamp_ : committed_timestamp_;
198*6dbdd20aSAndroid Build Coastguard Worker   }
199*6dbdd20aSAndroid Build Coastguard Worker 
200*6dbdd20aSAndroid Build Coastguard Worker   uint64_t GetSizeForTesting(const std::vector<unwindstack::FrameData>& stack,
201*6dbdd20aSAndroid Build Coastguard Worker                              std::vector<std::string> build_ids);
202*6dbdd20aSAndroid Build Coastguard Worker   uint64_t GetMaxForTesting(const std::vector<unwindstack::FrameData>& stack,
203*6dbdd20aSAndroid Build Coastguard Worker                             std::vector<std::string> build_ids);
204*6dbdd20aSAndroid Build Coastguard Worker   uint64_t GetMaxCountForTesting(
205*6dbdd20aSAndroid Build Coastguard Worker       const std::vector<unwindstack::FrameData>& stack,
206*6dbdd20aSAndroid Build Coastguard Worker       std::vector<std::string> build_ids);
207*6dbdd20aSAndroid Build Coastguard Worker 
GetTimestampForTesting()208*6dbdd20aSAndroid Build Coastguard Worker   uint64_t GetTimestampForTesting() { return committed_timestamp_; }
209*6dbdd20aSAndroid Build Coastguard Worker 
210*6dbdd20aSAndroid Build Coastguard Worker  private:
211*6dbdd20aSAndroid Build Coastguard Worker   struct Allocation {
AllocationAllocation212*6dbdd20aSAndroid Build Coastguard Worker     Allocation(uint64_t size,
213*6dbdd20aSAndroid Build Coastguard Worker                uint64_t asize,
214*6dbdd20aSAndroid Build Coastguard Worker                uint64_t seq,
215*6dbdd20aSAndroid Build Coastguard Worker                CallstackAllocations* csa)
216*6dbdd20aSAndroid Build Coastguard Worker         : sample_size(size), alloc_size(asize), sequence_number(seq) {
217*6dbdd20aSAndroid Build Coastguard Worker       SetCallstackAllocations(csa);
218*6dbdd20aSAndroid Build Coastguard Worker     }
219*6dbdd20aSAndroid Build Coastguard Worker 
220*6dbdd20aSAndroid Build Coastguard Worker     Allocation() = default;
221*6dbdd20aSAndroid Build Coastguard Worker     Allocation(const Allocation&) = delete;
AllocationAllocation222*6dbdd20aSAndroid Build Coastguard Worker     Allocation(Allocation&& other) noexcept {
223*6dbdd20aSAndroid Build Coastguard Worker       sample_size = other.sample_size;
224*6dbdd20aSAndroid Build Coastguard Worker       alloc_size = other.alloc_size;
225*6dbdd20aSAndroid Build Coastguard Worker       sequence_number = other.sequence_number;
226*6dbdd20aSAndroid Build Coastguard Worker       callstack_allocations_ = other.callstack_allocations_;
227*6dbdd20aSAndroid Build Coastguard Worker       other.callstack_allocations_ = nullptr;
228*6dbdd20aSAndroid Build Coastguard Worker     }
229*6dbdd20aSAndroid Build Coastguard Worker 
~AllocationAllocation230*6dbdd20aSAndroid Build Coastguard Worker     ~Allocation() { SetCallstackAllocations(nullptr); }
231*6dbdd20aSAndroid Build Coastguard Worker 
SetCallstackAllocationsAllocation232*6dbdd20aSAndroid Build Coastguard Worker     void SetCallstackAllocations(CallstackAllocations* callstack_allocations) {
233*6dbdd20aSAndroid Build Coastguard Worker       if (callstack_allocations_)
234*6dbdd20aSAndroid Build Coastguard Worker         callstack_allocations_->allocs--;
235*6dbdd20aSAndroid Build Coastguard Worker       callstack_allocations_ = callstack_allocations;
236*6dbdd20aSAndroid Build Coastguard Worker       if (callstack_allocations_)
237*6dbdd20aSAndroid Build Coastguard Worker         callstack_allocations_->allocs++;
238*6dbdd20aSAndroid Build Coastguard Worker     }
239*6dbdd20aSAndroid Build Coastguard Worker 
callstack_allocationsAllocation240*6dbdd20aSAndroid Build Coastguard Worker     CallstackAllocations* callstack_allocations() const {
241*6dbdd20aSAndroid Build Coastguard Worker       return callstack_allocations_;
242*6dbdd20aSAndroid Build Coastguard Worker     }
243*6dbdd20aSAndroid Build Coastguard Worker 
244*6dbdd20aSAndroid Build Coastguard Worker     uint64_t sample_size;
245*6dbdd20aSAndroid Build Coastguard Worker     uint64_t alloc_size;
246*6dbdd20aSAndroid Build Coastguard Worker     uint64_t sequence_number;
247*6dbdd20aSAndroid Build Coastguard Worker 
248*6dbdd20aSAndroid Build Coastguard Worker    private:
249*6dbdd20aSAndroid Build Coastguard Worker     CallstackAllocations* callstack_allocations_ = nullptr;
250*6dbdd20aSAndroid Build Coastguard Worker   };
251*6dbdd20aSAndroid Build Coastguard Worker 
252*6dbdd20aSAndroid Build Coastguard Worker   struct PendingOperation {
253*6dbdd20aSAndroid Build Coastguard Worker     uint64_t allocation_address;
254*6dbdd20aSAndroid Build Coastguard Worker     uint64_t timestamp;
255*6dbdd20aSAndroid Build Coastguard Worker   };
256*6dbdd20aSAndroid Build Coastguard Worker 
MaybeCreateCallstackAllocations(GlobalCallstackTrie::Node * node)257*6dbdd20aSAndroid Build Coastguard Worker   CallstackAllocations* MaybeCreateCallstackAllocations(
258*6dbdd20aSAndroid Build Coastguard Worker       GlobalCallstackTrie::Node* node) {
259*6dbdd20aSAndroid Build Coastguard Worker     auto callstack_allocations_it = callstack_allocations_.find(node);
260*6dbdd20aSAndroid Build Coastguard Worker     if (callstack_allocations_it == callstack_allocations_.end()) {
261*6dbdd20aSAndroid Build Coastguard Worker       GlobalCallstackTrie::IncrementNode(node);
262*6dbdd20aSAndroid Build Coastguard Worker       bool inserted;
263*6dbdd20aSAndroid Build Coastguard Worker       std::tie(callstack_allocations_it, inserted) =
264*6dbdd20aSAndroid Build Coastguard Worker           callstack_allocations_.emplace(node, node);
265*6dbdd20aSAndroid Build Coastguard Worker       PERFETTO_DCHECK(inserted);
266*6dbdd20aSAndroid Build Coastguard Worker     }
267*6dbdd20aSAndroid Build Coastguard Worker     return &callstack_allocations_it->second;
268*6dbdd20aSAndroid Build Coastguard Worker   }
269*6dbdd20aSAndroid Build Coastguard Worker 
270*6dbdd20aSAndroid Build Coastguard Worker   void RecordOperation(uint64_t sequence_number,
271*6dbdd20aSAndroid Build Coastguard Worker                        const PendingOperation& operation);
272*6dbdd20aSAndroid Build Coastguard Worker 
273*6dbdd20aSAndroid Build Coastguard Worker   // Commits a malloc or free operation.
274*6dbdd20aSAndroid Build Coastguard Worker   // See comment of pending_operations_ for encoding of malloc and free
275*6dbdd20aSAndroid Build Coastguard Worker   // operations.
276*6dbdd20aSAndroid Build Coastguard Worker   //
277*6dbdd20aSAndroid Build Coastguard Worker   // Committing a malloc operation: Add the allocations size to
278*6dbdd20aSAndroid Build Coastguard Worker   // CallstackAllocation::allocated.
279*6dbdd20aSAndroid Build Coastguard Worker   // Committing a free operation: Add the allocation's size to
280*6dbdd20aSAndroid Build Coastguard Worker   // CallstackAllocation::freed and delete the allocation.
281*6dbdd20aSAndroid Build Coastguard Worker   void CommitOperation(uint64_t sequence_number,
282*6dbdd20aSAndroid Build Coastguard Worker                        const PendingOperation& operation);
283*6dbdd20aSAndroid Build Coastguard Worker 
AddToCallstackAllocations(uint64_t ts,const Allocation & alloc)284*6dbdd20aSAndroid Build Coastguard Worker   void AddToCallstackAllocations(uint64_t ts, const Allocation& alloc) {
285*6dbdd20aSAndroid Build Coastguard Worker     if (dump_at_max_mode_) {
286*6dbdd20aSAndroid Build Coastguard Worker       current_unfreed_ += alloc.sample_size;
287*6dbdd20aSAndroid Build Coastguard Worker       alloc.callstack_allocations()->value.retain_max.cur += alloc.sample_size;
288*6dbdd20aSAndroid Build Coastguard Worker       alloc.callstack_allocations()->value.retain_max.cur_count++;
289*6dbdd20aSAndroid Build Coastguard Worker 
290*6dbdd20aSAndroid Build Coastguard Worker       if (current_unfreed_ <= max_unfreed_)
291*6dbdd20aSAndroid Build Coastguard Worker         return;
292*6dbdd20aSAndroid Build Coastguard Worker 
293*6dbdd20aSAndroid Build Coastguard Worker       if (max_sequence_number_ == alloc.sequence_number - 1) {
294*6dbdd20aSAndroid Build Coastguard Worker         // We know the only CallstackAllocation that has max != cur is the
295*6dbdd20aSAndroid Build Coastguard Worker         // one we just updated.
296*6dbdd20aSAndroid Build Coastguard Worker         alloc.callstack_allocations()->value.retain_max.max =
297*6dbdd20aSAndroid Build Coastguard Worker             alloc.callstack_allocations()->value.retain_max.cur;
298*6dbdd20aSAndroid Build Coastguard Worker         alloc.callstack_allocations()->value.retain_max.max_count =
299*6dbdd20aSAndroid Build Coastguard Worker             alloc.callstack_allocations()->value.retain_max.cur_count;
300*6dbdd20aSAndroid Build Coastguard Worker       } else {
301*6dbdd20aSAndroid Build Coastguard Worker         for (auto& p : callstack_allocations_) {
302*6dbdd20aSAndroid Build Coastguard Worker           // We need to reset max = cur for every CallstackAllocation, as we
303*6dbdd20aSAndroid Build Coastguard Worker           // do not know which ones have changed since the last max.
304*6dbdd20aSAndroid Build Coastguard Worker           // TODO(fmayer): Add an index to speed this up
305*6dbdd20aSAndroid Build Coastguard Worker           CallstackAllocations& csa = p.second;
306*6dbdd20aSAndroid Build Coastguard Worker           csa.value.retain_max.max = csa.value.retain_max.cur;
307*6dbdd20aSAndroid Build Coastguard Worker           csa.value.retain_max.max_count = csa.value.retain_max.cur_count;
308*6dbdd20aSAndroid Build Coastguard Worker         }
309*6dbdd20aSAndroid Build Coastguard Worker       }
310*6dbdd20aSAndroid Build Coastguard Worker       max_sequence_number_ = alloc.sequence_number;
311*6dbdd20aSAndroid Build Coastguard Worker       max_unfreed_ = current_unfreed_;
312*6dbdd20aSAndroid Build Coastguard Worker       max_timestamp_ = ts;
313*6dbdd20aSAndroid Build Coastguard Worker     } else {
314*6dbdd20aSAndroid Build Coastguard Worker       alloc.callstack_allocations()->value.totals.allocated +=
315*6dbdd20aSAndroid Build Coastguard Worker           alloc.sample_size;
316*6dbdd20aSAndroid Build Coastguard Worker       alloc.callstack_allocations()->value.totals.allocation_count++;
317*6dbdd20aSAndroid Build Coastguard Worker     }
318*6dbdd20aSAndroid Build Coastguard Worker   }
319*6dbdd20aSAndroid Build Coastguard Worker 
SubtractFromCallstackAllocations(const Allocation & alloc)320*6dbdd20aSAndroid Build Coastguard Worker   void SubtractFromCallstackAllocations(const Allocation& alloc) {
321*6dbdd20aSAndroid Build Coastguard Worker     if (dump_at_max_mode_) {
322*6dbdd20aSAndroid Build Coastguard Worker       current_unfreed_ -= alloc.sample_size;
323*6dbdd20aSAndroid Build Coastguard Worker       alloc.callstack_allocations()->value.retain_max.cur -= alloc.sample_size;
324*6dbdd20aSAndroid Build Coastguard Worker       alloc.callstack_allocations()->value.retain_max.cur_count--;
325*6dbdd20aSAndroid Build Coastguard Worker     } else {
326*6dbdd20aSAndroid Build Coastguard Worker       alloc.callstack_allocations()->value.totals.freed += alloc.sample_size;
327*6dbdd20aSAndroid Build Coastguard Worker       alloc.callstack_allocations()->value.totals.free_count++;
328*6dbdd20aSAndroid Build Coastguard Worker     }
329*6dbdd20aSAndroid Build Coastguard Worker   }
330*6dbdd20aSAndroid Build Coastguard Worker 
331*6dbdd20aSAndroid Build Coastguard Worker   // We cannot use an interner here, because after the last allocation goes
332*6dbdd20aSAndroid Build Coastguard Worker   // away, we still need to keep the CallstackAllocations around until the next
333*6dbdd20aSAndroid Build Coastguard Worker   // dump.
334*6dbdd20aSAndroid Build Coastguard Worker   std::map<GlobalCallstackTrie::Node*, CallstackAllocations>
335*6dbdd20aSAndroid Build Coastguard Worker       callstack_allocations_;
336*6dbdd20aSAndroid Build Coastguard Worker 
337*6dbdd20aSAndroid Build Coastguard Worker   std::vector<std::pair<decltype(callstack_allocations_)::iterator, uint64_t>>
338*6dbdd20aSAndroid Build Coastguard Worker       dead_callstack_allocations_;
339*6dbdd20aSAndroid Build Coastguard Worker 
340*6dbdd20aSAndroid Build Coastguard Worker   std::map<uint64_t /* allocation address */, Allocation> allocations_;
341*6dbdd20aSAndroid Build Coastguard Worker 
342*6dbdd20aSAndroid Build Coastguard Worker   // An operation is either a commit of an allocation or freeing of an
343*6dbdd20aSAndroid Build Coastguard Worker   // allocation. An operation is a free if its seq_id is larger than
344*6dbdd20aSAndroid Build Coastguard Worker   // the sequence_number of the corresponding allocation. It is a commit if its
345*6dbdd20aSAndroid Build Coastguard Worker   // seq_id is equal to the sequence_number of the corresponding allocation.
346*6dbdd20aSAndroid Build Coastguard Worker   //
347*6dbdd20aSAndroid Build Coastguard Worker   // If its seq_id is less than the sequence_number of the corresponding
348*6dbdd20aSAndroid Build Coastguard Worker   // allocation it could be either, but is ignored either way.
349*6dbdd20aSAndroid Build Coastguard Worker   std::map<uint64_t /* seq_id */, PendingOperation /* allocation address */>
350*6dbdd20aSAndroid Build Coastguard Worker       pending_operations_;
351*6dbdd20aSAndroid Build Coastguard Worker 
352*6dbdd20aSAndroid Build Coastguard Worker   uint64_t committed_timestamp_ = 0;
353*6dbdd20aSAndroid Build Coastguard Worker   // The sequence number all mallocs and frees have been handled up to.
354*6dbdd20aSAndroid Build Coastguard Worker   uint64_t committed_sequence_number_ = 0;
355*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie* callsites_;
356*6dbdd20aSAndroid Build Coastguard Worker 
357*6dbdd20aSAndroid Build Coastguard Worker   const bool dump_at_max_mode_ = false;
358*6dbdd20aSAndroid Build Coastguard Worker   // The following members are only used if dump_at_max_mode_ == true.
359*6dbdd20aSAndroid Build Coastguard Worker   uint64_t max_sequence_number_ = 0;
360*6dbdd20aSAndroid Build Coastguard Worker   uint64_t current_unfreed_ = 0;
361*6dbdd20aSAndroid Build Coastguard Worker   uint64_t max_unfreed_ = 0;
362*6dbdd20aSAndroid Build Coastguard Worker   uint64_t max_timestamp_ = 0;
363*6dbdd20aSAndroid Build Coastguard Worker 
364*6dbdd20aSAndroid Build Coastguard Worker   // We index by abspc, which is unique as long as the maps do not change.
365*6dbdd20aSAndroid Build Coastguard Worker   // This is why we ClearFrameCache after we reparsed maps.
366*6dbdd20aSAndroid Build Coastguard Worker   std::unordered_map<uint64_t /* abs pc */, Interned<Frame>> frame_cache_;
367*6dbdd20aSAndroid Build Coastguard Worker };
368*6dbdd20aSAndroid Build Coastguard Worker 
369*6dbdd20aSAndroid Build Coastguard Worker }  // namespace profiling
370*6dbdd20aSAndroid Build Coastguard Worker }  // namespace perfetto
371*6dbdd20aSAndroid Build Coastguard Worker 
372*6dbdd20aSAndroid Build Coastguard Worker #endif  // SRC_PROFILING_MEMORY_BOOKKEEPING_H_
373