xref: /aosp_15_r20/external/perfetto/src/profiling/memory/bookkeeping.cc (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 #include "src/profiling/memory/bookkeeping.h"
18*6dbdd20aSAndroid Build Coastguard Worker 
19*6dbdd20aSAndroid Build Coastguard Worker #include <fcntl.h>
20*6dbdd20aSAndroid Build Coastguard Worker #include <sys/stat.h>
21*6dbdd20aSAndroid Build Coastguard Worker #include <sys/types.h>
22*6dbdd20aSAndroid Build Coastguard Worker 
23*6dbdd20aSAndroid Build Coastguard Worker #include <cinttypes>
24*6dbdd20aSAndroid Build Coastguard Worker 
25*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/base/logging.h"
26*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/file_utils.h"
27*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/scoped_file.h"
28*6dbdd20aSAndroid Build Coastguard Worker #include "src/profiling/common/callstack_trie.h"
29*6dbdd20aSAndroid Build Coastguard Worker 
30*6dbdd20aSAndroid Build Coastguard Worker namespace perfetto {
31*6dbdd20aSAndroid Build Coastguard Worker namespace profiling {
32*6dbdd20aSAndroid Build Coastguard Worker 
RecordMalloc(const std::vector<unwindstack::FrameData> & callstack,const std::vector<std::string> & build_ids,uint64_t address,uint64_t sample_size,uint64_t alloc_size,uint64_t sequence_number,uint64_t timestamp)33*6dbdd20aSAndroid Build Coastguard Worker void HeapTracker::RecordMalloc(
34*6dbdd20aSAndroid Build Coastguard Worker     const std::vector<unwindstack::FrameData>& callstack,
35*6dbdd20aSAndroid Build Coastguard Worker     const std::vector<std::string>& build_ids,
36*6dbdd20aSAndroid Build Coastguard Worker     uint64_t address,
37*6dbdd20aSAndroid Build Coastguard Worker     uint64_t sample_size,
38*6dbdd20aSAndroid Build Coastguard Worker     uint64_t alloc_size,
39*6dbdd20aSAndroid Build Coastguard Worker     uint64_t sequence_number,
40*6dbdd20aSAndroid Build Coastguard Worker     uint64_t timestamp) {
41*6dbdd20aSAndroid Build Coastguard Worker   PERFETTO_CHECK(callstack.size() == build_ids.size());
42*6dbdd20aSAndroid Build Coastguard Worker   std::vector<Interned<Frame>> frames;
43*6dbdd20aSAndroid Build Coastguard Worker   frames.reserve(callstack.size());
44*6dbdd20aSAndroid Build Coastguard Worker   for (size_t i = 0; i < callstack.size(); ++i) {
45*6dbdd20aSAndroid Build Coastguard Worker     const unwindstack::FrameData& loc = callstack[i];
46*6dbdd20aSAndroid Build Coastguard Worker     const std::string& build_id = build_ids[i];
47*6dbdd20aSAndroid Build Coastguard Worker     auto frame_it = frame_cache_.find(loc.pc);
48*6dbdd20aSAndroid Build Coastguard Worker     if (frame_it != frame_cache_.end()) {
49*6dbdd20aSAndroid Build Coastguard Worker       frames.emplace_back(frame_it->second);
50*6dbdd20aSAndroid Build Coastguard Worker     } else {
51*6dbdd20aSAndroid Build Coastguard Worker       frames.emplace_back(callsites_->InternCodeLocation(loc, build_id));
52*6dbdd20aSAndroid Build Coastguard Worker       frame_cache_.emplace(loc.pc, frames.back());
53*6dbdd20aSAndroid Build Coastguard Worker     }
54*6dbdd20aSAndroid Build Coastguard Worker   }
55*6dbdd20aSAndroid Build Coastguard Worker 
56*6dbdd20aSAndroid Build Coastguard Worker   auto it = allocations_.find(address);
57*6dbdd20aSAndroid Build Coastguard Worker   if (it != allocations_.end()) {
58*6dbdd20aSAndroid Build Coastguard Worker     Allocation& alloc = it->second;
59*6dbdd20aSAndroid Build Coastguard Worker     PERFETTO_DCHECK(alloc.sequence_number != sequence_number);
60*6dbdd20aSAndroid Build Coastguard Worker     if (alloc.sequence_number < sequence_number) {
61*6dbdd20aSAndroid Build Coastguard Worker       // As we are overwriting the previous allocation, the previous allocation
62*6dbdd20aSAndroid Build Coastguard Worker       // must have been freed.
63*6dbdd20aSAndroid Build Coastguard Worker       //
64*6dbdd20aSAndroid Build Coastguard Worker       // This makes the sequencing a bit incorrect. We are overwriting this
65*6dbdd20aSAndroid Build Coastguard Worker       // allocation, so we prentend both the alloc and the free for this have
66*6dbdd20aSAndroid Build Coastguard Worker       // already happened at committed_sequence_number_, while in fact the free
67*6dbdd20aSAndroid Build Coastguard Worker       // might not have happened until right before this operation.
68*6dbdd20aSAndroid Build Coastguard Worker 
69*6dbdd20aSAndroid Build Coastguard Worker       if (alloc.sequence_number > committed_sequence_number_) {
70*6dbdd20aSAndroid Build Coastguard Worker         // Only count the previous allocation if it hasn't already been
71*6dbdd20aSAndroid Build Coastguard Worker         // committed to avoid double counting it.
72*6dbdd20aSAndroid Build Coastguard Worker         AddToCallstackAllocations(timestamp, alloc);
73*6dbdd20aSAndroid Build Coastguard Worker       }
74*6dbdd20aSAndroid Build Coastguard Worker 
75*6dbdd20aSAndroid Build Coastguard Worker       SubtractFromCallstackAllocations(alloc);
76*6dbdd20aSAndroid Build Coastguard Worker       GlobalCallstackTrie::Node* node = callsites_->CreateCallsite(frames);
77*6dbdd20aSAndroid Build Coastguard Worker       alloc.sample_size = sample_size;
78*6dbdd20aSAndroid Build Coastguard Worker       alloc.alloc_size = alloc_size;
79*6dbdd20aSAndroid Build Coastguard Worker       alloc.sequence_number = sequence_number;
80*6dbdd20aSAndroid Build Coastguard Worker       alloc.SetCallstackAllocations(MaybeCreateCallstackAllocations(node));
81*6dbdd20aSAndroid Build Coastguard Worker     }
82*6dbdd20aSAndroid Build Coastguard Worker   } else {
83*6dbdd20aSAndroid Build Coastguard Worker     GlobalCallstackTrie::Node* node = callsites_->CreateCallsite(frames);
84*6dbdd20aSAndroid Build Coastguard Worker     allocations_.emplace(address,
85*6dbdd20aSAndroid Build Coastguard Worker                          Allocation(sample_size, alloc_size, sequence_number,
86*6dbdd20aSAndroid Build Coastguard Worker                                     MaybeCreateCallstackAllocations(node)));
87*6dbdd20aSAndroid Build Coastguard Worker   }
88*6dbdd20aSAndroid Build Coastguard Worker 
89*6dbdd20aSAndroid Build Coastguard Worker   RecordOperation(sequence_number, {address, timestamp});
90*6dbdd20aSAndroid Build Coastguard Worker }
91*6dbdd20aSAndroid Build Coastguard Worker 
RecordOperation(uint64_t sequence_number,const PendingOperation & operation)92*6dbdd20aSAndroid Build Coastguard Worker void HeapTracker::RecordOperation(uint64_t sequence_number,
93*6dbdd20aSAndroid Build Coastguard Worker                                   const PendingOperation& operation) {
94*6dbdd20aSAndroid Build Coastguard Worker   if (sequence_number != committed_sequence_number_ + 1) {
95*6dbdd20aSAndroid Build Coastguard Worker     pending_operations_.emplace(sequence_number, operation);
96*6dbdd20aSAndroid Build Coastguard Worker     return;
97*6dbdd20aSAndroid Build Coastguard Worker   }
98*6dbdd20aSAndroid Build Coastguard Worker 
99*6dbdd20aSAndroid Build Coastguard Worker   CommitOperation(sequence_number, operation);
100*6dbdd20aSAndroid Build Coastguard Worker 
101*6dbdd20aSAndroid Build Coastguard Worker   // At this point some other pending operations might be eligible to be
102*6dbdd20aSAndroid Build Coastguard Worker   // committed.
103*6dbdd20aSAndroid Build Coastguard Worker   auto it = pending_operations_.begin();
104*6dbdd20aSAndroid Build Coastguard Worker   while (it != pending_operations_.end() &&
105*6dbdd20aSAndroid Build Coastguard Worker          it->first == committed_sequence_number_ + 1) {
106*6dbdd20aSAndroid Build Coastguard Worker     CommitOperation(it->first, it->second);
107*6dbdd20aSAndroid Build Coastguard Worker     it = pending_operations_.erase(it);
108*6dbdd20aSAndroid Build Coastguard Worker   }
109*6dbdd20aSAndroid Build Coastguard Worker }
110*6dbdd20aSAndroid Build Coastguard Worker 
CommitOperation(uint64_t sequence_number,const PendingOperation & operation)111*6dbdd20aSAndroid Build Coastguard Worker void HeapTracker::CommitOperation(uint64_t sequence_number,
112*6dbdd20aSAndroid Build Coastguard Worker                                   const PendingOperation& operation) {
113*6dbdd20aSAndroid Build Coastguard Worker   committed_sequence_number_++;
114*6dbdd20aSAndroid Build Coastguard Worker   if (operation.timestamp)
115*6dbdd20aSAndroid Build Coastguard Worker     committed_timestamp_ = operation.timestamp;
116*6dbdd20aSAndroid Build Coastguard Worker 
117*6dbdd20aSAndroid Build Coastguard Worker   uint64_t address = operation.allocation_address;
118*6dbdd20aSAndroid Build Coastguard Worker 
119*6dbdd20aSAndroid Build Coastguard Worker   // We will see many frees for addresses we do not know about.
120*6dbdd20aSAndroid Build Coastguard Worker   auto leaf_it = allocations_.find(address);
121*6dbdd20aSAndroid Build Coastguard Worker   if (leaf_it == allocations_.end())
122*6dbdd20aSAndroid Build Coastguard Worker     return;
123*6dbdd20aSAndroid Build Coastguard Worker 
124*6dbdd20aSAndroid Build Coastguard Worker   Allocation& value = leaf_it->second;
125*6dbdd20aSAndroid Build Coastguard Worker   if (value.sequence_number == sequence_number) {
126*6dbdd20aSAndroid Build Coastguard Worker     AddToCallstackAllocations(operation.timestamp, value);
127*6dbdd20aSAndroid Build Coastguard Worker   } else if (value.sequence_number < sequence_number) {
128*6dbdd20aSAndroid Build Coastguard Worker     SubtractFromCallstackAllocations(value);
129*6dbdd20aSAndroid Build Coastguard Worker     allocations_.erase(leaf_it);
130*6dbdd20aSAndroid Build Coastguard Worker   }
131*6dbdd20aSAndroid Build Coastguard Worker   // else (value.sequence_number > sequence_number:
132*6dbdd20aSAndroid Build Coastguard Worker   //  This allocation has been replaced by a newer one in RecordMalloc.
133*6dbdd20aSAndroid Build Coastguard Worker   //  This code commits ther previous allocation's malloc (and implicit free
134*6dbdd20aSAndroid Build Coastguard Worker   //  that must have happened, as there is now a new allocation at the same
135*6dbdd20aSAndroid Build Coastguard Worker   //  address). This means that this operation, be it a malloc or a free, must
136*6dbdd20aSAndroid Build Coastguard Worker   //  be treated as a no-op.
137*6dbdd20aSAndroid Build Coastguard Worker }
138*6dbdd20aSAndroid Build Coastguard Worker 
GetSizeForTesting(const std::vector<unwindstack::FrameData> & stack,std::vector<std::string> build_ids)139*6dbdd20aSAndroid Build Coastguard Worker uint64_t HeapTracker::GetSizeForTesting(
140*6dbdd20aSAndroid Build Coastguard Worker     const std::vector<unwindstack::FrameData>& stack,
141*6dbdd20aSAndroid Build Coastguard Worker     std::vector<std::string> build_ids) {
142*6dbdd20aSAndroid Build Coastguard Worker   PERFETTO_DCHECK(!dump_at_max_mode_);
143*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie::Node* node =
144*6dbdd20aSAndroid Build Coastguard Worker       callsites_->CreateCallsite(stack, build_ids);
145*6dbdd20aSAndroid Build Coastguard Worker   // Hack to make it go away again if it wasn't used before.
146*6dbdd20aSAndroid Build Coastguard Worker   // This is only good because this is used for testing only.
147*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie::IncrementNode(node);
148*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie::DecrementNode(node);
149*6dbdd20aSAndroid Build Coastguard Worker   auto it = callstack_allocations_.find(node);
150*6dbdd20aSAndroid Build Coastguard Worker   if (it == callstack_allocations_.end()) {
151*6dbdd20aSAndroid Build Coastguard Worker     return 0;
152*6dbdd20aSAndroid Build Coastguard Worker   }
153*6dbdd20aSAndroid Build Coastguard Worker   const CallstackAllocations& alloc = it->second;
154*6dbdd20aSAndroid Build Coastguard Worker   return alloc.value.totals.allocated - alloc.value.totals.freed;
155*6dbdd20aSAndroid Build Coastguard Worker }
156*6dbdd20aSAndroid Build Coastguard Worker 
GetMaxForTesting(const std::vector<unwindstack::FrameData> & stack,std::vector<std::string> build_ids)157*6dbdd20aSAndroid Build Coastguard Worker uint64_t HeapTracker::GetMaxForTesting(
158*6dbdd20aSAndroid Build Coastguard Worker     const std::vector<unwindstack::FrameData>& stack,
159*6dbdd20aSAndroid Build Coastguard Worker     std::vector<std::string> build_ids) {
160*6dbdd20aSAndroid Build Coastguard Worker   PERFETTO_DCHECK(dump_at_max_mode_);
161*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie::Node* node =
162*6dbdd20aSAndroid Build Coastguard Worker       callsites_->CreateCallsite(stack, build_ids);
163*6dbdd20aSAndroid Build Coastguard Worker   // Hack to make it go away again if it wasn't used before.
164*6dbdd20aSAndroid Build Coastguard Worker   // This is only good because this is used for testing only.
165*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie::IncrementNode(node);
166*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie::DecrementNode(node);
167*6dbdd20aSAndroid Build Coastguard Worker   auto it = callstack_allocations_.find(node);
168*6dbdd20aSAndroid Build Coastguard Worker   if (it == callstack_allocations_.end()) {
169*6dbdd20aSAndroid Build Coastguard Worker     return 0;
170*6dbdd20aSAndroid Build Coastguard Worker   }
171*6dbdd20aSAndroid Build Coastguard Worker   const CallstackAllocations& alloc = it->second;
172*6dbdd20aSAndroid Build Coastguard Worker   return alloc.value.retain_max.max;
173*6dbdd20aSAndroid Build Coastguard Worker }
174*6dbdd20aSAndroid Build Coastguard Worker 
GetMaxCountForTesting(const std::vector<unwindstack::FrameData> & stack,std::vector<std::string> build_ids)175*6dbdd20aSAndroid Build Coastguard Worker uint64_t HeapTracker::GetMaxCountForTesting(
176*6dbdd20aSAndroid Build Coastguard Worker     const std::vector<unwindstack::FrameData>& stack,
177*6dbdd20aSAndroid Build Coastguard Worker     std::vector<std::string> build_ids) {
178*6dbdd20aSAndroid Build Coastguard Worker   PERFETTO_DCHECK(dump_at_max_mode_);
179*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie::Node* node =
180*6dbdd20aSAndroid Build Coastguard Worker       callsites_->CreateCallsite(stack, build_ids);
181*6dbdd20aSAndroid Build Coastguard Worker   // Hack to make it go away again if it wasn't used before.
182*6dbdd20aSAndroid Build Coastguard Worker   // This is only good because this is used for testing only.
183*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie::IncrementNode(node);
184*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie::DecrementNode(node);
185*6dbdd20aSAndroid Build Coastguard Worker   auto it = callstack_allocations_.find(node);
186*6dbdd20aSAndroid Build Coastguard Worker   if (it == callstack_allocations_.end()) {
187*6dbdd20aSAndroid Build Coastguard Worker     return 0;
188*6dbdd20aSAndroid Build Coastguard Worker   }
189*6dbdd20aSAndroid Build Coastguard Worker   const CallstackAllocations& alloc = it->second;
190*6dbdd20aSAndroid Build Coastguard Worker   return alloc.value.retain_max.max_count;
191*6dbdd20aSAndroid Build Coastguard Worker }
192*6dbdd20aSAndroid Build Coastguard Worker 
193*6dbdd20aSAndroid Build Coastguard Worker }  // namespace profiling
194*6dbdd20aSAndroid Build Coastguard Worker }  // namespace perfetto
195