xref: /aosp_15_r20/system/extras/simpleperf/BranchListFile.h (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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 #pragma once
18 
19 #include "ETMDecoder.h"
20 #include "RegEx.h"
21 #include "thread_tree.h"
22 #include "utils.h"
23 
24 namespace simpleperf {
25 
26 // When processing binary info in an input file, the binaries are identified by their path.
27 // But this isn't sufficient when merging binary info from multiple input files. Because
28 // binaries for the same path may be changed between generating input files. So after processing
29 // each input file, we create BinaryKeys to identify binaries, which consider path, build_id and
30 // kernel_start_addr (for vmlinux). kernel_start_addr affects how addresses in ETMBinary
31 // are interpreted for vmlinux.
32 struct BinaryKey {
33   std::string path;
34   BuildId build_id;
35   uint64_t kernel_start_addr = 0;
36 
BinaryKeyBinaryKey37   BinaryKey() {}
38 
BinaryKeyBinaryKey39   BinaryKey(const std::string& path, BuildId build_id) : path(path), build_id(build_id) {}
40 
BinaryKeyBinaryKey41   BinaryKey(const Dso* dso, uint64_t kernel_start_addr) : path(dso->Path()) {
42     build_id = Dso::FindExpectedBuildIdForPath(dso->Path());
43     if (build_id.IsEmpty()) {
44       GetBuildId(*dso, build_id);
45     }
46     if (dso->type() == DSO_KERNEL) {
47       this->kernel_start_addr = kernel_start_addr;
48     }
49   }
50 
51   bool operator==(const BinaryKey& other) const {
52     return path == other.path && build_id == other.build_id &&
53            kernel_start_addr == other.kernel_start_addr;
54   }
55 };
56 
57 struct BinaryKeyHash {
operatorBinaryKeyHash58   size_t operator()(const BinaryKey& key) const noexcept {
59     size_t seed = 0;
60     HashCombine(seed, key.path);
61     HashCombine(seed, key.build_id);
62     if (key.kernel_start_addr != 0) {
63       HashCombine(seed, key.kernel_start_addr);
64     }
65     return seed;
66   }
67 };
68 
69 class BinaryFilter {
70  public:
BinaryFilter(const RegEx * binary_name_regex)71   BinaryFilter(const RegEx* binary_name_regex) : binary_name_regex_(binary_name_regex) {}
72 
SetRegex(const RegEx * binary_name_regex)73   void SetRegex(const RegEx* binary_name_regex) {
74     binary_name_regex_ = binary_name_regex;
75     dso_filter_cache_.clear();
76   }
77 
Filter(const Dso * dso)78   bool Filter(const Dso* dso) {
79     auto lookup = dso_filter_cache_.find(dso);
80     if (lookup != dso_filter_cache_.end()) {
81       return lookup->second;
82     }
83     bool match = Filter(dso->Path());
84     dso_filter_cache_.insert({dso, match});
85     return match;
86   }
87 
Filter(const std::string & path)88   bool Filter(const std::string& path) {
89     return binary_name_regex_ == nullptr || binary_name_regex_->Search(path);
90   }
91 
92  private:
93   const RegEx* binary_name_regex_;
94   std::unordered_map<const Dso*, bool> dso_filter_cache_;
95 };
96 
97 using UnorderedETMBranchMap =
98     std::unordered_map<uint64_t, std::unordered_map<std::vector<bool>, uint64_t>>;
99 
100 struct ETMBinary {
101   DsoType dso_type;
102   UnorderedETMBranchMap branch_map;
103 
MergeETMBinary104   void Merge(const ETMBinary& other) {
105     for (auto& other_p : other.branch_map) {
106       auto it = branch_map.find(other_p.first);
107       if (it == branch_map.end()) {
108         branch_map[other_p.first] = std::move(other_p.second);
109       } else {
110         auto& map2 = it->second;
111         for (auto& other_p2 : other_p.second) {
112           auto it2 = map2.find(other_p2.first);
113           if (it2 == map2.end()) {
114             map2[other_p2.first] = other_p2.second;
115           } else {
116             OverflowSafeAdd(it2->second, other_p2.second);
117           }
118         }
119       }
120     }
121   }
122 
GetOrderedBranchMapETMBinary123   ETMBranchMap GetOrderedBranchMap() const {
124     ETMBranchMap result;
125     for (const auto& p : branch_map) {
126       uint64_t addr = p.first;
127       const auto& b_map = p.second;
128       result[addr] = std::map<std::vector<bool>, uint64_t>(b_map.begin(), b_map.end());
129     }
130     return result;
131   }
132 };
133 
134 using ETMBinaryMap = std::unordered_map<BinaryKey, ETMBinary, BinaryKeyHash>;
135 bool ETMBinaryMapToString(const ETMBinaryMap& binary_map, std::string& s);
136 bool StringToETMBinaryMap(const std::string& s, ETMBinaryMap& binary_map);
137 
138 // Convert ETM data into branch lists while recording.
139 class ETMBranchListGenerator {
140  public:
141   static std::unique_ptr<ETMBranchListGenerator> Create(bool dump_maps_from_proc);
142 
143   virtual ~ETMBranchListGenerator();
144   virtual void SetExcludePid(pid_t pid) = 0;
145   virtual void SetBinaryFilter(const RegEx* binary_name_regex) = 0;
146   virtual bool ProcessRecord(const Record& r, bool& consumed) = 0;
147   virtual ETMBinaryMap GetETMBinaryMap() = 0;
148 };
149 
150 struct LBRBranch {
151   // If from_binary_id >= 1, it refers to LBRData.binaries[from_binary_id - 1]. Otherwise, it's
152   // invalid.
153   uint32_t from_binary_id = 0;
154   // If to_binary_id >= 1, it refers to LBRData.binaries[to_binary_id - 1]. Otherwise, it's invalid.
155   uint32_t to_binary_id = 0;
156   uint64_t from_vaddr_in_file = 0;
157   uint64_t to_vaddr_in_file = 0;
158 };
159 
160 struct LBRSample {
161   // If binary_id >= 1, it refers to LBRData.binaries[binary_id - 1]. Otherwise, it's invalid.
162   uint32_t binary_id = 0;
163   uint64_t vaddr_in_file = 0;
164   std::vector<LBRBranch> branches;
165 };
166 
167 struct LBRData {
168   std::vector<LBRSample> samples;
169   std::vector<BinaryKey> binaries;
170 };
171 
172 bool LBRDataToString(const LBRData& data, std::string& s);
173 
174 namespace proto {
175 class BranchList;
176 class ETMBinary;
177 class LBRData;
178 }  // namespace proto
179 
180 class BranchListProtoWriter {
181  private:
182   // This value is choosen to prevent exceeding the 2GB size limit for a protobuf message.
183   static constexpr size_t kMaxBranchesPerMessage = 100000000;
184 
185  public:
186   static std::unique_ptr<BranchListProtoWriter> CreateForFile(
187       const std::string& output_filename, bool compress,
188       size_t max_branches_per_message = kMaxBranchesPerMessage);
189   static std::unique_ptr<BranchListProtoWriter> CreateForString(
190       std::string* output_str, bool compress,
191       size_t max_branches_per_message = kMaxBranchesPerMessage);
192 
193   bool Write(const ETMBinaryMap& etm_data);
194   bool Write(const LBRData& lbr_data);
195 
196  private:
BranchListProtoWriter(const std::string & output_filename,std::string * output_str,bool compress,size_t max_branches_per_message)197   BranchListProtoWriter(const std::string& output_filename, std::string* output_str, bool compress,
198                         size_t max_branches_per_message)
199       : output_filename_(output_filename),
200         compress_(compress),
201         max_branches_per_message_(max_branches_per_message),
202         output_fp_(nullptr, fclose),
203         output_str_(output_str) {}
204 
205   bool WriteHeader();
206   bool WriteProtoBranchList(proto::BranchList& branch_list);
207   bool WriteData(const void* data, size_t size);
208 
209   const std::string output_filename_;
210   const bool compress_;
211   const size_t max_branches_per_message_;
212   std::unique_ptr<FILE, decltype(&fclose)> output_fp_;
213   std::string* output_str_;
214 };
215 
216 class BranchListProtoReader {
217  public:
218   static std::unique_ptr<BranchListProtoReader> CreateForFile(const std::string& input_filename);
219   static std::unique_ptr<BranchListProtoReader> CreateForString(const std::string& input_str);
220   bool Read(ETMBinaryMap& etm_data, LBRData& lbr_data);
221 
222  private:
BranchListProtoReader(const std::string & input_filename,const std::string & input_str)223   BranchListProtoReader(const std::string& input_filename, const std::string& input_str)
224       : input_filename_(input_filename), input_fp_(nullptr, fclose), input_str_(input_str) {}
225   bool ReadProtoBranchList(uint32_t size, proto::BranchList& proto_branch_list);
226   bool AddETMBinary(const proto::ETMBinary& proto_binary, ETMBinaryMap& etm_data);
227   void AddLBRData(const proto::LBRData& proto_lbr_data, LBRData& lbr_data);
228   void Rewind();
229   bool ReadData(void* data, size_t size);
230   bool ReadOldFileFormat(ETMBinaryMap& etm_data, LBRData& lbr_data);
231 
232   const std::string input_filename_;
233   std::unique_ptr<FILE, decltype(&fclose)> input_fp_;
234   const std::string& input_str_;
235   size_t input_str_pos_ = 0;
236   bool compress_ = false;
237 };
238 
239 bool DumpBranchListFile(std::string filename);
240 
241 // for testing
242 std::string ETMBranchToProtoString(const std::vector<bool>& branch);
243 std::vector<bool> ProtoStringToETMBranch(const std::string& s, size_t bit_size);
244 
245 }  // namespace simpleperf
246