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