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