From f897452e2305dc19e9f6689029da74cdf07045f5 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Mon, 17 Jul 2017 14:36:37 -0700 Subject: [PATCH] simpleperf: add test for dumping regs for tracepoint events. If the test fails, probably a kernel patch is missing: 5b09a094f2 arm64: perf: Fix callchain parse error with kernel tracepoint events To support the test, also enable recording tracepoint events in app's context. Bug: http://b/29520177 Test: run CtsSimpleperfTestCases64 on devices. Change-Id: I085114113732366305e92f6a1e6c3b6efc6ff5ff --- simpleperf/cmd_record.cpp | 41 +++++++++++------- simpleperf/cmd_record_test.cpp | 31 ++++++++++++++ simpleperf/cmd_stat.cpp | 10 ++++- simpleperf/environment.cpp | 45 +++++++++++++++++++- simpleperf/environment.h | 3 +- simpleperf/event_attr.cpp | 5 ++- simpleperf/event_type.cpp | 53 +++++++++++++++++++++--- simpleperf/event_type.h | 2 + simpleperf/nonlinux_support/nonlinux_support.cpp | 4 ++ simpleperf/workload.cpp | 2 +- 10 files changed, 169 insertions(+), 27 deletions(-) diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp index d37c2a84..8bd7962f 100644 --- a/simpleperf/cmd_record.cpp +++ b/simpleperf/cmd_record.cpp @@ -153,6 +153,7 @@ class RecordCommand : public Command { #if 0 // Below options are only used internally and shouldn't be visible to the public. "--in-app We are already running in the app's context.\n" +"--tracepoint-events file_name Read tracepoint events from [file_name] instead of tracefs.\n" #endif // clang-format on ), @@ -241,14 +242,9 @@ class RecordCommand : public Command { }; bool RecordCommand::Run(const std::vector& args) { - // 0. Do some environment preparation. if (!CheckPerfEventLimit()) { return false; } - if (!InitPerfClock()) { - return false; - } - PrepareVdsoFile(); // 1. Parse options, and use default measured event type if not given. std::vector workload_args; @@ -261,7 +257,7 @@ bool RecordCommand::Run(const std::vector& args) { // root. if (!IsRoot()) { return RunInAppContext(app_package_name_, "record", args, workload_args.size(), - record_filename_); + record_filename_, !event_selection_set_.GetTracepointEvents().empty()); } } if (event_selection_set_.empty()) { @@ -272,9 +268,15 @@ bool RecordCommand::Run(const std::vector& args) { if (!SetEventSelectionFlags()) { return false; } + + // 2. Do some environment preparation. ScopedCurrentArch scoped_arch(GetMachineArch()); + if (!InitPerfClock()) { + return false; + } + PrepareVdsoFile(); - // 2. Create workload. + // 3. Create workload. std::unique_ptr workload; if (!workload_args.empty()) { workload = Workload::CreateWorkload(workload_args); @@ -310,7 +312,7 @@ bool RecordCommand::Run(const std::vector& args) { need_to_check_targets = true; } - // 3. Open perf_event_files, create mapped buffers for perf_event_files. + // 4. Open perf_event_files, create mapped buffers for perf_event_files. if (!event_selection_set_.OpenEventFiles(cpus_)) { return false; } @@ -319,12 +321,12 @@ bool RecordCommand::Run(const std::vector& args) { return false; } - // 4. Create perf.data. + // 5. Create perf.data. if (!CreateAndInitRecordFile()) { return false; } - // 5. Add read/signal/periodic Events. + // 6. Add read/signal/periodic Events. auto callback = std::bind(&RecordCommand::ProcessRecord, this, std::placeholders::_1); if (!event_selection_set_.PrepareToReadMmapEventData(callback)) { @@ -348,7 +350,7 @@ bool RecordCommand::Run(const std::vector& args) { } } - // 6. Write records in mapped buffers of perf_event_files to output file while + // 7. Write records in mapped buffers of perf_event_files to output file while // workload is running. start_sampling_time_in_ns_ = GetPerfClock(); LOG(VERBOSE) << "start_sampling_time is " << start_sampling_time_in_ns_ @@ -369,7 +371,7 @@ bool RecordCommand::Run(const std::vector& args) { return false; } - // 7. Dump additional features, and close record file. + // 8. Dump additional features, and close record file. if (!DumpAdditionalFeatures(args)) { return false; } @@ -377,14 +379,14 @@ bool RecordCommand::Run(const std::vector& args) { return false; } - // 8. Unwind dwarf callchain. + // 9. Unwind dwarf callchain. if (post_unwind_) { if (!PostUnwind(args)) { return false; } } - // 9. Show brief record result. + // 10. Show brief record result. LOG(INFO) << "Samples recorded: " << sample_record_count_ << ". Samples lost: " << lost_record_count_ << "."; if (sample_record_count_ + lost_record_count_ != 0) { @@ -584,6 +586,13 @@ bool RecordCommand::ParseOptions(const std::vector& args, return false; } event_selection_set_.AddMonitoredThreads(tids); + } else if (args[i] == "--tracepoint-events") { + if (!NextArgumentOrError(args, &i)) { + return false; + } + if (!SetTracepointEventsFilePath(args[i])) { + return false; + } } else { ReportUnknownOption(args, i); return false; @@ -726,8 +735,8 @@ bool RecordCommand::DumpKernelSymbol() { bool RecordCommand::DumpTracingData() { std::vector tracepoint_event_types = event_selection_set_.GetTracepointEvents(); - if (tracepoint_event_types.empty()) { - return true; // No need to dump tracing data. + if (tracepoint_event_types.empty() || !CanRecordRawData()) { + return true; // No need to dump tracing data, or can't do it. } std::vector tracing_data; if (!GetTracingData(tracepoint_event_types, &tracing_data)) { diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp index fcedcee1..8b70fcd2 100644 --- a/simpleperf/cmd_record_test.cpp +++ b/simpleperf/cmd_record_test.cpp @@ -429,3 +429,34 @@ TEST(record_cmd, cpu_clock_for_a_long_time) { ASSERT_TRUE(RecordCmd()->Run( {"-e", "cpu-clock", "-o", tmpfile.path, "-p", pid, "--duration", "3"})); } + +TEST(record_cmd, dump_regs_for_tracepoint_events) { + // Check if the kernel can dump registers for tracepoint events. + // If not, probably a kernel patch below is missing: + // "5b09a094f2 arm64: perf: Fix callchain parse error with kernel tracepoint events" + std::vector> workloads; + CreateProcesses(1, &workloads); + std::string pid = std::to_string(workloads[0]->GetPid()); + TemporaryFile tmpfile; + ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "-p", pid, "-e", "sched:sched_switch", + "-g", "--no-unwind", "--duration", "1"})); + + // If the kernel patch is missing, all regs dumped in sample records are zero. + std::unique_ptr reader = RecordFileReader::CreateInstance(tmpfile.path); + CHECK(reader != nullptr); + std::unique_ptr r; + bool regs_all_zero = true; + while (reader->ReadRecord(r) && r && regs_all_zero) { + if (r->type() != PERF_RECORD_SAMPLE) { + continue; + } + SampleRecord* s = static_cast(r.get()); + for (size_t i = 0; i < s->regs_user_data.reg_nr; ++i) { + if (s->regs_user_data.regs[i] != 0u) { + regs_all_zero = false; + break; + } + } + } + ASSERT_FALSE(regs_all_zero); +} diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp index 4c79801c..a9d5036b 100644 --- a/simpleperf/cmd_stat.cpp +++ b/simpleperf/cmd_stat.cpp @@ -307,6 +307,7 @@ class StatCommand : public Command { #if 0 // Below options are only used internally and shouldn't be visible to the public. "--in-app We are already running in the app's context.\n" +"--tracepoint-events file_name Read tracepoint events from [file_name] instead of tracefs.\n" #endif // clang-format on ), @@ -359,7 +360,7 @@ bool StatCommand::Run(const std::vector& args) { if (!app_package_name_.empty() && !in_app_context_) { if (!IsRoot()) { return RunInAppContext(app_package_name_, "stat", args, workload_args.size(), - output_filename_); + output_filename_, !event_selection_set_.GetTracepointEvents().empty()); } } if (event_selection_set_.empty()) { @@ -554,6 +555,13 @@ bool StatCommand::ParseOptions(const std::vector& args, return false; } event_selection_set_.AddMonitoredThreads(tids); + } else if (args[i] == "--tracepoint-events") { + if (!NextArgumentOrError(args, &i)) { + return false; + } + if (!SetTracepointEventsFilePath(args[i])) { + return false; + } } else if (args[i] == "--verbose") { verbose_mode_ = true; } else { diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp index e0027fc6..bf5e663e 100644 --- a/simpleperf/environment.cpp +++ b/simpleperf/environment.cpp @@ -38,6 +38,7 @@ #include #endif +#include "event_type.h" #include "IOEventLoop.h" #include "read_elf.h" #include "thread_tree.h" @@ -352,6 +353,11 @@ static bool ReadPerfEventParanoid(int* value) { return true; } +bool CanRecordRawData() { + int value; + return IsRoot() || (ReadPerfEventParanoid(&value) && value == -1); +} + static const char* GetLimitLevelDescription(int limit_level) { switch (limit_level) { case -1: return "unlimited"; @@ -541,16 +547,34 @@ int WaitForAppProcess(const std::string& package_name) { } } +class ScopedFile { + public: + ScopedFile(const std::string& filepath, std::string app_package_name = "") + : filepath_(filepath), app_package_name_(app_package_name) {} + + ~ScopedFile() { + if (app_package_name_.empty()) { + unlink(filepath_.c_str()); + } else { + Workload::RunCmd({"run-as", app_package_name_, "rm", "-rf", filepath_}); + } + } + + private: + std::string filepath_; + std::string app_package_name_; +}; + bool RunInAppContext(const std::string& app_package_name, const std::string& cmd, const std::vector& args, size_t workload_args_size, - const std::string& output_filepath) { + const std::string& output_filepath, bool need_tracepoint_events) { // 1. Test if the package exists. if (!Workload::RunCmd({"run-as", app_package_name, "echo", ">/dev/null"}, false)) { LOG(ERROR) << "Package " << app_package_name << "doesn't exist or isn't debuggable."; return false; } - // 2. Copy simpleperf binary to the package. + // 2. Copy simpleperf binary to the package. Create tracepoint_file if needed. std::string simpleperf_path; if (!android::base::Readlink("/proc/self/exe", &simpleperf_path)) { PLOG(ERROR) << "ReadLink failed"; @@ -559,12 +583,29 @@ bool RunInAppContext(const std::string& app_package_name, const std::string& cmd if (!Workload::RunCmd({"run-as", app_package_name, "cp", simpleperf_path, "simpleperf"})) { return false; } + ScopedFile scoped_simpleperf("simpleperf", app_package_name); + std::unique_ptr scoped_tracepoint_file; + const std::string tracepoint_file = "/data/local/tmp/tracepoint_events"; + if (need_tracepoint_events) { + // Since we can't read tracepoint events from tracefs in app's context, we need to prepare + // them in tracepoint_file in shell's context, and pass the path of tracepoint_file to the + // child process using --tracepoint-events option. + if (!android::base::WriteStringToFile(GetTracepointEvents(), tracepoint_file)) { + PLOG(ERROR) << "Failed to store tracepoint events"; + return false; + } + scoped_tracepoint_file.reset(new ScopedFile(tracepoint_file)); + } // 3. Prepare to start child process to profile. std::string output_basename = output_filepath.empty() ? "" : android::base::Basename(output_filepath); std::vector new_args = {"run-as", app_package_name, "./simpleperf", cmd, "--in-app"}; + if (need_tracepoint_events) { + new_args.push_back("--tracepoint-events"); + new_args.push_back(tracepoint_file); + } for (size_t i = 0; i < args.size(); ++i) { if (i >= args.size() - workload_args_size || args[i] != "-o") { new_args.push_back(args[i]); diff --git a/simpleperf/environment.h b/simpleperf/environment.h index 603c9e60..b1c52ea0 100644 --- a/simpleperf/environment.h +++ b/simpleperf/environment.h @@ -72,6 +72,7 @@ bool CheckPerfEventLimit(); bool GetMaxSampleFrequency(uint64_t* max_sample_freq); bool CheckSampleFrequency(uint64_t sample_freq); bool CheckKernelSymbolAddresses(); +bool CanRecordRawData(); #if defined(__linux__) static inline uint64_t GetSystemClock() { @@ -94,7 +95,7 @@ void PrepareVdsoFile(); int WaitForAppProcess(const std::string& package_name); bool RunInAppContext(const std::string& app_package_name, const std::string& cmd, const std::vector& args, size_t workload_args_size, - const std::string& output_filepath); + const std::string& output_filepath, bool need_tracepoint_events); // Below two functions are only used in cts tests, to force stat/record cmd to run in app's context. void SetDefaultAppPackageName(const std::string& package_name); diff --git a/simpleperf/event_attr.cpp b/simpleperf/event_attr.cpp index 19364489..7c58081e 100644 --- a/simpleperf/event_attr.cpp +++ b/simpleperf/event_attr.cpp @@ -23,6 +23,7 @@ #include +#include "environment.h" #include "event_type.h" #include "utils.h" @@ -92,7 +93,9 @@ perf_event_attr CreateDefaultPerfEventAttr(const EventType& event_type) { if (attr.type == PERF_TYPE_TRACEPOINT) { // Tracepoint information are stored in raw data in sample records. - attr.sample_type |= PERF_SAMPLE_RAW; + if (CanRecordRawData()) { + attr.sample_type |= PERF_SAMPLE_RAW; + } } return attr; } diff --git a/simpleperf/event_type.cpp b/simpleperf/event_type.cpp index a69067d5..bc639d12 100644 --- a/simpleperf/event_type.cpp +++ b/simpleperf/event_type.cpp @@ -16,6 +16,7 @@ #include "event_type.h" +#include #include #include #include @@ -23,6 +24,8 @@ #include #include +#include +#include #include #include "event_attr.h" @@ -35,12 +38,42 @@ static const std::vector static_event_type_array = { #include "event_type_table.h" }; -static const std::vector GetTracepointEventTypes() { +static std::string tracepoint_events; + +bool SetTracepointEventsFilePath(const std::string& filepath) { + if (!android::base::ReadFileToString(filepath, &tracepoint_events)) { + PLOG(ERROR) << "Failed to read " << filepath; + return false; + } + return true; +} + +std::string GetTracepointEvents() { + std::string result; + for (const EventType& event : GetAllEventTypes()) { + if (!result.empty()) { + result.push_back('\n'); + } + result += android::base::StringPrintf("%s %" PRIu64, event.name.c_str(), event.config); + } + return result; +} + +static std::vector GetTracepointEventTypesFromString(const std::string& s) { std::vector result; - if (!IsRoot()) { - // Not having permission to profile tracing events. - return result; + for (auto& line : android::base::Split(s, "\n")) { + std::vector items = android::base::Split(line, " "); + CHECK_EQ(items.size(), 2u); + std::string event_name = items[0]; + uint64_t id; + CHECK(android::base::ParseUint(items[1].c_str(), &id)); + result.push_back(EventType(event_name, PERF_TYPE_TRACEPOINT, id, "", "")); } + return result; +} + +static std::vector GetTracepointEventTypesFromTraceFs() { + std::vector result; const std::string tracepoint_dirname = "/sys/kernel/debug/tracing/events"; for (const auto& system_name : GetSubDirs(tracepoint_dirname)) { std::string system_path = tracepoint_dirname + "/" + system_name; @@ -59,6 +92,16 @@ static const std::vector GetTracepointEventTypes() { result.push_back(EventType(system_name + ":" + event_name, PERF_TYPE_TRACEPOINT, id, "", "")); } } + return result; +} + +static std::vector GetTracepointEventTypes() { + std::vector result; + if (!tracepoint_events.empty()) { + result = GetTracepointEventTypesFromString(tracepoint_events); + } else { + result = GetTracepointEventTypesFromTraceFs(); + } std::sort(result.begin(), result.end(), [](const EventType& type1, const EventType& type2) { return type1.name < type2.name; }); return result; @@ -69,7 +112,7 @@ const std::vector& GetAllEventTypes() { if (event_type_array.empty()) { event_type_array.insert(event_type_array.end(), static_event_type_array.begin(), static_event_type_array.end()); - const std::vector tracepoint_array = GetTracepointEventTypes(); + std::vector tracepoint_array = GetTracepointEventTypes(); event_type_array.insert(event_type_array.end(), tracepoint_array.begin(), tracepoint_array.end()); } diff --git a/simpleperf/event_type.h b/simpleperf/event_type.h index a804b083..6ec544d8 100644 --- a/simpleperf/event_type.h +++ b/simpleperf/event_type.h @@ -52,6 +52,8 @@ struct EventType { std::string limited_arch; }; +bool SetTracepointEventsFilePath(const std::string& filepath); +std::string GetTracepointEvents(); const std::vector& GetAllEventTypes(); const EventType* FindEventTypeByName(const std::string& name); diff --git a/simpleperf/nonlinux_support/nonlinux_support.cpp b/simpleperf/nonlinux_support/nonlinux_support.cpp index f132dd92..8c245f15 100644 --- a/simpleperf/nonlinux_support/nonlinux_support.cpp +++ b/simpleperf/nonlinux_support/nonlinux_support.cpp @@ -28,3 +28,7 @@ std::vector UnwindCallChain(int, const ThreadEntry&, const RegSet&, bool GetKernelBuildId(BuildId*) { return false; } + +bool CanRecordRawData() { + return false; +} diff --git a/simpleperf/workload.cpp b/simpleperf/workload.cpp index 4aaeb2a9..60c9ed15 100644 --- a/simpleperf/workload.cpp +++ b/simpleperf/workload.cpp @@ -45,7 +45,7 @@ bool Workload::RunCmd(const std::vector& args, bool report_error) { std::string arg_str = android::base::Join(args, ' '); int ret = system(arg_str.c_str()); if (ret != 0 && report_error) { - PLOG(ERROR) << "Failed to run cmd " << arg_str; + LOG(ERROR) << "Failed to run cmd " << arg_str << ", exitcode " << ret; return false; } return ret == 0; -- 2.11.0