Home | History | Annotate | Download | only in perfprofd
      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