Merge \\"Support for converting callchain profiles.\\" am: 05c98248e6
am: 342b992f85

Change-Id: I1d1f18dc1aa21f830bde03ec45ef121f94f0c5bf
diff --git a/perfprofd/perf_data_converter.cc b/perfprofd/perf_data_converter.cc
index 9d99753..e3d3737 100644
--- a/perfprofd/perf_data_converter.cc
+++ b/perfprofd/perf_data_converter.cc
@@ -7,6 +7,29 @@
 
 namespace wireless_android_logging_awp {
 
+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) {}
@@ -28,6 +51,7 @@
 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
@@ -40,8 +64,16 @@
 
   typedef map<string, BinaryProfile> ModuleProfileMap;
   typedef map<string, ModuleProfileMap> ProgramProfileMap;
+
+  // 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;
   for (const auto &event : parser.parsed_events()) {
     if (!event.raw_event ||
         event.raw_event->header.type != PERF_RECORD_SAMPLE) {
@@ -49,17 +81,35 @@
     }
     string dso_name = event.dso_and_offset.dso_name();
     string program_name;
-    if (dso_name == "[kernel.kallsyms]_text") {
-      program_name = "kernel";
-      dso_name = "[kernel.kallsyms]";
+    const string kernel_name = "[kernel.kallsyms]";
+    if (dso_name.substr(0, kernel_name.length()) == kernel_name) {
+      dso_name = kernel_name;
+      program_name = "[kernel.kallsyms]";
     } else if (event.command() == "") {
       program_name = "unknown_program";
     } else {
       program_name = event.command();
     }
-    name_profile_map[program_name][dso_name].address_count_map[
-        event.dso_and_offset.offset()]++;
     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;
+      name_profile_map[program_name][dso_name].callchain_count_map[cc]++;
+    } else if (!event.branch_stack.empty()) {
+      CHECK(!seen_callchain && "examining branch stack");
+      seen_branch_stack = true;
+      name_profile_map[program_name][dso_name].address_count_map[
+          event.dso_and_offset.offset()]++;
+    } else {
+      name_profile_map[program_name][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();
@@ -122,6 +172,16 @@
         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) {
+          int32 module_id = name_id_map[d_o.dso_name()];
+          address_samples->add_load_module_id(module_id);
+          address_samples->add_address(d_o.offset());
+        }
+      }
     }
   }
   return ret;
diff --git a/perfprofd/tests/README.txt b/perfprofd/tests/README.txt
index 4d8db99..949e73f 100644
--- a/perfprofd/tests/README.txt
+++ b/perfprofd/tests/README.txt
@@ -1,13 +1,19 @@
-Native tests for 'perfprofd'. Please run with 'runtest perfprofd'
-(a.k.a. "$ANDROID_BUILD_TOP"/development/testrunner/runtest.py).
+Native tests for 'perfprofd'. Please run with
+
+   runtest --path=system/extras/perfprofd/tests
+
+(where runtest == $ANDROID_BUILD_TOP"/development/testrunner/runtest.py).
 
 Notes:
 
-1. One of the testpoints in this test suite performs a live 'perf'
-run on the device; before invoking the test be sure that 'perf'
-has been built and installed on the device in /system/bin/perf
+1. Several of the testpoints in this unit tests perform a live 'simpleperf'
+run on the device (if you are using a userdebug build, simpleperf should
+already be available in /system/xbin/simpleperf).
 
-2. The daemon under test, perfprofd, is broken into a main function, a
+2. Part of the test is a system-wide profile, meaning that you will
+need to run 'adb root' prior to test execution.
+
+3. The daemon under test, perfprofd, is broken into a main function, a
 "core" library, and a "utils library. Picture:
 
 	+-----------+   perfprofdmain.o
diff --git a/perfprofd/tests/perfprofd_test.cc b/perfprofd/tests/perfprofd_test.cc
index 36a59ca..efda4bc 100644
--- a/perfprofd/tests/perfprofd_test.cc
+++ b/perfprofd/tests/perfprofd_test.cc
@@ -25,6 +25,7 @@
 #include <fcntl.h>
 
 #include <android-base/stringprintf.h>
+#include <cutils/properties.h>
 
 #include "perfprofdcore.h"
 #include "configreader.h"
@@ -595,8 +596,9 @@
                      encodedProfile);
 
 
-  // Expect 29 load modules
+  // Expect 4 programs 8 load modules
   EXPECT_EQ(4, encodedProfile.programs_size());
+  EXPECT_EQ(8, encodedProfile.load_modules_size());
 
   // Check a couple of load modules
   { const auto &lm0 = encodedProfile.load_modules(0);
@@ -621,23 +623,23 @@
   }
 
   // Examine some of the samples now
