OSDN Git Service

Simpleperf: add test for reporting callgraph of shared libraries in apk file.
[android-x86/system-extras.git] / simpleperf / cmd_report.cpp
index 9fe751f..3d778ab 100644 (file)
 #include <unordered_set>
 #include <vector>
 
-#include <base/logging.h>
-#include <base/stringprintf.h>
-#include <base/strings.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 
 #include "command.h"
 #include "dwarf_unwind.h"
@@ -255,6 +256,7 @@ class ReportCommand : public Command {
             "    -i <file>     Specify path of record file, default is perf.data.\n"
             "    -n            Print the sample count for each item.\n"
             "    --no-demangle        Don't demangle symbol names.\n"
+            "    -o report_file_name  Set report file name, default is stdout.\n"
             "    --pid pid1,pid2,...\n"
             "                  Report only for selected pids.\n"
             "    --sort key1,key2,...\n"
@@ -268,10 +270,12 @@ class ReportCommand : public Command {
             "    --vmlinux <file>\n"
             "                  Parse kernel symbols from <file>.\n"),
         record_filename_("perf.data"),
+        record_file_arch_(GetBuildArch()),
         use_branch_address_(false),
         accumulate_callchain_(false),
         print_callgraph_(false),
-        callgraph_show_callee_(true) {
+        callgraph_show_callee_(true),
+        report_fp_(nullptr) {
     compare_sample_func_t compare_sample_callback = std::bind(
         &ReportCommand::CompareSampleEntry, this, std::placeholders::_1, std::placeholders::_2);
     sample_tree_ =
@@ -288,15 +292,18 @@ class ReportCommand : public Command {
   void ProcessSampleRecord(const SampleRecord& r);
   bool ReadFeaturesFromRecordFile();
   int CompareSampleEntry(const SampleEntry& sample1, const SampleEntry& sample2);
-  void PrintReport();
+  bool PrintReport();
   void PrintReportContext();
   void CollectReportWidth();
   void CollectReportEntryWidth(const SampleEntry& sample);
   void PrintReportHeader();
   void PrintReportEntry(const SampleEntry& sample);
   void PrintCallGraph(const SampleEntry& sample);
+  void PrintCallGraphEntry(size_t depth, std::string prefix, const std::unique_ptr<CallChainNode>& node,
+                           uint64_t parent_period, bool last);
 
   std::string record_filename_;
+  ArchType record_file_arch_;
   std::unique_ptr<RecordFileReader> record_file_reader_;
   perf_event_attr event_attr_;
   std::vector<std::unique_ptr<Displayable>> displayable_items_;
@@ -308,6 +315,9 @@ class ReportCommand : public Command {
   bool accumulate_callchain_;
   bool print_callgraph_;
   bool callgraph_show_callee_;
+
+  std::string report_filename_;
+  FILE* report_fp_;
 };
 
 bool ReportCommand::Run(const std::vector<std::string>& args) {
@@ -328,10 +338,13 @@ bool ReportCommand::Run(const std::vector<std::string>& args) {
   if (!ReadFeaturesFromRecordFile()) {
     return false;
   }
+  ScopedCurrentArch scoped_arch(record_file_arch_);
   ReadSampleTreeFromRecordFile();
 
   // 3. Show collected information.
-  PrintReport();
+  if (!PrintReport()) {
+    return false;
+  }
 
   return true;
 }
@@ -353,11 +366,11 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
     } else if (args[i] == "--children") {
       accumulate_callchain_ = true;
     } else if (args[i] == "--comms" || args[i] == "--dsos") {
+      std::unordered_set<std::string>& filter = (args[i] == "--comms" ? comm_filter : dso_filter);
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
       std::vector<std::string> strs = android::base::Split(args[i], ",");
-      std::unordered_set<std::string>& filter = (args[i] == "--comms" ? comm_filter : dso_filter);
       filter.insert(strs.begin(), strs.end());
 
     } else if (args[i] == "-g") {
@@ -385,6 +398,11 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
 
     } else if (args[i] == "--no-demangle") {
       demangle = false;
+    } else if (args[i] == "-o") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      report_filename_ = args[i];
 
     } else if (args[i] == "--pids" || args[i] == "--tids") {
       if (!NextArgumentOrError(args, &i)) {
@@ -392,9 +410,9 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
       }
       std::vector<std::string> strs = android::base::Split(args[i], ",");
       std::vector<int> ids;
-      for (auto& s : strs) {
+      for (const auto& s : strs) {
         int id;
-        if (!StringToPid(s, &id)) {
+        if (!android::base::ParseInt(s.c_str(), &id, 0)) {
           LOG(ERROR) << "invalid id in " << args[i] << " option: " << s;
           return false;
         }
@@ -550,7 +568,8 @@ void ReportCommand::ProcessSampleRecord(const SampleRecord& r) {
         RegSet regs = CreateRegSet(r.regs_user_data.reg_mask, r.regs_user_data.regs);
         std::vector<char> stack(r.stack_user_data.data.begin(),
                                 r.stack_user_data.data.begin() + r.stack_user_data.data.size());
-        std::vector<uint64_t> unwind_ips = UnwindCallChain(*sample->thread, regs, stack);
+        std::vector<uint64_t> unwind_ips =
+            UnwindCallChain(ScopedCurrentArch::GetCurrentArch(), *sample->thread, regs, stack);
         if (!unwind_ips.empty()) {
           ips.push_back(PERF_CONTEXT_USER);
           ips.insert(ips.end(), unwind_ips.begin(), unwind_ips.end());
@@ -618,7 +637,8 @@ bool ReportCommand::ReadFeaturesFromRecordFile() {
 
   std::string arch = record_file_reader_->ReadFeatureString(PerfFileFormat::FEAT_ARCH);
   if (!arch.empty()) {
-    if (!SetCurrentArch(arch)) {
+    record_file_arch_ = GetArchType(arch);
+    if (record_file_arch_ == ARCH_UNSUPPORTED) {
       return false;
     }
   }
@@ -640,13 +660,29 @@ int ReportCommand::CompareSampleEntry(const SampleEntry& sample1, const SampleEn
   return 0;
 }
 
-void ReportCommand::PrintReport() {
+bool ReportCommand::PrintReport() {
+  std::unique_ptr<FILE, decltype(&fclose)> file_handler(nullptr, fclose);
+  if (report_filename_.empty()) {
+    report_fp_ = stdout;
+  } else {
+    report_fp_ = fopen(report_filename_.c_str(), "w");
+    if (report_fp_ == nullptr) {
+      PLOG(ERROR) << "failed to open file " << report_filename_;
+      return false;
+    }
+    file_handler.reset(report_fp_);
+  }
   PrintReportContext();
   CollectReportWidth();
   PrintReportHeader();
   sample_tree_->VisitAllSamples(
       std::bind(&ReportCommand::PrintReportEntry, this, std::placeholders::_1));
-  fflush(stdout);
+  fflush(report_fp_);
+  if (ferror(report_fp_) != 0) {
+    PLOG(ERROR) << "print report failed";
+    return false;
+  }
+  return true;
 }
 
 void ReportCommand::PrintReportContext() {
@@ -659,11 +695,11 @@ void ReportCommand::PrintReportContext() {
         android::base::StringPrintf("(type %u, config %llu)", event_attr_.type, event_attr_.config);
   }
   if (!record_cmdline_.empty()) {
-    printf("Cmdline: %s\n", record_cmdline_.c_str());
+    fprintf(report_fp_, "Cmdline: %s\n", record_cmdline_.c_str());
   }
-  printf("Samples: %" PRIu64 " of event '%s'\n", sample_tree_->TotalSamples(),
-         event_type_name.c_str());
-  printf("Event count: %" PRIu64 "\n\n", sample_tree_->TotalPeriod());
+  fprintf(report_fp_, "Samples: %" PRIu64 " of event '%s'\n", sample_tree_->TotalSamples(),
+          event_type_name.c_str());
+  fprintf(report_fp_, "Event count: %" PRIu64 "\n\n", sample_tree_->TotalPeriod());
 }
 
 void ReportCommand::CollectReportWidth() {
@@ -681,9 +717,9 @@ void ReportCommand::PrintReportHeader() {
   for (size_t i = 0; i < displayable_items_.size(); ++i) {
     auto& item = displayable_items_[i];
     if (i != displayable_items_.size() - 1) {
-      printf("%-*s  ", static_cast<int>(item->Width()), item->Name().c_str());
+      fprintf(report_fp_, "%-*s  ", static_cast<int>(item->Width()), item->Name().c_str());
     } else {
-      printf("%s\n", item->Name().c_str());
+      fprintf(report_fp_, "%s\n", item->Name().c_str());
     }
   }
 }
@@ -692,9 +728,9 @@ void ReportCommand::PrintReportEntry(const SampleEntry& sample) {
   for (size_t i = 0; i < displayable_items_.size(); ++i) {
     auto& item = displayable_items_[i];
     if (i != displayable_items_.size() - 1) {
-      printf("%-*s  ", static_cast<int>(item->Width()), item->Show(sample).c_str());
+      fprintf(report_fp_, "%-*s  ", static_cast<int>(item->Width()), item->Show(sample).c_str());
     } else {
-      printf("%s\n", item->Show(sample).c_str());
+      fprintf(report_fp_, "%s\n", item->Show(sample).c_str());
     }
   }
   if (print_callgraph_) {
@@ -702,15 +738,26 @@ void ReportCommand::PrintReportEntry(const SampleEntry& sample) {
   }
 }
 
-static void PrintCallGraphEntry(size_t depth, std::string prefix,
-                                const std::unique_ptr<CallChainNode>& node, uint64_t parent_period,
-                                bool last) {
+void ReportCommand::PrintCallGraph(const SampleEntry& sample) {
+  std::string prefix = "       ";
+  fprintf(report_fp_, "%s|\n", prefix.c_str());
+  fprintf(report_fp_, "%s-- %s\n", prefix.c_str(), sample.symbol->DemangledName());
+  prefix.append(3, ' ');
+  for (size_t i = 0; i < sample.callchain.children.size(); ++i) {
+    PrintCallGraphEntry(1, prefix, sample.callchain.children[i], sample.callchain.children_period,
+                        (i + 1 == sample.callchain.children.size()));
+  }
+}
+
+void ReportCommand::PrintCallGraphEntry(size_t depth, std::string prefix,
+                                        const std::unique_ptr<CallChainNode>& node,
+                                        uint64_t parent_period, bool last) {
   if (depth > 20) {
     LOG(WARNING) << "truncated callgraph at depth " << depth;
     return;
   }
   prefix += "|";
-  printf("%s\n", prefix.c_str());
+  fprintf(report_fp_, "%s\n", prefix.c_str());
   if (last) {
     prefix.back() = ' ';
   }
@@ -719,10 +766,10 @@ static void PrintCallGraphEntry(size_t depth, std::string prefix,
     double percentage = 100.0 * (node->period + node->children_period) / parent_period;
     percentage_s = android::base::StringPrintf("--%.2lf%%-- ", percentage);
   }
-  printf("%s%s%s\n", prefix.c_str(), percentage_s.c_str(), node->chain[0]->symbol->DemangledName());
+  fprintf(report_fp_, "%s%s%s\n", prefix.c_str(), percentage_s.c_str(), node->chain[0]->symbol->DemangledName());
   prefix.append(percentage_s.size(), ' ');
   for (size_t i = 1; i < node->chain.size(); ++i) {
-    printf("%s%s\n", prefix.c_str(), node->chain[i]->symbol->DemangledName());
+    fprintf(report_fp_, "%s%s\n", prefix.c_str(), node->chain[i]->symbol->DemangledName());
   }
 
   for (size_t i = 0; i < node->children.size(); ++i) {
@@ -731,17 +778,6 @@ static void PrintCallGraphEntry(size_t depth, std::string prefix,
   }
 }
 
-void ReportCommand::PrintCallGraph(const SampleEntry& sample) {
-  std::string prefix = "       ";
-  printf("%s|\n", prefix.c_str());
-  printf("%s-- %s\n", prefix.c_str(), sample.symbol->DemangledName());
-  prefix.append(3, ' ');
-  for (size_t i = 0; i < sample.callchain.children.size(); ++i) {
-    PrintCallGraphEntry(1, prefix, sample.callchain.children[i], sample.callchain.children_period,
-                        (i + 1 == sample.callchain.children.size()));
-  }
-}
-
-__attribute__((constructor)) static void RegisterReportCommand() {
+void RegisterReportCommand() {
   RegisterCommand("report", [] { return std::unique_ptr<Command>(new ReportCommand()); });
 }