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