diff options
author | Than McIntosh <thanm@google.com> | 2016-06-10 11:02:40 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2016-06-10 11:02:40 +0000 |
commit | 05c98248e61f97ae8271f7a01c039c19f03400de (patch) | |
tree | 45375742a4ee8e6e043558665a2a1a5dc373cd6d | |
parent | 47600b7efdf006bc9aa1177294425696058637fe (diff) | |
parent | be1b17745edcecc74da63a7a76cb555e9a938275 (diff) | |
download | extras-android-n-preview-4.tar.gz |
Merge "Support for converting callchain profiles."android-n-preview-4
-rw-r--r-- | perfprofd/perf_data_converter.cc | 70 | ||||
-rw-r--r-- | perfprofd/tests/README.txt | 18 | ||||
-rw-r--r-- | perfprofd/tests/perfprofd_test.cc | 78 |
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 |