1 2 #include "perf_data_converter.h" 3 4 #include <algorithm> 5 #include <limits> 6 #include <map> 7 #include <memory> 8 #include <set> 9 #include <unordered_map> 10 11 #include <android-base/logging.h> 12 #include <android-base/macros.h> 13 #include <android-base/strings.h> 14 #include <perf_data_utils.h> 15 #include <perf_parser.h> 16 #include <perf_protobuf_io.h> 17 18 #include "perfprofd_record.pb.h" 19 #include "perf_data.pb.h" 20 21 #include "map_utils.h" 22 #include "quipper_helper.h" 23 #include "symbolizer.h" 24 25 using std::map; 26 27 namespace android { 28 namespace perfprofd { 29 30 namespace { 31 32 void AddSymbolInfo(PerfprofdRecord* record, 33 ::quipper::PerfParser& perf_parser, 34 ::perfprofd::Symbolizer* symbolizer) { 35 std::unordered_set<std::string> filenames_w_build_id; 36 for (auto& perf_build_id : record->perf_data().build_ids()) { 37 filenames_w_build_id.insert(perf_build_id.filename()); 38 } 39 40 std::unordered_set<std::string> files_wo_build_id; 41 { 42 quipper::MmapEventIterator it(record->perf_data()); 43 for (; it != it.end(); ++it) { 44 const ::quipper::PerfDataProto_MMapEvent* mmap_event = &it->mmap_event(); 45 if (!mmap_event->has_filename() || !mmap_event->has_start() || !mmap_event->has_len()) { 46 // Don't care. 47 continue; 48 } 49 if (filenames_w_build_id.count(mmap_event->filename()) == 0) { 50 files_wo_build_id.insert(mmap_event->filename()); 51 } 52 } 53 } 54 if (files_wo_build_id.empty()) { 55 return; 56 } 57 58 struct Dso { 59 uint64_t min_vaddr; 60 RangeMap<std::string, uint64_t> symbols; 61 explicit Dso(uint64_t min_vaddr_in) : min_vaddr(min_vaddr_in) { 62 } 63 }; 64 std::unordered_map<std::string, Dso> files; 65 66 auto it = record->perf_data().events().begin(); 67 auto end = record->perf_data().events().end(); 68 auto parsed_it = perf_parser.parsed_events().begin(); 69 auto parsed_end = perf_parser.parsed_events().end(); 70 for (; it != end; ++it, ++parsed_it) { 71 CHECK(parsed_it != parsed_end); 72 if (!it->has_sample_event()) { 73 continue; 74 } 75 76 const ::quipper::PerfDataProto_SampleEvent& sample_event = it->sample_event(); 77 78 if (android::base::kEnableDChecks) { 79 // Check that the parsed_event and sample_event are consistent. 80 CHECK_EQ(parsed_it->callchain.size(), sample_event.callchain_size()); 81 } 82 83 auto check_address = [&](const std::string& dso_name, uint64_t offset) { 84 if (files_wo_build_id.count(dso_name) == 0) { 85 return; 86 } 87 88 // OK, that's a hit in the mmap segment (w/o build id). 89 90 Dso* dso_data; 91 { 92 auto dso_it = files.find(dso_name); 93 constexpr uint64_t kNoMinAddr = std::numeric_limits<uint64_t>::max(); 94 if (dso_it == files.end()) { 95 uint64_t min_vaddr; 96 bool has_min_vaddr = symbolizer->GetMinExecutableVAddr(dso_name, &min_vaddr); 97 if (!has_min_vaddr) { 98 min_vaddr = kNoMinAddr; 99 } 100 auto it = files.emplace(dso_name, Dso(min_vaddr)); 101 dso_data = &it.first->second; 102 } else { 103 dso_data = &dso_it->second; 104 } 105 if (dso_data->min_vaddr == kNoMinAddr) { 106 return; 107 } 108 } 109 110 // TODO: Is min_vaddr necessary here? 111 const uint64_t file_addr = offset; 112 113 std::string symbol = symbolizer->Decode(dso_name, file_addr); 114 if (symbol.empty()) { 115 return; 116 } 117 118 dso_data->symbols.Insert(symbol, file_addr); 119 }; 120 if (sample_event.has_ip() && parsed_it->dso_and_offset.dso_info_ != nullptr) { 121 check_address(parsed_it->dso_and_offset.dso_info_->name, parsed_it->dso_and_offset.offset_); 122 } 123 if (sample_event.callchain_size() > 0) { 124 for (auto& callchain_data: parsed_it->callchain) { 125 if (callchain_data.dso_info_ == nullptr) { 126 continue; 127 } 128 check_address(callchain_data.dso_info_->name, callchain_data.offset_); 129 } 130 } 131 } 132 133 if (!files.empty()) { 134 // We have extra symbol info, create proto messages now. 135 for (auto& file_data : files) { 136 const std::string& filename = file_data.first; 137 const Dso& dso = file_data.second; 138 if (dso.symbols.empty()) { 139 continue; 140 } 141 142 PerfprofdRecord_SymbolInfo* symbol_info = record->add_symbol_info(); 143 symbol_info->set_filename(filename); 144 symbol_info->set_filename_md5_prefix(::quipper::Md5Prefix(filename)); 145 symbol_info->set_min_vaddr(dso.min_vaddr); 146 for (auto& aggr_sym : dso.symbols) { 147 PerfprofdRecord_SymbolInfo_Symbol* symbol = symbol_info->add_symbols(); 148 symbol->set_addr(*aggr_sym.second.offsets.begin()); 149 symbol->set_size(*aggr_sym.second.offsets.rbegin() - *aggr_sym.second.offsets.begin() + 1); 150 symbol->set_name(aggr_sym.second.symbol); 151 symbol->set_name_md5_prefix(::quipper::Md5Prefix(aggr_sym.second.symbol)); 152 } 153 } 154 } 155 } 156 157 } // namespace 158 159 PerfprofdRecord* 160 RawPerfDataToAndroidPerfProfile(const string &perf_file, 161 ::perfprofd::Symbolizer* symbolizer) { 162 std::unique_ptr<PerfprofdRecord> ret(new PerfprofdRecord()); 163 ret->set_id(0); // TODO. 164 165 ::quipper::PerfParserOptions options = {}; 166 options.do_remap = true; 167 options.discard_unused_events = true; 168 options.read_missing_buildids = true; 169 170 ::quipper::PerfDataProto* perf_data = ret->mutable_perf_data(); 171 172 ::quipper::PerfReader reader; 173 if (!reader.ReadFile(perf_file)) return nullptr; 174 175 ::quipper::PerfParser parser(&reader, options); 176 if (!parser.ParseRawEvents()) return nullptr; 177 178 if (!reader.Serialize(perf_data)) return nullptr; 179 180 // Append parser stats to protobuf. 181 ::quipper::PerfSerializer::SerializeParserStats(parser.stats(), perf_data); 182 183 // TODO: Symbolization. 184 if (symbolizer != nullptr) { 185 AddSymbolInfo(ret.get(), parser, symbolizer); 186 } 187 188 return ret.release(); 189 } 190 191 } // namespace perfprofd 192 } // namespace android 193