OSDN Git Service

Perfprofd: Use external quipper
[android-x86/system-extras.git] / perfprofd / perf_data_converter.cc
index 9d35d33..ffd1444 100644 (file)
 #include <algorithm>
 #include <limits>
 #include <map>
+#include <memory>
 #include <unordered_map>
 
+#include <android-base/macros.h>
 #include <android-base/strings.h>
+#include <perf_parser.h>
+#include <perf_protobuf_io.h>
 
-#include "perf_profile.pb.h"
+#include "perfprofd_record.pb.h"
 
-#include "quipper/perf_parser.h"
 #include "symbolizer.h"
 
 using std::map;
 
-namespace wireless_android_logging_awp {
+namespace android {
+namespace perfprofd {
 
-// Flag to turn off symbolization, even if a symbolizer is given.
-static constexpr bool kUseSymbolizer = true;
-
-// If this flag is true, symbols will be computed on-device for all samples. If this
-// flag is false, this will only be done for modules without a build id (i.e., where
-// symbols cannot be derived in the cloud).
-//
-// This is turned off for now to conserve space.
-static constexpr bool kUseSymbolizerForModulesWithBuildId = false;
-
-typedef quipper::ParsedEvent::DSOAndOffset DSOAndOffset;
-typedef std::vector<DSOAndOffset> callchain;
-
-struct callchain_lt {
-  bool operator()(const callchain *c1, const callchain *c2) const {
-    if (c1->size() != c2->size()) {
-      return c1->size() < c2->size();
-    }
-    for (unsigned idx = 0; idx < c1->size(); ++idx) {
-      const DSOAndOffset *do1 = &(*c1)[idx];
-      const DSOAndOffset *do2 = &(*c2)[idx];
-      if (do1->offset() != do2->offset()) {
-        return do1->offset() < do2->offset();
-      }
-      int rc = do1->dso_name().compare(do2->dso_name());
-      if (rc) {
-        return rc < 0;
-      }
-    }
-    return false;
-  }
-};
-
-struct RangeTarget {
-  RangeTarget(uint64 start, uint64 end, uint64 to)
-      : start(start), end(end), to(to) {}
-
-  bool operator<(const RangeTarget &r) const {
-    if (start != r.start) {
-      return start < r.start;
-    } else if (end != r.end) {
-      return end < r.end;
-    } else {
-      return to < r.to;
-    }
-  }
-  uint64 start;
-  uint64 end;
-  uint64 to;
-};
-
-struct BinaryProfile {
-  map<uint64, uint64> address_count_map;
-  map<RangeTarget, uint64> range_count_map;
-  map<const callchain *, uint64, callchain_lt> callchain_count_map;
-};
-
-wireless_android_play_playlog::AndroidPerfProfile*
+PerfprofdRecord*
 RawPerfDataToAndroidPerfProfile(const string &perf_file,
-                                ::perfprofd::Symbolizer* symbolizer) {
-  quipper::PerfParser parser;
-  if (!parser.ReadFile(perf_file) || !parser.ParseRawEvents()) {
-    return nullptr;
-  }
-  std::unique_ptr<wireless_android_play_playlog::AndroidPerfProfile> ret(
-      new wireless_android_play_playlog::AndroidPerfProfile());
-
-  using ModuleProfileMap = std::map<string, BinaryProfile>;
-  using Program = std::pair<uint32_t /* index into process name table, or uint32_t max */,
-                            std::string /* program name = comm of thread */>;
-  using ProgramProfileMap = std::map<Program, ModuleProfileMap>;
+                                ::perfprofd::Symbolizer* symbolizer ATTRIBUTE_UNUSED) {
+  std::unique_ptr<PerfprofdRecord> ret(new PerfprofdRecord());
+  ret->set_id(0);  // TODO.
 
-  struct ProcessNameTable {
-    std::vector<std::string> names;
-    std::unordered_map<std::string, uint32_t> index_lookup;
-  };
-  constexpr uint32_t kNoProcessNameTableEntry = std::numeric_limits<uint32_t>::max();
-  ProcessNameTable process_name_table;
+  quipper::PerfParserOptions options = {};
+  options.do_remap = true;
+  options.discard_unused_events = true;
+  options.read_missing_buildids = true;
 
-  // Note: the callchain_count_map member in BinaryProfile contains
-  // pointers into callchains owned by "parser" above, meaning
-  // that once the parser is destroyed, callchain pointers in
-  // name_profile_map will become stale (e.g. keep these two
-  // together in the same region).
-  ProgramProfileMap name_profile_map;
-  uint64 total_samples = 0;
-  bool seen_branch_stack = false;
-  bool seen_callchain = false;
-
-  auto is_kernel_dso = [](const std::string& dso) {
-    constexpr const char* kKernelDsos[] = {
-        "[kernel.kallsyms]",
-        "[vdso]",
-    };
-    for (auto kernel_dso : kKernelDsos) {
-      if (dso == kernel_dso) {
-        return true;
-      }
-    }
-    return false;
-  };
-
-  for (const auto &event : parser.parsed_events()) {
-    if (!event.raw_event ||
-        event.raw_event->header.type != PERF_RECORD_SAMPLE) {
-      continue;
-    }
-    string dso_name = event.dso_and_offset.dso_name();
-    Program program_id;
-    {
-      std::string program_name = event.command();
-      const std::string kernel_name = "[kernel.kallsyms]";
-      if (android::base::StartsWith(dso_name, kernel_name)) {
-        dso_name = kernel_name;
-        if (program_name == "") {
-          program_name = "kernel";
-        }
-      } else if (program_name == "") {
-        if (is_kernel_dso(dso_name)) {
-          program_name = "kernel";
-        } else {
-          program_name = "unknown_program";
-        }
-      }
-      std::string process_name = event.process_command();
-      uint32_t process_name_index = kNoProcessNameTableEntry;
-      if (!process_name.empty()) {
-        auto name_iter = process_name_table.index_lookup.find(process_name);
-        if (name_iter == process_name_table.index_lookup.end()) {
-          process_name_index = process_name_table.names.size();
-          process_name_table.index_lookup.emplace(process_name, process_name_index);
-          process_name_table.names.push_back(process_name);
-        } else {
-          process_name_index = name_iter->second;
-        }
-      }
-      program_id = std::make_pair(process_name_index, program_name);
-    }
-    ModuleProfileMap& module_profile_map = name_profile_map[program_id];
-
-    total_samples++;
-    // We expect to see either all callchain events, all branch stack
-    // events, or all flat sample events, not a mix. For callchains,
-    // however, it can be the case that none of the IPs in a chain
-    // are mappable, in which case the parsed/mapped chain will appear
-    // empty (appearing as a flat sample).
-    if (!event.callchain.empty()) {
-      CHECK(!seen_branch_stack && "examining callchain");
-      seen_callchain = true;
-      const callchain *cc = &event.callchain;
-      module_profile_map[dso_name].callchain_count_map[cc]++;
-    } else if (!event.branch_stack.empty()) {
-      CHECK(!seen_callchain && "examining branch stack");
-      seen_branch_stack = true;
-      module_profile_map[dso_name].address_count_map[
-          event.dso_and_offset.offset()]++;
-    } else {
-      module_profile_map[dso_name].address_count_map[
-          event.dso_and_offset.offset()]++;
-    }
-    for (size_t i = 1; i < event.branch_stack.size(); i++) {
-      if (dso_name == event.branch_stack[i - 1].to.dso_name()) {
-        uint64 start = event.branch_stack[i].to.offset();
-        uint64 end = event.branch_stack[i - 1].from.offset();
-        uint64 to = event.branch_stack[i - 1].to.offset();
-        // The interval between two taken branches should not be too large.
-        if (end < start || end - start > (1 << 20)) {
-          LOG(WARNING) << "Bogus LBR data: " << start << "->" << end;
-          continue;
-        }
-        module_profile_map[dso_name].range_count_map[
-            RangeTarget(start, end, to)]++;
-      }
-    }
-  }
+  quipper::PerfDataProto* perf_data = ret->mutable_perf_data();
 
-  struct ModuleData {
-    int index = 0;
-
-    std::vector<std::string> symbols;
-    std::unordered_map<uint64_t, size_t> addr_to_symbol_index;
-
-    wireless_android_play_playlog::LoadModule* module = nullptr;
-  };
-  map<string, ModuleData> name_data_map;
-  for (const auto &program_profile : name_profile_map) {
-    for (const auto &module_profile : program_profile.second) {
-      name_data_map[module_profile.first] = ModuleData();
-    }
-  }
-  int current_index = 0;
-  for (auto iter = name_data_map.begin(); iter != name_data_map.end(); ++iter) {
-    iter->second.index = current_index++;
-  }
-
-  map<string, string> name_buildid_map;
-  parser.GetFilenamesToBuildIDs(&name_buildid_map);
-  ret->set_total_samples(total_samples);
-
-  for (auto& name_data : name_data_map) {
-    auto load_module = ret->add_load_modules();
-    load_module->set_name(name_data.first);
-    auto nbmi = name_buildid_map.find(name_data.first);
-    bool has_build_id = nbmi != name_buildid_map.end();
-    if (has_build_id) {
-      const std::string &build_id = nbmi->second;
-      if (build_id.size() == 40 && build_id.substr(32) == "00000000") {
-        load_module->set_build_id(build_id.substr(0, 32));
-      } else {
-        load_module->set_build_id(build_id);
-      }
-    }
-    if (kUseSymbolizer && symbolizer != nullptr && !is_kernel_dso(name_data.first)) {
-      if (kUseSymbolizerForModulesWithBuildId || !has_build_id) {
-        // Add the module to signal that we'd want to add symbols.
-        name_data.second.module = load_module;
-      }
-    }
-  }
-
-  auto symbolize = [symbolizer](ModuleData* module_data,
-                                const std::string& dso,
-                                uint64_t address) {
-    if (module_data->module == nullptr) {
-      return address;
-    }
-    auto it = module_data->addr_to_symbol_index.find(address);
-    size_t index = std::numeric_limits<size_t>::max();
-    if (it == module_data->addr_to_symbol_index.end()) {
-      std::string symbol = symbolizer->Decode(dso, address);
-      if (!symbol.empty()) {
-        // Deduplicate symbols.
-        auto it = std::find(module_data->symbols.begin(), module_data->symbols.end(), symbol);
-        if (it == module_data->symbols.end()) {
-          index = module_data->symbols.size();
-          module_data->symbols.push_back(symbol);
-        } else {
-          index = it - module_data->symbols.begin();
-        }
-        module_data->addr_to_symbol_index.emplace(address, index);
-      }
-    } else {
-      index = it->second;
-    }
-    if (index != std::numeric_limits<size_t>::max()) {
-      // Note: consider an actual entry in the proto? Maybe a oneof? But that
-          //       will be complicated with the separate repeated addr & module.
-      address = std::numeric_limits<size_t>::max() - index;
-    }
-    return address;
-  };
-
-  for (const auto &program_profile : name_profile_map) {
-    auto program = ret->add_programs();
-    const Program& program_id = program_profile.first;
-    program->set_name(program_id.second);
-    if (program_id.first != kNoProcessNameTableEntry) {
-      program->set_process_name_id(program_id.first);
-    }
-    for (const auto &module_profile : program_profile.second) {
-      ModuleData& module_data = name_data_map[module_profile.first];
-      int32 module_id = module_data.index;
-      auto module = program->add_modules();
-      module->set_load_module_id(module_id);
-
-      // TODO: Templatize to avoid branch overhead?
-      for (const auto &addr_count : module_profile.second.address_count_map) {
-        auto address_samples = module->add_address_samples();
-
-        uint64_t address = symbolize(&module_data, module_profile.first, addr_count.first);
-        address_samples->add_address(address);
-        address_samples->set_count(addr_count.second);
-      }
-      for (const auto &range_count : module_profile.second.range_count_map) {
-        auto range_samples = module->add_range_samples();
-        range_samples->set_start(range_count.first.start);
-        range_samples->set_end(range_count.first.end);
-        range_samples->set_to(range_count.first.to);
-        range_samples->set_count(range_count.second);
-      }
-      for (const auto &callchain_count :
-               module_profile.second.callchain_count_map) {
-        auto address_samples = module->add_address_samples();
-        address_samples->set_count(callchain_count.second);
-        for (const auto &d_o : *callchain_count.first) {
-          ModuleData& module_data = name_data_map[d_o.dso_name()];
-          int32 module_id = module_data.index;
-          address_samples->add_load_module_id(module_id);
-          address_samples->add_address(symbolize(&module_data, d_o.dso_name(), d_o.offset()));
-        }
-      }
-    }
-  }
-
-  for (auto& name_data : name_data_map) {
-    auto load_module = name_data.second.module;
-    if (load_module != nullptr) {
-      for (const std::string& symbol : name_data.second.symbols) {
-        load_module->add_symbol(symbol);
-      }
-    }
+  if (!quipper::SerializeFromFileWithOptions(perf_file, options, perf_data)) {
+    return nullptr;
   }
 
-  if (!process_name_table.names.empty()) {
-    wireless_android_play_playlog::ProcessNames* process_names = ret->mutable_process_names();
-    for (const std::string& name : process_name_table.names) {
-      process_names->add_name(name);
-    }
-  }
+  // TODO: Symbolization.
 
   return ret.release();
 }
 
-}  // namespace wireless_android_logging_awp
+}  // namespace perfprofd
+}  // namespace android