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