Fix oatdump for app with --boot-image and add test.
Test: oatdump_app_test
Bug: 67081292
Change-Id: I2e0bccac4ab866f6b54855f9795b16f2ea30c9bb
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 4a35ccf..524dc05 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -184,6 +184,7 @@
ART_GTEST_heap_verification_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps VerifierDepsMulti MultiDex
ART_GTEST_dex_to_dex_decompiler_test_DEX_DEPS := VerifierDeps DexToDexDecompiler
+ART_GTEST_oatdump_app_test_DEX_DEPS := ProfileTestMultiDex
# The elf writer test has dependencies on core.oat.
ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
@@ -303,6 +304,11 @@
oatdumpd-target
ART_GTEST_oatdump_image_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS)
ART_GTEST_oatdump_image_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS)
+ART_GTEST_oatdump_app_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) \
+ dex2oatd-host \
+ dex2oatds-host
+ART_GTEST_oatdump_app_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS) \
+ dex2oatd-target
ART_GTEST_patchoat_test_HOST_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index 012100d..71e276d 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -121,6 +121,7 @@
"art_gtest_defaults",
],
srcs: [
+ "oatdump_app_test.cc",
"oatdump_test.cc",
"oatdump_image_test.cc",
],
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 82bb88a..c8e20e4 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1675,10 +1675,10 @@
if ((method_access_flags & kAccNative) == 0) {
ScopedObjectAccess soa(Thread::Current());
Runtime* const runtime = Runtime::Current();
- Handle<mirror::DexCache> dex_cache(
- hs->NewHandle(runtime->GetClassLinker()->RegisterDexFile(*dex_file, nullptr)));
- CHECK(dex_cache != nullptr);
DCHECK(options_.class_loader_ != nullptr);
+ Handle<mirror::DexCache> dex_cache = hs->NewHandle(
+ runtime->GetClassLinker()->RegisterDexFile(*dex_file, options_.class_loader_->Get()));
+ CHECK(dex_cache != nullptr);
return verifier::MethodVerifier::VerifyMethodAndDump(
soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_,
class_def, code_item, nullptr, method_access_flags);
@@ -2997,7 +2997,7 @@
// Need well-known-classes.
WellKnownClasses::Init(self->GetJniEnv());
- // Need to register dex files to get a working dex cache.
+ // Open dex files.
OatFile* oat_file_ptr = oat_file.get();
ClassLinker* class_linker = runtime->GetClassLinker();
runtime->GetOatFileManager().RegisterOatFile(std::move(oat_file));
@@ -3005,9 +3005,6 @@
std::string error_msg;
const DexFile* const dex_file = OpenDexFile(odf, &error_msg);
CHECK(dex_file != nullptr) << error_msg;
- ObjPtr<mirror::DexCache> dex_cache =
- class_linker->RegisterDexFile(*dex_file, nullptr);
- CHECK(dex_cache != nullptr);
class_path->push_back(dex_file);
}
@@ -3018,6 +3015,13 @@
jobject class_loader = class_linker->CreatePathClassLoader(self, *class_path);
+ // Need to register dex files to get a working dex cache.
+ for (const DexFile* dex_file : *class_path) {
+ ObjPtr<mirror::DexCache> dex_cache = class_linker->RegisterDexFile(
+ *dex_file, self->DecodeJObject(class_loader)->AsClassLoader());
+ CHECK(dex_cache != nullptr);
+ }
+
return class_loader;
}
diff --git a/oatdump/oatdump_app_test.cc b/oatdump/oatdump_app_test.cc
new file mode 100644
index 0000000..f125222
--- /dev/null
+++ b/oatdump/oatdump_app_test.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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 "oatdump_test.h"
+
+namespace art {
+
+TEST_F(OatDumpTest, TestAppWithBootImage) {
+ std::string error_msg;
+ ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {}, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestAppWithBootImageStatic) {
+ TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+ std::string error_msg;
+ ASSERT_TRUE(GenerateAppOdexFile(kStatic, {}, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg;
+}
+
+TEST_F(OatDumpTest, TestPicAppWithBootImage) {
+ std::string error_msg;
+ ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--compile-pic"}, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestPicAppWithBootImageStatic) {
+ TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+ std::string error_msg;
+ ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--compile-pic"}, &error_msg)) << error_msg;
+ ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg;
+}
+
+} // namespace art
diff --git a/oatdump/oatdump_image_test.cc b/oatdump/oatdump_image_test.cc
index e9cc922..d054ece 100644
--- a/oatdump/oatdump_image_test.cc
+++ b/oatdump/oatdump_image_test.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 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.
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index d0f05d9..d4bed6b 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -66,14 +66,15 @@
// Linking flavor.
enum Flavor {
- kDynamic, // oatdump(d)
- kStatic, // oatdump(d)s
+ kDynamic, // oatdump(d), dex2oat(d)
+ kStatic, // oatdump(d)s, dex2oat(d)s
};
- // Returns path to the oatdump binary.
- std::string GetOatDumpFilePath(Flavor flavor) {
+ // Returns path to the oatdump/dex2oat binary.
+ std::string GetExecutableFilePath(Flavor flavor, const char* name) {
std::string root = GetTestAndroidRoot();
- root += "/bin/oatdump";
+ root += "/bin/";
+ root += name;
if (kIsDebugBuild) {
root += "d";
}
@@ -85,6 +86,7 @@
enum Mode {
kModeOat,
+ kModeOatWithBootImage,
kModeArt,
kModeSymbolize,
};
@@ -95,13 +97,56 @@
kListAndCode
};
+ std::string GetAppBaseName() {
+ // Use ProfileTestMultiDex as it contains references to boot image strings
+ // that shall use different code for PIC and non-PIC.
+ return "ProfileTestMultiDex";
+ }
+
+ std::string GetAppOdexName() {
+ return tmp_dir_ + "/" + GetAppBaseName() + ".odex";
+ }
+
+ bool GenerateAppOdexFile(Flavor flavor,
+ const std::vector<std::string>& args,
+ /*out*/ std::string* error_msg) {
+ std::string dex2oat_path = GetExecutableFilePath(flavor, "dex2oat");
+ std::vector<std::string> exec_argv = {
+ dex2oat_path,
+ "--runtime-arg",
+ "-Xms64m",
+ "--runtime-arg",
+ "-Xmx512m",
+ "--runtime-arg",
+ "-Xnorelocate",
+ "--boot-image=" + GetCoreArtLocation(),
+ "--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)),
+ "--dex-file=" + GetTestDexFileName(GetAppBaseName().c_str()),
+ "--oat-file=" + GetAppOdexName(),
+ "--compiler-filter=speed"
+ };
+ 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) {
+ close(pipe_fd);
+ int status = 0;
+ if (waitpid(pid, &status, 0) != -1) {
+ result = (status == 0);
+ }
+ }
+ return result;
+ }
+
// Run the test with custom arguments.
bool Exec(Flavor flavor,
Mode mode,
const std::vector<std::string>& args,
Display display,
- std::string* error_msg) {
- std::string file_path = GetOatDumpFilePath(flavor);
+ /*out*/ std::string* error_msg) {
+ std::string file_path = GetExecutableFilePath(flavor, "oatdump");
EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
@@ -133,6 +178,11 @@
expected_prefixes.push_back("IMAGE LOCATION:");
expected_prefixes.push_back("IMAGE BEGIN:");
expected_prefixes.push_back("kDexCaches:");
+ } else if (mode == kModeOatWithBootImage) {
+ exec_argv.push_back("--boot-image=" + GetCoreArtLocation());
+ exec_argv.push_back("--instruction-set=" + std::string(
+ GetInstructionSetString(kRuntimeISA)));
+ exec_argv.push_back("--oat-file=" + GetAppOdexName());
} else {
CHECK_EQ(static_cast<size_t>(mode), static_cast<size_t>(kModeOat));
exec_argv.push_back("--oat-file=" + core_oat_location_);
@@ -140,39 +190,10 @@
}
exec_argv.insert(exec_argv.end(), args.begin(), args.end());
- bool result = true;
- // We must set --android-root.
- int link[2];
- if (pipe(link) == -1) {
- *error_msg = strerror(errno);
- return false;
- }
-
- const pid_t pid = fork();
- if (pid == -1) {
- *error_msg = strerror(errno);
- 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);
- } else {
- close(link[1]);
+ 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;
@@ -188,7 +209,7 @@
memmove(&line[0], &line[spaces], line_len);
}
ssize_t bytes_read =
- TEMP_FAILURE_RETRY(read(link[0], &line[line_len], kLineMax - line_len));
+ TEMP_FAILURE_RETRY(read(pipe_fd, &line[line_len], kLineMax - line_len));
if (bytes_read <= 0) {
break;
}
@@ -219,7 +240,7 @@
EXPECT_GT(total, 0u);
}
LOG(INFO) << "Processed bytes " << total;
- close(link[0]);
+ close(pipe_fd);
int status = 0;
if (waitpid(pid, &status, 0) != -1) {
result = (status == 0);
@@ -236,6 +257,49 @@
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();
+ } else {
+ close(link[1]);
+ *pipe_fd = link[0];
+ return true;
+ }
+ }
+
std::string tmp_dir_;
private: