diff options
-rw-r--r-- | perfprofd/Android.mk | 2 | ||||
-rw-r--r-- | perfprofd/perfprofdcore.cc | 5 | ||||
-rw-r--r-- | perfprofd/quipper/perf_internals.h | 9 | ||||
-rw-r--r-- | perfprofd/quipper/perf_parser.cc | 16 | ||||
-rw-r--r-- | perfprofd/quipper/perf_reader.cc | 54 | ||||
-rw-r--r-- | perfprofd/quipper/perf_utils.cc | 4 | ||||
-rw-r--r-- | perfprofd/tests/Android.mk | 13 | ||||
-rw-r--r-- | perfprofd/tests/callchain.canned.perf.data | bin | 0 -> 256412 bytes | |||
-rw-r--r-- | perfprofd/tests/perfprofd_test.cc | 74 |
9 files changed, 166 insertions, 11 deletions
diff --git a/perfprofd/Android.mk b/perfprofd/Android.mk index 6409f834..0a07949e 100644 --- a/perfprofd/Android.mk +++ b/perfprofd/Android.mk @@ -73,3 +73,5 @@ include $(BUILD_EXECUTABLE) # Clean temp vars perfprofd_cppflags := proto_header_dir := + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/perfprofd/perfprofdcore.cc b/perfprofd/perfprofdcore.cc index 134c05c2..746ed410 100644 --- a/perfprofd/perfprofdcore.cc +++ b/perfprofd/perfprofdcore.cc @@ -549,7 +549,7 @@ static PROFILE_RESULT invoke_perf(const std::string &perf_path, } // marshall arguments - constexpr unsigned max_args = 12; + constexpr unsigned max_args = 13; const char *argv[max_args]; unsigned slot = 0; argv[slot++] = perf_path.c_str(); @@ -571,6 +571,9 @@ static PROFILE_RESULT invoke_perf(const std::string &perf_path, // system wide profiling argv[slot++] = "-a"; + // no need for kernel symbols + argv[slot++] = "--no-dump-kernel-symbols"; + // sleep <duration> argv[slot++] = "/system/bin/sleep"; std::string d_str = android::base::StringPrintf("%u", duration); diff --git a/perfprofd/quipper/perf_internals.h b/perfprofd/quipper/perf_internals.h index ef5a785d..6b2113e4 100644 --- a/perfprofd/quipper/perf_internals.h +++ b/perfprofd/quipper/perf_internals.h @@ -61,4 +61,13 @@ class PerfSampleCustodian { typedef perf_event event_t; +// +// Custom / user-specific records emitted by simpleperf. +// These need to be kept in sync with the simpleperf sources. +// +enum simpleperf_record_type { + SIMPLE_PERF_RECORD_TYPE_START = 32768, + SIMPLE_PERF_RECORD_KERNEL_SYMBOL, +}; + #endif diff --git a/perfprofd/quipper/perf_parser.cc b/perfprofd/quipper/perf_parser.cc index 504b4f01..0821612c 100644 --- a/perfprofd/quipper/perf_parser.cc +++ b/perfprofd/quipper/perf_parser.cc @@ -215,6 +215,8 @@ bool PerfParser::ProcessEvents() { VLOG(1) << "Parsed event type: " << event.header.type << ". Doing nothing."; break; + case SIMPLE_PERF_RECORD_KERNEL_SYMBOL: + break; default: LOG(ERROR) << "Unknown event type: " << event.header.type; return false; @@ -286,12 +288,14 @@ bool PerfParser::MapSampleEvent(ParsedEvent* parsed_event) { mapping_failed = true; } - // Write the remapped data back to the raw event regardless of whether it was - // entirely successfully remapped. A single failed remap should not - // invalidate all the other remapped entries. - if (!WritePerfSampleInfo(sample_info, parsed_event->raw_event)) { - LOG(ERROR) << "Failed to write back remapped sample info."; - return false; + if (options_.do_remap) { + // Write the remapped data back to the raw event regardless of + // whether it was entirely successfully remapped. A single failed + // remap should not invalidate all the other remapped entries. + if (!WritePerfSampleInfo(sample_info, parsed_event->raw_event)) { + LOG(ERROR) << "Failed to write back remapped sample info."; + return false; + } } return !mapping_failed; diff --git a/perfprofd/quipper/perf_reader.cc b/perfprofd/quipper/perf_reader.cc index 99731d45..1b397ac1 100644 --- a/perfprofd/quipper/perf_reader.cc +++ b/perfprofd/quipper/perf_reader.cc @@ -353,6 +353,8 @@ size_t ReadPerfSampleFromData(const perf_event_type event_type, const uint64_t sample_fields, const uint64_t read_format, bool swap_bytes, + const perf_event_attr &attr0, + size_t n_attrs, struct perf_sample* sample) { const uint64_t* initial_array_ptr = array; @@ -460,9 +462,34 @@ size_t ReadPerfSampleFromData(const perf_event_type event_type, array = ReadBranchStack(array, swap_bytes, sample); } + // { u64 abi, + // u64 regs[nr]; } && PERF_SAMPLE_REGS_USER + if (sample_fields & PERF_SAMPLE_REGS_USER) { + uint64_t abi = MaybeSwap(*array++, swap_bytes); + if (abi != 0) { + assert(n_attrs == 1); + uint64_t reg_mask = attr0.sample_regs_user; + size_t bit_nr = 0; + for (size_t i = 0; i < 64; ++i) { + if ((reg_mask >> i) & 1) { + bit_nr++; + } + } + array += bit_nr; + } + } + + // { u64 size, + // u64 regs[nr]; } && PERF_SAMPLE_STACK_USER + if (sample_fields & PERF_SAMPLE_STACK_USER) { + uint64_t size = MaybeSwap(*array++, swap_bytes); + if (size != 0) { + array += (size / sizeof(uint64_t)); + array += 1; // for dyn_size + } + } + static const u64 kUnimplementedSampleFields = - PERF_SAMPLE_REGS_USER | - PERF_SAMPLE_STACK_USER | PERF_SAMPLE_WEIGHT | PERF_SAMPLE_DATA_SRC | PERF_SAMPLE_TRANSACTION; @@ -616,6 +643,11 @@ size_t WritePerfSampleToData(const perf_event_type event_type, } } + // + // Unsupported sample types. + // + CHECK(!(sample_fields & PERF_SAMPLE_STACK_USER|PERF_SAMPLE_REGS_USER)); + return (array - initial_array_ptr) * sizeof(uint64_t); } @@ -769,6 +801,7 @@ bool PerfReader::IsSupportedEventType(uint32_t type) { case PERF_RECORD_LOST: case PERF_RECORD_THROTTLE: case PERF_RECORD_UNTHROTTLE: + case SIMPLE_PERF_RECORD_KERNEL_SYMBOL: return true; case PERF_RECORD_READ: case PERF_RECORD_MAX: @@ -788,6 +821,10 @@ bool PerfReader::ReadPerfSampleInfo(const event_t& event, return false; } + // We want to completely ignore these records + if (event.header.type == SIMPLE_PERF_RECORD_KERNEL_SYMBOL) + return true; + uint64_t sample_format = GetSampleFieldsForEventType(event.header.type, sample_type_); uint64_t offset = GetPerfSampleDataOffset(event); @@ -797,6 +834,8 @@ bool PerfReader::ReadPerfSampleInfo(const event_t& event, sample_format, read_format_, is_cross_endian_, + attrs_[0].attr, + attrs_.size(), sample); size_t expected_size = event.header.size - offset; @@ -1391,7 +1430,14 @@ bool PerfReader::ReadPerfEventBlock(const event_t& event) { if (is_cross_endian_) ByteSwap(&size); - if (size > sizeof(event_t)) { + // + // Special case for kernel symbol record, which may be very + // large -- this is safe to do since we will be skipping over + // the kernel symbols entirely later on. + // + if (event.header.type == SIMPLE_PERF_RECORD_KERNEL_SYMBOL) + size = sizeof(event_t); + else if (size > sizeof(event_t)) { LOG(INFO) << "Data size: " << size << " sizeof(event_t): " << sizeof(event_t); return false; @@ -1452,6 +1498,8 @@ bool PerfReader::ReadPerfEventBlock(const event_t& event) { ByteSwap(&event_copy->read.time_running); ByteSwap(&event_copy->read.id); break; + case SIMPLE_PERF_RECORD_KERNEL_SYMBOL: + break; default: LOG(FATAL) << "Unknown event type: " << type; } diff --git a/perfprofd/quipper/perf_utils.cc b/perfprofd/quipper/perf_utils.cc index 02fa9e06..4f6fdc3d 100644 --- a/perfprofd/quipper/perf_utils.cc +++ b/perfprofd/quipper/perf_utils.cc @@ -105,6 +105,7 @@ uint64_t GetSampleFieldsForEventType(uint32_t event_type, PERF_SAMPLE_STREAM_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_IDENTIFIER; break; case PERF_RECORD_SAMPLE: + case SIMPLE_PERF_RECORD_KERNEL_SYMBOL: break; default: LOG(FATAL) << "Unknown event type " << event_type; @@ -140,6 +141,9 @@ uint64_t GetPerfSampleDataOffset(const event_t& event) { offset = sizeof(event.mmap2) - sizeof(event.mmap2.filename) + GetUint64AlignedStringLength(event.mmap2.filename); break; + case SIMPLE_PERF_RECORD_KERNEL_SYMBOL: + offset = 0; + break; default: LOG(FATAL) << "Unknown/unsupported event type " << event.header.type; break; diff --git a/perfprofd/tests/Android.mk b/perfprofd/tests/Android.mk index bdd82e07..45c2779e 100644 --- a/perfprofd/tests/Android.mk +++ b/perfprofd/tests/Android.mk @@ -17,7 +17,7 @@ LOCAL_SRC_FILES := perfprofdmockutils.cc include $(BUILD_STATIC_LIBRARY) # -# Canned perf.data files needed by unit test. +# Canned perf.data file needed by unit test. # include $(CLEAR_VARS) LOCAL_MODULE := canned.perf.data @@ -28,6 +28,17 @@ LOCAL_SRC_FILES := canned.perf.data include $(BUILD_PREBUILT) # +# Second canned perf.data file needed by unit test. +# +include $(CLEAR_VARS) +LOCAL_MODULE := callchain.canned.perf.data +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := DATA +LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest/perfprofd_test +LOCAL_SRC_FILES := callchain.canned.perf.data +include $(BUILD_PREBUILT) + +# # Unit test for perfprofd # include $(CLEAR_VARS) diff --git a/perfprofd/tests/callchain.canned.perf.data b/perfprofd/tests/callchain.canned.perf.data Binary files differnew file mode 100644 index 00000000..8d843935 --- /dev/null +++ b/perfprofd/tests/callchain.canned.perf.data diff --git a/perfprofd/tests/perfprofd_test.cc b/perfprofd/tests/perfprofd_test.cc index 3a32204b..36a59ca1 100644 --- a/perfprofd/tests/perfprofd_test.cc +++ b/perfprofd/tests/perfprofd_test.cc @@ -570,6 +570,80 @@ TEST_F(PerfProfdTest, BasicRunWithCannedPerf) } } +TEST_F(PerfProfdTest, CallchainRunWithCannedPerf) +{ + // This test makes sure that the perf.data converter + // can handle call chains. + // + std::string input_perf_data(test_dir); + input_perf_data += "/callchain.canned.perf.data"; + + // Set up config to avoid these annotations (they are tested elsewhere) + ConfigReader config; + config.overrideUnsignedEntry("collect_cpu_utilization", 0); + config.overrideUnsignedEntry("collect_charging_state", 0); + config.overrideUnsignedEntry("collect_camera_active", 0); + + // Kick off encoder and check return code + PROFILE_RESULT result = + encode_to_proto(input_perf_data, encoded_file_path(0).c_str(), config, 0); + EXPECT_EQ(OK_PROFILE_COLLECTION, result); + + // Read and decode the resulting perf.data.encoded file + wireless_android_play_playlog::AndroidPerfProfile encodedProfile; + readEncodedProfile("BasicRunWithCannedPerf", + encodedProfile); + + + // Expect 29 load modules + EXPECT_EQ(4, encodedProfile.programs_size()); + + // Check a couple of load modules + { const auto &lm0 = encodedProfile.load_modules(0); + std::string act_lm0 = encodedLoadModuleToString(lm0); + std::string sqact0 = squeezeWhite(act_lm0, "actual for lm 0"); + const std::string expected_lm0 = RAW_RESULT( + name: "/system/bin/dex2oat" + build_id: "ee12bd1a1de39422d848f249add0afc4" + ); + std::string sqexp0 = squeezeWhite(expected_lm0, "expected_lm0"); + EXPECT_STREQ(sqexp0.c_str(), sqact0.c_str()); + } + { const auto &lm1 = encodedProfile.load_modules(1); + std::string act_lm1 = encodedLoadModuleToString(lm1); + std::string sqact1 = squeezeWhite(act_lm1, "actual for lm 1"); + const std::string expected_lm1 = RAW_RESULT( + name: "/system/bin/linker" + build_id: "a36715f673a4a0aa76ef290124c516cc" + ); + std::string sqexp1 = squeezeWhite(expected_lm1, "expected_lm1"); + EXPECT_STREQ(sqexp1.c_str(), sqact1.c_str()); + } + + // Examine some of the samples now + { const auto &p1 = encodedProfile.programs(0); + const auto &lm1 = p1.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 } + ); + 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); + 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 } + ); + std::string sqexp2 = squeezeWhite(expected_lm2, "expected_lm2"); + EXPECT_STREQ(sqexp2.c_str(), sqact2.c_str()); + } +} + TEST_F(PerfProfdTest, BasicRunWithLivePerf) { // |