summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2017-07-18 02:48:06 +0000
committerandroid-build-merger <android-build-merger@google.com>2017-07-18 02:48:06 +0000
commiteb21435bf2801beb8e6f5f26e4b5e14a2b484384 (patch)
treeb45d70f389bb97bcebad029a6209ce10213a62b7
parent878c04ed7daef94d7fcbc021a519d899de728671 (diff)
parent87fcb8c6c18fa934f34b0af5f2f812076c90fabf (diff)
downloadextras-eb21435bf2801beb8e6f5f26e4b5e14a2b484384.tar.gz
Merge "simpleperf: add test for dumping regs for tracepoint events."
am: 87fcb8c6c1 Change-Id: If27da0ebd6cc1a25bb0f44b9ce8b80ed1ff5c60f
-rw-r--r--simpleperf/cmd_record.cpp41
-rw-r--r--simpleperf/cmd_record_test.cpp31
-rw-r--r--simpleperf/cmd_stat.cpp10
-rw-r--r--simpleperf/environment.cpp45
-rw-r--r--simpleperf/environment.h3
-rw-r--r--simpleperf/event_attr.cpp5
-rw-r--r--simpleperf/event_type.cpp53
-rw-r--r--simpleperf/event_type.h2
-rw-r--r--simpleperf/nonlinux_support/nonlinux_support.cpp4
-rw-r--r--simpleperf/workload.cpp2
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<std::string>& 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<std::string> workload_args;
@@ -261,7 +257,7 @@ bool RecordCommand::Run(const std::vector<std::string>& 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<std::string>& 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> workload;
if (!workload_args.empty()) {
workload = Workload::CreateWorkload(workload_args);
@@ -310,7 +312,7 @@ bool RecordCommand::Run(const std::vector<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<const EventType*> 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<char> 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<std::unique_ptr<Workload>> 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<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
+ CHECK(reader != nullptr);
+ std::unique_ptr<Record> 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<SampleRecord*>(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<std::string>& 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<std::string>& 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 <sys/system_properties.h>
#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<std::string>& 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<ScopedFile> 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<std::string> 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<std::string>& 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 <android-base/logging.h>
+#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 <inttypes.h>
#include <unistd.h>
#include <algorithm>
#include <string>
@@ -23,6 +24,8 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include "event_attr.h"
@@ -35,12 +38,42 @@ static const std::vector<EventType> static_event_type_array = {
#include "event_type_table.h"
};
-static const std::vector<EventType> 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<EventType> GetTracepointEventTypesFromString(const std::string& s) {
std::vector<EventType> result;
- if (!IsRoot()) {
- // Not having permission to profile tracing events.
- return result;
+ for (auto& line : android::base::Split(s, "\n")) {
+ std::vector<std::string> 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<EventType> GetTracepointEventTypesFromTraceFs() {
+ std::vector<EventType> 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<EventType> GetTracepointEventTypes() {
result.push_back(EventType(system_name + ":" + event_name, PERF_TYPE_TRACEPOINT, id, "", ""));
}
}
+ return result;
+}
+
+static std::vector<EventType> GetTracepointEventTypes() {
+ std::vector<EventType> 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<EventType>& 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<EventType> tracepoint_array = GetTracepointEventTypes();
+ std::vector<EventType> 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<EventType>& 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<uint64_t> 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<std::string>& 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;