-  { const auto &p1 = encodedProfile.programs(0);
-    const auto &lm1 = p1.modules(0);
+  { const auto &p0 = encodedProfile.programs(0);
+    const auto &lm1 = p0.modules(0);
     std::string act_lm1 = encodedModuleSamplesToString(lm1);
     std::string sqact1 = squeezeWhite(act_lm1, "actual for lm1");
     const std::string expected_lm1 = RAW_RESULT(
         load_module_id: 0
-        address_samples { address: 108460 count: 2 }
+        address_samples { address: 108552 count: 2 }
                                                 );
     std::string sqexp1 = squeezeWhite(expected_lm1, "expected_lm1");
     EXPECT_STREQ(sqexp1.c_str(), sqact1.c_str());
   }
-  { const auto &p1 = encodedProfile.programs(1);
-    const auto &lm2 = p1.modules(2);
+  { const auto &p4 = encodedProfile.programs(3);
+    const auto &lm2 = p4.modules(1);
     std::string act_lm2 = encodedModuleSamplesToString(lm2);
     std::string sqact2 = squeezeWhite(act_lm2, "actual for lm2");
     const std::string expected_lm2 = RAW_RESULT(
-        load_module_id: 3 address_samples { address: 686690 count: 1 } address_samples { address: 693516 count: 1 } address_samples { address: 693770 count: 1 } address_samples { address: 694362 count: 1 } address_samples { address: 695874 count: 1 } address_samples { address: 720318 count: 2 } address_samples { address: 1510368 count: 1 } address_samples { address: 1715444 count: 1 } address_samples { address: 2809724 count: 1 } address_samples { address: 3200568 count: 1 }
+        load_module_id: 2 address_samples { address: 403913 count: 1 } address_samples { address: 840761 count: 1 } address_samples { address: 846481 count: 1 } address_samples { address: 999053 count: 1 } address_samples { address: 1012959 count: 1 } address_samples { address: 1524309 count: 1 } address_samples { address: 1580779 count: 1 } address_samples { address: 4287986288 count: 1 }
                                                 );
     std::string sqexp2 = squeezeWhite(expected_lm2, "expected_lm2");
     EXPECT_STREQ(sqexp2.c_str(), sqact2.c_str());
@@ -759,6 +761,68 @@
                      expected, "BasicRunWithLivePerf", true);
 }
 
+TEST_F(PerfProfdTest, CallChainRunWithLivePerf)
+{
+  //
+  // Callchain profiles are only supported on certain devices.
+  // For now this test is stubbed out except when run on "angler".
+  //
+  char propBuf[PROPERTY_VALUE_MAX];
+  propBuf[0] = '\0';
+  property_get("ro.hardware", propBuf, "");
+  if (strcmp(propBuf, "angler")) {
+    return;
+  }
+
+  //
+  // Collect a callchain profile, so as to exercise the code in
+  // perf_data post-processing that digests callchains.
+  //
+  PerfProfdRunner runner;
+  std::string ddparam("destination_directory="); ddparam += dest_dir;
+  runner.addToConfig(ddparam);
+  std::string cfparam("config_directory="); cfparam += test_dir;
+  runner.addToConfig(cfparam);
+  runner.addToConfig("main_loop_iterations=1");
+  runner.addToConfig("use_fixed_seed=12345678");
+  runner.addToConfig("max_unprocessed_profiles=100");
+  runner.addToConfig("collection_interval=9999");
+  runner.addToConfig("stack_profile=1");
+  runner.addToConfig("sample_duration=2");
+
+  // Create semaphore file
+  runner.create_semaphore_file();
+
+  // Kick off daemon
+  int daemon_main_return_code = runner.invoke();
+
+  // Check return code from daemon
+  EXPECT_EQ(0, daemon_main_return_code);
+
+  // Read and decode the resulting perf.data.encoded file
+  wireless_android_play_playlog::AndroidPerfProfile encodedProfile;
+  readEncodedProfile("CallChainRunWithLivePerf", encodedProfile);
+
+  // Examine what we get back. Since it's a live profile, we can't
+  // really do much in terms of verifying the contents.
+  EXPECT_LT(0, encodedProfile.programs_size());
+
+  // Verify log contents
+  const std::string expected = RAW_RESULT(
+      I: starting Android Wide Profiling daemon
+      I: config file path set to /data/nativetest/perfprofd_test/perfprofd.conf
+      I: random seed set to 12345678
+      I: sleep 674 seconds
+      I: initiating profile collection
+      I: profile collection complete
+      I: sleep 9325 seconds
+      I: finishing Android Wide Profiling daemon
+                                          );
+  // check to make sure log excerpt matches
+  compareLogMessages(mock_perfprofdutils_getlogged(),
+                     expected, "CallChainRunWithLivePerf", true);
+}
+
 int main(int argc, char **argv) {
   executable_path = argv[0];
   // switch to / before starting testing (perfprofd