• 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<FrameData> & callstack,uint64_t address,uint64_t sample_size,uint64_t alloc_size,uint64_t sequence_number,uint64_t timestamp)32 void HeapTracker::RecordMalloc(const std::vector<FrameData>& callstack,
33                                uint64_t address,
34                                uint64_t sample_size,
35                                uint64_t alloc_size,
36                                uint64_t sequence_number,
37                                uint64_t timestamp) {
38   std::vector<Interned<Frame>> frames;
39   frames.reserve(callstack.size());
40   for (const FrameData& loc : callstack) {
41     auto frame_it = frame_cache_.find(loc.frame.pc);
42     if (frame_it != frame_cache_.end()) {
43       frames.emplace_back(frame_it->second);
44     } else {
45       frames.emplace_back(callsites_->InternCodeLocation(loc));
46       frame_cache_.emplace(loc.frame.pc, frames.back());
47     }
48   }
49 
50   auto it = allocations_.find(address);
51   if (it != allocations_.end()) {
52     Allocation& alloc = it->second;
53     PERFETTO_DCHECK(alloc.sequence_number != sequence_number);
54     if (alloc.sequence_number < sequence_number) {
55       // As we are overwriting the previous allocation, the previous allocation
56       // must have been freed.
57       //
58       // This makes the sequencing a bit incorrect. We are overwriting this
59       // allocation, so we prentend both the alloc and the free for this have
60       // already happened at committed_sequence_number_, while in fact the free
61       // might not have happened until right before this operation.
62 
63       if (alloc.sequence_number > committed_sequence_number_) {
64         // Only count the previous allocation if it hasn't already been
65         // committed to avoid double counting it.
66         AddToCallstackAllocations(timestamp, alloc);
67       }
68 
69       SubtractFromCallstackAllocations(alloc);
70       GlobalCallstackTrie::Node* node = callsites_->CreateCallsite(frames);
71       alloc.sample_size = sample_size;
72       alloc.alloc_size = alloc_size;
73       alloc.sequence_number = sequence_number;
74       alloc.SetCallstackAllocations(MaybeCreateCallstackAllocations(node));
75     }
76   } else {
77     GlobalCallstackTrie::Node* node = callsites_->CreateCallsite(frames);
78     allocations_.emplace(address,
79                          Allocation(sample_size, alloc_size, sequence_number,
80                                     MaybeCreateCallstackAllocations(node)));
81   }
82 
83   RecordOperation(sequence_number, {address, timestamp});
84 }
85 
RecordOperation(uint64_t sequence_number,const PendingOperation & operation)86 void HeapTracker::RecordOperation(uint64_t sequence_number,
87                                   const PendingOperation& operation) {
88   if (sequence_number != committed_sequence_number_ + 1) {
89     pending_operations_.emplace(sequence_number, operation);
90     return;
91   }
92 
93   CommitOperation(sequence_number, operation);
94 
95   // At this point some other pending operations might be eligible to be
96   // committed.
97   auto it = pending_operations_.begin();
98   while (it != pending_operations_.end() &&
99          it->first == committed_sequence_number_ + 1) {
100     CommitOperation(it->first, it->second);
101     it = pending_operations_.erase(it);
102   }
103 }
104 
CommitOperation(uint64_t sequence_number,const PendingOperation & operation)105 void HeapTracker::CommitOperation(uint64_t sequence_number,
106                                   const PendingOperation& operation) {
107   committed_sequence_number_++;
108   committed_timestamp_ = operation.timestamp;
109 
110   uint64_t address = operation.allocation_address;
111 
112   // We will see many frees for addresses we do not know about.
113   auto leaf_it = allocations_.find(address);
114   if (leaf_it == allocations_.end())
115     return;
116 
117   Allocation& value = leaf_it->second;
118   if (value.sequence_number == sequence_number) {
119     AddToCallstackAllocations(operation.timestamp, value);
120   } else if (value.sequence_number < sequence_number) {
121     SubtractFromCallstackAllocations(value);
122     allocations_.erase(leaf_it);
123   }
124   // else (value.sequence_number > sequence_number:
125   //  This allocation has been replaced by a newer one in RecordMalloc.
126   //  This code commits ther previous allocation's malloc (and implicit free
127   //  that must have happened, as there is now a new allocation at the same
128   //  address). This means that this operation, be it a malloc or a free, must
129   //  be treated as a no-op.
130 }
131 
GetSizeForTesting(const std::vector<FrameData> & stack)132 uint64_t HeapTracker::GetSizeForTesting(const std::vector<FrameData>& stack) {
133   PERFETTO_DCHECK(!dump_at_max_mode_);
134   GlobalCallstackTrie::Node* node = callsites_->CreateCallsite(stack);
135   // Hack to make it go away again if it wasn't used before.
136   // This is only good because this is used for testing only.
137   GlobalCallstackTrie::IncrementNode(node);
138   GlobalCallstackTrie::DecrementNode(node);
139   auto it = callstack_allocations_.find(node);
140   if (it == callstack_allocations_.end()) {
141     return 0;
142   }
143   const CallstackAllocations& alloc = it->second;
144   return alloc.value.totals.allocated - alloc.value.totals.freed;
145 }
146 
GetMaxForTesting(const std::vector<FrameData> & stack)147 uint64_t HeapTracker::GetMaxForTesting(const std::vector<FrameData>& stack) {
148   PERFETTO_DCHECK(dump_at_max_mode_);
149   GlobalCallstackTrie::Node* node = callsites_->CreateCallsite(stack);
150   // Hack to make it go away again if it wasn't used before.
151   // This is only good because this is used for testing only.
152   GlobalCallstackTrie::IncrementNode(node);
153   GlobalCallstackTrie::DecrementNode(node);
154   auto it = callstack_allocations_.find(node);
155   if (it == callstack_allocations_.end()) {
156     return 0;
157   }
158   const CallstackAllocations& alloc = it->second;
159   return alloc.value.retain_max.max;
160 }
161 
162 
163 }  // namespace profiling
164 }  // namespace perfetto
165