[automerger skipped] Add SafetyNet logging to JNI::NewStringUTF. am: 4b56bb8ce2 am: a575d6f7e4 am: 90825620b4 -s ours am: 340de1c79a -s ours am: dbb1a04dfd -s ours
am skip reason: Change-Id I653db8be0c0a45302f0d1c54285c02d2d052a9f4 with SHA-1 69fc841b84 is in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/art/+/13184710
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: I3c15894efefbe052fd1e92e9077592143f2af3f1
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 101e5c4..a70b34d 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -20,7 +20,6 @@
#include "gtest/gtest.h"
-#include "base/mutex.h"
#include "base/utils.h"
#include "jdwp_provider.h"
#include "experimental_flags.h"
@@ -578,4 +577,21 @@
EXPECT_KEY_VALUE(map, M::MethodTrace, Unit{});
EXPECT_KEY_VALUE(map, M::LargeObjectSpace, gc::space::LargeObjectSpaceType::kMap);
} // TEST_F
+
+TEST_F(CmdlineParserTest, TypesNotInRuntime) {
+ CmdlineType<std::vector<int32_t>> ct;
+ auto success0 =
+ CmdlineParseResult<std::vector<int32_t>>::Success(std::vector<int32_t>({1, 2, 3, 4}));
+ EXPECT_EQ(success0, ct.Parse("1,2,3,4"));
+ auto success1 = CmdlineParseResult<std::vector<int32_t>>::Success(std::vector<int32_t>({0}));
+ EXPECT_EQ(success1, ct.Parse("1"));
+
+ EXPECT_FALSE(ct.Parse("").IsSuccess());
+ EXPECT_FALSE(ct.Parse(",").IsSuccess());
+ EXPECT_FALSE(ct.Parse("1,").IsSuccess());
+ EXPECT_FALSE(ct.Parse(",1").IsSuccess());
+ EXPECT_FALSE(ct.Parse("1a2").IsSuccess());
+ EXPECT_EQ(CmdlineResult::kOutOfRange, ct.Parse("1,10000000000000").GetStatus());
+ EXPECT_EQ(CmdlineResult::kOutOfRange, ct.Parse("-10000000000000,123").GetStatus());
+} // TEST_F
} // namespace art
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index dd9221d..6f784b3 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -405,6 +405,39 @@
static const char* Name() { return "ParseStringList<Separator>"; }
};
+template <>
+struct CmdlineType<std::vector<int32_t>> : CmdlineTypeParser<std::vector<int32_t>> {
+ using Result = CmdlineParseResult<std::vector<int32_t>>;
+
+ Result Parse(const std::string& args) {
+ std::vector<int32_t> list;
+ const char* pos = args.c_str();
+ errno = 0;
+
+ while (true) {
+ char* end = nullptr;
+ int64_t value = strtol(pos, &end, 10);
+ if (pos == end || errno == EINVAL) {
+ return Result::Failure("Failed to parse integer from " + args);
+ } else if ((errno == ERANGE) || // NOLINT [runtime/int] [4]
+ value < std::numeric_limits<int32_t>::min() ||
+ value > std::numeric_limits<int32_t>::max()) {
+ return Result::OutOfRange("Failed to parse integer from " + args + "; out of range");
+ }
+ list.push_back(static_cast<int32_t>(value));
+ if (*end == '\0') {
+ break;
+ } else if (*end != ',') {
+ return Result::Failure(std::string("Unexpected character: ") + *end);
+ }
+ pos = end + 1;
+ }
+ return Result::Success(std::move(list));
+ }
+
+ static const char* Name() { return "std::vector<int32_t>"; }
+};
+
static gc::CollectorType ParseCollectorType(const std::string& option) {
if (option == "MS" || option == "nonconcurrent") {
return gc::kCollectorTypeMS;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 3086882..a436b98 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -6227,12 +6227,20 @@
CheckValidReg(holder_reg.GetCode());
UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
temps.Exclude(ip0, ip1);
- // If base_reg differs from holder_reg, the offset was too large and we must have emitted
- // an explicit null check before the load. Otherwise, for implicit null checks, we need to
- // null-check the holder as we do not necessarily do that check before going to the thunk.
+ // In the case of a field load (with relaxed semantic), if `base_reg` differs from
+ // `holder_reg`, the offset was too large and we must have emitted (during the construction
+ // of the HIR graph, see `art::HInstructionBuilder::BuildInstanceFieldAccess`) and preserved
+ // (see `art::PrepareForRegisterAllocation::VisitNullCheck`) an explicit null check before
+ // the load. Otherwise, for implicit null checks, we need to null-check the holder as we do
+ // not necessarily do that check before going to the thunk.
+ //
+ // In the case of a field load with load-acquire semantics (where `base_reg` always differs
+ // from `holder_reg`), we also need an explicit null check when implicit null checks are
+ // allowed, as we do not emit one before going to the thunk.
vixl::aarch64::Label throw_npe_label;
vixl::aarch64::Label* throw_npe = nullptr;
- if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) {
+ if (GetCompilerOptions().GetImplicitNullChecks() &&
+ (holder_reg.Is(base_reg) || (kind == BakerReadBarrierKind::kAcquire))) {
throw_npe = &throw_npe_label;
__ Cbz(holder_reg.W(), throw_npe);
}
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 6469c69..507c453 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -9357,9 +9357,12 @@
BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
temps.Exclude(ip);
- // If base_reg differs from holder_reg, the offset was too large and we must have emitted
- // an explicit null check before the load. Otherwise, for implicit null checks, we need to
- // null-check the holder as we do not necessarily do that check before going to the thunk.
+ // In the case of a field load, if `base_reg` differs from
+ // `holder_reg`, the offset was too large and we must have emitted (during the construction
+ // of the HIR graph, see `art::HInstructionBuilder::BuildInstanceFieldAccess`) and preserved
+ // (see `art::PrepareForRegisterAllocation::VisitNullCheck`) an explicit null check before
+ // the load. Otherwise, for implicit null checks, we need to null-check the holder as we do
+ // not necessarily do that check before going to the thunk.
vixl32::Label throw_npe_label;
vixl32::Label* throw_npe = nullptr;
if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 278523e..9961608 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -29,11 +29,15 @@
#include <type_traits>
#include <vector>
-#if defined(__linux__) && defined(__arm__)
+#if defined(__linux__)
+#include <sched.h>
+#if defined(__arm__)
#include <sys/personality.h>
#include <sys/utsname.h>
+#endif // __arm__
#endif
+#include "android-base/parseint.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
@@ -222,6 +226,10 @@
UsageError(" host system.");
UsageError(" Example: -j12");
UsageError("");
+ UsageError(" --cpu-set=<set>: sets the cpu affinity to <set>. The <set> argument is a comma");
+ UsageError(" separated list of CPUs.");
+ UsageError(" Example: --cpu-set=0,1,2,3");
+ UsageError("");
UsageError(" --dex-file=<dex-file>: specifies a .dex, .jar, or .apk file to compile.");
UsageError(" Example: --dex-file=/system/framework/core.jar");
UsageError("");
@@ -500,6 +508,36 @@
exit(EXIT_FAILURE);
}
+
+// Set CPU affinity from a string containing a comma-separated list of numeric CPU identifiers.
+static void SetCpuAffinity(const std::vector<int32_t>& cpu_list) {
+#ifdef __linux__
+ int cpu_count = sysconf(_SC_NPROCESSORS_CONF);
+ cpu_set_t target_cpu_set;
+ CPU_ZERO(&target_cpu_set);
+
+ for (int32_t cpu : cpu_list) {
+ if (cpu >= 0 && cpu < cpu_count) {
+ CPU_SET(cpu, &target_cpu_set);
+ } else {
+ // Argument error is considered fatal, suggests misconfigured system properties.
+ Usage("Invalid cpu \"d\" specified in --cpu-set argument (nprocessors = %d)",
+ cpu, cpu_count);
+ }
+ }
+
+ if (sched_setaffinity(getpid(), sizeof(target_cpu_set), &target_cpu_set) == -1) {
+ // Failure to set affinity may be outside control of requestor, log warning rather than
+ // treating as fatal.
+ PLOG(WARNING) << "Failed to set CPU affinity.";
+ }
+#else
+ LOG(WARNING) << "--cpu-set not supported on this platform.";
+#endif // __linux__
+}
+
+
+
// The primary goal of the watchdog is to prevent stuck build servers
// during development when fatal aborts lead to a cascade of failures
// that result in a deadlock.
@@ -929,6 +967,10 @@
}
}
+ if (!cpu_set_.empty()) {
+ SetCpuAffinity(cpu_set_);
+ }
+
if (compiler_options_->inline_max_code_units_ == CompilerOptions::kUnsetInlineMaxCodeUnits) {
compiler_options_->inline_max_code_units_ = CompilerOptions::kDefaultInlineMaxCodeUnits;
}
@@ -1136,6 +1178,7 @@
AssignIfExists(args, M::Threads, &thread_count_);
AssignIfExists(args, M::ImageClasses, &image_classes_filename_);
AssignIfExists(args, M::ImageClassesZip, &image_classes_zip_filename_);
+ AssignIfExists(args, M::CpuSet, &cpu_set_);
AssignIfExists(args, M::Passes, &passes_to_run_filename_);
AssignIfExists(args, M::BootImage, &parser_options->boot_image_filename);
AssignIfExists(args, M::AndroidRoot, &android_root_);
@@ -2743,6 +2786,7 @@
std::unique_ptr<ClassLoaderContext> stored_class_loader_context_;
size_t thread_count_;
+ std::vector<int32_t> cpu_set_;
uint64_t start_ns_;
uint64_t start_cputime_ns_;
std::unique_ptr<WatchDog> watchdog_;
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index 4a19fb1..80c9a16 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -206,6 +206,9 @@
.Define("-j_")
.WithType<unsigned int>()
.IntoKey(M::Threads)
+ .Define("--cpu-set=_")
+ .WithType<std::vector<int32_t>>()
+ .IntoKey(M::CpuSet)
.Define("--android-root=_")
.WithType<std::string>()
.IntoKey(M::AndroidRoot)
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index 0717e1a..8201c3c 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -53,6 +53,7 @@
DEX2OAT_OPTIONS_KEY (bool, Watchdog)
DEX2OAT_OPTIONS_KEY (int, WatchdogTimeout)
DEX2OAT_OPTIONS_KEY (unsigned int, Threads)
+DEX2OAT_OPTIONS_KEY (std::vector<std::int32_t>, CpuSet)
DEX2OAT_OPTIONS_KEY (std::vector<std::string>, ImageFilenames)
DEX2OAT_OPTIONS_KEY (std::string, ImageClasses)
DEX2OAT_OPTIONS_KEY (std::string, ImageClassesZip)
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 34fd146..925f25e 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -1163,6 +1163,18 @@
return VerificationResult::kVerifies;
}
+// Returns true if absolute `path` ends with relative `suffix` starting at
+// a directory name boundary, i.e. after a '/'. For example, "foo/bar"
+// is a valid suffix of "/data/foo/bar" but not "/data-foo/bar".
+static inline bool AbsolutePathHasRelativeSuffix(const std::string& path,
+ const std::string& suffix) {
+ DCHECK(IsAbsoluteLocation(path));
+ DCHECK(!IsAbsoluteLocation(suffix));
+ return (path.size() > suffix.size()) &&
+ (path[path.size() - suffix.size() - 1u] == '/') &&
+ (std::string_view(path).substr(/*pos*/ path.size() - suffix.size()) == suffix);
+}
+
bool ClassLoaderContext::ClassLoaderInfoMatch(
const ClassLoaderInfo& info,
const ClassLoaderInfo& expected_info,
@@ -1197,40 +1209,34 @@
// and the other as an absolute one.
bool is_dex_name_absolute = IsAbsoluteLocation(info.classpath[k]);
bool is_expected_dex_name_absolute = IsAbsoluteLocation(expected_info.classpath[k]);
- std::string dex_name;
- std::string expected_dex_name;
+ bool dex_names_match = false;
+
if (is_dex_name_absolute == is_expected_dex_name_absolute) {
// If both locations are absolute or relative then compare them as they are.
// This is usually the case for: shared libraries and secondary dex files.
- dex_name = info.classpath[k];
- expected_dex_name = expected_info.classpath[k];
+ dex_names_match = (info.classpath[k] == expected_info.classpath[k]);
} else if (is_dex_name_absolute) {
// The runtime name is absolute but the compiled name (the expected one) is relative.
// This is the case for split apks which depend on base or on other splits.
- dex_name = info.classpath[k];
- OatFile::ResolveRelativeEncodedDexLocation(info.classpath[k].c_str(),
- expected_info.classpath[k],
- &expected_dex_name);
+ dex_names_match =
+ AbsolutePathHasRelativeSuffix(info.classpath[k], expected_info.classpath[k]);
} else if (is_expected_dex_name_absolute) {
// The runtime name is relative but the compiled name is absolute.
// There is no expected use case that would end up here as dex files are always loaded
// with their absolute location. However, be tolerant and do the best effort (in case
// there are unexpected new use case...).
- OatFile::ResolveRelativeEncodedDexLocation(expected_info.classpath[k].c_str(),
- info.classpath[k],
- &dex_name);
- expected_dex_name = expected_info.classpath[k];
+ dex_names_match =
+ AbsolutePathHasRelativeSuffix(expected_info.classpath[k], info.classpath[k]);
} else {
// Both locations are relative. In this case there's not much we can be sure about
// except that the names are the same. The checksum will ensure that the files are
// are same. This should not happen outside testing and manual invocations.
- dex_name = info.classpath[k];
- expected_dex_name = expected_info.classpath[k];
+ dex_names_match = (info.classpath[k] == expected_info.classpath[k]);
}
// Compare the locations.
- if (dex_name != expected_dex_name) {
+ if (!dex_names_match) {
LOG(WARNING) << "ClassLoaderContext classpath element mismatch"
<< ". expected=" << expected_info.classpath[k]
<< ", found=" << info.classpath[k]
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 0c36fd4..0083278 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -1402,7 +1402,18 @@
std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader);
- ASSERT_EQ(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")),
+ std::string context_with_no_base_dir = context->EncodeContextForOatFile("");
+ ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir),
+ ClassLoaderContext::VerificationResult::kVerifies);
+
+ std::string dex_location = GetTestDexFileName("MultiDex");
+ size_t pos = dex_location.rfind('/');
+ ASSERT_NE(std::string::npos, pos);
+ std::string parent = dex_location.substr(0, pos);
+
+ std::string context_with_base_dir = context->EncodeContextForOatFile(parent);
+ ASSERT_NE(context_with_base_dir, context_with_no_base_dir);
+ ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir),
ClassLoaderContext::VerificationResult::kVerifies);
}
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 3e3d199..4a4fac5 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1996,14 +1996,9 @@
// Step 0: Extra zygote work.
- // Step 0.a: If we're the zygote, mark boot.
- if (loader.IsZygote() && CanWriteToDalvikCache(image_isa)) {
- MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());
- }
-
loader.FindImageFiles();
- // Step 0.b: If we're the zygote, check for free space, and prune the cache preemptively,
+ // Step 0.a: If we're the zygote, check for free space, and prune the cache preemptively,
// if necessary. While the runtime may be fine (it is pretty tolerant to
// out-of-disk-space situations), other parts of the platform are not.
//
diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h
index 0eab35f..c491893 100644
--- a/runtime/gc/space/image_space_fs.h
+++ b/runtime/gc/space/image_space_fs.h
@@ -104,71 +104,6 @@
}
}
-// We write out an empty file to the zygote's ISA specific cache dir at the start of
-// every zygote boot and delete it when the boot completes. If we find a file already
-// present, it usually means the boot didn't complete. We wipe the entire dalvik
-// cache if that's the case.
-static void MarkZygoteStart(const InstructionSet isa, const uint32_t max_failed_boots) {
- const std::string isa_subdir = GetDalvikCache(GetInstructionSetString(isa));
- CHECK(!isa_subdir.empty()) << "Dalvik cache not found";
- const std::string boot_marker = isa_subdir + "/.booting";
- const char* file_name = boot_marker.c_str();
-
- uint32_t num_failed_boots = 0;
- std::unique_ptr<File> file(OS::OpenFileReadWrite(file_name));
- if (file.get() == nullptr) {
- file.reset(OS::CreateEmptyFile(file_name));
-
- if (file.get() == nullptr) {
- int saved_errno = errno;
- PLOG(WARNING) << "Failed to create boot marker.";
- if (saved_errno != ENOSPC) {
- return;
- }
-
- LOG(WARNING) << "Pruning dalvik cache because of low-memory situation.";
- impl::DeleteDirectoryContents(isa_subdir, false);
-
- // Try once more.
- file.reset(OS::OpenFileReadWrite(file_name));
- if (file == nullptr) {
- PLOG(WARNING) << "Failed to create boot marker.";
- return;
- }
- }
- } else {
- if (!file->ReadFully(&num_failed_boots, sizeof(num_failed_boots))) {
- PLOG(WARNING) << "Failed to read boot marker.";
- file->Erase();
- return;
- }
- }
-
- if (max_failed_boots != 0 && num_failed_boots > max_failed_boots) {
- LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache";
- impl::DeleteDirectoryContents(isa_subdir, false);
- }
-
- ++num_failed_boots;
- VLOG(startup) << "Number of failed boots on : " << boot_marker << " = " << num_failed_boots;
-
- if (lseek(file->Fd(), 0, SEEK_SET) == -1) {
- PLOG(WARNING) << "Failed to write boot marker.";
- file->Erase();
- return;
- }
-
- if (!file->WriteFully(&num_failed_boots, sizeof(num_failed_boots))) {
- PLOG(WARNING) << "Failed to write boot marker.";
- file->Erase();
- return;
- }
-
- if (file->FlushCloseOrErase() != 0) {
- PLOG(WARNING) << "Failed to flush boot marker.";
- }
-}
-
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index e3b95b9..1055057 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -342,10 +342,13 @@
// things not rendering correctly. E.g. b/16858794
static constexpr bool kWarnJniAbort = false;
+// Disable native JNI checking pending stack walk re-evaluation (b/136276414).
+static constexpr bool kNativeJniCheckEnabled = false;
+
template<typename T>
ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (IsWhitelistedNativeCaller()) {
+ if (kNativeJniCheckEnabled && IsWhitelistedNativeCaller()) {
return false;
}
return hiddenapi::ShouldDenyAccessToMember(
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 05015e3..54dae10 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1425,13 +1425,17 @@
// SetVdex will take ownership of the VdexFile.
SetVdex(vdex_file.release());
- // Create a dummy OatHeader.
+ // Create a dummy OatHeader with a key store containing only the compiler
+ // filter (it helps debugging and is required by
+ // OatHeader::GetCompilerFilter).
std::unique_ptr<const InstructionSetFeatures> isa_features =
InstructionSetFeatures::FromCppDefines();
+ SafeMap<std::string, std::string> store;
+ store.Put(OatHeader::kCompilerFilter, CompilerFilter::NameOfFilter(CompilerFilter::kVerify));
oat_header_.reset(OatHeader::Create(kRuntimeISA,
isa_features.get(),
dex_files.size(),
- nullptr));
+ &store));
const uint8_t* begin = reinterpret_cast<const uint8_t*>(oat_header_.get());
SetBegin(begin);
SetEnd(begin + oat_header_->GetHeaderSize());
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 70ed7c8..be0e30a 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1833,8 +1833,11 @@
group_name_field->GetObject(thread_group)->AsString();
group_name = (group_name_string != nullptr) ? group_name_string->ToModifiedUtf8() : "<null>";
}
+ } else if (thread != nullptr) {
+ priority = thread->GetNativePriority();
} else {
- priority = GetNativePriority();
+ PaletteStatus status = PaletteSchedGetPriority(tid, &priority);
+ CHECK(status == PaletteStatus::kOkay || status == PaletteStatus::kCheckErrno);
}
std::string scheduler_group_name(GetSchedulerGroupName(tid));
@@ -4266,15 +4269,13 @@
}
void Thread::SetNativePriority(int new_priority) {
- // ART tests on JVM can reach this code path, use tid = 0 as shorthand for current thread.
- PaletteStatus status = PaletteSchedSetPriority(0, new_priority);
+ PaletteStatus status = PaletteSchedSetPriority(GetTid(), new_priority);
CHECK(status == PaletteStatus::kOkay || status == PaletteStatus::kCheckErrno);
}
-int Thread::GetNativePriority() {
+int Thread::GetNativePriority() const {
int priority = 0;
- // ART tests on JVM can reach this code path, use tid = 0 as shorthand for current thread.
- PaletteStatus status = PaletteSchedGetPriority(0, &priority);
+ PaletteStatus status = PaletteSchedGetPriority(GetTid(), &priority);
CHECK(status == PaletteStatus::kOkay || status == PaletteStatus::kCheckErrno);
return priority;
}
diff --git a/runtime/thread.h b/runtime/thread.h
index dd483c1..ae04600 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -375,12 +375,12 @@
void SetNativePriority(int newPriority);
/*
- * Returns the thread priority for the current thread by querying the system.
+ * Returns the priority of this thread by querying the system.
* This is useful when attaching a thread through JNI.
*
* Returns a value from 1 to 10 (compatible with java.lang.Thread values).
*/
- static int GetNativePriority();
+ int GetNativePriority() const;
// Guaranteed to be non-zero.
uint32_t GetThreadId() const {
diff --git a/test/1004-checker-volatile-ref-load/expected.txt b/test/1004-checker-volatile-ref-load/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/1004-checker-volatile-ref-load/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/1004-checker-volatile-ref-load/info.txt b/test/1004-checker-volatile-ref-load/info.txt
new file mode 100644
index 0000000..1ba4a52
--- /dev/null
+++ b/test/1004-checker-volatile-ref-load/info.txt
@@ -0,0 +1,6 @@
+Regression test for b/140507091: Check that an explicit null check is
+emitted for a volatile field load on ARM64 at the beginning of the
+Baker read barrier thunk, so that a null holder object is properly
+caught (and throws a NullPointerException as expected), instead of
+trigerring a SIGSEGV, when the Concurrent Copying GC is in the marking
+phase.
diff --git a/test/1004-checker-volatile-ref-load/run b/test/1004-checker-volatile-ref-load/run
new file mode 100644
index 0000000..bdaa71b
--- /dev/null
+++ b/test/1004-checker-volatile-ref-load/run
@@ -0,0 +1,18 @@
+#! /bin/bash
+#
+# Copyright (C) 2019 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.
+
+# Limit the managed heap to 16 MiB to force more garbage collections.
+exec ${RUN} $@ --runtime-option -Xmx16m
diff --git a/test/1004-checker-volatile-ref-load/src/Main.java b/test/1004-checker-volatile-ref-load/src/Main.java
new file mode 100644
index 0000000..9542f36
--- /dev/null
+++ b/test/1004-checker-volatile-ref-load/src/Main.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+class Foo {
+ volatile Object bar;
+}
+
+public class Main {
+
+ public static void main(String[] args) {
+ Main main = new Main();
+ main.test();
+ System.out.println("passed");
+ }
+
+ // Check that no explicit null check is emitted for the field load of volatile
+ // field `Foo.bar` before entering the Baker read barrier thunk.
+ //
+ // Note: We cannot check the ARM64 assembly code of the Baker read barrier
+ // thunk code, as it is not emitted in the CFG output.
+ //
+ /// CHECK-START-ARM64: void Main.test() disassembly (after)
+ /// CHECK: <<Foo:l\d+>> InstanceFieldGet [{{l\d+}}] field_name:Main.foo field_type:Reference loop:<<Loop:B\d+>>
+ /// CHECK: NullCheck [<<Foo>>] dex_pc:<<PC:\d+>> loop:<<Loop>>
+ /// CHECK-NEXT: InstanceFieldGet [<<Foo>>] dex_pc:<<PC>> field_name:Foo.bar field_type:Reference loop:<<Loop>>
+ /// CHECK-NEXT: add w<<BaseRegNum:\d+>>, {{w\d+}}, #0x8 (8)
+ /// CHECK-NEXT: adr lr, #+0x{{c|10}}
+ // The following instruction (generated by
+ // `art::arm64::CodeGeneratorARM64::EmitBakerReadBarrierCbnz`) checks the
+ // Marking Register (X20) and goes into the Baker read barrier thunk if MR is
+ // not null. The null offset (#+0x0) in the CBNZ instruction is a placeholder
+ // for the offset to the Baker read barrier thunk (which is not yet set when
+ // the CFG output is emitted).
+ /// CHECK-NEXT: cbnz x20, #+0x0
+ /// CHECK-NEXT: ldar {{w\d+}}, [x<<BaseRegNum>>]
+
+ public void test() {
+ // Continually check that reading field `foo.bar` throws a
+ // NullPointerException while allocating over 64 MiB of memory (with heap
+ // size limited to 16 MiB), in order to increase memory pressure and
+ // eventually trigger a concurrent garbage collection, which will start by
+ // putting the GC in marking mode and enable read barriers (when the
+ // Concurrent Copying collector is used).
+ for (int i = 0; i != 64 * 1024; ++i) {
+ allocateAtLeast1KiB();
+ try {
+ // Read volatile field `bar` of `foo`, which is null, and is expected
+ // to produce a NullPointerException. On ARM64, this is implemented as a
+ // load-acquire (LDAR instruction).
+ //
+ // When the Concurrent Copying GC is marking, read barriers are enabled
+ // and the field load executes code from a Baker read barrier thunk.
+ // On ARM64, there used to be a bug in this thunk for the load-acquire
+ // case, where an explicit null check was missing, triggering an
+ // unhandled SIGSEGV when trying to load the lock word from the volatile
+ // field (b/140507091).
+ Object foo_bar = foo.bar;
+ } catch (NullPointerException e) {
+ continue;
+ }
+ // We should not be here.
+ throw new Error("Expected NullPointerException");
+ }
+ }
+
+ // Allocate at least 1 KiB of memory on the managed heap.
+ // Retain some allocated memory and release old allocations so that the
+ // garbage collector has something to do.
+ public static void allocateAtLeast1KiB() {
+ memory[allocationIndex] = new Object[1024 / 4];
+ ++allocationIndex;
+ if (allocationIndex == memory.length) {
+ allocationIndex = 0;
+ }
+ }
+
+ public static Object[] memory = new Object[1024];
+ public static int allocationIndex = 0;
+
+ private Foo foo;
+
+}
diff --git a/test/692-vdex-inmem-loader/src/Main.java b/test/692-vdex-inmem-loader/src/Main.java
index 53f4c36..3ebe2c1 100644
--- a/test/692-vdex-inmem-loader/src/Main.java
+++ b/test/692-vdex-inmem-loader/src/Main.java
@@ -57,6 +57,13 @@
if (invokeMethod) {
loader.loadClass("art.ClassB").getDeclaredMethod("printHello").invoke(null);
+
+ if (expectedBackedByOat) {
+ String filter = getCompilerFilter(loader.loadClass("art.ClassB"));
+ if (!("verify".equals(filter))) {
+ throw new Error("Expected verify, got " + filter);
+ }
+ }
}
}
@@ -118,6 +125,7 @@
private static native boolean hasVdexFile(ClassLoader loader);
private static native boolean isBackedByOatFile(ClassLoader loader);
private static native boolean areClassesPreverified(ClassLoader loader);
+ private static native String getCompilerFilter(Class cls);
// Defined in 674-hiddenapi.
private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
diff --git a/test/720-thread-priority/expected.txt b/test/720-thread-priority/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/720-thread-priority/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/720-thread-priority/info.txt b/test/720-thread-priority/info.txt
new file mode 100644
index 0000000..0122261
--- /dev/null
+++ b/test/720-thread-priority/info.txt
@@ -0,0 +1,2 @@
+Regression test for Thread.setPriority, which used to always set the priority
+to the main thread, instead of the passed one.
diff --git a/test/720-thread-priority/src/Main.java b/test/720-thread-priority/src/Main.java
new file mode 100644
index 0000000..9ea5cfc
--- /dev/null
+++ b/test/720-thread-priority/src/Main.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class Main {
+ static final CountDownLatch processStarted = new CountDownLatch(1);
+ static final CountDownLatch prioritySet = new CountDownLatch(1);
+
+ static int initialPlatformPriority = 0;
+ static int maxPlatformPriority = 0;
+
+ static class MyThread extends Thread {
+ public void run() {
+ try {
+ int priority = getThreadPlatformPriority();
+ if (priority != initialPlatformPriority) {
+ throw new Error("Expected " + initialPlatformPriority + ", got " + priority);
+ }
+ processStarted.countDown();
+ prioritySet.await();
+ priority = getThreadPlatformPriority();
+ if (priority != maxPlatformPriority) {
+ throw new Error("Expected " + maxPlatformPriority + ", got " + priority);
+ }
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ // Fetch priorities from the main thread to know what to compare against.
+ int javaPriority = Thread.currentThread().getPriority();
+ initialPlatformPriority = getThreadPlatformPriority();
+ Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
+ maxPlatformPriority = getThreadPlatformPriority();
+ Thread.currentThread().setPriority(javaPriority);
+
+ MyThread t1 = new MyThread();
+ t1.start();
+ processStarted.await();
+
+ t1.setPriority(Thread.MAX_PRIORITY);
+ prioritySet.countDown();
+ t1.join();
+ }
+
+ private static native int getThreadPlatformPriority();
+}
diff --git a/test/720-thread-priority/thread_priority.cc b/test/720-thread-priority/thread_priority.cc
new file mode 100644
index 0000000..db4a2b2
--- /dev/null
+++ b/test/720-thread-priority/thread_priority.cc
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 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 <sys/time.h>
+#include <sys/resource.h>
+
+#include "base/macros.h"
+#include "base/utils.h"
+#include "jni.h"
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getThreadPlatformPriority(
+ JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass clazz ATTRIBUTE_UNUSED) {
+ return getpriority(PRIO_PROCESS, art::GetTid());
+}
diff --git a/test/911-get-stack-trace/src/art/PrintThread.java b/test/911-get-stack-trace/src/art/PrintThread.java
index 798db06..94f3a33 100644
--- a/test/911-get-stack-trace/src/art/PrintThread.java
+++ b/test/911-get-stack-trace/src/art/PrintThread.java
@@ -42,7 +42,7 @@
// may not exist depending on the environment.
public final static String IGNORE_THREAD_NAME_REGEX =
"Binder:|RenderThread|hwuiTask|Jit thread pool worker|Instr:|JDWP|Profile Saver|main|" +
- "queued-work-looper|InstrumentationConnectionThread|intel_svc_streamer_thread";
+ "queued-work-looper|InstrumentationConnectionThread|intel_svc_streamer_thread|ForkJoinPool";
public final static Matcher IGNORE_THREADS =
Pattern.compile(IGNORE_THREAD_NAME_REGEX).matcher("");
diff --git a/test/Android.bp b/test/Android.bp
index 9cf5606..cb9f612 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -496,6 +496,7 @@
"674-hiddenapi/hiddenapi.cc",
"692-vdex-inmem-loader/vdex_inmem_loader.cc",
"708-jit-cache-churn/jit.cc",
+ "720-thread-priority/thread_priority.cc",
"800-smali/jni.cc",
"909-attach-agent/disallow_debugging.cc",
"1001-app-image-regions/app_image_regions.cc",
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index a0b2f1e..ba2d46e 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -66,6 +66,24 @@
return (oat_dex_file != nullptr) ? JNI_TRUE : JNI_FALSE;
}
+extern "C" JNIEXPORT jobject JNICALL Java_Main_getCompilerFilter(JNIEnv* env,
+ jclass caller ATTRIBUTE_UNUSED,
+ jclass cls) {
+ ScopedObjectAccess soa(env);
+
+ ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
+ const DexFile& dex_file = klass->GetDexFile();
+ const OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
+ if (oat_dex_file == nullptr) {
+ return nullptr;
+ }
+
+ std::string filter =
+ CompilerFilter::NameOfFilter(oat_dex_file->GetOatFile()->GetCompilerFilter());
+ return soa.AddLocalReference<jobject>(
+ mirror::String::AllocFromModifiedUtf8(soa.Self(), filter.c_str()));
+}
+
// public static native boolean runtimeIsSoftFail();
extern "C" JNIEXPORT jboolean JNICALL Java_Main_runtimeIsSoftFail(JNIEnv* env ATTRIBUTE_UNUSED,
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 0bff4c2..9e57764 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1239,6 +1239,19 @@
"description": [ "Working as intended tests that don't pass with baseline." ]
},
{
+ "tests": ["1004-checker-volatile-ref-load"],
+ "env_vars": {"ART_USE_READ_BARRIER": "false"},
+ "bug": "b/140507091",
+ "description": ["Test containing Checker assertions expecting Baker read barriers."]
+ },
+ {
+ "tests": ["1004-checker-volatile-ref-load"],
+ "env_vars": {"ART_READ_BARRIER_TYPE": "TABLELOOKUP"},
+ "bug": "b/140507091",
+ "description": ["Test containing Checker assertions expecting Baker read barriers."]
+ },
+ {
+
"tests": ["570-checker-osr-locals"],
"variant": "jvm",
"bug": "b/154802847",