2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "sample_tree.h"
19 #include <base/logging.h>
21 #include "environment.h"
23 bool MapComparator::operator()(const MapEntry* map1, const MapEntry* map2) const {
24 if (map1->start_addr != map2->start_addr) {
25 return map1->start_addr < map2->start_addr;
27 if (map1->len != map2->len) {
28 return map1->len < map2->len;
30 if (map1->time != map2->time) {
31 return map1->time < map2->time;
36 void SampleTree::AddThread(int pid, int tid, const std::string& comm) {
37 auto it = thread_tree_.find(tid);
38 if (it == thread_tree_.end()) {
39 ThreadEntry* thread = new ThreadEntry{
42 std::set<MapEntry*, MapComparator>(), // maps
44 auto pair = thread_tree_.insert(std::make_pair(tid, std::unique_ptr<ThreadEntry>(thread)));
48 thread_comm_storage_.push_back(std::unique_ptr<std::string>(new std::string(comm)));
49 it->second->comm = thread_comm_storage_.back()->c_str();
52 void SampleTree::ForkThread(int pid, int tid, int ppid, int ptid) {
53 ThreadEntry* parent = FindThreadOrNew(ppid, ptid);
54 ThreadEntry* child = FindThreadOrNew(pid, tid);
55 child->comm = parent->comm;
56 child->maps = parent->maps;
59 static void RemoveOverlappedMap(std::set<MapEntry*, MapComparator>* map_set, const MapEntry* map) {
60 for (auto it = map_set->begin(); it != map_set->end();) {
61 if ((*it)->start_addr >= map->start_addr + map->len) {
64 if ((*it)->start_addr + (*it)->len <= map->start_addr) {
67 it = map_set->erase(it);
72 void SampleTree::AddKernelMap(uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time,
73 const std::string& filename) {
74 // kernel map len can be 0 when record command is not run in supervisor mode.
78 DsoEntry* dso = FindKernelDsoOrNew(filename);
79 MapEntry* map = new MapEntry{
80 start_addr, len, pgoff, time, dso,
82 map_storage_.push_back(std::unique_ptr<MapEntry>(map));
83 RemoveOverlappedMap(&kernel_map_tree_, map);
84 auto pair = kernel_map_tree_.insert(map);
88 DsoEntry* SampleTree::FindKernelDsoOrNew(const std::string& filename) {
89 if (filename == DEFAULT_KERNEL_MMAP_NAME) {
90 if (kernel_dso_ == nullptr) {
91 kernel_dso_ = DsoFactory::GetInstance()->LoadKernel();
93 return kernel_dso_.get();
95 auto it = module_dso_tree_.find(filename);
96 if (it == module_dso_tree_.end()) {
97 module_dso_tree_[filename] = DsoFactory::GetInstance()->LoadKernelModule(filename);
98 it = module_dso_tree_.find(filename);
100 return it->second.get();
103 void SampleTree::AddThreadMap(int pid, int tid, uint64_t start_addr, uint64_t len, uint64_t pgoff,
104 uint64_t time, const std::string& filename) {
105 ThreadEntry* thread = FindThreadOrNew(pid, tid);
106 DsoEntry* dso = FindUserDsoOrNew(filename);
107 MapEntry* map = new MapEntry{
108 start_addr, len, pgoff, time, dso,
110 map_storage_.push_back(std::unique_ptr<MapEntry>(map));
111 RemoveOverlappedMap(&thread->maps, map);
112 auto pair = thread->maps.insert(map);
116 ThreadEntry* SampleTree::FindThreadOrNew(int pid, int tid) {
117 auto it = thread_tree_.find(tid);
118 if (it == thread_tree_.end()) {
119 AddThread(pid, tid, "unknown");
120 it = thread_tree_.find(tid);
122 CHECK_EQ(pid, it->second.get()->pid) << "tid = " << tid;
124 return it->second.get();
127 DsoEntry* SampleTree::FindUserDsoOrNew(const std::string& filename) {
128 auto it = user_dso_tree_.find(filename);
129 if (it == user_dso_tree_.end()) {
130 user_dso_tree_[filename] = DsoFactory::GetInstance()->LoadDso(filename);
131 it = user_dso_tree_.find(filename);
133 return it->second.get();
136 static bool IsIpInMap(uint64_t ip, const MapEntry* map) {
137 return (map->start_addr <= ip && map->start_addr + map->len > ip);
140 const MapEntry* SampleTree::FindMap(const ThreadEntry* thread, uint64_t ip, bool in_kernel) {
141 // Construct a map_entry which is strictly after the searched map_entry, based on MapComparator.
142 MapEntry find_map = {
150 auto it = thread->maps.upper_bound(&find_map);
151 if (it != thread->maps.begin() && IsIpInMap(ip, *--it)) {
155 auto it = kernel_map_tree_.upper_bound(&find_map);
156 if (it != kernel_map_tree_.begin() && IsIpInMap(ip, *--it)) {
160 return &unknown_map_;
163 SampleEntry* SampleTree::AddSample(int pid, int tid, uint64_t ip, uint64_t time, uint64_t period,
165 const ThreadEntry* thread = FindThreadOrNew(pid, tid);
166 const MapEntry* map = FindMap(thread, ip, in_kernel);
167 const SymbolEntry* symbol = FindSymbol(map, ip);
169 SampleEntry value(ip, time, period, 0, 1, thread, map, symbol);
171 return InsertSample(value);
174 void SampleTree::AddBranchSample(int pid, int tid, uint64_t from_ip, uint64_t to_ip,
175 uint64_t branch_flags, uint64_t time, uint64_t period) {
176 const ThreadEntry* thread = FindThreadOrNew(pid, tid);
177 const MapEntry* from_map = FindMap(thread, from_ip, false);
178 if (from_map == &unknown_map_) {
179 from_map = FindMap(thread, from_ip, true);
181 const SymbolEntry* from_symbol = FindSymbol(from_map, from_ip);
182 const MapEntry* to_map = FindMap(thread, to_ip, false);
183 if (to_map == &unknown_map_) {
184 to_map = FindMap(thread, to_ip, true);
186 const SymbolEntry* to_symbol = FindSymbol(to_map, to_ip);
188 SampleEntry value(to_ip, time, period, 0, 1, thread, to_map, to_symbol);
189 value.branch_from.ip = from_ip;
190 value.branch_from.map = from_map;
191 value.branch_from.symbol = from_symbol;
192 value.branch_from.flags = branch_flags;
197 SampleEntry* SampleTree::AddCallChainSample(int pid, int tid, uint64_t ip, uint64_t time,
198 uint64_t period, bool in_kernel,
199 const std::vector<SampleEntry*>& callchain) {
200 const ThreadEntry* thread = FindThreadOrNew(pid, tid);
201 const MapEntry* map = FindMap(thread, ip, in_kernel);
202 const SymbolEntry* symbol = FindSymbol(map, ip);
204 SampleEntry value(ip, time, 0, period, 0, thread, map, symbol);
206 auto it = sample_tree_.find(&value);
207 if (it != sample_tree_.end()) {
208 SampleEntry* sample = *it;
209 // Process only once for recursive function call.
210 if (std::find(callchain.begin(), callchain.end(), sample) != callchain.end()) {
214 return InsertSample(value);
217 SampleEntry* SampleTree::InsertSample(SampleEntry& value) {
219 auto it = sample_tree_.find(&value);
220 if (it == sample_tree_.end()) {
221 result = AllocateSample(value);
222 auto pair = sample_tree_.insert(result);
226 result->period += value.period;
227 result->accumulated_period += value.accumulated_period;
228 result->sample_count += value.sample_count;
230 total_samples_ += value.sample_count;
231 total_period_ += value.period;
235 SampleEntry* SampleTree::AllocateSample(SampleEntry& value) {
236 SampleEntry* sample = new SampleEntry(std::move(value));
237 sample_storage_.push_back(std::unique_ptr<SampleEntry>(sample));
241 const SymbolEntry* SampleTree::FindSymbol(const MapEntry* map, uint64_t ip) {
242 uint64_t offset_in_file;
243 if (map->dso == kernel_dso_.get()) {
246 offset_in_file = ip - map->start_addr + map->pgoff;
248 const SymbolEntry* symbol = map->dso->FindSymbol(offset_in_file);
249 if (symbol == nullptr) {
250 symbol = &unknown_symbol_;
255 void SampleTree::InsertCallChainForSample(SampleEntry* sample,
256 const std::vector<SampleEntry*>& callchain,
258 sample->callchain.AddCallChain(callchain, period);
261 void SampleTree::VisitAllSamples(std::function<void(const SampleEntry&)> callback) {
262 if (sorted_sample_tree_.size() != sample_tree_.size()) {
263 sorted_sample_tree_.clear();
264 for (auto& sample : sample_tree_) {
265 sample->callchain.SortByPeriod();
266 sorted_sample_tree_.insert(sample);
269 for (auto& sample : sorted_sample_tree_) {