summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThan McIntosh <thanm@google.com>2016-06-10 11:02:40 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2016-06-10 11:02:40 +0000
commit05c98248e61f97ae8271f7a01c039c19f03400de (patch)
tree45375742a4ee8e6e043558665a2a1a5dc373cd6d
parent47600b7efdf006bc9aa1177294425696058637fe (diff)
parentbe1b17745edcecc74da63a7a76cb555e9a938275 (diff)
downloadextras-android-n-preview-4.tar.gz
Merge "Support for converting callchain profiles."android-n-preview-4
-rw-r--r--perfprofd/perf_data_converter.cc70
-rw-r--r--perfprofd/tests/README.txt18
-rw-r--r--perfprofd/tests/perfprofd_test.cc78
3 files changed, 148 insertions, 18 deletions
diff --git a/perfprofd/perf_data_converter.cc b/perfprofd/perf_data_converter.cc
index 9d997538..e3d37371 100644
--- a/perfprofd/perf_data_converter.cc
+++ b/perfprofd/perf_data_converter.cc
@@ -7,6 +7,29 @@ using std::map;
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 RangeTarget {
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 @@ RawPerfDataToAndroidPerfProfile(const string &perf_file) {
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 @@ RawPerfDataToAndroidPerfProfile(const string &perf_file) {
}
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 @@ RawPerfDataToAndroidPerfProfile(const string &perf_file) {
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 4d8db99e..949e73f0 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. Part of the test is a system-wide profile, meaning that you will
+need to run 'adb root' prior to test execution.
-2. The daemon under test, perfprofd, is broken into a main function, a
+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 36a59ca1..efda4bcd 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 @@ TEST_F(PerfProfdTest, CallchainRunWithCannedPerf)
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 @@ TEST_F(PerfProfdTest, CallchainRunWithCannedPerf)
}
// 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 @@ TEST_F(PerfProfdTest, MultipleRunWithLivePerf)
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