Simpleperf: add option to report branch stack information.

Bug: 19483574
Change-Id: If98e6d3e7a171bb4f2aa7f5d4be43586a6286f56
diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp
index ffba9ea..d7635cc 100644
--- a/simpleperf/cmd_report.cpp
+++ b/simpleperf/cmd_report.cpp
@@ -17,6 +17,7 @@
 #include <inttypes.h>
 #include <functional>
 #include <map>
+#include <set>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -138,6 +139,63 @@
     .print_function = PrintSymbol,
 };
 
+static int CompareDsoFrom(const SampleEntry& sample1, const SampleEntry& sample2) {
+  return strcmp(sample1.branch_from.map->dso->path.c_str(),
+                sample2.branch_from.map->dso->path.c_str());
+}
+
+static std::string PrintHeaderDsoFrom() {
+  return "Source Shared Object";
+}
+
+static std::string PrintDsoFrom(const SampleEntry& sample) {
+  return sample.branch_from.map->dso->path;
+}
+
+static ReportItem report_dso_from = {
+    .compare_function = CompareDsoFrom,
+    .print_header_function = PrintHeaderDsoFrom,
+    .print_function = PrintDsoFrom,
+};
+
+static std::string PrintHeaderDsoTo() {
+  return "Target Shared Object";
+}
+
+static ReportItem report_dso_to = {
+    .compare_function = CompareDso,
+    .print_header_function = PrintHeaderDsoTo,
+    .print_function = PrintDso,
+};
+
+static int CompareSymbolFrom(const SampleEntry& sample1, const SampleEntry& sample2) {
+  return strcmp(sample1.branch_from.symbol->name.c_str(), sample2.branch_from.symbol->name.c_str());
+}
+
+static std::string PrintHeaderSymbolFrom() {
+  return "Source Symbol";
+}
+
+static std::string PrintSymbolFrom(const SampleEntry& sample) {
+  return sample.branch_from.symbol->name;
+}
+
+static ReportItem report_symbol_from = {
+    .compare_function = CompareSymbolFrom,
+    .print_header_function = PrintHeaderSymbolFrom,
+    .print_function = PrintSymbolFrom,
+};
+
+static std::string PrintHeaderSymbolTo() {
+  return "Target Symbol";
+}
+
+static ReportItem report_symbol_to = {
+    .compare_function = CompareSymbol,
+    .print_header_function = PrintHeaderSymbolTo,
+    .print_function = PrintSymbol,
+};
+
 static std::string PrintHeaderSampleCount() {
   return "Sample";
 }
@@ -153,23 +211,40 @@
 };
 
 static std::unordered_map<std::string, ReportItem*> report_item_map = {
-    {"comm", &report_comm}, {"pid", &report_pid},       {"tid", &report_tid},
-    {"dso", &report_dso},   {"symbol", &report_symbol},
+    {"comm", &report_comm},
+    {"pid", &report_pid},
+    {"tid", &report_tid},
+    {"dso", &report_dso},
+    {"symbol", &report_symbol},
+    {"dso_from", &report_dso_from},
+    {"dso_to", &report_dso_to},
+    {"symbol_from", &report_symbol_from},
+    {"symbol_to", &report_symbol_to}};
+
+static std::set<std::string> branch_sort_keys = {
+    "dso_from", "dso_to", "symbol_from", "symbol_to",
 };
 
 class ReportCommand : public Command {
  public:
   ReportCommand()
-      : Command("report", "report sampling information in perf.data",
-                "Usage: simpleperf report [options]\n"
-                "    -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"
-                "    --sort key1,key2,... Select the keys to sort and print the report.\n"
-                "                         Possible keys include pid, tid, comm, dso, symbol.\n"
-                "                         Default keys are \"comm,pid,tid,dso,symbol\"\n"
-                "    --symfs <dir>  Look for files with symbols relative to this directory.\n"),
-        record_filename_("perf.data") {
+      : Command(
+            "report", "report sampling information in perf.data",
+            "Usage: simpleperf report [options]\n"
+            "    -b            Use the branch-to addresses in sampled take branches instead of\n"
+            "                  the instruction addresses. Only valid for perf.data recorded with\n"
+            "                  -b/-j option."
+            "    -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"
+            "    --sort key1,key2,...\n"
+            "                  Select the keys to sort and print the report. Possible keys\n"
+            "                  include pid, tid, comm, dso, symbol, dso_from, dso_to, symbol_from\n"
+            "                  symbol_to. dso_from, dso_to, symbol_from, symbol_to can only be\n"
+            "                  used with -b option. Default keys are \"comm,pid,tid,dso,symbol\"\n"
+            "    --symfs <dir>  Look for files with symbols relative to this directory.\n"),
+        record_filename_("perf.data"),
+        use_branch_address_(false) {
   }
 
   bool Run(const std::vector<std::string>& args);
@@ -191,6 +266,7 @@
   perf_event_attr event_attr_;
   std::vector<ReportItem*> report_items_;
   std::unique_ptr<SampleTree> sample_tree_;
+  bool use_branch_address_;
 };
 
 bool ReportCommand::Run(const std::vector<std::string>& args) {
@@ -209,9 +285,7 @@
   }
   ReadSampleTreeFromRecordFile();
 
-  // 3. Read symbol table from elf files.
-
-  // 4. Show collected information.
+  // 3. Show collected information.
   PrintReport();
 
   return true;
@@ -219,8 +293,11 @@
 
 bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
   bool print_sample_count = false;
+  std::vector<std::string> sort_keys = {"comm", "pid", "tid", "dso", "symbol"};
   for (size_t i = 0; i < args.size(); ++i) {
-    if (args[i] == "-i") {
+    if (args[i] == "-b") {
+      use_branch_address_ = true;
+    } else if (args[i] == "-i") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
@@ -236,16 +313,7 @@
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
-      std::vector<std::string> sort_keys = android::base::Split(args[i], ",");
-      for (auto& key : sort_keys) {
-        auto it = report_item_map.find(key);
-        if (it != report_item_map.end()) {
-          report_items_.push_back(it->second);
-        } else {
-          LOG(ERROR) << "Unknown sort key: " << key;
-          return false;
-        }
-      }
+      sort_keys = android::base::Split(args[i], ",");
     } else if (args[i] == "--symfs") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
@@ -259,15 +327,21 @@
     }
   }
 
