diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-01-10 18:43:21 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-01-10 18:43:21 +0000 |
commit | a358a1939c1300ea331e9ce9b87a7a869e8750e3 (patch) | |
tree | ef09510a48bde4a424c2427fc7eac3fb8e179336 | |
parent | b853df34583e3ad1deac3d56678714523605a8b0 (diff) | |
parent | 147f8bb89d7e785716275ef2c770f600376c81fa (diff) | |
download | art-aml_tz5_341510010.tar.gz |
Snap for 11296156 from 147f8bb89d7e785716275ef2c770f600376c81fa to mainline-tzdata5-releaseaml_tz5_341510070aml_tz5_341510050aml_tz5_341510010aml_tz5_341510010
Change-Id: I0631baca79cadbd2390cf64ce1e0331450d23a48
37 files changed, 847 insertions, 366 deletions
diff --git a/artd/Android.bp b/artd/Android.bp index 1c964f489c..4862914839 100644 --- a/artd/Android.bp +++ b/artd/Android.bp @@ -35,6 +35,7 @@ cc_defaults { "profman_headers", ], shared_libs: [ + "libartservice", "libarttools", // Contains "libc++fs". "libbase", "libbinder_ndk", diff --git a/artd/artd.cc b/artd/artd.cc index 04a01a9ca4..8df6a70bc4 100644 --- a/artd/artd.cc +++ b/artd/artd.cc @@ -72,6 +72,7 @@ #include "path_utils.h" #include "profman/profman_result.h" #include "selinux/android.h" +#include "service.h" #include "tools/cmdline_builder.h" #include "tools/tools.h" @@ -107,6 +108,7 @@ using ::android::base::Split; using ::android::base::StringReplace; using ::android::base::WriteStringToFd; using ::android::fs_mgr::FstabEntry; +using ::art::service::ValidateDexPath; using ::art::tools::CmdlineBuilder; using ::ndk::ScopedAStatus; @@ -1152,44 +1154,6 @@ ScopedAStatus Artd::isInDalvikCache(const std::string& in_dexFile, bool* _aidl_r return NonFatal(ART_FORMAT("Fstab entries not found for '{}'", in_dexFile)); } -ScopedAStatus Artd::validateDexPath(const std::string& in_dexPath, - std::optional<std::string>* _aidl_return) { - if (Result<void> result = ValidateDexPath(in_dexPath); !result.ok()) { - *_aidl_return = result.error().message(); - } else { - *_aidl_return = std::nullopt; - } - return ScopedAStatus::ok(); -} - -ScopedAStatus Artd::validateClassLoaderContext(const std::string& in_dexPath, - const std::string& in_classLoaderContext, - std::optional<std::string>* _aidl_return) { - if (in_classLoaderContext == ClassLoaderContext::kUnsupportedClassLoaderContextEncoding) { - *_aidl_return = std::nullopt; - return ScopedAStatus::ok(); - } - - std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(in_classLoaderContext); - if (context == nullptr) { - *_aidl_return = ART_FORMAT("Class loader context '{}' is invalid", in_classLoaderContext); - return ScopedAStatus::ok(); - } - - std::vector<std::string> flattened_context = context->FlattenDexPaths(); - std::string dex_dir = Dirname(in_dexPath); - for (const std::string& context_element : flattened_context) { - std::string context_path = std::filesystem::path(dex_dir).append(context_element); - if (Result<void> result = ValidateDexPath(context_path); !result.ok()) { - *_aidl_return = result.error().message(); - return ScopedAStatus::ok(); - } - } - - *_aidl_return = std::nullopt; - return ScopedAStatus::ok(); -} - Result<void> Artd::Start() { OR_RETURN(SetLogVerbosity()); diff --git a/artd/artd.h b/artd/artd.h index 774f11a1d1..da85a035d9 100644 --- a/artd/artd.h +++ b/artd/artd.h @@ -166,13 +166,6 @@ class Artd : public aidl::com::android::server::art::BnArtd { ndk::ScopedAStatus isInDalvikCache(const std::string& in_dexFile, bool* _aidl_return) override; - ndk::ScopedAStatus validateDexPath(const std::string& in_dexPath, - std::optional<std::string>* _aidl_return) override; - - ndk::ScopedAStatus validateClassLoaderContext(const std::string& in_dexPath, - const std::string& in_classLoaderContext, - std::optional<std::string>* _aidl_return) override; - android::base::Result<void> Start(); private: diff --git a/artd/binder/com/android/server/art/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl index 3b55297ab2..5465795010 100644 --- a/artd/binder/com/android/server/art/IArtd.aidl +++ b/artd/binder/com/android/server/art/IArtd.aidl @@ -178,17 +178,4 @@ interface IArtd { * Throws fatal and non-fatal errors. */ boolean isInDalvikCache(@utf8InCpp String dexFile); - - /** - * Returns an error message if the given dex path is invalid, or null if the validation - * passes. - */ - @nullable @utf8InCpp String validateDexPath(@utf8InCpp String dexPath); - - /** - * Returns an error message if the given class loader context is invalid, or null if the - * validation passes. - */ - @nullable @utf8InCpp String validateClassLoaderContext(@utf8InCpp String dexPath, - @utf8InCpp String classLoaderContext); } diff --git a/artd/path_utils.cc b/artd/path_utils.cc index 6ff9b95bc1..95e4395a1c 100644 --- a/artd/path_utils.cc +++ b/artd/path_utils.cc @@ -30,6 +30,7 @@ #include "file_utils.h" #include "fstab/fstab.h" #include "oat_file_assistant.h" +#include "service.h" #include "tools/tools.h" namespace art { @@ -47,6 +48,9 @@ using ::android::base::StartsWith; using ::android::fs_mgr::Fstab; using ::android::fs_mgr::FstabEntry; using ::android::fs_mgr::ReadFstabFromProcMounts; +using ::art::service::ValidateDexPath; +using ::art::service::ValidatePathElement; +using ::art::service::ValidatePathElementSubstring; using PrebuiltProfilePath = ProfilePath::PrebuiltProfilePath; using PrimaryCurProfilePath = ProfilePath::PrimaryCurProfilePath; @@ -56,45 +60,6 @@ using SecondaryRefProfilePath = ProfilePath::SecondaryRefProfilePath; using TmpProfilePath = ProfilePath::TmpProfilePath; using WritableProfilePath = ProfilePath::WritableProfilePath; -Result<void> ValidateAbsoluteNormalPath(const std::string& path_str) { - if (path_str.empty()) { - return Errorf("Path is empty"); - } - if (path_str.find('\0') != std::string::npos) { - return Errorf("Path '{}' has invalid character '\\0'", path_str); - } - std::filesystem::path path(path_str); - if (!path.is_absolute()) { - return Errorf("Path '{}' is not an absolute path", path_str); - } - if (path.lexically_normal() != path_str) { - return Errorf("Path '{}' is not in normal form", path_str); - } - return {}; -} - -Result<void> ValidatePathElementSubstring(const std::string& path_element_substring, - const std::string& name) { - if (path_element_substring.empty()) { - return Errorf("{} is empty", name); - } - if (path_element_substring.find('/') != std::string::npos) { - return Errorf("{} '{}' has invalid character '/'", name, path_element_substring); - } - if (path_element_substring.find('\0') != std::string::npos) { - return Errorf("{} '{}' has invalid character '\\0'", name, path_element_substring); - } - return {}; -} - -Result<void> ValidatePathElement(const std::string& path_element, const std::string& name) { - OR_RETURN(ValidatePathElementSubstring(path_element, name)); - if (path_element == "." || path_element == "..") { - return Errorf("Invalid {} '{}'", name, path_element); - } - return {}; -} - Result<std::string> GetAndroidDataOrError() { std::string error_msg; std::string result = GetAndroidDataSafe(&error_msg); @@ -155,11 +120,6 @@ Result<std::vector<std::string>> ListManagedFiles() { return tools::Glob(patterns); } -Result<void> ValidateDexPath(const std::string& dex_path) { - OR_RETURN(ValidateAbsoluteNormalPath(dex_path)); - return {}; -} - Result<std::string> BuildArtBinPath(const std::string& binary_name) { return ART_FORMAT("{}/bin/{}", OR_RETURN(GetArtRootOrError()), binary_name); } diff --git a/artd/path_utils.h b/artd/path_utils.h index a126118137..62d6005bf6 100644 --- a/artd/path_utils.h +++ b/artd/path_utils.h @@ -31,8 +31,6 @@ namespace artd { // Returns all existing files that are managed by artd. android::base::Result<std::vector<std::string>> ListManagedFiles(); -android::base::Result<void> ValidateDexPath(const std::string& dex_path); - android::base::Result<std::string> BuildArtBinPath(const std::string& binary_name); // Returns the absolute path to the OAT file built from the `ArtifactsPath`. diff --git a/artd/path_utils_test.cc b/artd/path_utils_test.cc index c9fb809d03..626690787a 100644 --- a/artd/path_utils_test.cc +++ b/artd/path_utils_test.cc @@ -63,29 +63,12 @@ TEST_F(PathUtilsTest, BuildOatPathDalvikCache) { HasValue(android_data_ + "/dalvik-cache/arm64/a@b.apk@classes.dex")); } -TEST_F(PathUtilsTest, BuildOatPathEmptyDexPath) { - EXPECT_THAT(BuildOatPath(ArtifactsPath{.dexPath = "", .isa = "arm64", .isInDalvikCache = false}), - HasError(WithMessage("Path is empty"))); -} - -TEST_F(PathUtilsTest, BuildOatPathRelativeDexPath) { +TEST_F(PathUtilsTest, BuildOatPathInvalidDexPath) { EXPECT_THAT( BuildOatPath(ArtifactsPath{.dexPath = "a/b.apk", .isa = "arm64", .isInDalvikCache = false}), HasError(WithMessage("Path 'a/b.apk' is not an absolute path"))); } -TEST_F(PathUtilsTest, BuildOatPathNonNormalDexPath) { - EXPECT_THAT(BuildOatPath(ArtifactsPath{ - .dexPath = "/a/c/../b.apk", .isa = "arm64", .isInDalvikCache = false}), - HasError(WithMessage("Path '/a/c/../b.apk' is not in normal form"))); -} - -TEST_F(PathUtilsTest, BuildOatPathNul) { - EXPECT_THAT(BuildOatPath(ArtifactsPath{ - .dexPath = "/a/\0/b.apk"s, .isa = "arm64", .isInDalvikCache = false}), - HasError(WithMessage("Path '/a/\0/b.apk' has invalid character '\\0'"s))); -} - TEST_F(PathUtilsTest, BuildOatPathInvalidIsa) { EXPECT_THAT(BuildOatPath( ArtifactsPath{.dexPath = "/a/b.apk", .isa = "invalid", .isInDalvikCache = false}), @@ -110,51 +93,27 @@ TEST_F(PathUtilsTest, BuildPrimaryRefProfilePathPackageNameOk) { EXPECT_THAT(BuildPrimaryRefProfilePath( PrimaryRefProfilePath{.packageName = "...", .profileName = "primary"}), HasValue(android_data_ + "/misc/profiles/ref/.../primary.prof")); - EXPECT_THAT(BuildPrimaryRefProfilePath( - PrimaryRefProfilePath{.packageName = "!@#$%^&*()_+-=", .profileName = "primary"}), - HasValue(android_data_ + "/misc/profiles/ref/!@#$%^&*()_+-=/primary.prof")); } TEST_F(PathUtilsTest, BuildPrimaryRefProfilePathPackageNameWrong) { EXPECT_THAT(BuildPrimaryRefProfilePath( - PrimaryRefProfilePath{.packageName = "", .profileName = "primary"}), - HasError(WithMessage("packageName is empty"))); - EXPECT_THAT(BuildPrimaryRefProfilePath( - PrimaryRefProfilePath{.packageName = ".", .profileName = "primary"}), - HasError(WithMessage("Invalid packageName '.'"))); - EXPECT_THAT(BuildPrimaryRefProfilePath( PrimaryRefProfilePath{.packageName = "..", .profileName = "primary"}), HasError(WithMessage("Invalid packageName '..'"))); EXPECT_THAT(BuildPrimaryRefProfilePath( PrimaryRefProfilePath{.packageName = "a/b", .profileName = "primary"}), HasError(WithMessage("packageName 'a/b' has invalid character '/'"))); - EXPECT_THAT(BuildPrimaryRefProfilePath( - PrimaryRefProfilePath{.packageName = "a\0b"s, .profileName = "primary"}), - HasError(WithMessage("packageName 'a\0b' has invalid character '\\0'"s))); } TEST_F(PathUtilsTest, BuildPrimaryRefProfilePathProfileNameOk) { EXPECT_THAT(BuildPrimaryRefProfilePath( - PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "."}), - HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/..prof")); - EXPECT_THAT(BuildPrimaryRefProfilePath( PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = ".."}), HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/...prof")); - EXPECT_THAT(BuildPrimaryRefProfilePath(PrimaryRefProfilePath{.packageName = "com.android.foo", - .profileName = "!@#$%^&*()_+-="}), - HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/!@#$%^&*()_+-=.prof")); } TEST_F(PathUtilsTest, BuildPrimaryRefProfilePathProfileNameWrong) { EXPECT_THAT(BuildPrimaryRefProfilePath( - PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = ""}), - HasError(WithMessage("profileName is empty"))); - EXPECT_THAT(BuildPrimaryRefProfilePath( PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "a/b"}), HasError(WithMessage("profileName 'a/b' has invalid character '/'"))); - EXPECT_THAT(BuildPrimaryRefProfilePath( - PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "a\0b"s}), - HasError(WithMessage("profileName 'a\0b' has invalid character '\\0'"s))); } TEST_F(PathUtilsTest, BuildFinalProfilePathForPrimary) { @@ -194,18 +153,8 @@ TEST_F(PathUtilsTest, BuildTmpProfilePathIdWrong) { EXPECT_THAT(BuildTmpProfilePath(TmpProfilePath{ .finalPath = PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "primary"}, - .id = ""}), - HasError(WithMessage("id is empty"))); - EXPECT_THAT(BuildTmpProfilePath(TmpProfilePath{ - .finalPath = PrimaryRefProfilePath{.packageName = "com.android.foo", - .profileName = "primary"}, .id = "123/45"}), HasError(WithMessage("id '123/45' has invalid character '/'"))); - EXPECT_THAT(BuildTmpProfilePath(TmpProfilePath{ - .finalPath = PrimaryRefProfilePath{.packageName = "com.android.foo", - .profileName = "primary"}, - .id = "123\0a"s}), - HasError(WithMessage("id '123\0a' has invalid character '\\0'"s))); } TEST_F(PathUtilsTest, BuildPrebuiltProfilePath) { diff --git a/build/sdk/Android.bp b/build/sdk/Android.bp index b47bdc2fee..8723d20ff3 100644 --- a/build/sdk/Android.bp +++ b/build/sdk/Android.bp @@ -160,11 +160,6 @@ art_module_exports { prebuilt_visibility: prebuilt_visibility, - java_libs: [ - // Needed for grpc-grpc-java - "okhttp-norepackage", - ], - target: { host: { // Set in target.host because the top level compile_multilib diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 7341774889..e37d18d148 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -620,7 +620,7 @@ art_cc_test { // Counterpart to art_standalone_dex2oat_tests for tests that go into CTS. art_cc_test { name: "art_standalone_dex2oat_cts_tests", - defaults: ["art_standalone_gtest_defaults"], + defaults: ["art_cts_gtest_defaults"], srcs: ["dex2oat_cts_test.cc"], data: [ ":art-gtest-jars-Main", @@ -638,5 +638,8 @@ art_cc_test { "libziparchive", ], test_config: "art_standalone_dex2oat_cts_tests.xml", - test_suites: ["cts"], + test_suites: [ + "cts", + "mcts-art", + ], } diff --git a/dex2oat/dex2oat_cts_test.cc b/dex2oat/dex2oat_cts_test.cc index 254126446d..25cd94bb93 100644 --- a/dex2oat/dex2oat_cts_test.cc +++ b/dex2oat/dex2oat_cts_test.cc @@ -14,24 +14,252 @@ * limitations under the License. */ +#include <dirent.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <fstream> +#include <functional> +#include <memory> +#include <string> +#include <vector> + +#include "android-base/file.h" +#include "android-base/unique_fd.h" #include "base/file_utils.h" -#include "dex2oat_environment_test.h" +#include "base/os.h" +#include "base/stl_util.h" +#include "base/unix_file/fd_file.h" +#include "gtest/gtest.h" namespace art { +namespace { + +void ClearDirectory(const char* dirpath, bool recursive = true) { + ASSERT_TRUE(dirpath != nullptr); + DIR* dir = opendir(dirpath); + ASSERT_TRUE(dir != nullptr); + dirent* e; + struct stat s; + while ((e = readdir(dir)) != nullptr) { + if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) { + continue; + } + std::string filename(dirpath); + filename.push_back('/'); + filename.append(e->d_name); + int stat_result = lstat(filename.c_str(), &s); + ASSERT_EQ(0, stat_result) << "unable to stat " << filename; + if (S_ISDIR(s.st_mode)) { + if (recursive) { + ClearDirectory(filename.c_str()); + int rmdir_result = rmdir(filename.c_str()); + ASSERT_EQ(0, rmdir_result) << filename; + } + } else { + int unlink_result = unlink(filename.c_str()); + ASSERT_EQ(0, unlink_result) << filename; + } + } + closedir(dir); +} + +class Dex2oatScratchDirs { + public: + void SetUp(const std::string& android_data) { + // Create a scratch directory to work from. + + // Get the realpath of the android data. The oat dir should always point to real location + // when generating oat files in dalvik-cache. This avoids complicating the unit tests + // when matching the expected paths. + UniqueCPtr<const char[]> android_data_real(realpath(android_data.c_str(), nullptr)); + ASSERT_TRUE(android_data_real != nullptr) + << "Could not get the realpath of the android data" << android_data << strerror(errno); + + scratch_dir_.assign(android_data_real.get()); + scratch_dir_ += "/Dex2oatEnvironmentTest"; + ASSERT_EQ(0, mkdir(scratch_dir_.c_str(), 0700)); + + // Create a subdirectory in scratch for odex files. + odex_oat_dir_ = scratch_dir_ + "/oat"; + ASSERT_EQ(0, mkdir(odex_oat_dir_.c_str(), 0700)); + + odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA)); + ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700)); + } + + void TearDown() { + ClearDirectory(odex_dir_.c_str()); + ASSERT_EQ(0, rmdir(odex_dir_.c_str())); -class Dex2oatCtsTest : public CommonArtTest, public Dex2oatScratchDirs { + ClearDirectory(odex_oat_dir_.c_str()); + ASSERT_EQ(0, rmdir(odex_oat_dir_.c_str())); + + ClearDirectory(scratch_dir_.c_str()); + ASSERT_EQ(0, rmdir(scratch_dir_.c_str())); + } + + // Scratch directory, for dex and odex files (oat files will go in the + // dalvik cache). + const std::string& GetScratchDir() const { return scratch_dir_; } + + // Odex directory is the subdirectory in the scratch directory where odex + // files should be located. + const std::string& GetOdexDir() const { return odex_dir_; } + + private: + std::string scratch_dir_; + std::string odex_oat_dir_; + std::string odex_dir_; +}; + +class Dex2oatCtsTest : public Dex2oatScratchDirs, public testing::Test { public: + void SetUpAndroidDataDir(std::string& android_data) { + android_data = "/data/local/tmp"; + android_data += "/art-data-XXXXXX"; + if (mkdtemp(&android_data[0]) == nullptr) { + PLOG(FATAL) << "mkdtemp(\"" << &android_data[0] << "\") failed"; + } + setenv("ANDROID_DATA", android_data.c_str(), 1); + } + + void TearDownAndroidDataDir(const std::string& android_data, + bool fail_on_error) { + if (fail_on_error) { + ASSERT_EQ(rmdir(android_data.c_str()), 0); + } else { + rmdir(android_data.c_str()); + } + } + + std::string GetTestDexFileName(const char* name) const { + CHECK(name != nullptr); + // The needed jar files for gtest are located next to the gtest binary itself. + std::string executable_dir = android::base::GetExecutableDirectory(); + for (auto ext : {".jar", ".dex"}) { + std::string path = executable_dir + "/art-gtest-jars-" + name + ext; + if (OS::FileExists(path.c_str())) { + return path; + } + } + LOG(FATAL) << "Test file " << name << " not found"; + UNREACHABLE(); + } + void SetUp() override { - CommonArtTest::SetUp(); + SetUpAndroidDataDir(android_data_); Dex2oatScratchDirs::SetUp(android_data_); } void TearDown() override { Dex2oatScratchDirs::TearDown(); - CommonArtTest::TearDown(); + TearDownAndroidDataDir(android_data_, true); + } + + struct ForkAndExecResult { + enum Stage { + kLink, + kFork, + kWaitpid, + kFinished, + }; + Stage stage; + int status_code; + + bool StandardSuccess() { + return stage == kFinished && WIFEXITED(status_code) && WEXITSTATUS(status_code) == 0; + } + }; + using OutputHandlerFn = std::function<void(char*, size_t)>; + using PostForkFn = std::function<bool()>; + + ForkAndExecResult ForkAndExec( + const std::vector<std::string>& argv, + const PostForkFn& post_fork, + const OutputHandlerFn& handler) { + ForkAndExecResult result; + result.status_code = 0; + result.stage = ForkAndExecResult::kLink; + + std::vector<const char*> c_args; + c_args.reserve(argv.size() + 1); + for (const std::string& str : argv) { + c_args.push_back(str.c_str()); + } + c_args.push_back(nullptr); + + android::base::unique_fd link[2]; + { + int link_fd[2]; + + if (pipe(link_fd) == -1) { + return result; + } + link[0].reset(link_fd[0]); + link[1].reset(link_fd[1]); + } + + result.stage = ForkAndExecResult::kFork; + + pid_t pid = fork(); + if (pid == -1) { + return result; + } + + if (pid == 0) { + if (!post_fork()) { + LOG(ERROR) << "Failed post-fork function"; + exit(1); + UNREACHABLE(); + } + + // Redirect stdout and stderr. + dup2(link[1].get(), STDOUT_FILENO); + dup2(link[1].get(), STDERR_FILENO); + + link[0].reset(); + link[1].reset(); + + execv(c_args[0], const_cast<char* const*>(c_args.data())); + exit(1); + UNREACHABLE(); + } + + result.stage = ForkAndExecResult::kWaitpid; + link[1].reset(); + + char buffer[128] = { 0 }; + ssize_t bytes_read = 0; + while (TEMP_FAILURE_RETRY(bytes_read = read(link[0].get(), buffer, 128)) > 0) { + handler(buffer, bytes_read); + } + handler(buffer, 0u); // End with a virtual write of zero length to simplify clients. + + link[0].reset(); + + if (waitpid(pid, &result.status_code, 0) == -1) { + return result; + } + + result.stage = ForkAndExecResult::kFinished; + return result; + } + + ForkAndExecResult ForkAndExec( + const std::vector<std::string>& argv, const PostForkFn& post_fork, std::string* output) { + auto string_collect_fn = [output](char* buf, size_t len) { + *output += std::string(buf, len); + }; + return ForkAndExec(argv, post_fork, string_collect_fn); } protected: + std::string android_data_; + // Stripped down counterpart to Dex2oatEnvironmentTest::Dex2Oat that only adds // enough arguments for our purposes. int Dex2Oat(const std::vector<std::string>& dex2oat_args, @@ -63,6 +291,8 @@ class Dex2oatCtsTest : public CommonArtTest, public Dex2oatScratchDirs { } }; +} // namespace + // Run dex2oat with --enable-palette-compilation-hooks to force calls to // PaletteNotify{Start,End}Dex2oatCompilation. TEST_F(Dex2oatCtsTest, CompilationHooks) { diff --git a/libartbase/base/globals.h b/libartbase/base/globals.h index 4103154db1..14e056d8ad 100644 --- a/libartbase/base/globals.h +++ b/libartbase/base/globals.h @@ -118,12 +118,6 @@ static constexpr bool kHostStaticBuildEnabled = true; static constexpr bool kHostStaticBuildEnabled = false; #endif -// System property for phenotype flag to test disabling compact dex and in -// particular dexlayout. -// TODO(b/256664509): Clean this up. -static constexpr char kPhDisableCompactDex[] = - "persist.device_config.runtime_native_boot.disable_compact_dex"; - } // namespace art #endif // ART_LIBARTBASE_BASE_GLOBALS_H_ diff --git a/libartpalette/Android.bp b/libartpalette/Android.bp index 6e7415e7b1..60607097d8 100644 --- a/libartpalette/Android.bp +++ b/libartpalette/Android.bp @@ -114,10 +114,6 @@ art_cc_library { art_cc_defaults { name: "art_libartpalette_tests_defaults", srcs: ["apex/palette_test.cc"], - shared_libs: [ - "libartpalette", - "libnativehelper", - ], target: { android: { static_libs: ["libmodules-utils-build"], @@ -133,6 +129,10 @@ art_cc_test { "art_gtest_defaults", "art_libartpalette_tests_defaults", ], + shared_libs: [ + "libartpalette", + "libnativehelper", + ], host_supported: true, } @@ -141,9 +141,19 @@ art_cc_test { art_cc_test { name: "art_standalone_libartpalette_tests", defaults: [ - "art_standalone_gtest_defaults", + "art_cts_gtest_defaults", "art_libartpalette_tests_defaults", ], + static_libs: [ + "libartpalette", + "libc++fs", + // libnativehelper_lazy has the glue we need to dlsym the platform-only + // APIs we need, like JniInvocationInit. + "libnativehelper_lazy", + ], test_config_template: ":art-gtests-target-standalone-cts-template", - test_suites: ["cts"], + test_suites: [ + "cts", + "mcts-art", + ], } diff --git a/libartpalette/apex/palette_test.cc b/libartpalette/apex/palette_test.cc index 63072c491b..f2b4858e66 100644 --- a/libartpalette/apex/palette_test.cc +++ b/libartpalette/apex/palette_test.cc @@ -28,8 +28,8 @@ #include "android/api-level.h" #endif -#include "base/common_art_test.h" #include "gtest/gtest.h" +#include "nativehelper/JniInvocation.h" \ namespace { @@ -88,27 +88,28 @@ TEST_F(PaletteClientTest, Ashmem) { #endif } -class PaletteClientJniTest : public art::CommonArtTest {}; - -TEST_F(PaletteClientJniTest, JniInvocation) { +TEST_F(PaletteClientTest, JniInvocation) { +#ifndef ART_TARGET_ANDROID + // On host we need to set up a boot classpath and pass it in here. Let's not + // bother since this test is only for native API coverage on target. + GTEST_SKIP() << "Will only spin up a VM on Android"; +#else bool enabled; EXPECT_EQ(PALETTE_STATUS_OK, PaletteShouldReportJniInvocations(&enabled)); - std::string boot_class_path_string = - GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames()); - std::string boot_class_path_locations_string = - GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations()); + // This calls JniInvocationInit, which is necessary to load the VM. It's not + // public but still stable. + JniInvocation jni_invocation; + ASSERT_TRUE(jni_invocation.Init(nullptr)); + JavaVMInitArgs vm_args; JavaVMOption options[] = { - {.optionString = boot_class_path_string.c_str(), .extraInfo = nullptr}, - {.optionString = boot_class_path_locations_string.c_str(), .extraInfo = nullptr}, - }; - JavaVMInitArgs vm_args = { - .version = JNI_VERSION_1_6, - .nOptions = std::size(options), - .options = options, - .ignoreUnrecognized = JNI_TRUE, + {.optionString = "-verbose:jni", .extraInfo = nullptr}, }; + vm_args.version = JNI_VERSION_1_6; + vm_args.nOptions = std::size(options); + vm_args.options = options; + vm_args.ignoreUnrecognized = JNI_TRUE; JavaVM* jvm = nullptr; JNIEnv* env = nullptr; @@ -119,6 +120,7 @@ TEST_F(PaletteClientJniTest, JniInvocation) { PaletteNotifyEndJniInvocation(env); EXPECT_EQ(JNI_OK, jvm->DestroyJavaVM()); +#endif } TEST_F(PaletteClientTest, SetTaskProfiles) { diff --git a/libartservice/service/Android.bp b/libartservice/service/Android.bp index 7faa20d2fa..e013d688d7 100644 --- a/libartservice/service/Android.bp +++ b/libartservice/service/Android.bp @@ -30,8 +30,11 @@ cc_defaults { srcs: [ "native/service.cc", ], + export_include_dirs: ["native"], shared_libs: [ + "libarttools", // Contains "libc++fs". "libbase", + "libnativehelper", ], } @@ -43,6 +46,8 @@ cc_library { "com.android.art.debug", ], shared_libs: [ + "libart", + "libartbase", ], } @@ -57,6 +62,8 @@ cc_library { "com.android.art.debug", ], shared_libs: [ + "libartd", + "libartbased", ], } @@ -178,6 +185,9 @@ art_cc_defaults { srcs: [ "native/service_test.cc", ], + static_libs: [ + "libgmock", + ], } // Version of ART gtest `art_libartservice_tests` bundled with the ART APEX on target. @@ -225,6 +235,7 @@ android_test { ], jni_libs: [ + "libartservice", // The two libraries below are required by ExtendedMockito. "libdexmakerjvmtiagent", "libstaticjvmtiagent", diff --git a/libartservice/service/java/com/android/server/art/ArtJni.java b/libartservice/service/java/com/android/server/art/ArtJni.java new file mode 100644 index 0000000000..2cc9ea8911 --- /dev/null +++ b/libartservice/service/java/com/android/server/art/ArtJni.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2023 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. + */ + +package com.android.server.art; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import dalvik.system.VMRuntime; + +/** + * JNI methods for ART Service, with wrappers added for testability because Mockito cannot mock JNI + * methods. + * + * @hide + */ +public class ArtJni { + static { + if (VMRuntime.getRuntime().vmLibrary().equals("libartd.so")) { + System.loadLibrary("artserviced"); + } else { + System.loadLibrary("artservice"); + } + } + + private ArtJni() {} + + /** + * Returns an error message if the given dex path is invalid, or null if the validation passes. + */ + @Nullable + public static String validateDexPath(@NonNull String dexPath) { + return validateDexPathNative(dexPath); + } + + /** + * Returns an error message if the given class loader context is invalid, or null if the + * validation passes. + */ + @Nullable + public static String validateClassLoaderContext( + @NonNull String dexPath, @NonNull String classLoaderContext) { + return validateClassLoaderContextNative(dexPath, classLoaderContext); + } + + @Nullable private static native String validateDexPathNative(@NonNull String dexPath); + @Nullable + private static native String validateClassLoaderContextNative( + @NonNull String dexPath, @NonNull String classLoaderContext); +} diff --git a/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java index 97b3c8a186..c73f71b9e3 100644 --- a/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java +++ b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java @@ -566,7 +566,8 @@ public class DexUseManagerLocal { } mDexUse = new DexUse(); if (proto != null) { - mDexUse.fromProto(proto, this::validateDexPath, this::validateClassLoaderContext); + mDexUse.fromProto( + proto, ArtJni::validateDexPath, ArtJni::validateClassLoaderContext); } } } @@ -600,12 +601,12 @@ public class DexUseManagerLocal { for (var entry : classLoaderContextByDexContainerFile.entrySet()) { Utils.assertNonEmpty(entry.getKey()); - String errorMsg = validateDexPath(entry.getKey()); + String errorMsg = ArtJni.validateDexPath(entry.getKey()); if (errorMsg != null) { throw new IllegalArgumentException(errorMsg); } Utils.assertNonEmpty(entry.getValue()); - errorMsg = validateClassLoaderContext(entry.getKey(), entry.getValue()); + errorMsg = ArtJni.validateClassLoaderContext(entry.getKey(), entry.getValue()); if (errorMsg != null) { throw new IllegalArgumentException(errorMsg); } @@ -623,29 +624,6 @@ public class DexUseManagerLocal { } } - @Nullable - private String validateDexPath(@NonNull String dexPath) { - try { - return mInjector.getArtd().validateDexPath(dexPath); - } catch (RemoteException e) { - String errorMsg = "Failed to validate dex path " + dexPath; - Log.e(TAG, errorMsg, e); - return errorMsg; - } - } - - @Nullable - private String validateClassLoaderContext( - @NonNull String dexPath, @NonNull String classLoaderContext) { - try { - return mInjector.getArtd().validateClassLoaderContext(dexPath, classLoaderContext); - } catch (RemoteException e) { - String errorMsg = "Failed to validate class loader context " + classLoaderContext; - Log.e(TAG, errorMsg, e); - return errorMsg; - } - } - /** @hide */ @Nullable public String getSecondaryClassLoaderContext( diff --git a/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java b/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java index f34cd434ed..83cb81c1df 100644 --- a/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java +++ b/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java @@ -81,8 +81,8 @@ public class DexUseManagerTest { private static final String SPLIT_APK = "/data/app/" + OWNING_PKG_NAME + "/split_0.apk"; @Rule - public StaticMockitoRule mockitoRule = - new StaticMockitoRule(SystemProperties.class, Constants.class, Process.class); + public StaticMockitoRule mockitoRule = new StaticMockitoRule( + SystemProperties.class, Constants.class, Process.class, ArtJni.class); private final UserHandle mUserHandle = Binder.getCallingUserHandle(); @@ -155,8 +155,8 @@ public class DexUseManagerTest { mTempFile = File.createTempFile("package-dex-usage", ".pb"); mTempFile.deleteOnExit(); - lenient().when(mArtd.validateDexPath(any())).thenReturn(null); - lenient().when(mArtd.validateClassLoaderContext(any(), any())).thenReturn(null); + lenient().when(ArtJni.validateDexPath(any())).thenReturn(null); + lenient().when(ArtJni.validateClassLoaderContext(any(), any())).thenReturn(null); lenient().when(mInjector.getArtd()).thenReturn(mArtd); lenient().when(mInjector.getCurrentTimeMillis()).thenReturn(0l); @@ -712,14 +712,14 @@ public class DexUseManagerTest { @Test(expected = IllegalArgumentException.class) public void testInvalidDexPath() throws Exception { - lenient().when(mArtd.validateDexPath(any())).thenReturn("invalid"); + lenient().when(ArtJni.validateDexPath(any())).thenReturn("invalid"); mDexUseManager.notifyDexContainersLoaded( mSnapshot, OWNING_PKG_NAME, Map.of("/a/b.jar", "PCL[]")); } @Test(expected = IllegalArgumentException.class) public void testInvalidClassLoaderContext() throws Exception { - lenient().when(mArtd.validateClassLoaderContext(any(), any())).thenReturn("invalid"); + lenient().when(ArtJni.validateClassLoaderContext(any(), any())).thenReturn("invalid"); mDexUseManager.notifyDexContainersLoaded( mSnapshot, OWNING_PKG_NAME, Map.of("/a/b.jar", "PCL[]")); } diff --git a/libartservice/service/native/service.cc b/libartservice/service/native/service.cc index d33cb59515..7d223e5c35 100644 --- a/libartservice/service/native/service.cc +++ b/libartservice/service/native/service.cc @@ -16,12 +16,105 @@ #include "service.h" +#include <jni.h> + +#include <filesystem> + +#include "android-base/errors.h" +#include "android-base/file.h" +#include "android-base/result.h" +#include "class_loader_context.h" +#include "nativehelper/utils.h" + namespace art { namespace service { -std::string getMsg() { - return "hello world!"; +using ::android::base::Dirname; +using ::android::base::Result; + +Result<void> ValidateAbsoluteNormalPath(const std::string& path_str) { + if (path_str.empty()) { + return Errorf("Path is empty"); + } + if (path_str.find('\0') != std::string::npos) { + return Errorf("Path '{}' has invalid character '\\0'", path_str); + } + std::filesystem::path path(path_str); + if (!path.is_absolute()) { + return Errorf("Path '{}' is not an absolute path", path_str); + } + if (path.lexically_normal() != path_str) { + return Errorf("Path '{}' is not in normal form", path_str); + } + return {}; } +Result<void> ValidatePathElementSubstring(const std::string& path_element_substring, + const std::string& name) { + if (path_element_substring.empty()) { + return Errorf("{} is empty", name); + } + if (path_element_substring.find('/') != std::string::npos) { + return Errorf("{} '{}' has invalid character '/'", name, path_element_substring); + } + if (path_element_substring.find('\0') != std::string::npos) { + return Errorf("{} '{}' has invalid character '\\0'", name, path_element_substring); + } + return {}; } + +Result<void> ValidatePathElement(const std::string& path_element, const std::string& name) { + OR_RETURN(ValidatePathElementSubstring(path_element, name)); + if (path_element == "." || path_element == "..") { + return Errorf("Invalid {} '{}'", name, path_element); + } + return {}; } + +Result<void> ValidateDexPath(const std::string& dex_path) { + OR_RETURN(ValidateAbsoluteNormalPath(dex_path)); + return {}; +} + +extern "C" JNIEXPORT jstring JNICALL +Java_com_android_server_art_ArtJni_validateDexPathNative(JNIEnv* env, jobject, jstring j_dex_path) { + std::string dex_path(GET_UTF_OR_RETURN(env, j_dex_path)); + + if (Result<void> result = ValidateDexPath(dex_path); !result.ok()) { + return CREATE_UTF_OR_RETURN(env, result.error().message()).release(); + } else { + return nullptr; + } +} + +extern "C" JNIEXPORT jstring JNICALL +Java_com_android_server_art_ArtJni_validateClassLoaderContextNative( + JNIEnv* env, jobject, jstring j_dex_path, jstring j_class_loader_context) { + ScopedUtfChars dex_path = GET_UTF_OR_RETURN(env, j_dex_path); + std::string class_loader_context(GET_UTF_OR_RETURN(env, j_class_loader_context)); + + if (class_loader_context == ClassLoaderContext::kUnsupportedClassLoaderContextEncoding) { + return nullptr; + } + + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(class_loader_context); + if (context == nullptr) { + return CREATE_UTF_OR_RETURN( + env, ART_FORMAT("Class loader context '{}' is invalid", class_loader_context)) + .release(); + } + + std::vector<std::string> flattened_context = context->FlattenDexPaths(); + std::string dex_dir = Dirname(dex_path); + for (const std::string& context_element : flattened_context) { + std::string context_path = std::filesystem::path(dex_dir).append(context_element); + if (Result<void> result = ValidateDexPath(context_path); !result.ok()) { + return CREATE_UTF_OR_RETURN(env, result.error().message()).release(); + } + } + + return nullptr; +} + +} // namespace service +} // namespace art diff --git a/libartservice/service/native/service.h b/libartservice/service/native/service.h index 2b680a28a4..7f43f17f0b 100644 --- a/libartservice/service/native/service.h +++ b/libartservice/service/native/service.h @@ -19,10 +19,20 @@ #include <string> +#include "android-base/result.h" + namespace art { namespace service { -std::string getMsg(); +android::base::Result<void> ValidateAbsoluteNormalPath(const std::string& path_str); + +android::base::Result<void> ValidatePathElementSubstring(const std::string& path_element_substring, + const std::string& name); + +android::base::Result<void> ValidatePathElement(const std::string& path_element, + const std::string& name); + +android::base::Result<void> ValidateDexPath(const std::string& dex_path); } // namespace service } // namespace art diff --git a/libartservice/service/native/service_test.cc b/libartservice/service/native/service_test.cc index eb84b732c7..8300bf5e76 100644 --- a/libartservice/service/native/service_test.cc +++ b/libartservice/service/native/service_test.cc @@ -15,14 +15,97 @@ */ #include "service.h" + +#include "android-base/result-gmock.h" #include "gtest/gtest.h" namespace art { +namespace service { +namespace { + +using ::android::base::testing::HasError; +using ::android::base::testing::Ok; +using ::android::base::testing::WithMessage; + +using std::literals::operator""s; // NOLINT class ArtServiceTest : public testing::Test {}; -TEST_F(ArtServiceTest, Hello) { - EXPECT_EQ("hello world!", art::service::getMsg()); +TEST_F(ArtServiceTest, ValidatePathElementOk) { + EXPECT_THAT(ValidatePathElement("com.android.foo", "packageName"), Ok()); + EXPECT_THAT(ValidatePathElement("...", "packageName"), Ok()); + EXPECT_THAT(ValidatePathElement("!@#$%^&*()_+-=", "packageName"), Ok()); +} + +TEST_F(ArtServiceTest, ValidatePathElementEmpty) { + EXPECT_THAT(ValidatePathElement("", "packageName"), + HasError(WithMessage("packageName is empty"))); +} + +TEST_F(ArtServiceTest, ValidatePathElementDot) { + EXPECT_THAT(ValidatePathElement(".", "packageName"), + HasError(WithMessage("Invalid packageName '.'"))); +} + +TEST_F(ArtServiceTest, ValidatePathElementDotDot) { + EXPECT_THAT(ValidatePathElement("..", "packageName"), + HasError(WithMessage("Invalid packageName '..'"))); +} + +TEST_F(ArtServiceTest, ValidatePathElementSlash) { + EXPECT_THAT(ValidatePathElement("a/b", "packageName"), + HasError(WithMessage("packageName 'a/b' has invalid character '/'"))); +} + +TEST_F(ArtServiceTest, ValidatePathElementNul) { + EXPECT_THAT(ValidatePathElement("a\0b"s, "packageName"), + HasError(WithMessage("packageName 'a\0b' has invalid character '\\0'"s))); +} + +TEST_F(ArtServiceTest, ValidatePathElementSubstringOk) { + EXPECT_THAT(ValidatePathElementSubstring("com.android.foo", "packageName"), Ok()); + EXPECT_THAT(ValidatePathElementSubstring(".", "packageName"), Ok()); + EXPECT_THAT(ValidatePathElementSubstring("..", "packageName"), Ok()); + EXPECT_THAT(ValidatePathElementSubstring("...", "packageName"), Ok()); + EXPECT_THAT(ValidatePathElementSubstring("!@#$%^&*()_+-=", "packageName"), Ok()); +} + +TEST_F(ArtServiceTest, ValidatePathElementSubstringEmpty) { + EXPECT_THAT(ValidatePathElementSubstring("", "packageName"), + HasError(WithMessage("packageName is empty"))); +} + +TEST_F(ArtServiceTest, ValidatePathElementSubstringSlash) { + EXPECT_THAT(ValidatePathElementSubstring("a/b", "packageName"), + HasError(WithMessage("packageName 'a/b' has invalid character '/'"))); +} + +TEST_F(ArtServiceTest, ValidatePathElementSubstringNul) { + EXPECT_THAT(ValidatePathElementSubstring("a\0b"s, "packageName"), + HasError(WithMessage("packageName 'a\0b' has invalid character '\\0'"s))); +} + +TEST_F(ArtServiceTest, ValidateDexPathOk) { EXPECT_THAT(ValidateDexPath("/a/b.apk"), Ok()); } + +TEST_F(ArtServiceTest, ValidateDexPathEmpty) { + EXPECT_THAT(ValidateDexPath(""), HasError(WithMessage("Path is empty"))); +} + +TEST_F(ArtServiceTest, ValidateDexPathRelative) { + EXPECT_THAT(ValidateDexPath("a/b.apk"), + HasError(WithMessage("Path 'a/b.apk' is not an absolute path"))); +} + +TEST_F(ArtServiceTest, ValidateDexPathNonNormal) { + EXPECT_THAT(ValidateDexPath("/a/c/../b.apk"), + HasError(WithMessage("Path '/a/c/../b.apk' is not in normal form"))); +} + +TEST_F(ArtServiceTest, ValidateDexPathNul) { + EXPECT_THAT(ValidateDexPath("/a/\0/b.apk"s), + HasError(WithMessage("Path '/a/\0/b.apk' has invalid character '\\0'"s))); } +} // namespace +} // namespace service } // namespace art diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp index f0e010664c..73f93a6782 100644 --- a/libdexfile/Android.bp +++ b/libdexfile/Android.bp @@ -385,7 +385,6 @@ art_cc_defaults { "external/dex_file_ext_test.cc", ], shared_libs: [ - "libartbase", "libdexfile", ], header_libs: [ @@ -402,6 +401,9 @@ art_cc_test { "art_test_defaults", "art_libdexfile_external_tests_defaults", ], + shared_libs: [ + "libartbase", + ], } // Standalone version of ART gtest `art_libdexfile_external_tests`, not bundled with the ART APEX on @@ -412,6 +414,9 @@ art_cc_test { "art_standalone_test_defaults", "art_libdexfile_external_tests_defaults", ], + static_libs: [ + "libartbase", + ], test_config_template: ":art-gtests-target-standalone-cts-template", test_suites: ["cts"], // For backed-by API coverage. diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h index d9c5211cc8..cc2f641a38 100644 --- a/libdexfile/dex/dex_file-inl.h +++ b/libdexfile/dex/dex_file-inl.h @@ -319,7 +319,14 @@ bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream, // Emit what was previously there, if anything if (local_in_reg[reg].is_live_) { local_in_reg[reg].end_address_ = address; - new_local_callback(local_in_reg[reg]); + // Parameters with generic types cannot be encoded in the debug_info_item header. So d8 + // encodes it as null in the header with start and end address as 0. There will be a + // START_LOCAL_EXTENDED that will declare the parameter with correct signature + // Debuggers get confused when they see empty ranges. So don't emit them. + // See b/297843934 for more details. + if (local_in_reg[reg].end_address_ != 0) { + new_local_callback(local_in_reg[reg]); + } } local_in_reg[reg].name_ = index_to_string_data(name_idx); diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp index f1725fc278..b2d29f10c7 100644 --- a/libnativebridge/tests/Android.bp +++ b/libnativebridge/tests/Android.bp @@ -188,6 +188,7 @@ cc_test { "cts", "general-tests", "mts-art", + "mcts-art", ], } diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp index 96f56775f6..16449ac745 100644 --- a/libnativeloader/Android.bp +++ b/libnativeloader/Android.bp @@ -183,6 +183,7 @@ art_cc_test { test_suites: [ "cts", "mts-art", + "mcts-art", ], } diff --git a/odrefresh/odr_config.h b/odrefresh/odr_config.h index 78af97c9a0..5aee588992 100644 --- a/odrefresh/odr_config.h +++ b/odrefresh/odr_config.h @@ -74,7 +74,6 @@ struct SystemPropertyConfig { const android::base::NoDestructor<std::vector<SystemPropertyConfig>> kSystemProperties{ {SystemPropertyConfig{.name = "persist.device_config.runtime_native_boot.force_disable_uffd_gc", .default_value = "false"}, - SystemPropertyConfig{.name = kPhDisableCompactDex, .default_value = "false"}, SystemPropertyConfig{.name = kSystemPropertySystemServerCompilerFilterOverride, .default_value = ""}, // For testing only (cf. odsign_e2e_tests_full). diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index 03a432dbf4..b051bfb5c6 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -42,6 +42,50 @@ namespace art { namespace gc { namespace collector { +namespace { + +// Report a GC metric via the ATrace interface. +void TraceGCMetric(const char* name, int64_t value) { + // ART's interface with systrace (through libartpalette) only supports + // reporting 32-bit (signed) integer values at the moment. Upon + // underflows/overflows, clamp metric values at `int32_t` min/max limits and + // report these events via a corresponding underflow/overflow counter; also + // log a warning about the first underflow/overflow occurrence. + // + // TODO(b/300015145): Consider extending libarpalette to allow reporting this + // value as a 64-bit (signed) integer (instead of a 32-bit (signed) integer). + // Note that this is likely unnecessary at the moment (November 2023) for any + // size-related GC metric, given the maximum theoretical size of a managed + // heap (4 GiB). + if (UNLIKELY(value < std::numeric_limits<int32_t>::min())) { + ATraceIntegerValue(name, std::numeric_limits<int32_t>::min()); + std::string underflow_counter_name = std::string(name) + " int32_t underflow"; + ATraceIntegerValue(underflow_counter_name.c_str(), 1); + static bool int32_underflow_reported = false; + if (!int32_underflow_reported) { + LOG(WARNING) << "GC Metric \"" << name << "\" with value " << value + << " causing a 32-bit integer underflow"; + int32_underflow_reported = true; + } + return; + } + if (UNLIKELY(value > std::numeric_limits<int32_t>::max())) { + ATraceIntegerValue(name, std::numeric_limits<int32_t>::max()); + std::string overflow_counter_name = std::string(name) + " int32_t overflow"; + ATraceIntegerValue(overflow_counter_name.c_str(), 1); + static bool int32_overflow_reported = false; + if (!int32_overflow_reported) { + LOG(WARNING) << "GC Metric \"" << name << "\" with value " << value + << " causing a 32-bit integer overflow"; + int32_overflow_reported = true; + } + return; + } + ATraceIntegerValue(name, value); +} + +} // namespace + Iteration::Iteration() : duration_ns_(0), timings_("GC iteration timing logger", true, VLOG_IS_ON(heap)) { Reset(kGcCauseBackground, false); // Reset to some place holder values. @@ -238,6 +282,12 @@ void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) { gc_duration_->Add(NsToMs(current_iteration->GetDurationNs())); gc_duration_delta_->Add(NsToMs(current_iteration->GetDurationNs())); } + + // Report some metrics via the ATrace interface, to surface them in Perfetto. + TraceGCMetric("freed_normal_object_bytes", current_iteration->GetFreedBytes()); + TraceGCMetric("freed_large_object_bytes", current_iteration->GetFreedLargeObjectBytes()); + TraceGCMetric("freed_bytes", freed_bytes); + is_transaction_active_ = false; } diff --git a/runtime/image.cc b/runtime/image.cc index 37437b4b86..fda755353d 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -257,48 +257,36 @@ const char* ImageHeader::GetImageSectionName(ImageSections index) { } } -// If `image_storage_mode` is compressed, compress data from `source` -// into `storage`, and return an array pointing to the compressed. -// If the mode is uncompressed, just return an array pointing to `source`. -static ArrayRef<const uint8_t> MaybeCompressData(ArrayRef<const uint8_t> source, - ImageHeader::StorageMode image_storage_mode, - /*out*/ dchecked_vector<uint8_t>* storage) { +// Compress data from `source` into `storage`. +static bool CompressData(ArrayRef<const uint8_t> source, + ImageHeader::StorageMode image_storage_mode, + /*out*/ dchecked_vector<uint8_t>* storage) { const uint64_t compress_start_time = NanoTime(); - switch (image_storage_mode) { - case ImageHeader::kStorageModeLZ4: { - storage->resize(LZ4_compressBound(source.size())); - size_t data_size = LZ4_compress_default( - reinterpret_cast<char*>(const_cast<uint8_t*>(source.data())), - reinterpret_cast<char*>(storage->data()), - source.size(), - storage->size()); - storage->resize(data_size); - break; - } - case ImageHeader::kStorageModeLZ4HC: { - // Bound is same as non HC. - storage->resize(LZ4_compressBound(source.size())); - size_t data_size = LZ4_compress_HC( - reinterpret_cast<const char*>(const_cast<uint8_t*>(source.data())), - reinterpret_cast<char*>(storage->data()), - source.size(), - storage->size(), - LZ4HC_CLEVEL_MAX); - storage->resize(data_size); - break; - } - case ImageHeader::kStorageModeUncompressed: { - return source; - } - default: { - LOG(FATAL) << "Unsupported"; - UNREACHABLE(); - } + // Bound is same for both LZ4 and LZ4HC. + storage->resize(LZ4_compressBound(source.size())); + size_t data_size = 0; + if (image_storage_mode == ImageHeader::kStorageModeLZ4) { + data_size = LZ4_compress_default( + reinterpret_cast<char*>(const_cast<uint8_t*>(source.data())), + reinterpret_cast<char*>(storage->data()), + source.size(), + storage->size()); + } else { + DCHECK_EQ(image_storage_mode, ImageHeader::kStorageModeLZ4HC); + data_size = LZ4_compress_HC( + reinterpret_cast<const char*>(const_cast<uint8_t*>(source.data())), + reinterpret_cast<char*>(storage->data()), + source.size(), + storage->size(), + LZ4HC_CLEVEL_MAX); + } + + if (data_size == 0) { + return false; } + storage->resize(data_size); - DCHECK(image_storage_mode == ImageHeader::kStorageModeLZ4 || - image_storage_mode == ImageHeader::kStorageModeLZ4HC); VLOG(image) << "Compressed from " << source.size() << " to " << storage->size() << " in " << PrettyDuration(NanoTime() - compress_start_time); if (kIsDebugBuild) { @@ -319,7 +307,7 @@ static ArrayRef<const uint8_t> MaybeCompressData(ArrayRef<const uint8_t> source, CHECK_EQ(decompressed_size, decompressed.size()); CHECK_EQ(memcmp(source.data(), decompressed.data(), source.size()), 0) << image_storage_mode; } - return ArrayRef<const uint8_t>(*storage); + return true; } bool ImageHeader::WriteData(const ImageFileGuard& image_file, @@ -360,10 +348,16 @@ bool ImageHeader::WriteData(const ImageFileGuard& image_file, for (const std::pair<uint32_t, uint32_t> block : block_sources) { ArrayRef<const uint8_t> raw_image_data(data + block.first, block.second); dchecked_vector<uint8_t> compressed_data; - ArrayRef<const uint8_t> image_data = - MaybeCompressData(raw_image_data, image_storage_mode, &compressed_data); - - if (!is_compressed) { + ArrayRef<const uint8_t> image_data; + if (is_compressed) { + if (!CompressData(raw_image_data, image_storage_mode, &compressed_data)) { + *error_msg = "Error compressing data for " + + image_file->GetPath() + ": " + std::string(strerror(errno)); + return false; + } + image_data = ArrayRef<const uint8_t>(compressed_data); + } else { + image_data = raw_image_data; // For uncompressed, preserve alignment since the image will be directly mapped. out_offset = block.first; } diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 34f9045a33..30631ac683 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -1537,16 +1537,21 @@ void* JitCodeCache::MoreCore(const void* mspace, intptr_t increment) { void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_locations, std::vector<ProfileMethodInfo>& methods) { + ScopedTrace trace(__FUNCTION__); Thread* self = Thread::Current(); WaitUntilInlineCacheAccessible(self); + std::vector<ProfilingInfo*> copies; // TODO: Avoid read barriers for potentially dead methods. // ScopedDebugDisallowReadBarriers sddrb(self); - MutexLock mu(self, *Locks::jit_lock_); - ScopedTrace trace(__FUNCTION__); - for (const auto& entry : profiling_infos_) { - ArtMethod* method = entry.first; - ProfilingInfo* info = entry.second; - DCHECK_EQ(method, info->GetMethod()); + { + MutexLock mu(self, *Locks::jit_lock_); + copies.reserve(profiling_infos_.size()); + for (const auto& entry : profiling_infos_) { + copies.push_back(entry.second); + } + } + for (ProfilingInfo* info : copies) { + ArtMethod* method = info->GetMethod(); const DexFile* dex_file = method->GetDexFile(); const std::string base_location = DexFileLoader::GetBaseLocation(dex_file->GetLocation()); if (!ContainsElement(dex_base_locations, base_location)) { diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index ce2b5609fc..7fed9f3827 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -310,7 +310,7 @@ int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target_compiler_fil bool profile_changed, bool downgrade) { OatFileInfo& info = GetBestInfo(); - if (info.CheckDisableCompactDexExperiment()) { // TODO(b/256664509): Clean this up. + if (info.CheckDisableCompactDex()) { // TODO(b/256664509): Clean this up. return kDex2OatFromScratch; } DexOptNeeded dexopt_needed = info.GetDexOptNeeded( @@ -331,7 +331,7 @@ bool OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target_compiler_fi DexOptTrigger dexopt_trigger, /*out*/ DexOptStatus* dexopt_status) { OatFileInfo& info = GetBestInfo(); - if (info.CheckDisableCompactDexExperiment()) { // TODO(b/256664509): Clean this up. + if (info.CheckDisableCompactDex()) { // TODO(b/256664509): Clean this up. dexopt_status->location_ = kLocationNoneOrError; return true; } @@ -1275,14 +1275,10 @@ std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() { return std::unique_ptr<OatFile>(); } -// Check if we should reject vdex containing cdex code as part of the -// disable_cdex experiment. +// Check if we should reject vdex containing cdex code as part of the cdex +// deprecation. // TODO(b/256664509): Clean this up. -bool OatFileAssistant::OatFileInfo::CheckDisableCompactDexExperiment() { - std::string ph_disable_compact_dex = android::base::GetProperty(kPhDisableCompactDex, "false"); - if (ph_disable_compact_dex != "true") { - return false; - } +bool OatFileAssistant::OatFileInfo::CheckDisableCompactDex() { const OatFile* oat_file = GetFile(); if (oat_file == nullptr) { return false; diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 54287eb5f1..c81dea1210 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -434,10 +434,10 @@ class OatFileAssistant { // the OatFileInfo object. std::unique_ptr<OatFile> ReleaseFileForUse(); - // Check if we should reject vdex containing cdex code as part of the - // disable_cdex experiment. + // Check if we should reject vdex containing cdex code as part of the cdex + // deprecation. // TODO(b/256664509): Clean this up. - bool CheckDisableCompactDexExperiment(); + bool CheckDisableCompactDex(); private: // Returns true if the oat file is usable but at least one dexopt trigger is matched. This diff --git a/runtime/trace.cc b/runtime/trace.cc index 74509ae0be..81c86f1923 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -113,7 +113,7 @@ uint64_t GetTimestamp() { return t; } -#if defined(__i386__) || defined(__x86_64__) +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) // Here we compute the scaling factor by sleeping for a millisecond. Alternatively, we could // generate raw timestamp counter and also time using clock_gettime at the start and the end of the // trace. We can compute the frequency of timestamp counter upadtes in the post processing step @@ -130,7 +130,9 @@ double computeScalingFactor() { DCHECK(scaling_factor > 0.0) << scaling_factor; return scaling_factor; } +#endif +#if defined(__i386__) || defined(__x86_64__) double GetScalingFactorForX86() { uint32_t eax, ebx, ecx; asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx) : "a"(0x0), "c"(0)); @@ -185,7 +187,13 @@ void InitializeTimestampCounters() { uint64_t freq = 0; // See Arm Architecture Registers Armv8 section System Registers asm volatile("mrs %0, cntfrq_el0" : "=r"(freq)); - tsc_to_microsec_scaling_factor = seconds_to_microseconds / static_cast<double>(freq); + if (freq == 0) { + // It is expected that cntfrq_el0 is correctly setup during system initialization but some + // devices don't do this. In such cases fall back to computing the frequency. See b/315139000. + tsc_to_microsec_scaling_factor = computeScalingFactor(); + } else { + tsc_to_microsec_scaling_factor = seconds_to_microseconds / static_cast<double>(freq); + } #elif defined(__i386__) || defined(__x86_64__) tsc_to_microsec_scaling_factor = GetScalingFactorForX86(); #else diff --git a/test/018-stack-overflow/Android.bp b/test/018-stack-overflow/Android.bp index ef817a8099..29ba5eafe3 100644 --- a/test/018-stack-overflow/Android.bp +++ b/test/018-stack-overflow/Android.bp @@ -21,7 +21,10 @@ java_test { ":art-run-test-018-stack-overflow-expected-stdout", ":art-run-test-018-stack-overflow-expected-stderr", ], - test_suites: ["cts"], + test_suites: [ + "cts", + "mcts-art", + ], } // Test's expected standard output. diff --git a/test/048-reflect-v8/Android.bp b/test/048-reflect-v8/Android.bp index f43f4b02d6..3b279bf0f8 100644 --- a/test/048-reflect-v8/Android.bp +++ b/test/048-reflect-v8/Android.bp @@ -21,7 +21,10 @@ java_test { ":art-run-test-048-reflect-v8-expected-stdout", ":art-run-test-048-reflect-v8-expected-stderr", ], - test_suites: ["cts"], + test_suites: [ + "cts", + "mcts-art", + ], } // Test's expected standard output. diff --git a/test/1911-get-local-var-table/src/art/Test1911.java b/test/1911-get-local-var-table/src/art/Test1911.java index 4dd9054bf0..3e2eec21b7 100644 --- a/test/1911-get-local-var-table/src/art/Test1911.java +++ b/test/1911-get-local-var-table/src/art/Test1911.java @@ -18,8 +18,10 @@ package art; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; +import java.lang.Integer; import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.ArrayList; import java.util.Base64; import java.util.HashSet; import java.util.Set; @@ -27,8 +29,8 @@ import java.util.Set; public class Test1911 { // Class/dex file containing the following class. // - // CLASS_BYTES generated with java version 1.8.0_45: javac -g art/Target.java - // DEX_BYTES generated with dx version 1.14: dx --dex --output=./classes.dex art/Target.class + // CLASS_BYTES generated with java version 17.0.4.1: javac -g art/Target.java + // DEX_BYTES generated with d8 version 8.3.7-dev: d8 --debug art/Target.class // // package art; // import java.util.ArrayList; @@ -53,58 +55,76 @@ public class Test1911 { // long q = 3 * p; // doNothing(p, q, o, i); // } + // public void testGenericParameters(ArrayList<Integer> array, int i, Integer val) { + // array.set(i, val); + // } // } public static byte[] CLASS_BYTES = Base64.getDecoder().decode( - "yv66vgAAADQARgoABAAuCQANAC8KAA0AMAcAMQY/0zMzMzMzMwoAMgAzCgA0ADUHADYKAAkALgoA" + - "NwA4CgA5ADoHADsBAAN6enoBAAFJAQAGPGluaXQ+AQAEKEkpVgEABENvZGUBAA9MaW5lTnVtYmVy" + - "VGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAMTGFydC9UYXJnZXQ7AQADeHh4AQAB" + - "cQEACWRvTm90aGluZwEAFihbTGphdmEvbGFuZy9PYmplY3Q7KVYBAARvYmpzAQATW0xqYXZhL2xh" + - "bmcvT2JqZWN0OwEAC2RvU29tZXRoaW5nAQABRgEAAWkBAAFEAQABegEAAXgBAAF5AQABbwEAEkxq" + - "YXZhL2xhbmcvT2JqZWN0OwEAFUxqYXZhL3V0aWwvQXJyYXlMaXN0OwEAAXABAAFKAQAWTG9jYWxW" + - "YXJpYWJsZVR5cGVUYWJsZQEAKkxqYXZhL3V0aWwvQXJyYXlMaXN0PExqYXZhL2xhbmcvSW50ZWdl" + - "cjs+OwEADVN0YWNrTWFwVGFibGUBAApTb3VyY2VGaWxlAQALVGFyZ2V0LmphdmEMABAAPAwADgAP" + - "DAAZABoBABBqYXZhL2xhbmcvT2JqZWN0BwA9DAA+AD8HAEAMAD4AQQEAE2phdmEvdXRpbC9BcnJh" + - "eUxpc3QHAEIMAD4AQwcARAwAPgBFAQAKYXJ0L1RhcmdldAEAAygpVgEAD2phdmEvbGFuZy9GbG9h" + - "dAEAB3ZhbHVlT2YBABQoRilMamF2YS9sYW5nL0Zsb2F0OwEAEGphdmEvbGFuZy9Eb3VibGUBABUo" + - "RClMamF2YS9sYW5nL0RvdWJsZTsBABFqYXZhL2xhbmcvSW50ZWdlcgEAFihJKUxqYXZhL2xhbmcv" + - "SW50ZWdlcjsBAA5qYXZhL2xhbmcvTG9uZwEAEyhKKUxqYXZhL2xhbmcvTG9uZzsAIQANAAQAAAAB" + - "AAEADgAPAAAAAwABABAAEQABABIAAABYAAIAAwAAAA4qtwABGwdoPSoctQACsQAAAAIAEwAAABIA" + - "BAAAAAUABAAGAAgABwANAAgAFAAAACAAAwAAAA4AFQAWAAAAAAAOABcADwABAAgABgAYAA8AAgCJ" + - "ABkAGgABABIAAAAvAAEAAQAAAAUquAADsQAAAAIAEwAAAAYAAQAAAAkAFAAAAAwAAQAAAAUAGwAc" + - "AAAAAQAdABEAAQASAAABWAAFAAgAAACCBL0ABFkDKlO4AAMbBmA9Az4dHBtoogAvHB1khjgEFAAF" + - "FwSNazkFBb0ABFkDFwS4AAdTWQQYBbgACFO4AAOEAwGn/9C7AARZtwABTrsACVm3AAo6BAcbgDYF" + - "BhUFaIU3Bge9AARZAxUFuAALU1kEFga4AAxTWQUtU1kGGQRTuAADsQAAAAQAEwAAADYADQAAAAsA" + - "CwAMAA8ADQAYAA4AHgAPACcAEAA+AA0ARAASAEwAEwBVABQAWgAVAGEAFgCBABcAFAAAAGYACgAe" + - "ACAAGAAeAAQAJwAXAB8AIAAFABEAMwAhAA8AAwAAAIIAFQAWAAAAAACCACIADwABAA8AcwAjAA8A" + - "AgBMADYAJAAlAAMAVQAtAB8AJgAEAFoAKAAnAA8ABQBhACEAGAAoAAYAKQAAAAwAAQBVAC0AHwAq" + - "AAQAKwAAAAoAAv0AEQEB+gAyAAEALAAAAAIALQ=="); + "yv66vgAAADcAUQoABAA2CQAOADcKAA4AOAcAOQY/0zMzMzMzMwoAOgA7CgA8AD0HAD4KAAkANgoA" + + "PwBACgBBAEIKAAkAQwcARAEAA3p6egEAAUkBAAY8aW5pdD4BAAQoSSlWAQAEQ29kZQEAD0xpbmVO" + + "dW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAxMYXJ0L1RhcmdldDsBAAN4" + + "eHgBAAFxAQAJZG9Ob3RoaW5nAQAWKFtMamF2YS9sYW5nL09iamVjdDspVgEABG9ianMBABNbTGph" + + "dmEvbGFuZy9PYmplY3Q7AQALZG9Tb21ldGhpbmcBAAFGAQABaQEAAUQBAAF6AQABeAEAAXkBAAFv" + + "AQASTGphdmEvbGFuZy9PYmplY3Q7AQAVTGphdmEvdXRpbC9BcnJheUxpc3Q7AQABcAEAAUoBABZM" + + "b2NhbFZhcmlhYmxlVHlwZVRhYmxlAQAqTGphdmEvdXRpbC9BcnJheUxpc3Q8TGphdmEvbGFuZy9J" + + "bnRlZ2VyOz47AQANU3RhY2tNYXBUYWJsZQEAFXRlc3RHZW5lcmljUGFyYW1ldGVycwEALChMamF2" + + "YS91dGlsL0FycmF5TGlzdDtJTGphdmEvbGFuZy9JbnRlZ2VyOylWAQAFYXJyYXkBAAN2YWwBABNM" + + "amF2YS9sYW5nL0ludGVnZXI7AQAJU2lnbmF0dXJlAQBBKExqYXZhL3V0aWwvQXJyYXlMaXN0PExq" + + "YXZhL2xhbmcvSW50ZWdlcjs+O0lMamF2YS9sYW5nL0ludGVnZXI7KVYBAApTb3VyY2VGaWxlAQAL" + + "VGFyZ2V0LmphdmEMABEARQwADwAQDAAaABsBABBqYXZhL2xhbmcvT2JqZWN0BwBGDABHAEgHAEkM" + + "AEcASgEAE2phdmEvdXRpbC9BcnJheUxpc3QHAEsMAEcATAcATQwARwBODABPAFABAAphcnQvVGFy" + + "Z2V0AQADKClWAQAPamF2YS9sYW5nL0Zsb2F0AQAHdmFsdWVPZgEAFChGKUxqYXZhL2xhbmcvRmxv" + + "YXQ7AQAQamF2YS9sYW5nL0RvdWJsZQEAFShEKUxqYXZhL2xhbmcvRG91YmxlOwEAEWphdmEvbGFu" + + "Zy9JbnRlZ2VyAQAWKEkpTGphdmEvbGFuZy9JbnRlZ2VyOwEADmphdmEvbGFuZy9Mb25nAQATKEop" + + "TGphdmEvbGFuZy9Mb25nOwEAA3NldAEAJyhJTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcv" + + "T2JqZWN0OwAhAA4ABAAAAAEAAQAPABAAAAAEAAEAEQASAAEAEwAAAFgAAgADAAAADiq3AAEbB2g9" + + "Khy1AAKxAAAAAgAUAAAAEgAEAAAACAAEAAkACAAKAA0ACwAVAAAAIAADAAAADgAWABcAAAAAAA4A" + + "GAAQAAEACAAGABkAEAACAIkAGgAbAAEAEwAAAC8AAQABAAAABSq4AAOxAAAAAgAUAAAABgABAAAA" + + "DQAVAAAADAABAAAABQAcAB0AAAABAB4AEgABABMAAAFYAAUACAAAAIIEvQAEWQMqU7gAAxsGYD0D" + + "Ph0cG2iiAC8cHWSGOAQUAAUXBI1rOQUFvQAEWQMXBLgAB1NZBBgFuAAIU7gAA4QDAaf/0LsABFm3" + + "AAFOuwAJWbcACjoEBxuANgUGFQVohTcGB70ABFkDFQW4AAtTWQQWBrgADFNZBS1TWQYZBFO4AAOx" + + "AAAABAAUAAAANgANAAAAEAALABEADwASABgAEwAeABQAJwAVAD4AEgBEABcATAAYAFUAGQBaABoA" + + "YQAbAIEAHAAVAAAAZgAKAB4AIAAZAB8ABAAnABcAIAAhAAUAEQAzACIAEAADAAAAggAWABcAAAAA" + + "AIIAIwAQAAEADwBzACQAEAACAEwANgAlACYAAwBVAC0AIAAnAAQAWgAoACgAEAAFAGEAIQAZACkA" + + "BgAqAAAADAABAFUALQAgACsABAAsAAAACgAC/QARAQH6ADIAAQAtAC4AAgATAAAAZgADAAQAAAAI" + + "KxwttgANV7EAAAADABQAAAAKAAIAAAAfAAcAIAAVAAAAKgAEAAAACAAWABcAAAAAAAgALwAnAAEA" + + "AAAIACAAEAACAAAACAAwADEAAwAqAAAADAABAAAACAAvACsAAQAyAAAAAgAzAAEANAAAAAIANQ=="); public static byte[] DEX_BYTES = Base64.getDecoder().decode( - "ZGV4CjAzNQCQtgjEV631Ma/btYyIy2IzqHWNN+nZiwl0BQAAcAAAAHhWNBIAAAAAAAAAANQEAAAk" + - "AAAAcAAAAA0AAAAAAQAABwAAADQBAAABAAAAiAEAAAkAAACQAQAAAQAAANgBAAB8AwAA+AEAAB4D" + - "AAAmAwAAKQMAACwDAAAvAwAAMgMAADYDAAA6AwAAPgMAAEIDAABQAwAAZAMAAHcDAACMAwAAngMA" + - "ALIDAADJAwAA9QMAAAIEAAAFBAAACQQAAA0EAAAiBAAALQQAADoEAAA9BAAAQAQAAEYEAABJBAAA" + - "TAQAAFIEAABbBAAAXgQAAGMEAABmBAAAaQQAAAEAAAACAAAAAwAAAAQAAAAJAAAACgAAAAsAAAAM" + - "AAAADQAAAA4AAAAPAAAAEgAAABUAAAAFAAAABQAAAPgCAAAGAAAABgAAAAADAAAHAAAABwAAAAgD" + - "AAAIAAAACAAAABADAAASAAAACwAAAAAAAAATAAAACwAAAAgDAAAUAAAACwAAABgDAAAEAAIAIwAA" + - "AAQABQAAAAAABAAGABYAAAAEAAUAFwAAAAUAAAAeAAAABgABAB4AAAAHAAIAHgAAAAgAAwAeAAAA" + - "CQAEAAAAAAAKAAQAAAAAAAQAAAABAAAACQAAAAAAAAARAAAAAAAAAL4EAAAAAAAAAwACAAEAAABu" + - "BAAACAAAAHAQBwABANoAAgRZEAAADgABAAEAAQAAAHsEAAAEAAAAcRABAAAADgAQAAIAAgAAAIEE" + - "AABcAAAAEhkjmQwAEgpNDgkKcRABAAkA2AUPAxIIkgkFDzWYJACRCQUIgpYYCjMzMzMzM9M/iWyt" + - "AAoMEikjmQwAEgpxEAQABgAMC00LCQoSGnEgAwAQAAwLTQsJCnEQAQAJANgICAEo2yIDCQBwEAcA" + - "AwAiAgoAcBAIAAIA3gQPBNoJBAOBlhJJI5kMABIKcRAFAAQADAtNCwkKEhpxIAYAdgAMC00LCQoS" + - "Kk0DCQoSOk0CCQpxEAEACQAOAAEAAAAAAAAAAQAAAAEAAAABAAAAAgAAAAEAAAADAAAAAQAAAAwA" + - "Bjxpbml0PgABRAABRgABSQABSgACTEQAAkxGAAJMSQACTEoADExhcnQvVGFyZ2V0OwASTGphdmEv" + - "bGFuZy9Eb3VibGU7ABFMamF2YS9sYW5nL0Zsb2F0OwATTGphdmEvbGFuZy9JbnRlZ2VyOwAQTGph" + - "dmEvbGFuZy9Mb25nOwASTGphdmEvbGFuZy9PYmplY3Q7ABVMamF2YS91dGlsL0FycmF5TGlzdDsA" + - "KkxqYXZhL3V0aWwvQXJyYXlMaXN0PExqYXZhL2xhbmcvSW50ZWdlcjs+OwALVGFyZ2V0LmphdmEA" + - "AVYAAlZJAAJWTAATW0xqYXZhL2xhbmcvT2JqZWN0OwAJZG9Ob3RoaW5nAAtkb1NvbWV0aGluZwAB" + - "aQABbwAEb2JqcwABcAABcQAEdGhpcwAHdmFsdWVPZgABeAADeHh4AAF5AAF6AAN6enoABQEhBw48" + - "LQMAHQMtAAkBGwcOAAsBIAcOli0DBSIDAQEDCCMDSzwDBh0ChwMAGQEBFAtABQAFBloDAxoKWgQC" + - "GQsRLQMEHAM8AwYdBAEaDwAAAQIBAAEAgYAE+AMBiQGYBAIBsAQADQAAAAAAAAABAAAAAAAAAAEA" + - "AAAkAAAAcAAAAAIAAAANAAAAAAEAAAMAAAAHAAAANAEAAAQAAAABAAAAiAEAAAUAAAAJAAAAkAEA" + - "AAYAAAABAAAA2AEAAAEgAAADAAAA+AEAAAEQAAAFAAAA+AIAAAIgAAAkAAAAHgMAAAMgAAADAAAA" + - "bgQAAAAgAAABAAAAvgQAAAAQAAABAAAA1AQAAA=="); - + "ZGV4CjAzNQAALyjG3vy0POIlfGUh9Q7yf3NFwlp6VbWoBwAAcAAAAHhWNBIAAAAAAAAAAOQGAAAz" + + "AAAAcAAAAA8AAAA8AQAACgAAAHgBAAABAAAA8AEAAAwAAAD4AQAAAQAAAFgCAAAwBQAAeAIAACIE" + + "AAAlBAAAKQQAADEEAAA2BAAAOQQAADwEAAA/BAAAQgQAAEYEAABKBAAATgQAAFMEAABXBAAAZQQA" + + "AIQEAACYBAAAqwQAAMAEAADSBAAA5gQAAP0EAAAUBQAAQAUAAE0FAABQBQAAVAUAAFgFAABeBQAA" + + "YQUAAGUFAAB6BQAAgQUAAIwFAACZBQAAnAUAAKMFAACmBQAArAUAAK8FAACyBQAAtwUAAM4FAADT" + + "BQAA2gUAAOMFAADmBQAA6wUAAO4FAADxBQAA9gUAAAQAAAAFAAAABgAAAAcAAAANAAAADgAAAA8A" + + "AAAQAAAAEQAAABIAAAATAAAAFAAAABgAAAAcAAAAHgAAAAgAAAAGAAAA6AMAAAkAAAAHAAAA8AMA" + + "AAoAAAAIAAAA+AMAAAwAAAAJAAAAAAQAAAsAAAAKAAAACAQAABgAAAAMAAAAAAAAABkAAAAMAAAA" + + "+AMAABsAAAAMAAAAEAQAABoAAAAMAAAAHAQAAB0AAAANAAAA6AMAAAQAAgAxAAAABAAGAAIAAAAE" + + "AAgAIAAAAAQABgAhAAAABAAHACkAAAAGAAkAIwAAAAYAAAAsAAAABwABACwAAAAIAAIALAAAAAkA" + + "AwAsAAAACgAFAAIAAAALAAUAAgAAAAsABAAoAAAABAAAAAEAAAAKAAAAAAAAABcAAADMBgAApgYA" + + "AAAAAAADAAIAAQAAAIwDAAAIAAAAcBAJAAEA2gACBFkQAAAOAAEAAQABAAAAmAMAAAQAAABxEAEA" + + "AAAOAA4AAgACAAAAnQMAAFoAAAASECMBDgASAk0MAQJxEAEAAQDYAQ0DEgOSBAENEiU1QyQAkQQB" + + "A4JEGAYzMzMzMzPTP4lIcSAEAJgArQgIBnEQBgAEAAwGcSAFAJgADAcjVQ4ATQYFAk0HBQBxEAEA" + + "BQDYAwMBKNoiAwoAcBAJAAMAIgQLAHAQCgAEAN4GDQTaBwYDgXdxEAcABgAMCXEgCACHAAwKEksj" + + "uw4ATQkLAk0KCwBNAwsFEjBNBAsAcRABAAsADgAEAAQAAwAAANsDAAAEAAAAbjALACEDDgAIAS8O" + + "PC0DACgDLQANASYOABABLg6WLQMBMAMBAQMDMQNaPAMEKAK0AwgjAQERCwUEBQhABQNaAwMlC1oE" + + "BCMMFy0DBicDPAMHKAQBFw8AHwMAIysOBAEgDBc8AAEAAAAAAAAAAQAAAAEAAAABAAAAAgAAAAEA" + + "AAADAAAAAgAAAAIACgADAAAACwACAAgAAAABAAAADgABKAACKVYABjxpbml0PgADPjtJAAFEAAFG" + + "AAFJAAFKAAJMRAACTEYAAkxJAANMSUwAAkxKAAxMYXJ0L1RhcmdldDsAHUxkYWx2aWsvYW5ub3Rh" + + "dGlvbi9TaWduYXR1cmU7ABJMamF2YS9sYW5nL0RvdWJsZTsAEUxqYXZhL2xhbmcvRmxvYXQ7ABNM" + + "amF2YS9sYW5nL0ludGVnZXI7ABBMamF2YS9sYW5nL0xvbmc7ABJMamF2YS9sYW5nL09iamVjdDsA" + + "FUxqYXZhL3V0aWwvQXJyYXlMaXN0OwAVTGphdmEvdXRpbC9BcnJheUxpc3Q8ACpMamF2YS91dGls" + + "L0FycmF5TGlzdDxMamF2YS9sYW5nL0ludGVnZXI7PjsAC1RhcmdldC5qYXZhAAFWAAJWSQACVkwA" + + "BFZMSUwAAVoAAlpEABNbTGphdmEvbGFuZy9PYmplY3Q7AAVhcnJheQAJZG9Ob3RoaW5nAAtkb1Nv" + + "bWV0aGluZwABaQAFaXNOYU4AAW8ABG9ianMAAXAAAXEAA3NldAAVdGVzdEdlbmVyaWNQYXJhbWV0" + + "ZXJzAAN2YWwABXZhbHVlAAd2YWx1ZU9mAAF4AAN4eHgAAXkAAXoAA3p6egCbAX5+RDh7ImJhY2tl" + + "bmQiOiJkZXgiLCJjb21waWxhdGlvbi1tb2RlIjoiZGVidWciLCJoYXMtY2hlY2tzdW1zIjpmYWxz" + + "ZSwibWluLWFwaSI6MSwic2hhLTEiOiIzMTAxYWQ2Zjc0ZWUyMzI1MjhkZmM2NmEyNjE3YTkzODM4" + + "NGU2NmVhIiwidmVyc2lvbiI6IjguMy43LWRldiJ9AAIFASscBhcAFxUXERcDFxEXAQABAgIAAQCB" + + "gAT4BAGJAZgFAgGwBQEB9AYAAAAAAAEAAACUBgAAwAYAAAAAAAABAAAAAAAAAAMAAADEBgAAEAAA" + + "AAAAAAABAAAAAAAAAAEAAAAzAAAAcAAAAAIAAAAPAAAAPAEAAAMAAAAKAAAAeAEAAAQAAAABAAAA" + + "8AEAAAUAAAAMAAAA+AEAAAYAAAABAAAAWAIAAAEgAAAEAAAAeAIAAAMgAAAEAAAAjAMAAAEQAAAH" + + "AAAA6AMAAAIgAAAzAAAAIgQAAAQgAAABAAAAlAYAAAAgAAABAAAApgYAAAMQAAACAAAAwAYAAAYg" + + "AAABAAAAzAYAAAAQAAABAAAA5AYAAA=="); // The variables of the functions in the above Target class. public static Set<Locals.VariableDescription>[] CONSTRUCTOR_VARIABLES = new Set[] { @@ -149,23 +169,49 @@ public class Test1911 { 4))), // ART Local variable table new HashSet<>(Arrays.asList( - new Locals.VariableDescription(19, 31, "q", "F", null, 6), - new Locals.VariableDescription(55, 37, "o", "Ljava/lang/Object;", null, 3), - new Locals.VariableDescription(0, 92, "this", "Lart/Target;", null, 14), - new Locals.VariableDescription(12, 80, "z", "I", null, 8), - new Locals.VariableDescription(11, 81, "y", "I", null, 5), - new Locals.VariableDescription(62, 30, "p", "I", null, 4), - new Locals.VariableDescription(0, 92, "x", "I", null, 15), - new Locals.VariableDescription(27, 23, "i", "D", null, 0), - new Locals.VariableDescription(65, 27, "q", "J", null, 6), - new Locals.VariableDescription(60, - 32, + new Locals.VariableDescription(20, 28, "q", "F", null, 4), + new Locals.VariableDescription(56, 34, "o", "Ljava/lang/Object;", null, 3), + new Locals.VariableDescription(0, 90, "this", "Lart/Target;", null, 12), + new Locals.VariableDescription(12, 39, "z", "I", null, 3), + new Locals.VariableDescription(11, 79, "y", "I", null, 1), + new Locals.VariableDescription(63, 27, "p", "I", null, 6), + new Locals.VariableDescription(0, 90, "x", "I", null, 13), + new Locals.VariableDescription(31, 17, "i", "D", null, 8), + new Locals.VariableDescription(66, 24, "q", "J", null, 7), + new Locals.VariableDescription(61, + 29, "i", "Ljava/util/ArrayList;", "Ljava/util/ArrayList<Ljava/lang/Integer;>;", - 2))), + 4))), + }; + + public static Set<Locals.VariableDescription>[] TEST_GENERIC_PARAMETERS_VARIABLES = new Set[] { + // RI Local variable table + new HashSet<>(Arrays.asList( + new Locals.VariableDescription(0, 8, "this", "Lart/Target;", null, 0), + new Locals.VariableDescription(0, + 8, + "array", + "Ljava/util/ArrayList;", + "Ljava/util/ArrayList<Ljava/lang/Integer;>;", + 1), + new Locals.VariableDescription(0, 8, "i", "I", null, 2), + new Locals.VariableDescription(0, 8, "val", "Ljava/lang/Integer;", null, 3))), + // ART Local variable table + new HashSet<>(Arrays.asList( + new Locals.VariableDescription(0, 4, "this", "Lart/Target;", null, 0), + new Locals.VariableDescription(0, + 4, + "array", + "Ljava/util/ArrayList;", + "Ljava/util/ArrayList<Ljava/lang/Integer;>;", + 1), + new Locals.VariableDescription(0, 4, "i", "I", null, 2), + new Locals.VariableDescription(0, 4, "val", "Ljava/lang/Integer;", null, 3))), }; + // Get a classloader that can load the Target class. public static ClassLoader getClassLoader() throws Exception { try { @@ -213,6 +259,9 @@ public class Test1911 { DO_NOTHING_VARIABLES); CheckLocalVariableTable(target.getDeclaredMethod("doSomething", Integer.TYPE), DO_SOMETHING_VARIABLES); + CheckLocalVariableTable(target.getDeclaredMethod("testGenericParameters", + (new ArrayList<Integer>(0)).getClass(), Integer.TYPE, (new Integer(0)).getClass()), + TEST_GENERIC_PARAMETERS_VARIABLES); } } diff --git a/test/913-heaps/src/art/Test913.java b/test/913-heaps/src/art/Test913.java index 4fffa88198..8bd7cf4710 100644 --- a/test/913-heaps/src/art/Test913.java +++ b/test/913-heaps/src/art/Test913.java @@ -317,7 +317,8 @@ public class Test913 { BufferedReader reader = new BufferedReader(new FileReader("/proc/" + pid + "/maps")); String line; while ((line = reader.readLine()) != null) { - if (line.endsWith(".art")) { + // On host the mappings end with .art and on device they end with .art] + if (line.endsWith(".art]") || line.endsWith(".art")) { reader.close(); return true; } diff --git a/test/Android.bp b/test/Android.bp index 6bc59904d0..5fc8cf96a3 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -343,6 +343,43 @@ art_cc_defaults { ], } +// Variant of art_standalone_gtest_defaults that doesn't link dynamically to any +// internal ART libraries. +art_cc_defaults { + name: "art_cts_gtest_defaults", + defaults: [ + // Note: We don't include "art_debug_defaults" here, as standalone ART + // gtests link with the "non-d" versions of the libraries contained in + // the ART APEX, so that they can be used with all ART APEX flavors + // (including the Release ART APEX). + "art_standalone_test_defaults", + "art_gtest_common_defaults", + ], + gtest: true, + + // Support multilib variants (using different suffix per sub-architecture), which is needed on + // build targets with secondary architectures, as the MTS test suite packaging logic flattens + // all test artifacts into a single `testcases` directory. + compile_multilib: "both", + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + + static_libs: [ + "libartbase", + ], + + test_suites: [ + "general-tests", + "mts-art", + ], +} + // Properties common to `libart-gtest-defaults` and `libartd-gtest-defaults`. art_cc_defaults { name: "libart-gtest-common-defaults", diff --git a/test/art-gtests-target-standalone-cts-template.xml b/test/art-gtests-target-standalone-cts-template.xml index 3749c3e990..e862f171e2 100644 --- a/test/art-gtests-target-standalone-cts-template.xml +++ b/test/art-gtests-target-standalone-cts-template.xml @@ -30,8 +30,6 @@ <test class="com.android.tradefed.testtype.GTest" > <option name="native-test-device-path" value="/data/local/tmp/{MODULE}" /> <option name="module-name" value="{MODULE}" /> - <option name="ld-library-path-32" value="/apex/com.android.art/lib" /> - <option name="ld-library-path-64" value="/apex/com.android.art/lib64" /> </test> <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if |