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_