-  if (report_items_.empty()) {
-    report_items_.push_back(report_item_map["comm"]);
-    report_items_.push_back(report_item_map["pid"]);
-    report_items_.push_back(report_item_map["tid"]);
-    report_items_.push_back(report_item_map["dso"]);
-    report_items_.push_back(report_item_map["symbol"]);
-  }
   if (print_sample_count) {
-    report_items_.insert(report_items_.begin(), &report_sample_count);
+    report_items_.push_back(&report_sample_count);
+  }
+  for (auto& key : sort_keys) {
+    if (!use_branch_address_ && branch_sort_keys.find(key) != branch_sort_keys.end()) {
+      LOG(ERROR) << "sort key '" << key << "' can only be used with -b option.";
+      return false;
+    }
+    auto it = report_item_map.find(key);
+    if (it != report_item_map.end()) {
+      report_items_.push_back(it->second);
+    } else {
+      LOG(ERROR) << "Unknown sort key: " << key;
+      return false;
+    }
   }
   return true;
 }
@@ -279,6 +353,10 @@
     return false;
   }
   event_attr_ = attrs[0]->attr;
+  if (use_branch_address_ && (event_attr_.sample_type & PERF_SAMPLE_BRANCH_STACK) == 0) {
+    LOG(ERROR) << record_filename_ << " is not recorded with branch stack sampling option.";
+    return false;
+  }
   return true;
 }
 
@@ -310,9 +388,18 @@
       }
     } else if (record->header.type == PERF_RECORD_SAMPLE) {
       const SampleRecord& r = *static_cast<const SampleRecord*>(record.get());
-      bool in_kernel = (r.header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL;
-      sample_tree_->AddSample(r.tid_data.pid, r.tid_data.tid, r.ip_data.ip, r.time_data.time,
-                              r.period_data.period, in_kernel);
+      if (use_branch_address_ == false) {
+        bool in_kernel = (r.header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL;
+        sample_tree_->AddSample(r.tid_data.pid, r.tid_data.tid, r.ip_data.ip, r.time_data.time,
+                                r.period_data.period, in_kernel);
+      } else {
+        for (auto& item : r.branch_stack_data.stack) {
+          if (item.from != 0 && item.to != 0) {
+            sample_tree_->AddBranchSample(r.tid_data.pid, r.tid_data.tid, item.from, item.to,
+                                          item.flags, r.time_data.time, r.period_data.period);
+          }
+        }
+      }
     } else if (record->header.type == PERF_RECORD_COMM) {
       const CommRecord& r = *static_cast<const CommRecord*>(record.get());
       sample_tree_->AddThread(r.data.pid, r.data.tid, r.comm);