ART: Clean up test exec code
Add a helper for fork+exec of another program and collection of
that process' output. Use the helper in other code.
Clean up some tests. Move away from global #ifdef ARCH and
disable tests with the usual-style macros so that it's easier
to see refactoring issues immediately.
Test: mmma
Test: m test-art-host
Change-Id: Ic450e8a3bb24fc6fe423c0e1e007eb0bb34e22b4
diff --git a/oatdump/oatdump_app_test.cc b/oatdump/oatdump_app_test.cc
index 34b07d2..a344286 100644
--- a/oatdump/oatdump_app_test.cc
+++ b/oatdump/oatdump_app_test.cc
@@ -19,31 +19,23 @@
namespace art {
TEST_F(OatDumpTest, TestAppWithBootImage) {
- std::string error_msg;
- ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M"}, &error_msg)) << error_msg;
- ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg;
+ ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M"}));
+ ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode));
}
TEST_F(OatDumpTest, TestAppWithBootImageStatic) {
TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
- std::string error_msg;
- ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M"}, &error_msg)) << error_msg;
- ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg;
+ ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M"}));
+ ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode));
}
TEST_F(OatDumpTest, TestPicAppWithBootImage) {
- std::string error_msg;
- ASSERT_TRUE(
- GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M", "--compile-pic"}, &error_msg))
- << error_msg;
- ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg;
+ ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M", "--compile-pic"}));
+ ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode));
}
TEST_F(OatDumpTest, TestPicAppWithBootImageStatic) {
TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
- std::string error_msg;
- ASSERT_TRUE(
- GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M", "--compile-pic"}, &error_msg))
- << error_msg;
- ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg;
+ ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M", "--compile-pic"}));
+ ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode));
}
} // namespace art
diff --git a/oatdump/oatdump_image_test.cc b/oatdump/oatdump_image_test.cc
index d054ece..de48b04 100644
--- a/oatdump/oatdump_image_test.cc
+++ b/oatdump/oatdump_image_test.cc
@@ -19,25 +19,34 @@
namespace art {
// Disable tests on arm and mips as they are taking too long to run. b/27824283.
-#if !defined(__arm__) && !defined(__mips__)
+#define TEST_DISABLED_FOR_ARM_AND_MIPS() \
+ TEST_DISABLED_FOR_ARM(); \
+ TEST_DISABLED_FOR_ARM64(); \
+ TEST_DISABLED_FOR_MIPS(); \
+ TEST_DISABLED_FOR_MIPS64(); \
+
TEST_F(OatDumpTest, TestImage) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
std::string error_msg;
- ASSERT_TRUE(Exec(kDynamic, kModeArt, {}, kListAndCode, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kDynamic, kModeArt, {}, kListAndCode));
}
TEST_F(OatDumpTest, TestImageStatic) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
std::string error_msg;
- ASSERT_TRUE(Exec(kStatic, kModeArt, {}, kListAndCode, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kStatic, kModeArt, {}, kListAndCode));
}
TEST_F(OatDumpTest, TestOatImage) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
std::string error_msg;
- ASSERT_TRUE(Exec(kDynamic, kModeOat, {}, kListAndCode, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kDynamic, kModeOat, {}, kListAndCode));
}
TEST_F(OatDumpTest, TestOatImageStatic) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
std::string error_msg;
- ASSERT_TRUE(Exec(kStatic, kModeOat, {}, kListAndCode, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kStatic, kModeOat, {}, kListAndCode));
}
-#endif
+
} // namespace art
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index b4eddb9..bcba182 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -19,75 +19,92 @@
namespace art {
// Disable tests on arm and mips as they are taking too long to run. b/27824283.
-#if !defined(__arm__) && !defined(__mips__)
+#define TEST_DISABLED_FOR_ARM_AND_MIPS() \
+ TEST_DISABLED_FOR_ARM(); \
+ TEST_DISABLED_FOR_ARM64(); \
+ TEST_DISABLED_FOR_MIPS(); \
+ TEST_DISABLED_FOR_MIPS64(); \
+
TEST_F(OatDumpTest, TestNoDumpVmap) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
std::string error_msg;
- ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-dump:vmap"}, kListAndCode, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-dump:vmap"}, kListAndCode));
}
TEST_F(OatDumpTest, TestNoDumpVmapStatic) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
std::string error_msg;
- ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-dump:vmap"}, kListAndCode, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-dump:vmap"}, kListAndCode));
}
TEST_F(OatDumpTest, TestNoDisassemble) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
std::string error_msg;
- ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-disassemble"}, kListAndCode, &error_msg))
- << error_msg;
+ ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-disassemble"}, kListAndCode));
}
TEST_F(OatDumpTest, TestNoDisassembleStatic) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
std::string error_msg;
- ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-disassemble"}, kListAndCode, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-disassemble"}, kListAndCode));
}
TEST_F(OatDumpTest, TestListClasses) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
std::string error_msg;
- ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-classes"}, kListOnly, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-classes"}, kListOnly));
}
TEST_F(OatDumpTest, TestListClassesStatic) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
std::string error_msg;
- ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-classes"}, kListOnly, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-classes"}, kListOnly));
}
TEST_F(OatDumpTest, TestListMethods) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
std::string error_msg;
- ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-methods"}, kListOnly, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-methods"}, kListOnly));
}
TEST_F(OatDumpTest, TestListMethodsStatic) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
std::string error_msg;
- ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-methods"}, kListOnly, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-methods"}, kListOnly));
}
TEST_F(OatDumpTest, TestSymbolize) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
std::string error_msg;
- ASSERT_TRUE(Exec(kDynamic, kModeSymbolize, {}, kListOnly, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kDynamic, kModeSymbolize, {}, kListOnly));
}
TEST_F(OatDumpTest, TestSymbolizeStatic) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
std::string error_msg;
- ASSERT_TRUE(Exec(kStatic, kModeSymbolize, {}, kListOnly, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kStatic, kModeSymbolize, {}, kListOnly));
}
TEST_F(OatDumpTest, TestExportDex) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
// Test is failing on target, b/77469384.
TEST_DISABLED_FOR_TARGET();
std::string error_msg;
- ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly, &error_msg))
- << error_msg;
+ ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly));
const std::string dex_location = tmp_dir_+ "/core-oj-hostdex.jar_export.dex";
const std::string dexdump2 = GetExecutableFilePath("dexdump2",
/*is_debug*/false,
/*is_static*/false);
- ASSERT_TRUE(ForkAndExecAndWait({dexdump2, "-d", dex_location}, &error_msg)) << error_msg;
+ std::string output;
+ auto post_fork_fn = []() { return true; };
+ ForkAndExecResult res = ForkAndExec({dexdump2, "-d", dex_location}, post_fork_fn, &output);
+ ASSERT_TRUE(res.StandardSuccess());
}
TEST_F(OatDumpTest, TestExportDexStatic) {
+ TEST_DISABLED_FOR_ARM_AND_MIPS();
TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
std::string error_msg;
- ASSERT_TRUE(Exec(kStatic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly, &error_msg))
- << error_msg;
+ ASSERT_TRUE(Exec(kStatic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly));
}
-#endif
+
} // namespace art
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index 231163b..7f997b3 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -111,9 +111,8 @@
return tmp_dir_ + "/" + GetAppBaseName() + ".odex";
}
- bool GenerateAppOdexFile(Flavor flavor,
- const std::vector<std::string>& args,
- /*out*/ std::string* error_msg) {
+ ::testing::AssertionResult GenerateAppOdexFile(Flavor flavor,
+ const std::vector<std::string>& args) {
std::string dex2oat_path = GetExecutableFilePath(flavor, "dex2oat");
std::vector<std::string> exec_argv = {
dex2oat_path,
@@ -131,18 +130,32 @@
};
exec_argv.insert(exec_argv.end(), args.begin(), args.end());
- return ForkAndExecAndWait(exec_argv, error_msg);
+ auto post_fork_fn = []() {
+ setpgid(0, 0); // Change process groups, so we don't get reaped by ProcessManager.
+ // Ignore setpgid errors.
+ return setenv("ANDROID_LOG_TAGS", "*:e", 1) == 0; // We're only interested in errors and
+ // fatal logs.
+ };
+
+ std::string error_msg;
+ ForkAndExecResult res = ForkAndExec(exec_argv, post_fork_fn, &error_msg);
+ if (res.stage != ForkAndExecResult::kFinished) {
+ return ::testing::AssertionFailure() << strerror(errno);
+ }
+ return res.StandardSuccess() ? ::testing::AssertionSuccess()
+ : (::testing::AssertionFailure() << error_msg);
}
// Run the test with custom arguments.
- bool Exec(Flavor flavor,
- Mode mode,
- const std::vector<std::string>& args,
- Display display,
- /*out*/ std::string* error_msg) {
+ ::testing::AssertionResult Exec(Flavor flavor,
+ Mode mode,
+ const std::vector<std::string>& args,
+ Display display) {
std::string file_path = GetExecutableFilePath(flavor, "oatdump");
- EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
+ if (!OS::FileExists(file_path.c_str())) {
+ return ::testing::AssertionFailure() << file_path << " should be a valid file path";
+ }
// ScratchFile scratch;
std::vector<std::string> exec_argv = { file_path };
@@ -179,129 +192,118 @@
}
exec_argv.insert(exec_argv.end(), args.begin(), args.end());
- pid_t pid;
- int pipe_fd;
- bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg);
- if (result) {
- static const size_t kLineMax = 256;
- char line[kLineMax] = {};
- size_t line_len = 0;
- size_t total = 0;
- std::vector<bool> found(expected_prefixes.size(), false);
- while (true) {
- while (true) {
+ std::vector<bool> found(expected_prefixes.size(), false);
+ auto line_handle_fn = [&found, &expected_prefixes](const char* line, size_t line_len) {
+ if (line_len == 0) {
+ return;
+ }
+ // Check contents.
+ for (size_t i = 0; i < expected_prefixes.size(); ++i) {
+ const std::string& expected = expected_prefixes[i];
+ if (!found[i] &&
+ line_len >= expected.length() &&
+ memcmp(line, expected.c_str(), expected.length()) == 0) {
+ found[i] = true;
+ }
+ }
+ };
+
+ static constexpr size_t kLineMax = 256;
+ char line[kLineMax] = {};
+ size_t line_len = 0;
+ size_t total = 0;
+ bool ignore_next_line = false;
+ auto line_buf_fn = [&](char* buf, size_t len) {
+ total += len;
+
+ if (len == 0 && line_len > 0 && !ignore_next_line) {
+ // Everything done, handle leftovers.
+ line_handle_fn(line, line_len);
+ }
+
+ while (len > 0) {
+ // Copy buf into the free tail of the line buffer, and move input buffer along.
+ size_t copy = std::min(kLineMax - line_len, len);
+ memcpy(&line[line_len], buf, copy);
+ buf += copy;
+ len -= copy;
+
+ // Skip spaces. Declare a lambda for reuse (incurs a potential extra memmove).
+ auto trim_space = [&]() {
size_t spaces = 0;
- // Trim spaces at the start of the line.
for (; spaces < line_len && isspace(line[spaces]); ++spaces) {}
if (spaces > 0) {
line_len -= spaces;
memmove(&line[0], &line[spaces], line_len);
}
- ssize_t bytes_read =
- TEMP_FAILURE_RETRY(read(pipe_fd, &line[line_len], kLineMax - line_len));
- if (bytes_read <= 0) {
- break;
- }
- line_len += bytes_read;
- total += bytes_read;
- }
- if (line_len == 0) {
- break;
- }
- // Check contents.
- for (size_t i = 0; i < expected_prefixes.size(); ++i) {
- const std::string& expected = expected_prefixes[i];
- if (!found[i] &&
- line_len >= expected.length() &&
- memcmp(line, expected.c_str(), expected.length()) == 0) {
- found[i] = true;
+ };
+ trim_space(); // This is really only necessary if there wasn't any content in line before.
+
+ // Scan for newline characters.
+ size_t index = line_len;
+ line_len += copy;
+ while (index < line_len) {
+ if (line[index] == '\n') {
+ // Handle line.
+ if (!ignore_next_line) {
+ line_handle_fn(line, index);
+ }
+ // Move the rest to the front, but trim leading spaces.
+ line_len -= index + 1;
+ memmove(&line[0], &line[index + 1], line_len);
+ trim_space();
+ index = 0;
+ ignore_next_line = false;
+ } else {
+ index++;
}
}
- // Skip to next line.
- size_t next_line = 0;
- for (; next_line + 1 < line_len && line[next_line] != '\n'; ++next_line) {}
- line_len -= next_line + 1;
- memmove(&line[0], &line[next_line + 1], line_len);
- }
- if (mode == kModeSymbolize) {
- EXPECT_EQ(total, 0u);
- } else {
- EXPECT_GT(total, 0u);
- }
- LOG(INFO) << "Processed bytes " << total;
- close(pipe_fd);
- int status = 0;
- if (waitpid(pid, &status, 0) != -1) {
- result = (status == 0);
- }
- for (size_t i = 0; i < expected_prefixes.size(); ++i) {
- if (!found[i]) {
- LOG(ERROR) << "Did not find prefix " << expected_prefixes[i];
- result = false;
+ // Handle a full line without newline characters. Ignore the "next" line, as it is the
+ // tail end of this.
+ if (line_len == kLineMax) {
+ if (!ignore_next_line) {
+ line_handle_fn(line, kLineMax);
+ }
+ line_len = 0;
+ ignore_next_line = true;
}
}
+ };
+
+ auto post_fork_fn = []() {
+ setpgid(0, 0); // Change process groups, so we don't get reaped by ProcessManager.
+ return true; // Ignore setpgid failures.
+ };
+
+ ForkAndExecResult res = ForkAndExec(exec_argv, post_fork_fn, line_buf_fn);
+ if (res.stage != ForkAndExecResult::kFinished) {
+ return ::testing::AssertionFailure() << strerror(errno);
+ }
+ if (!res.StandardSuccess()) {
+ return ::testing::AssertionFailure() << "Did not terminate successfully: " << res.status_code;
}
- return result;
- }
-
- bool ForkAndExec(const std::vector<std::string>& exec_argv,
- /*out*/ pid_t* pid,
- /*out*/ int* pipe_fd,
- /*out*/ std::string* error_msg) {
- int link[2];
- if (pipe(link) == -1) {
- *error_msg = strerror(errno);
- return false;
- }
-
- *pid = fork();
- if (*pid == -1) {
- *error_msg = strerror(errno);
- close(link[0]);
- close(link[1]);
- return false;
- }
-
- if (*pid == 0) {
- dup2(link[1], STDOUT_FILENO);
- close(link[0]);
- close(link[1]);
- // change process groups, so we don't get reaped by ProcessManager
- setpgid(0, 0);
- // Use execv here rather than art::Exec to avoid blocking on waitpid here.
- std::vector<char*> argv;
- for (size_t i = 0; i < exec_argv.size(); ++i) {
- argv.push_back(const_cast<char*>(exec_argv[i].c_str()));
- }
- argv.push_back(nullptr);
- UNUSED(execv(argv[0], &argv[0]));
- const std::string command_line(android::base::Join(exec_argv, ' '));
- PLOG(ERROR) << "Failed to execv(" << command_line << ")";
- // _exit to avoid atexit handlers in child.
- _exit(1);
- UNREACHABLE();
+ if (mode == kModeSymbolize) {
+ EXPECT_EQ(total, 0u);
} else {
- close(link[1]);
- *pipe_fd = link[0];
- return true;
+ EXPECT_GT(total, 0u);
}
- }
- bool ForkAndExecAndWait(const std::vector<std::string>& exec_argv,
- /*out*/ std::string* error_msg) {
- pid_t pid;
- int pipe_fd;
- bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg);
- if (result) {
- close(pipe_fd);
- int status = 0;
- if (waitpid(pid, &status, 0) != -1) {
- result = (status == 0);
+ bool result = true;
+ std::ostringstream oss;
+ for (size_t i = 0; i < expected_prefixes.size(); ++i) {
+ if (!found[i]) {
+ oss << "Did not find prefix " << expected_prefixes[i] << std::endl;
+ result = false;
}
}
- return result;
+ if (!result) {
+ oss << "Processed bytes " << total;
+ }
+
+ return result ? ::testing::AssertionSuccess()
+ : (::testing::AssertionFailure() << oss.str());
}
std::string tmp_dir_;