| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #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> |
| |
| #if defined(__ANDROID__) |
| #include <sys/system_properties.h> |
| #endif |
| |
| #include "get_test_data.h" |
| #include "read_elf.h" |
| #include "utils.h" |
| |
| static std::string testdata_dir; |
| |
| #if defined(__ANDROID__) |
| 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; |
| ElfStatus result = ReadSectionFromElfFile("/proc/self/exe", testdata_section, &content); |
| if (result != ElfStatus::NO_ERROR) { |
| LOG(ERROR) << "failed to read section " << testdata_section |
| << ": " << result; |
| 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; |
| } |
| std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration); |
| 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; |
| } |
| } |
| return true; |
| } |
| |
| class SavedPerfHardenProperty { |
| public: |
| SavedPerfHardenProperty() { |
| __system_property_get("security.perf_harden", prop_value_); |
| if (!android::base::ReadFileToString("/proc/sys/kernel/perf_event_paranoid", |
| ¶noid_value_)) { |
| PLOG(ERROR) << "failed to read /proc/sys/kernel/perf_event_paranoid"; |
| } |
| } |
| |
| ~SavedPerfHardenProperty() { |
| if (strlen(prop_value_) != 0) { |
| __system_property_set("security.perf_harden", prop_value_); |
| // Sleep one second to wait for security.perf_harden changing |
| // /proc/sys/kernel/perf_event_paranoid. |
| sleep(1); |
| std::string paranoid_value; |
| if (!android::base::ReadFileToString("/proc/sys/kernel/perf_event_paranoid", |
| ¶noid_value)) { |
| PLOG(ERROR) << "failed to read /proc/sys/kernel/perf_event_paranoid"; |
| return; |
| } |
| if (paranoid_value_ != paranoid_value) { |
| LOG(ERROR) << "failed to restore /proc/sys/kernel/perf_event_paranoid"; |
| } |
| } |
| } |
| |
| private: |
| char prop_value_[PROP_VALUE_MAX]; |
| std::string paranoid_value_; |
| }; |
| |
| #endif // defined(__ANDROID__) |
| |
| int main(int argc, char** argv) { |
| InitLogging(argv, android::base::StderrLogger); |
| testing::InitGoogleTest(&argc, argv); |
| android::base::LogSeverity log_severity = android::base::WARNING; |
| |
| for (int i = 1; i < argc; ++i) { |
| if (strcmp(argv[i], "-t") == 0 && i + 1 < argc) { |
| testdata_dir = argv[i + 1]; |
| i++; |
| } else if (strcmp(argv[i], "--log") == 0) { |
| if (i + 1 < argc) { |
| ++i; |
| if (!GetLogSeverity(argv[i], &log_severity)) { |
| LOG(ERROR) << "Unknown log severity: " << argv[i]; |
| return 1; |
| } |
| } else { |
| LOG(ERROR) << "Missing argument for --log option.\n"; |
| return 1; |
| } |
| } |
| } |
| android::base::ScopedLogSeverity severity(log_severity); |
| |
| #if defined(__ANDROID__) |
| 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; |
| } |
| } |
| |
| // A cts test PerfEventParanoidTest.java is testing if |
| // /proc/sys/kernel/perf_event_paranoid is 3, so restore perf_harden |
| // value after current test to not break that test. |
| SavedPerfHardenProperty saved_perf_harden; |
| #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(); |
| } |
| |
| std::string GetTestData(const std::string& filename) { |
| return testdata_dir + filename; |
| } |
| |
| const std::string& GetTestDataDir() { |
| return testdata_dir; |
| } |