• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 "ETMBranchListFile.h"
18 
19 #include "ETMDecoder.h"
20 #include "system/extras/simpleperf/etm_branch_list.pb.h"
21 
22 namespace simpleperf {
23 
24 static constexpr const char* ETM_BRANCH_LIST_PROTO_MAGIC = "simpleperf:EtmBranchList";
25 
BranchToProtoString(const std::vector<bool> & branch)26 std::string BranchToProtoString(const std::vector<bool>& branch) {
27   size_t bytes = (branch.size() + 7) / 8;
28   std::string res(bytes, '\0');
29   for (size_t i = 0; i < branch.size(); i++) {
30     if (branch[i]) {
31       res[i >> 3] |= 1 << (i & 7);
32     }
33   }
34   return res;
35 }
36 
ProtoStringToBranch(const std::string & s,size_t bit_size)37 std::vector<bool> ProtoStringToBranch(const std::string& s, size_t bit_size) {
38   std::vector<bool> branch(bit_size, false);
39   for (size_t i = 0; i < bit_size; i++) {
40     if (s[i >> 3] & (1 << (i & 7))) {
41       branch[i] = true;
42     }
43   }
44   return branch;
45 }
46 
ToProtoBinaryType(DsoType dso_type)47 static std::optional<proto::ETMBranchList_Binary::BinaryType> ToProtoBinaryType(DsoType dso_type) {
48   switch (dso_type) {
49     case DSO_ELF_FILE:
50       return proto::ETMBranchList_Binary::ELF_FILE;
51     case DSO_KERNEL:
52       return proto::ETMBranchList_Binary::KERNEL;
53     case DSO_KERNEL_MODULE:
54       return proto::ETMBranchList_Binary::KERNEL_MODULE;
55     default:
56       LOG(ERROR) << "unexpected dso type " << dso_type;
57       return std::nullopt;
58   }
59 }
60 
BranchListBinaryMapToString(const BranchListBinaryMap & binary_map,std::string & s)61 bool BranchListBinaryMapToString(const BranchListBinaryMap& binary_map, std::string& s) {
62   proto::ETMBranchList branch_list_proto;
63   branch_list_proto.set_magic(ETM_BRANCH_LIST_PROTO_MAGIC);
64   std::vector<char> branch_buf;
65   for (const auto& p : binary_map) {
66     const BinaryKey& key = p.first;
67     const BranchListBinaryInfo& binary = p.second;
68     auto binary_proto = branch_list_proto.add_binaries();
69 
70     binary_proto->set_path(key.path);
71     if (!key.build_id.IsEmpty()) {
72       binary_proto->set_build_id(key.build_id.ToString().substr(2));
73     }
74     auto opt_binary_type = ToProtoBinaryType(binary.dso_type);
75     if (!opt_binary_type.has_value()) {
76       return false;
77     }
78     binary_proto->set_type(opt_binary_type.value());
79 
80     for (const auto& addr_p : binary.branch_map) {
81       auto addr_proto = binary_proto->add_addrs();
82       addr_proto->set_addr(addr_p.first);
83 
84       for (const auto& branch_p : addr_p.second) {
85         const std::vector<bool>& branch = branch_p.first;
86         auto branch_proto = addr_proto->add_branches();
87 
88         branch_proto->set_branch(BranchToProtoString(branch));
89         branch_proto->set_branch_size(branch.size());
90         branch_proto->set_count(branch_p.second);
91       }
92     }
93 
94     if (binary.dso_type == DSO_KERNEL) {
95       binary_proto->mutable_kernel_info()->set_kernel_start_addr(key.kernel_start_addr);
96     }
97   }
98   if (!branch_list_proto.SerializeToString(&s)) {
99     LOG(ERROR) << "failed to serialize branch list binary map";
100     return false;
101   }
102   return true;
103 }
104 
ToDsoType(proto::ETMBranchList_Binary::BinaryType binary_type)105 static std::optional<DsoType> ToDsoType(proto::ETMBranchList_Binary::BinaryType binary_type) {
106   switch (binary_type) {
107     case proto::ETMBranchList_Binary::ELF_FILE:
108       return DSO_ELF_FILE;
109     case proto::ETMBranchList_Binary::KERNEL:
110       return DSO_KERNEL;
111     case proto::ETMBranchList_Binary::KERNEL_MODULE:
112       return DSO_KERNEL_MODULE;
113     default:
114       LOG(ERROR) << "unexpected binary type " << binary_type;
115       return std::nullopt;
116   }
117 }
118 
BuildUnorderedBranchMap(const proto::ETMBranchList_Binary & binary_proto)119 static UnorderedBranchMap BuildUnorderedBranchMap(const proto::ETMBranchList_Binary& binary_proto) {
120   UnorderedBranchMap branch_map;
121   for (size_t i = 0; i < binary_proto.addrs_size(); i++) {
122     const auto& addr_proto = binary_proto.addrs(i);
123     auto& b_map = branch_map[addr_proto.addr()];
124     for (size_t j = 0; j < addr_proto.branches_size(); j++) {
125       const auto& branch_proto = addr_proto.branches(j);
126       std::vector<bool> branch =
127           ProtoStringToBranch(branch_proto.branch(), branch_proto.branch_size());
128       b_map[branch] = branch_proto.count();
129     }
130   }
131   return branch_map;
132 }
133 
StringToBranchListBinaryMap(const std::string & s,BranchListBinaryMap & binary_map)134 bool StringToBranchListBinaryMap(const std::string& s, BranchListBinaryMap& binary_map) {
135   proto::ETMBranchList branch_list_proto;
136   if (!branch_list_proto.ParseFromString(s)) {
137     PLOG(ERROR) << "failed to read ETMBranchList msg";
138     return false;
139   }
140   if (branch_list_proto.magic() != ETM_BRANCH_LIST_PROTO_MAGIC) {
141     PLOG(ERROR) << "not in format etm_branch_list.proto";
142     return false;
143   }
144 
145   for (size_t i = 0; i < branch_list_proto.binaries_size(); i++) {
146     const auto& binary_proto = branch_list_proto.binaries(i);
147     BinaryKey key(binary_proto.path(), BuildId(binary_proto.build_id()));
148     if (binary_proto.has_kernel_info()) {
149       key.kernel_start_addr = binary_proto.kernel_info().kernel_start_addr();
150     }
151     BranchListBinaryInfo& binary = binary_map[key];
152     auto dso_type = ToDsoType(binary_proto.type());
153     if (!dso_type) {
154       LOG(ERROR) << "invalid binary type " << binary_proto.type();
155       return false;
156     }
157     binary.dso_type = dso_type.value();
158     binary.branch_map = BuildUnorderedBranchMap(binary_proto);
159   }
160   return true;
161 }
162 
163 class ETMThreadTreeWhenRecording : public ETMThreadTree {
164  public:
ETMThreadTreeWhenRecording(bool dump_maps_from_proc)165   ETMThreadTreeWhenRecording(bool dump_maps_from_proc)
166       : dump_maps_from_proc_(dump_maps_from_proc) {}
167 
GetThreadTree()168   ThreadTree& GetThreadTree() { return thread_tree_; }
ExcludePid(pid_t pid)169   void ExcludePid(pid_t pid) { exclude_pid_ = pid; }
170 
FindThread(int tid)171   const ThreadEntry* FindThread(int tid) override {
172     const ThreadEntry* thread = thread_tree_.FindThread(tid);
173     if (thread == nullptr) {
174       if (dump_maps_from_proc_) {
175         thread = FindThreadFromProc(tid);
176       }
177       if (thread == nullptr) {
178         return nullptr;
179       }
180     }
181     if (exclude_pid_ && exclude_pid_ == thread->pid) {
182       return nullptr;
183     }
184 
185     if (dump_maps_from_proc_) {
186       DumpMapsFromProc(thread->pid);
187     }
188     return thread;
189   }
190 
DisableThreadExitRecords()191   void DisableThreadExitRecords() override { thread_tree_.DisableThreadExitRecords(); }
GetKernelMaps()192   const MapSet& GetKernelMaps() override { return thread_tree_.GetKernelMaps(); }
193 
194  private:
FindThreadFromProc(int tid)195   const ThreadEntry* FindThreadFromProc(int tid) {
196     std::string comm;
197     pid_t pid;
198     if (ReadThreadNameAndPid(tid, &comm, &pid)) {
199       thread_tree_.SetThreadName(pid, tid, comm);
200       return thread_tree_.FindThread(tid);
201     }
202     return nullptr;
203   }
204 
DumpMapsFromProc(int pid)205   void DumpMapsFromProc(int pid) {
206     if (dumped_processes_.count(pid) == 0) {
207       dumped_processes_.insert(pid);
208       std::vector<ThreadMmap> maps;
209       if (GetThreadMmapsInProcess(pid, &maps)) {
210         for (const auto& map : maps) {
211           thread_tree_.AddThreadMap(pid, pid, map.start_addr, map.len, map.pgoff, map.name);
212         }
213       }
214     }
215   }
216 
217   ThreadTree thread_tree_;
218   bool dump_maps_from_proc_;
219   std::unordered_set<int> dumped_processes_;
220   std::optional<pid_t> exclude_pid_;
221 };
222 
223 class ETMBranchListGeneratorImpl : public ETMBranchListGenerator {
224  public:
ETMBranchListGeneratorImpl(bool dump_maps_from_proc)225   ETMBranchListGeneratorImpl(bool dump_maps_from_proc)
226       : thread_tree_(dump_maps_from_proc), binary_filter_(nullptr) {}
227 
SetExcludePid(pid_t pid)228   void SetExcludePid(pid_t pid) override { thread_tree_.ExcludePid(pid); }
SetBinaryFilter(const RegEx * binary_name_regex)229   void SetBinaryFilter(const RegEx* binary_name_regex) override {
230     binary_filter_.SetRegex(binary_name_regex);
231   }
232 
233   bool ProcessRecord(const Record& r, bool& consumed) override;
234   BranchListBinaryMap GetBranchListBinaryMap() override;
235 
236  private:
237   struct AuxRecordData {
238     uint64_t start;
239     uint64_t end;
240     bool formatted;
AuxRecordDatasimpleperf::ETMBranchListGeneratorImpl::AuxRecordData241     AuxRecordData(uint64_t start, uint64_t end, bool formatted)
242         : start(start), end(end), formatted(formatted) {}
243   };
244 
245   struct PerCpuData {
246     std::vector<uint8_t> aux_data;
247     uint64_t data_offset = 0;
248     std::queue<AuxRecordData> aux_records;
249   };
250 
251   bool ProcessAuxRecord(const AuxRecord& r);
252   bool ProcessAuxTraceRecord(const AuxTraceRecord& r);
253   void ProcessBranchList(const ETMBranchList& branch_list);
254 
255   ETMThreadTreeWhenRecording thread_tree_;
256   uint64_t kernel_map_start_addr_ = 0;
257   BinaryFilter binary_filter_;
258   std::map<uint32_t, PerCpuData> cpu_map_;
259   std::unique_ptr<ETMDecoder> etm_decoder_;
260   std::unordered_map<Dso*, BranchListBinaryInfo> branch_list_binary_map_;
261 };
262 
ProcessRecord(const Record & r,bool & consumed)263 bool ETMBranchListGeneratorImpl::ProcessRecord(const Record& r, bool& consumed) {
264   consumed = true;  // No need to store any records.
265   uint32_t type = r.type();
266   if (type == PERF_RECORD_AUXTRACE_INFO) {
267     etm_decoder_ = ETMDecoder::Create(*static_cast<const AuxTraceInfoRecord*>(&r), thread_tree_);
268     if (!etm_decoder_) {
269       return false;
270     }
271     etm_decoder_->RegisterCallback(
272         [this](const ETMBranchList& branch) { ProcessBranchList(branch); });
273     return true;
274   }
275   if (type == PERF_RECORD_AUX) {
276     return ProcessAuxRecord(*static_cast<const AuxRecord*>(&r));
277   }
278   if (type == PERF_RECORD_AUXTRACE) {
279     return ProcessAuxTraceRecord(*static_cast<const AuxTraceRecord*>(&r));
280   }
281   if (type == PERF_RECORD_MMAP && r.InKernel()) {
282     auto& mmap_r = *static_cast<const MmapRecord*>(&r);
283     if (android::base::StartsWith(mmap_r.filename, DEFAULT_KERNEL_MMAP_NAME)) {
284       kernel_map_start_addr_ = mmap_r.data->addr;
285     }
286   }
287   thread_tree_.GetThreadTree().Update(r);
288   return true;
289 }
290 
ProcessAuxRecord(const AuxRecord & r)291 bool ETMBranchListGeneratorImpl::ProcessAuxRecord(const AuxRecord& r) {
292   OverflowResult result = SafeAdd(r.data->aux_offset, r.data->aux_size);
293   if (result.overflow || r.data->aux_size > SIZE_MAX) {
294     LOG(ERROR) << "invalid aux record";
295     return false;
296   }
297   size_t size = r.data->aux_size;
298   uint64_t start = r.data->aux_offset;
299   uint64_t end = result.value;
300   PerCpuData& data = cpu_map_[r.Cpu()];
301   if (start >= data.data_offset && end <= data.data_offset + data.aux_data.size()) {
302     // The ETM data is available. Process it now.
303     uint8_t* p = data.aux_data.data() + (start - data.data_offset);
304     if (!etm_decoder_) {
305       LOG(ERROR) << "ETMDecoder isn't created";
306       return false;
307     }
308     return etm_decoder_->ProcessData(p, size, !r.Unformatted(), r.Cpu());
309   }
310   // The ETM data isn't available. Put the aux record into queue.
311   data.aux_records.emplace(start, end, !r.Unformatted());
312   return true;
313 }
314 
ProcessAuxTraceRecord(const AuxTraceRecord & r)315 bool ETMBranchListGeneratorImpl::ProcessAuxTraceRecord(const AuxTraceRecord& r) {
316   OverflowResult result = SafeAdd(r.data->offset, r.data->aux_size);
317   if (result.overflow || r.data->aux_size > SIZE_MAX) {
318     LOG(ERROR) << "invalid auxtrace record";
319     return false;
320   }
321   size_t size = r.data->aux_size;
322   uint64_t start = r.data->offset;
323   uint64_t end = result.value;
324   PerCpuData& data = cpu_map_[r.Cpu()];
325   data.data_offset = start;
326   CHECK(r.location.addr != nullptr);
327   data.aux_data.resize(size);
328   memcpy(data.aux_data.data(), r.location.addr, size);
329 
330   // Process cached aux records.
331   while (!data.aux_records.empty() && data.aux_records.front().start < end) {
332     const AuxRecordData& aux = data.aux_records.front();
333     if (aux.start >= start && aux.end <= end) {
334       uint8_t* p = data.aux_data.data() + (aux.start - start);
335       if (!etm_decoder_) {
336         LOG(ERROR) << "ETMDecoder isn't created";
337         return false;
338       }
339       if (!etm_decoder_->ProcessData(p, aux.end - aux.start, aux.formatted, r.Cpu())) {
340         return false;
341       }
342     }
343     data.aux_records.pop();
344   }
345   return true;
346 }
347 
ProcessBranchList(const ETMBranchList & branch_list)348 void ETMBranchListGeneratorImpl::ProcessBranchList(const ETMBranchList& branch_list) {
349   if (!binary_filter_.Filter(branch_list.dso)) {
350     return;
351   }
352   auto& branch_map = branch_list_binary_map_[branch_list.dso].branch_map;
353   ++branch_map[branch_list.addr][branch_list.branch];
354 }
355 
GetBranchListBinaryMap()356 BranchListBinaryMap ETMBranchListGeneratorImpl::GetBranchListBinaryMap() {
357   BranchListBinaryMap binary_map;
358   for (auto& p : branch_list_binary_map_) {
359     Dso* dso = p.first;
360     BranchListBinaryInfo& binary = p.second;
361     binary.dso_type = dso->type();
362     BuildId build_id;
363     GetBuildId(*dso, build_id);
364     BinaryKey key(dso->Path(), build_id);
365     if (binary.dso_type == DSO_KERNEL) {
366       if (kernel_map_start_addr_ == 0) {
367         LOG(WARNING) << "Can't convert kernel ip addresses without kernel start addr. So remove "
368                         "branches for the kernel.";
369         continue;
370       }
371       key.kernel_start_addr = kernel_map_start_addr_;
372     }
373     binary_map[key] = std::move(binary);
374   }
375   return binary_map;
376 }
377 
Create(bool dump_maps_from_proc)378 std::unique_ptr<ETMBranchListGenerator> ETMBranchListGenerator::Create(bool dump_maps_from_proc) {
379   return std::unique_ptr<ETMBranchListGenerator>(
380       new ETMBranchListGeneratorImpl(dump_maps_from_proc));
381 }
382 
~ETMBranchListGenerator()383 ETMBranchListGenerator::~ETMBranchListGenerator() {}
384 
385 }  // namespace simpleperf
386