Home | History | Annotate | Download | only in simpleperf
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <gtest/gtest.h>
     18 
     19 #include <libgen.h>
     20 
     21 #include <memory>
     22 
     23 #include <android-base/file.h>
     24 #include <android-base/logging.h>
     25 #include <android-base/test_utils.h>
     26 #include <ziparchive/zip_archive.h>
     27 
     28 #if defined(__ANDROID__)
     29 #include <android-base/properties.h>
     30 #endif
     31 
     32 #include "command.h"
     33 #include "environment.h"
     34 #include "get_test_data.h"
     35 #include "read_elf.h"
     36 #include "test_util.h"
     37 #include "utils.h"
     38 #include "workload.h"
     39 
     40 static std::string testdata_dir;
     41 
     42 #if defined(__ANDROID__)
     43 static const std::string testdata_section = ".testzipdata";
     44 
     45 static bool ExtractTestDataFromElfSection() {
     46   if (!MkdirWithParents(testdata_dir)) {
     47     PLOG(ERROR) << "failed to create testdata_dir " << testdata_dir;
     48     return false;
     49   }
     50   std::string content;
     51   ElfStatus result = ReadSectionFromElfFile("/proc/self/exe", testdata_section, &content);
     52   if (result != ElfStatus::NO_ERROR) {
     53     LOG(ERROR) << "failed to read section " << testdata_section
     54                << ": " << result;
     55     return false;
     56   }
     57   TemporaryFile tmp_file;
     58   if (!android::base::WriteStringToFile(content, tmp_file.path)) {
     59     PLOG(ERROR) << "failed to write file " << tmp_file.path;
     60     return false;
     61   }
     62   ArchiveHelper ahelper(tmp_file.fd, tmp_file.path);
     63   if (!ahelper) {
     64     LOG(ERROR) << "failed to open archive " << tmp_file.path;
     65     return false;
     66   }
     67   ZipArchiveHandle& handle = ahelper.archive_handle();
     68   void* cookie;
     69   int ret = StartIteration(handle, &cookie, nullptr, nullptr);
     70   if (ret != 0) {
     71     LOG(ERROR) << "failed to start iterating zip entries";
     72     return false;
     73   }
     74   std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
     75   ZipEntry entry;
     76   ZipString name;
     77   while (Next(cookie, &entry, &name) == 0) {
     78     std::string entry_name(name.name, name.name + name.name_length);
     79     std::string path = testdata_dir + entry_name;
     80     // Skip dir.
     81     if (path.back() == '/') {
     82       continue;
     83     }
     84     if (!MkdirWithParents(path)) {
     85       LOG(ERROR) << "failed to create dir for " << path;
     86       return false;
     87     }
     88     FileHelper fhelper = FileHelper::OpenWriteOnly(path);
     89     if (!fhelper) {
     90       PLOG(ERROR) << "failed to create file " << path;
     91       return false;
     92     }
     93     std::vector<uint8_t> data(entry.uncompressed_length);
     94     if (ExtractToMemory(handle, &entry, data.data(), data.size()) != 0) {
     95       LOG(ERROR) << "failed to extract entry " << entry_name;
     96       return false;
     97     }
     98     if (!android::base::WriteFully(fhelper.fd(), data.data(), data.size())) {
     99       LOG(ERROR) << "failed to write file " << path;
    100       return false;
    101     }
    102   }
    103   return true;
    104 }
    105 
    106 class ScopedEnablingPerf {
    107  public:
    108   ScopedEnablingPerf() {
    109     prop_value_ = android::base::GetProperty("security.perf_harden", "");
    110     SetProp("0");
    111   }
    112 
    113   ~ScopedEnablingPerf() {
    114     if (!prop_value_.empty()) {
    115       SetProp(prop_value_);
    116     }
    117   }
    118 
    119  private:
    120   void SetProp(const std::string& value) {
    121     android::base::SetProperty("security.perf_harden", value);
    122 
    123     // Sleep one second to wait for security.perf_harden changing
    124     // /proc/sys/kernel/perf_event_paranoid.
    125     sleep(1);
    126   }
    127 
    128   std::string prop_value_;
    129 };
    130 
    131 class ScopedWorkloadExecutable {
    132  public:
    133   ScopedWorkloadExecutable() {
    134     std::string executable_path;
    135     if (!android::base::Readlink("/proc/self/exe", &executable_path)) {
    136       PLOG(ERROR) << "ReadLink failed";
    137     }
    138     Workload::RunCmd({"run-as", GetDefaultAppPackageName(), "cp", executable_path, "workload"});
    139   }
    140 
    141   ~ScopedWorkloadExecutable() {
    142     Workload::RunCmd({"run-as", GetDefaultAppPackageName(), "rm", "workload"});
    143   }
    144 };
    145 
    146 class ScopedTempDir {
    147  public:
    148   ~ScopedTempDir() {
    149     Workload::RunCmd({"rm", "-rf", dir_.path});
    150   }
    151 
    152   char* path() {
    153     return dir_.path;
    154   }
    155 
    156  private:
    157   TemporaryDir dir_;
    158 };
    159 
    160 #endif  // defined(__ANDROID__)
    161 
    162 int main(int argc, char** argv) {
    163   android::base::InitLogging(argv, android::base::StderrLogger);
    164   android::base::LogSeverity log_severity = android::base::WARNING;
    165 
    166 #if defined(RUN_IN_APP_CONTEXT)
    167   // When RUN_IN_APP_CONTEXT macro is defined, the tests running record/stat commands will
    168   // be forced to run with '--app' option. It will copy the test binary to an Android application's
    169   // directory, and use run-as to run the binary as simpleperf executable.
    170   if (android::base::Basename(argv[0]) == "simpleperf") {
    171     return RunSimpleperfCmd(argc, argv) ? 0 : 1;
    172   } else if (android::base::Basename(argv[0]) == "workload") {
    173     RunWorkloadFunction();
    174   }
    175   SetDefaultAppPackageName(RUN_IN_APP_CONTEXT);
    176   ScopedWorkloadExecutable scoped_workload_executable;
    177 #endif
    178 
    179   for (int i = 1; i < argc; ++i) {
    180     if (strcmp(argv[i], "-t") == 0 && i + 1 < argc) {
    181       testdata_dir = argv[i + 1];
    182       i++;
    183     } else if (strcmp(argv[i], "--log") == 0) {
    184       if (i + 1 < argc) {
    185         ++i;
    186         if (!GetLogSeverity(argv[i], &log_severity)) {
    187           LOG(ERROR) << "Unknown log severity: " << argv[i];
    188           return 1;
    189         }
    190       } else {
    191         LOG(ERROR) << "Missing argument for --log option.\n";
    192         return 1;
    193       }
    194     }
    195   }
    196   android::base::ScopedLogSeverity severity(log_severity);
    197 
    198 #if defined(__ANDROID__)
    199   // A cts test PerfEventParanoidTest.java is testing if
    200   // /proc/sys/kernel/perf_event_paranoid is 3, so restore perf_harden
    201   // value after current test to not break that test.
    202   ScopedEnablingPerf scoped_enabling_perf;
    203 
    204   std::unique_ptr<ScopedTempDir> tmp_dir;
    205   if (!::testing::GTEST_FLAG(list_tests) && testdata_dir.empty()) {
    206     testdata_dir = std::string(dirname(argv[0])) + "/testdata";
    207     if (!IsDir(testdata_dir)) {
    208       tmp_dir.reset(new ScopedTempDir);
    209       testdata_dir = std::string(tmp_dir->path()) + "/";
    210       if (!ExtractTestDataFromElfSection()) {
    211         LOG(ERROR) << "failed to extract test data from elf section";
    212         return 1;
    213       }
    214     }
    215   }
    216 
    217 #endif
    218 
    219   testing::InitGoogleTest(&argc, argv);
    220   if (!::testing::GTEST_FLAG(list_tests) && testdata_dir.empty()) {
    221     printf("Usage: %s -t <testdata_dir>\n", argv[0]);
    222     return 1;
    223   }
    224   if (testdata_dir.back() != '/') {
    225     testdata_dir.push_back('/');
    226   }
    227   LOG(INFO) << "testdata is in " << testdata_dir;
    228   return RUN_ALL_TESTS();
    229 }
    230 
    231 std::string GetTestData(const std::string& filename) {
    232   return testdata_dir + filename;
    233 }
    234 
    235 const std::string& GetTestDataDir() {
    236   return testdata_dir;
    237 }
    238