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