simpleperf: support building cts test.
1. build cts test libraries.
2. change tests to use tmpfile instead of perf.data.
3. support extracting testdata from cts test file.
Bug: 27387280
Change-Id: I7c5db77f3157d586d0c9beb446b247626e7cce36
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk
index 3bd1a63..1ec496e 100644
--- a/simpleperf/Android.mk
+++ b/simpleperf/Android.mk
@@ -300,4 +300,40 @@
LOCAL_MULTILIB := first
include $(BUILD_HOST_NATIVE_TEST)
+
+# libsimpleperf_cts_test
+# =========================================================
+libsimpleperf_cts_test_src_files := \
+ $(libsimpleperf_src_files) \
+ $(libsimpleperf_src_files_linux) \
+ $(simpleperf_unit_test_src_files) \
+ $(simpleperf_unit_test_src_files_linux) \
+
+# libsimpleperf_cts_test target
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_MODULE := libsimpleperf_cts_test
+LOCAL_CPPFLAGS := $(simpleperf_cppflags_target) -DIN_CTS_TEST
+LOCAL_SRC_FILES := $(libsimpleperf_cts_test_src_files)
+LOCAL_STATIC_LIBRARIES := $(simpleperf_static_libraries_target)
+LOCAL_SHARED_LIBRARIES := $(simpleperf_shared_libraries_target)
+LOCAL_MULTILIB := both
+include $(LLVM_DEVICE_BUILD_MK)
+include $(BUILD_STATIC_TEST_LIBRARY)
+
+# libsimpleperf_cts_test linux host
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_MODULE := libsimpleperf_cts_test
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_CPPFLAGS := $(simpleperf_cppflags_host) -DIN_CTS_TEST
+LOCAL_CPPFLAGS_linux := $(simpleperf_cppflags_host_linux)
+LOCAL_SRC_FILES := $(libsimpleperf_cts_test_src_files)
+LOCAL_STATIC_LIBRARIES := $(simpleperf_static_libraries_host)
+LOCAL_SHARED_LIBRARIES_linux := $(simpleperf_shared_libraries_host_linux)
+LOCAL_LDLIBS_linux := $(simpleperf_ldlibs_host_linux)
+LOCAL_MULTILIB := both
+include $(LLVM_HOST_BUILD_MK)
+include $(BUILD_HOST_STATIC_TEST_LIBRARY)
+
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/simpleperf/cmd_dumprecord_test.cpp b/simpleperf/cmd_dumprecord_test.cpp
index 574fb2a..441851f 100644
--- a/simpleperf/cmd_dumprecord_test.cpp
+++ b/simpleperf/cmd_dumprecord_test.cpp
@@ -17,27 +17,12 @@
#include <gtest/gtest.h>
#include "command.h"
-#include "test_util.h"
+#include "get_test_data.h"
-class DumpRecordCommandTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- record_cmd = CreateCommandInstance("record");
- ASSERT_TRUE(record_cmd != nullptr);
- dumprecord_cmd = CreateCommandInstance("dump");
- ASSERT_TRUE(dumprecord_cmd != nullptr);
- }
-
- std::unique_ptr<Command> record_cmd;
- std::unique_ptr<Command> dumprecord_cmd;
-};
-
-TEST_F(DumpRecordCommandTest, no_options) {
- ASSERT_TRUE(record_cmd->Run({"-a", "sleep", SLEEP_SEC}));
- ASSERT_TRUE(dumprecord_cmd->Run({}));
+static std::unique_ptr<Command> DumpCmd() {
+ return CreateCommandInstance("dump");
}
-TEST_F(DumpRecordCommandTest, record_file_option) {
- ASSERT_TRUE(record_cmd->Run({"-a", "-o", "perf2.data", "sleep", SLEEP_SEC}));
- ASSERT_TRUE(dumprecord_cmd->Run({"perf2.data"}));
+TEST(cmd_dump, record_file_option) {
+ ASSERT_TRUE(DumpCmd()->Run({GetTestData("perf.data")}));
}
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index 6f54fc6..a89febf 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -17,10 +17,14 @@
#include <gtest/gtest.h>
#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+
+#include <memory>
#include "command.h"
#include "environment.h"
#include "event_selection_set.h"
+#include "get_test_data.h"
#include "record.h"
#include "record_file.h"
#include "test_util.h"
@@ -31,34 +35,51 @@
return CreateCommandInstance("record");
}
+static bool RunRecordCmd(std::vector<std::string> v, const char* output_file = nullptr) {
+ std::unique_ptr<TemporaryFile> tmpfile;
+ std::string out_file;
+ if (output_file != nullptr) {
+ out_file = output_file;
+ } else {
+ tmpfile.reset(new TemporaryFile);
+ out_file = tmpfile->path;
+ }
+ v.insert(v.end(), {"-o", out_file, "sleep", SLEEP_SEC});
+ return RecordCmd()->Run(v);
+}
+
TEST(record_cmd, no_options) {
- ASSERT_TRUE(RecordCmd()->Run({"sleep", SLEEP_SEC}));
+ ASSERT_TRUE(RunRecordCmd({}));
}
TEST(record_cmd, system_wide_option) {
- ASSERT_TRUE(RecordCmd()->Run({"-a", "sleep", SLEEP_SEC}));
+ if (IsRoot()) {
+ ASSERT_TRUE(RunRecordCmd({"-a"}));
+ }
}
TEST(record_cmd, sample_period_option) {
- ASSERT_TRUE(RecordCmd()->Run({"-c", "100000", "sleep", SLEEP_SEC}));
+ ASSERT_TRUE(RunRecordCmd({"-c", "100000"}));
}
TEST(record_cmd, event_option) {
- ASSERT_TRUE(RecordCmd()->Run({"-e", "cpu-clock", "sleep", SLEEP_SEC}));
+ ASSERT_TRUE(RunRecordCmd({"-e", "cpu-clock"}));
}
TEST(record_cmd, freq_option) {
- ASSERT_TRUE(RecordCmd()->Run({"-f", "99", "sleep", SLEEP_SEC}));
- ASSERT_TRUE(RecordCmd()->Run({"-F", "99", "sleep", SLEEP_SEC}));
+ ASSERT_TRUE(RunRecordCmd({"-f", "99"}));
+ ASSERT_TRUE(RunRecordCmd({"-F", "99"}));
}
TEST(record_cmd, output_file_option) {
- ASSERT_TRUE(RecordCmd()->Run({"-o", "perf2.data", "sleep", SLEEP_SEC}));
+ TemporaryFile tmpfile;
+ ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "sleep", SLEEP_SEC}));
}
TEST(record_cmd, dump_kernel_mmap) {
- ASSERT_TRUE(RecordCmd()->Run({"sleep", SLEEP_SEC}));
- std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance("perf.data");
+ TemporaryFile tmpfile;
+ ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
+ std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
ASSERT_TRUE(reader != nullptr);
std::vector<std::unique_ptr<Record>> records = reader->DataSection();
ASSERT_GT(records.size(), 0U);
@@ -76,8 +97,9 @@
}
TEST(record_cmd, dump_build_id_feature) {
- ASSERT_TRUE(RecordCmd()->Run({"sleep", SLEEP_SEC}));
- std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance("perf.data");
+ TemporaryFile tmpfile;
+ ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
+ std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
ASSERT_TRUE(reader != nullptr);
const FileHeader& file_header = reader->FileHeader();
ASSERT_TRUE(file_header.features[FEAT_BUILD_ID / 8] & (1 << (FEAT_BUILD_ID % 8)));
@@ -85,16 +107,18 @@
}
TEST(record_cmd, tracepoint_event) {
- ASSERT_TRUE(RecordCmd()->Run({"-a", "-e", "sched:sched_switch", "sleep", SLEEP_SEC}));
+ if (IsRoot()) {
+ ASSERT_TRUE(RunRecordCmd({"-a", "-e", "sched:sched_switch"}));
+ }
}
TEST(record_cmd, branch_sampling) {
if (IsBranchSamplingSupported()) {
- ASSERT_TRUE(RecordCmd()->Run({"-a", "-b", "sleep", SLEEP_SEC}));
- ASSERT_TRUE(RecordCmd()->Run({"-j", "any,any_call,any_ret,ind_call", "sleep", SLEEP_SEC}));
- ASSERT_TRUE(RecordCmd()->Run({"-j", "any,k", "sleep", SLEEP_SEC}));
- ASSERT_TRUE(RecordCmd()->Run({"-j", "any,u", "sleep", SLEEP_SEC}));
- ASSERT_FALSE(RecordCmd()->Run({"-j", "u", "sleep", SLEEP_SEC}));
+ ASSERT_TRUE(RunRecordCmd({"-b"}));
+ ASSERT_TRUE(RunRecordCmd({"-j", "any,any_call,any_ret,ind_call"}));
+ ASSERT_TRUE(RunRecordCmd({"-j", "any,k"}));
+ ASSERT_TRUE(RunRecordCmd({"-j", "any,u"}));
+ ASSERT_FALSE(RunRecordCmd({"-j", "u"}));
} else {
GTEST_LOG_(INFO)
<< "This test does nothing as branch stack sampling is not supported on this device.";
@@ -102,18 +126,18 @@
}
TEST(record_cmd, event_modifier) {
- ASSERT_TRUE(RecordCmd()->Run({"-e", "cpu-cycles:u", "sleep", SLEEP_SEC}));
+ ASSERT_TRUE(RunRecordCmd({"-e", "cpu-cycles:u"}));
}
TEST(record_cmd, fp_callchain_sampling) {
- ASSERT_TRUE(RecordCmd()->Run({"--call-graph", "fp", "sleep", SLEEP_SEC}));
+ ASSERT_TRUE(RunRecordCmd({"--call-graph", "fp"}));
}
TEST(record_cmd, dwarf_callchain_sampling) {
if (IsDwarfCallChainSamplingSupported()) {
- ASSERT_TRUE(RecordCmd()->Run({"--call-graph", "dwarf", "sleep", SLEEP_SEC}));
- ASSERT_TRUE(RecordCmd()->Run({"--call-graph", "dwarf,16384", "sleep", SLEEP_SEC}));
- ASSERT_TRUE(RecordCmd()->Run({"-g", "sleep", SLEEP_SEC}));
+ ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf"}));
+ ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf,16384"}));
+ ASSERT_TRUE(RunRecordCmd({"-g"}));
} else {
GTEST_LOG_(INFO)
<< "This test does nothing as dwarf callchain sampling is not supported on this device.";
@@ -122,24 +146,24 @@
TEST(record_cmd, no_unwind_option) {
if (IsDwarfCallChainSamplingSupported()) {
- ASSERT_TRUE(RecordCmd()->Run({"--call-graph", "dwarf", "--no-unwind", "sleep", SLEEP_SEC}));
+ ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf", "--no-unwind"}));
} else {
GTEST_LOG_(INFO)
<< "This test does nothing as dwarf callchain sampling is not supported on this device.";
}
- ASSERT_FALSE(RecordCmd()->Run({"--no-unwind", "sleep", SLEEP_SEC}));
+ ASSERT_FALSE(RunRecordCmd({"--no-unwind"}));
}
TEST(record_cmd, post_unwind_option) {
if (IsDwarfCallChainSamplingSupported()) {
- ASSERT_TRUE(RecordCmd()->Run({"--call-graph", "dwarf", "--post-unwind", "sleep", SLEEP_SEC}));
+ ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf", "--post-unwind"}));
} else {
GTEST_LOG_(INFO)
<< "This test does nothing as dwarf callchain sampling is not supported on this device.";
}
- ASSERT_FALSE(RecordCmd()->Run({"--post-unwind", "sleep", SLEEP_SEC}));
+ ASSERT_FALSE(RunRecordCmd({"--post-unwind"}));
ASSERT_FALSE(
- RecordCmd()->Run({"--call-graph", "dwarf", "--no-unwind", "--post-unwind", "sleep", SLEEP_SEC}));
+ RunRecordCmd({"--call-graph", "dwarf", "--no-unwind", "--post-unwind"}));
}
TEST(record_cmd, existing_processes) {
@@ -147,7 +171,8 @@
CreateProcesses(2, &workloads);
std::string pid_list =
android::base::StringPrintf("%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
- ASSERT_TRUE(RecordCmd()->Run({"-p", pid_list}));
+ TemporaryFile tmpfile;
+ ASSERT_TRUE(RecordCmd()->Run({"-p", pid_list, "-o", tmpfile.path}));
}
TEST(record_cmd, existing_threads) {
@@ -156,7 +181,8 @@
// Process id can also be used as thread id in linux.
std::string tid_list =
android::base::StringPrintf("%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
- ASSERT_TRUE(RecordCmd()->Run({"-t", tid_list}));
+ TemporaryFile tmpfile;
+ ASSERT_TRUE(RecordCmd()->Run({"-t", tid_list, "-o", tmpfile.path}));
}
TEST(record_cmd, no_monitored_threads) {
@@ -164,17 +190,19 @@
}
TEST(record_cmd, more_than_one_event_types) {
- ASSERT_TRUE(RecordCmd()->Run({"-e", "cpu-cycles,cpu-clock", "sleep", SLEEP_SEC}));
- ASSERT_TRUE(RecordCmd()->Run({"-e", "cpu-cycles", "-e", "cpu-clock", "sleep", SLEEP_SEC}));
+ ASSERT_TRUE(RunRecordCmd({"-e", "cpu-cycles,cpu-clock"}));
+ ASSERT_TRUE(RunRecordCmd({"-e", "cpu-cycles", "-e", "cpu-clock"}));
}
TEST(record_cmd, cpu_option) {
- ASSERT_TRUE(RecordCmd()->Run({"--cpu", "0", "sleep", SLEEP_SEC}));
- ASSERT_TRUE(RecordCmd()->Run({"--cpu", "0", "-a", "sleep", SLEEP_SEC}));
+ ASSERT_TRUE(RunRecordCmd({"--cpu", "0"}));
+ if (IsRoot()) {
+ ASSERT_TRUE(RunRecordCmd({"--cpu", "0", "-a"}));
+ }
}
TEST(record_cmd, mmap_page_option) {
- ASSERT_TRUE(RecordCmd()->Run({"-m", "1", "sleep", SLEEP_SEC}));
- ASSERT_FALSE(RecordCmd()->Run({"-m", "0", "sleep", SLEEP_SEC}));
- ASSERT_FALSE(RecordCmd()->Run({"-m", "7", "sleep", SLEEP_SEC}));
+ ASSERT_TRUE(RunRecordCmd({"-m", "1"}));
+ ASSERT_FALSE(RunRecordCmd({"-m", "0"}));
+ ASSERT_FALSE(RunRecordCmd({"-m", "7"}));
}
diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
index b45e02f..27f1f09 100644
--- a/simpleperf/cmd_stat_test.cpp
+++ b/simpleperf/cmd_stat_test.cpp
@@ -19,6 +19,7 @@
#include <android-base/stringprintf.h>
#include "command.h"
+#include "get_test_data.h"
#include "test_util.h"
static std::unique_ptr<Command> StatCmd() {
@@ -34,7 +35,9 @@
}
TEST(stat_cmd, system_wide_option) {
- ASSERT_TRUE(StatCmd()->Run({"-a", "sleep", "1"}));
+ if (IsRoot()) {
+ ASSERT_TRUE(StatCmd()->Run({"-a", "sleep", "1"}));
+ }
}
TEST(stat_cmd, verbose_option) {
@@ -42,11 +45,13 @@
}
TEST(stat_cmd, tracepoint_event) {
- ASSERT_TRUE(StatCmd()->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"}));
+ if (IsRoot()) {
+ ASSERT_TRUE(StatCmd()->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"}));
+ }
}
TEST(stat_cmd, event_modifier) {
- ASSERT_TRUE(StatCmd()->Run({"-e", "cpu-cycles:u,sched:sched_switch:k", "sleep", "1"}));
+ ASSERT_TRUE(StatCmd()->Run({"-e", "cpu-cycles:u,cpu-cycles:k", "sleep", "1"}));
}
void CreateProcesses(size_t count, std::vector<std::unique_ptr<Workload>>* workloads) {
@@ -82,5 +87,7 @@
TEST(stat_cmd, cpu_option) {
ASSERT_TRUE(StatCmd()->Run({"--cpu", "0", "sleep", "1"}));
- ASSERT_TRUE(StatCmd()->Run({"--cpu", "0", "-a", "sleep", "1"}));
+ if (IsRoot()) {
+ ASSERT_TRUE(StatCmd()->Run({"--cpu", "0", "-a", "sleep", "1"}));
+ }
}
diff --git a/simpleperf/environment_test.cpp b/simpleperf/environment_test.cpp
index 2ad465f..6bca7b8 100644
--- a/simpleperf/environment_test.cpp
+++ b/simpleperf/environment_test.cpp
@@ -18,6 +18,7 @@
#include <functional>
#include <android-base/file.h>
+#include <android-base/test_utils.h>
#include "environment.h"
@@ -50,25 +51,24 @@
"ffffffffa005c4e4 d __warned.41698 [libsas]\n"
"aaaaaaaaaaaaaaaa T _text\n"
"cccccccccccccccc c ccccc\n";
- const char* tempfile = "tempfile_process_kernel_symbols";
- ASSERT_TRUE(android::base::WriteStringToFile(data, tempfile));
+ TemporaryFile tempfile;
+ ASSERT_TRUE(android::base::WriteStringToFile(data, tempfile.path));
KernelSymbol expected_symbol;
expected_symbol.addr = 0xffffffffa005c4e4ULL;
expected_symbol.type = 'd';
expected_symbol.name = "__warned.41698";
expected_symbol.module = "libsas";
ASSERT_TRUE(ProcessKernelSymbols(
- tempfile, std::bind(&KernelSymbolsMatch, std::placeholders::_1, expected_symbol)));
+ tempfile.path, std::bind(&KernelSymbolsMatch, std::placeholders::_1, expected_symbol)));
expected_symbol.addr = 0xaaaaaaaaaaaaaaaaULL;
expected_symbol.type = 'T';
expected_symbol.name = "_text";
expected_symbol.module = nullptr;
ASSERT_TRUE(ProcessKernelSymbols(
- tempfile, std::bind(&KernelSymbolsMatch, std::placeholders::_1, expected_symbol)));
+ tempfile.path, std::bind(&KernelSymbolsMatch, std::placeholders::_1, expected_symbol)));
expected_symbol.name = "non_existent_symbol";
ASSERT_FALSE(ProcessKernelSymbols(
- tempfile, std::bind(&KernelSymbolsMatch, std::placeholders::_1, expected_symbol)));
- ASSERT_EQ(0, unlink(tempfile));
+ tempfile.path, std::bind(&KernelSymbolsMatch, std::placeholders::_1, expected_symbol)));
}
diff --git a/simpleperf/get_test_data.h b/simpleperf/get_test_data.h
index 00d1a0b..02363d6 100644
--- a/simpleperf/get_test_data.h
+++ b/simpleperf/get_test_data.h
@@ -24,6 +24,8 @@
std::string GetTestData(const std::string& filename);
const std::string& GetTestDataDir();
+bool IsRoot();
+
static const std::string PERF_DATA = "perf.data";
static const std::string CALLGRAPH_FP_PERF_DATA = "perf_g_fp.data";
static const std::string BRANCH_PERF_DATA = "perf_b.data";
diff --git a/simpleperf/gtest_main.cpp b/simpleperf/gtest_main.cpp
index 5e463ab..8ff45c8 100644
--- a/simpleperf/gtest_main.cpp
+++ b/simpleperf/gtest_main.cpp
@@ -16,29 +16,112 @@
#include <gtest/gtest.h>
+#include <memory>
+
+#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/test_utils.h>
+#include <ziparchive/zip_archive.h>
#include "get_test_data.h"
+#include "read_elf.h"
#include "utils.h"
static std::string testdata_dir;
+#if defined(IN_CTS_TEST)
+static const std::string testdata_section = ".testzipdata";
+
+static bool ExtractTestDataFromElfSection() {
+ if (!MkdirWithParents(testdata_dir)) {
+ PLOG(ERROR) << "failed to create testdata_dir " << testdata_dir;
+ return false;
+ }
+ std::string content;
+ if (!ReadSectionFromElfFile("/proc/self/exe", testdata_section, &content)) {
+ LOG(ERROR) << "failed to read section " << testdata_section;
+ return false;
+ }
+ TemporaryFile tmp_file;
+ if (!android::base::WriteStringToFile(content, tmp_file.path)) {
+ PLOG(ERROR) << "failed to write file " << tmp_file.path;
+ return false;
+ }
+ ArchiveHelper ahelper(tmp_file.fd, tmp_file.path);
+ if (!ahelper) {
+ LOG(ERROR) << "failed to open archive " << tmp_file.path;
+ return false;
+ }
+ ZipArchiveHandle& handle = ahelper.archive_handle();
+ void* cookie;
+ int ret = StartIteration(handle, &cookie, nullptr, nullptr);
+ if (ret != 0) {
+ LOG(ERROR) << "failed to start iterating zip entries";
+ return false;
+ }
+ ZipEntry entry;
+ ZipString name;
+ while (Next(cookie, &entry, &name) == 0) {
+ std::string entry_name(name.name, name.name + name.name_length);
+ std::string path = testdata_dir + entry_name;
+ // Skip dir.
+ if (path.back() == '/') {
+ continue;
+ }
+ if (!MkdirWithParents(path)) {
+ LOG(ERROR) << "failed to create dir for " << path;
+ return false;
+ }
+ FileHelper fhelper = FileHelper::OpenWriteOnly(path);
+ if (!fhelper) {
+ PLOG(ERROR) << "failed to create file " << path;
+ return false;
+ }
+ std::vector<uint8_t> data(entry.uncompressed_length);
+ if (ExtractToMemory(handle, &entry, data.data(), data.size()) != 0) {
+ LOG(ERROR) << "failed to extract entry " << entry_name;
+ return false;
+ }
+ if (!android::base::WriteFully(fhelper.fd(), data.data(), data.size())) {
+ LOG(ERROR) << "failed to write file " << path;
+ return false;
+ }
+ }
+ EndIteration(cookie);
+ return true;
+}
+#endif // defined(IN_CTS_TEST)
+
int main(int argc, char** argv) {
InitLogging(argv, android::base::StderrLogger);
testing::InitGoogleTest(&argc, argv);
+
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "-t") == 0 && i + 1 < argc) {
testdata_dir = argv[i + 1];
- break;
+ i++;
}
}
- if (testdata_dir.empty()) {
- printf("Usage: simpleperf_unit_test -t <testdata_dir>\n");
+
+#if defined(IN_CTS_TEST)
+ std::unique_ptr<TemporaryDir> tmp_dir;
+ if (!::testing::GTEST_FLAG(list_tests) && testdata_dir.empty()) {
+ tmp_dir.reset(new TemporaryDir);
+ testdata_dir = std::string(tmp_dir->path) + "/";
+ if (!ExtractTestDataFromElfSection()) {
+ LOG(ERROR) << "failed to extract test data from elf section";
+ return 1;
+ }
+ }
+#endif
+ if (!::testing::GTEST_FLAG(list_tests) && testdata_dir.empty()) {
+ printf("Usage: %s -t <testdata_dir>\n", argv[0]);
return 1;
}
if (testdata_dir.back() != '/') {
testdata_dir.push_back('/');
}
+ LOG(INFO) << "testdata is in " << testdata_dir;
return RUN_ALL_TESTS();
}
@@ -49,3 +132,15 @@
const std::string& GetTestDataDir() {
return testdata_dir;
}
+
+bool IsRoot() {
+ static int is_root = -1;
+ if (is_root == -1) {
+#if defined(__linux__)
+ is_root = (getuid() == 0) ? 1 : 0;
+#else
+ is_root = 0;
+#endif
+ }
+ return is_root == 1;
+}
diff --git a/simpleperf/read_apk.cpp b/simpleperf/read_apk.cpp
index 8029dc5..18c76c2 100644
--- a/simpleperf/read_apk.cpp
+++ b/simpleperf/read_apk.cpp
@@ -31,29 +31,6 @@
#include "read_elf.h"
#include "utils.h"
-class ArchiveHelper {
- public:
- explicit ArchiveHelper(int fd, const std::string& debug_filename) : valid_(false) {
- int rc = OpenArchiveFd(fd, "", &handle_, false);
- if (rc == 0) {
- valid_ = true;
- } else {
- LOG(ERROR) << "Failed to open archive " << debug_filename << ": " << ErrorCodeString(rc);
- }
- }
- ~ArchiveHelper() {
- if (valid_) {
- CloseArchive(handle_);
- }
- }
- bool valid() const { return valid_; }
- ZipArchiveHandle &archive_handle() { return handle_; }
-
- private:
- ZipArchiveHandle handle_;
- bool valid_;
-};
-
std::map<ApkInspector::ApkOffset, std::unique_ptr<EmbeddedElf>> ApkInspector::embedded_elf_cache_;
EmbeddedElf* ApkInspector::FindElfInApkByOffset(const std::string& apk_path, uint64_t file_offset) {
@@ -75,13 +52,13 @@
if (!IsValidApkPath(apk_path)) {
return nullptr;
}
- FileHelper fhelper(apk_path.c_str());
+ FileHelper fhelper = FileHelper::OpenReadOnly(apk_path);
if (!fhelper) {
return nullptr;
}
ArchiveHelper ahelper(fhelper.fd(), apk_path);
- if (!ahelper.valid()) {
+ if (!ahelper) {
return nullptr;
}
ZipArchiveHandle &handle = ahelper.archive_handle();
@@ -135,12 +112,12 @@
if (!IsValidApkPath(apk_path)) {
return nullptr;
}
- FileHelper fhelper(apk_path.c_str());
+ FileHelper fhelper = FileHelper::OpenReadOnly(apk_path);
if (!fhelper) {
return nullptr;
}
ArchiveHelper ahelper(fhelper.fd(), apk_path);
- if (!ahelper.valid()) {
+ if (!ahelper) {
return nullptr;
}
ZipArchiveHandle& handle = ahelper.archive_handle();
diff --git a/simpleperf/read_elf.cpp b/simpleperf/read_elf.cpp
index 6551629..db33e0e 100644
--- a/simpleperf/read_elf.cpp
+++ b/simpleperf/read_elf.cpp
@@ -146,7 +146,7 @@
static BinaryRet OpenObjectFile(const std::string& filename, uint64_t file_offset = 0,
uint64_t file_size = 0) {
BinaryRet ret;
- FileHelper fhelper(filename);
+ FileHelper fhelper = FileHelper::OpenReadOnly(filename);
if (!fhelper) {
PLOG(DEBUG) << "failed to open " << filename;
return ret;
@@ -357,3 +357,43 @@
}
return result;
}
+
+template <class ELFT>
+bool ReadSectionFromELFFile(const llvm::object::ELFFile<ELFT>* elf, const std::string& section_name,
+ std::string* content) {
+ for (auto it = elf->begin_sections(); it != elf->end_sections(); ++it) {
+ auto name_or_err = elf->getSectionName(&*it);
+ if (name_or_err && *name_or_err == section_name) {
+ auto data_or_err = elf->getSectionContents(&*it);
+ if (!data_or_err) {
+ LOG(ERROR) << "failed to read section " << section_name;
+ return false;
+ }
+ content->append(data_or_err->begin(), data_or_err->end());
+ return true;
+ }
+ }
+ LOG(ERROR) << "can't find section " << section_name;
+ return false;
+}
+
+bool ReadSectionFromElfFile(const std::string& filename, const std::string& section_name,
+ std::string* content) {
+ if (!IsValidElfPath(filename)) {
+ return false;
+ }
+ BinaryRet ret = OpenObjectFile(filename);
+ if (ret.obj == nullptr) {
+ return false;
+ }
+ bool result = false;
+ if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(ret.obj)) {
+ result = ReadSectionFromELFFile(elf->getELFFile(), section_name, content);
+ } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(ret.obj)) {
+ result = ReadSectionFromELFFile(elf->getELFFile(), section_name, content);
+ } else {
+ LOG(ERROR) << "unknown elf format in file" << filename;
+ return false;
+ }
+ return result;
+}
diff --git a/simpleperf/read_elf.h b/simpleperf/read_elf.h
index 11dc8d8..a6c73c3 100644
--- a/simpleperf/read_elf.h
+++ b/simpleperf/read_elf.h
@@ -51,6 +51,9 @@
const BuildId& expected_build_id,
uint64_t* min_addr);
+bool ReadSectionFromElfFile(const std::string& filename, const std::string& section_name,
+ std::string* content);
+
// Expose the following functions for unit tests.
bool IsArmMappingSymbol(const char* name);
bool IsValidElfFile(int fd);
diff --git a/simpleperf/record_file_test.cpp b/simpleperf/record_file_test.cpp
index e4f963e..4648a64 100644
--- a/simpleperf/record_file_test.cpp
+++ b/simpleperf/record_file_test.cpp
@@ -20,6 +20,8 @@
#include <memory>
+#include <android-base/test_utils.h>
+
#include "environment.h"
#include "event_attr.h"
#include "event_type.h"
@@ -32,10 +34,6 @@
class RecordFileTest : public ::testing::Test {
protected:
- virtual void SetUp() {
- filename_ = "temporary.record_file";
- }
-
void AddEventType(const std::string& event_type_str) {
std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType(event_type_str);
ASSERT_TRUE(event_type_modifier != nullptr);
@@ -47,14 +45,14 @@
attr_ids_.push_back(attr_id);
}
- std::string filename_;
+ TemporaryFile tmpfile_;
std::vector<std::unique_ptr<perf_event_attr>> attrs_;
std::vector<AttrWithId> attr_ids_;
};
TEST_F(RecordFileTest, smoke) {
// Write to a record file.
- std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(filename_);
+ std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path);
ASSERT_TRUE(writer != nullptr);
// Write attr section.
@@ -78,7 +76,7 @@
ASSERT_TRUE(writer->Close());
// Read from a record file.
- std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(filename_);
+ std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
ASSERT_TRUE(reader != nullptr);
const std::vector<FileAttr>& file_attrs = reader->AttrSection();
ASSERT_EQ(1u, file_attrs.size());
@@ -102,7 +100,7 @@
TEST_F(RecordFileTest, records_sorted_by_time) {
// Write to a record file.
- std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(filename_);
+ std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path);
ASSERT_TRUE(writer != nullptr);
// Write attr section.
@@ -125,7 +123,7 @@
ASSERT_TRUE(writer->Close());
// Read from a record file.
- std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(filename_);
+ std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
ASSERT_TRUE(reader != nullptr);
std::vector<std::unique_ptr<Record>> records = reader->DataSection();
ASSERT_EQ(3u, records.size());
@@ -138,7 +136,7 @@
TEST_F(RecordFileTest, record_more_than_one_attr) {
// Write to a record file.
- std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(filename_);
+ std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path);
ASSERT_TRUE(writer != nullptr);
// Write attr section.
@@ -150,7 +148,7 @@
ASSERT_TRUE(writer->Close());
// Read from a record file.
- std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(filename_);
+ std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
ASSERT_TRUE(reader != nullptr);
const std::vector<FileAttr>& file_attrs = reader->AttrSection();
ASSERT_EQ(3u, file_attrs.size());
diff --git a/simpleperf/utils.cpp b/simpleperf/utils.cpp
index 2e68767..99e1e98 100644
--- a/simpleperf/utils.cpp
+++ b/simpleperf/utils.cpp
@@ -54,11 +54,15 @@
return result;
}
-FileHelper::FileHelper() : fd_(-1) {
+
+FileHelper FileHelper::OpenReadOnly(const std::string& filename) {
+ int fd = TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_BINARY));
+ return FileHelper(fd);
}
-FileHelper::FileHelper(const std::string& filename) {
- fd_ = TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_BINARY));
+FileHelper FileHelper::OpenWriteOnly(const std::string& filename) {
+ int fd = TEMP_FAILURE_RETRY(open(filename.c_str(), O_WRONLY | O_BINARY | O_CREAT, 0644));
+ return FileHelper(fd);
}
FileHelper::~FileHelper() {
@@ -67,6 +71,21 @@
}
}
+ArchiveHelper::ArchiveHelper(int fd, const std::string& debug_filename) : valid_(false) {
+ int rc = OpenArchiveFd(fd, "", &handle_, false);
+ if (rc == 0) {
+ valid_ = true;
+ } else {
+ LOG(ERROR) << "Failed to open archive " << debug_filename << ": " << ErrorCodeString(rc);
+ }
+}
+
+ArchiveHelper::~ArchiveHelper() {
+ if (valid_) {
+ CloseArchive(handle_);
+ }
+}
+
void PrintIndented(size_t indent, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
@@ -137,3 +156,27 @@
}
return 0;
}
+
+bool MkdirWithParents(const std::string& path) {
+ size_t prev_end = 0;
+ while (prev_end < path.size()) {
+ size_t next_end = path.find('/', prev_end + 1);
+ if (next_end == std::string::npos) {
+ break;
+ }
+ std::string dir_path = path.substr(0, next_end);
+ if (!IsDir(dir_path)) {
+#if defined(_WIN32)
+ int ret = mkdir(dir_path.c_str());
+#else
+ int ret = mkdir(dir_path.c_str(), 0755);
+#endif
+ if (ret != 0) {
+ PLOG(ERROR) << "failed to create dir " << dir_path;
+ return false;
+ }
+ }
+ prev_end = next_end;
+ }
+ return true;
+}
diff --git a/simpleperf/utils.h b/simpleperf/utils.h
index 6581a76..1164b1e 100644
--- a/simpleperf/utils.h
+++ b/simpleperf/utils.h
@@ -23,6 +23,7 @@
#include <vector>
#include <android-base/macros.h>
+#include <ziparchive/zip_archive.h>
#define ALIGN(value, alignment) (((value) + (alignment)-1) & ~((alignment)-1))
@@ -56,8 +57,14 @@
class FileHelper {
public:
- FileHelper();
- explicit FileHelper(const std::string& filename);
+ static FileHelper OpenReadOnly(const std::string& filename);
+ static FileHelper OpenWriteOnly(const std::string& filename);
+
+ FileHelper(FileHelper&& other) {
+ fd_ = other.fd_;
+ other.fd_ = -1;
+ }
+
~FileHelper();
explicit operator bool() const {
@@ -69,11 +76,32 @@
}
private:
+ FileHelper(int fd) : fd_(fd) {}
int fd_;
DISALLOW_COPY_AND_ASSIGN(FileHelper);
};
+
+class ArchiveHelper {
+ public:
+ ArchiveHelper(int fd, const std::string& debug_filename);
+ ~ArchiveHelper();
+
+ explicit operator bool() const {
+ return valid_;
+ }
+ ZipArchiveHandle &archive_handle() {
+ return handle_;
+ }
+
+ private:
+ ZipArchiveHandle handle_;
+ bool valid_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArchiveHelper);
+};
+
template <class T>
void MoveFromBinaryFormat(T& data, const char*& p) {
data = *reinterpret_cast<const T*>(p);
@@ -89,5 +117,6 @@
bool IsDir(const std::string& dirpath);
bool IsRegularFile(const std::string& filename);
uint64_t GetFileSize(const std::string& filename);
+bool MkdirWithParents(const std::string& path);
#endif // SIMPLE_PERF_UTILS_H_