Merge "ART: More refactoring of invoke-custom tests"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 4a35ccf..388ff06 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -339,6 +339,7 @@
art_profman_tests \
art_runtime_tests \
art_runtime_compiler_tests \
+ art_sigchain_tests \
ART_TARGET_GTEST_FILES := $(foreach m,$(ART_TEST_MODULES),\
$(ART_TEST_LIST_device_$(TARGET_ARCH)_$(m)))
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index a6681ec..8a604db 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2678,7 +2678,7 @@
}
if (GetCompilerOptions().IsBootImage()) {
// Prune garbage objects created during aborted transactions.
- Runtime::Current()->GetHeap()->CollectGarbage(true);
+ Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references */ true);
}
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 37d1317..3732b17 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1748,7 +1748,7 @@
soa.Self(),
soa.Decode<mirror::ClassLoader>(class_loader));
soa.Env()->GetVm()->DeleteGlobalRef(soa.Self(), class_loader);
- runtime_->GetHeap()->CollectGarbage(/*clear_soft_references*/ true);
+ runtime_->GetHeap()->CollectGarbage(/* clear_soft_references */ true);
ObjPtr<mirror::ClassLoader> decoded_weak = soa.Decode<mirror::ClassLoader>(weak_class_loader);
if (decoded_weak != nullptr) {
LOG(FATAL) << "Failed to unload class loader, path from root set: "
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 42d2280..a2ba816 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -149,7 +149,7 @@
ComputeLazyFieldsForImageClasses(); // Add useful information
}
}
- heap->CollectGarbage(false); // Remove garbage.
+ heap->CollectGarbage(/* clear_soft_references */ false); // Remove garbage.
if (kIsDebugBuild) {
ScopedObjectAccess soa(Thread::Current());
diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc
index 3c87fe2..975d194 100644
--- a/openjdkjvm/OpenjdkJvm.cc
+++ b/openjdkjvm/OpenjdkJvm.cc
@@ -311,7 +311,7 @@
LOG(INFO) << "Explicit GC skipped.";
return;
}
- art::Runtime::Current()->GetHeap()->CollectGarbage(false);
+ art::Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references */ false);
}
JNIEXPORT __attribute__((noreturn)) void JVM_Exit(jint status) {
diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc
index 3397210..aaa9ab4 100644
--- a/openjdkjvmti/ti_heap.cc
+++ b/openjdkjvmti/ti_heap.cc
@@ -1383,7 +1383,7 @@
}
jvmtiError HeapUtil::ForceGarbageCollection(jvmtiEnv* env ATTRIBUTE_UNUSED) {
- art::Runtime::Current()->GetHeap()->CollectGarbage(false);
+ art::Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references */ false);
return ERR(NONE);
}
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index 9e9cb16..42c9a84 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -54,6 +54,7 @@
"cortex-a35",
"exynos-m1",
"exynos-m2",
+ "exynos-m3",
"denver64",
"kryo"
};
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 8a24daa..3d2226c 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -763,7 +763,7 @@
#if ART_USE_FUTEXES
void ReaderWriterMutex::HandleSharedLockContention(Thread* self, int32_t cur_state) {
// Owner holds it exclusively, hang up.
- ScopedContentionRecorder scr(this, GetExclusiveOwnerTid(), SafeGetTid(self));
+ ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid());
++num_pending_readers_;
if (UNLIKELY(should_respond_to_empty_checkpoint_request_)) {
self->CheckEmptyCheckpointFromMutex();
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 602e094..5066385 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -949,7 +949,7 @@
JDWP::JdwpError Dbg::GetInstanceCounts(const std::vector<JDWP::RefTypeId>& class_ids,
std::vector<uint64_t>* counts) {
gc::Heap* heap = Runtime::Current()->GetHeap();
- heap->CollectGarbage(false, gc::GcCause::kGcCauseDebugger);
+ heap->CollectGarbage(/* clear_soft_references */ false, gc::GcCause::kGcCauseDebugger);
VariableSizedHandleScope hs(Thread::Current());
std::vector<Handle<mirror::Class>> classes;
counts->clear();
@@ -970,7 +970,7 @@
std::vector<JDWP::ObjectId>* instances) {
gc::Heap* heap = Runtime::Current()->GetHeap();
// We only want reachable instances, so do a GC.
- heap->CollectGarbage(false, gc::GcCause::kGcCauseDebugger);
+ heap->CollectGarbage(/* clear_soft_references */ false, gc::GcCause::kGcCauseDebugger);
JDWP::JdwpError error;
ObjPtr<mirror::Class> c = DecodeClass(class_id, &error);
if (c == nullptr) {
@@ -992,7 +992,7 @@
JDWP::JdwpError Dbg::GetReferringObjects(JDWP::ObjectId object_id, int32_t max_count,
std::vector<JDWP::ObjectId>* referring_objects) {
gc::Heap* heap = Runtime::Current()->GetHeap();
- heap->CollectGarbage(false, gc::GcCause::kGcCauseDebugger);
+ heap->CollectGarbage(/* clear_soft_references */ false, gc::GcCause::kGcCauseDebugger);
JDWP::JdwpError error;
ObjPtr<mirror::Object> o = gRegistry->Get<mirror::Object*>(object_id, &error);
if (o == nullptr) {
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index 2ee4239..a1d1986 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -289,7 +289,7 @@
return;
}
- // Wait for GC's sweeping to complete and allow new records
+ // Wait for GC's sweeping to complete and allow new records.
while (UNLIKELY((!kUseReadBarrier && !allow_new_record_) ||
(kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 3770085..7304697 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -1414,7 +1414,8 @@
MarkStackMode mark_stack_mode = mark_stack_mode_.LoadRelaxed();
if (mark_stack_mode == kMarkStackModeThreadLocal) {
// Process the thread-local mark stacks and the GC mark stack.
- count += ProcessThreadLocalMarkStacks(false, nullptr);
+ count += ProcessThreadLocalMarkStacks(/* disable_weak_ref_access */ false,
+ /* checkpoint_callback */ nullptr);
while (!gc_mark_stack_->IsEmpty()) {
mirror::Object* to_ref = gc_mark_stack_->PopBack();
ProcessMarkStackRef(to_ref);
@@ -1602,7 +1603,7 @@
DisableWeakRefAccessCallback dwrac(this);
// Process the thread local mark stacks one last time after switching to the shared mark stack
// mode and disable weak ref accesses.
- ProcessThreadLocalMarkStacks(true, &dwrac);
+ ProcessThreadLocalMarkStacks(/* disable_weak_ref_access */ true, &dwrac);
if (kVerboseMode) {
LOG(INFO) << "Switched to shared mark stack mode and disabled weak ref access";
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index cb43601..19b4acd 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3931,7 +3931,7 @@
if (new_backtrace) {
StackHandleScope<1> hs(self);
auto h = hs.NewHandleWrapper(obj);
- CollectGarbage(false);
+ CollectGarbage(/* clear_soft_references */ false);
unique_backtrace_count_.FetchAndAddSequentiallyConsistent(1);
} else {
seen_backtrace_count_.FetchAndAddSequentiallyConsistent(1);
diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc
index 6d426c2..2def524 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -59,7 +59,7 @@
}
}
}
- Runtime::Current()->GetHeap()->CollectGarbage(false);
+ Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references */ false);
}
TEST_F(HeapTest, HeapBitmapCapacityTest) {
diff --git a/runtime/gc/system_weak_test.cc b/runtime/gc/system_weak_test.cc
index dfbbd2a..21f5117 100644
--- a/runtime/gc/system_weak_test.cc
+++ b/runtime/gc/system_weak_test.cc
@@ -142,7 +142,7 @@
cswh.Set(GcRoot<mirror::Object>(s.Get()));
// Trigger a GC.
- Runtime::Current()->GetHeap()->CollectGarbage(false);
+ Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references */ false);
// Expect the holder to have been called.
EXPECT_EQ(CollectorDoesAllowOrBroadcast() ? 1U : 0U, cswh.allow_count_);
@@ -163,7 +163,7 @@
cswh.Set(GcRoot<mirror::Object>(mirror::String::AllocFromModifiedUtf8(soa.Self(), "ABC")));
// Trigger a GC.
- Runtime::Current()->GetHeap()->CollectGarbage(false);
+ Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references */ false);
// Expect the holder to have been called.
EXPECT_EQ(CollectorDoesAllowOrBroadcast() ? 1U : 0U, cswh.allow_count_);
@@ -187,7 +187,7 @@
cswh.Set(GcRoot<mirror::Object>(s.Get()));
// Trigger a GC.
- Runtime::Current()->GetHeap()->CollectGarbage(false);
+ Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references */ false);
// Expect the holder to have been called.
ASSERT_EQ(CollectorDoesAllowOrBroadcast() ? 1U : 0U, cswh.allow_count_);
@@ -202,7 +202,7 @@
Runtime::Current()->RemoveSystemWeakHolder(&cswh);
// Trigger another GC.
- Runtime::Current()->GetHeap()->CollectGarbage(false);
+ Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references */ false);
// Expectation: no change in the numbers.
EXPECT_EQ(CollectorDoesAllowOrBroadcast() ? 1U : 0U, cswh.allow_count_);
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 0ae6dbf..b6055cb 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -209,7 +209,9 @@
: StackVisitor(thread_in, context, kInstrumentationStackWalk),
instrumentation_stack_(thread_in->GetInstrumentationStack()),
instrumentation_exit_pc_(instrumentation_exit_pc),
- reached_existing_instrumentation_frames_(false), instrumentation_stack_depth_(0),
+ reached_existing_instrumentation_frames_(false),
+ should_be_at_top_(false),
+ instrumentation_stack_depth_(0),
last_return_pc_(0) {
}
@@ -233,6 +235,20 @@
return true; // Continue.
}
uintptr_t return_pc = GetReturnPc();
+ if (UNLIKELY(should_be_at_top_)) {
+ std::string thread_name;
+ GetThread()->GetThreadName(thread_name);
+ uint32_t dex_pc = dex::kDexNoIndex;
+ if (last_return_pc_ != 0 &&
+ GetCurrentOatQuickMethodHeader() != nullptr) {
+ dex_pc = GetCurrentOatQuickMethodHeader()->ToDexPc(m, last_return_pc_);
+ }
+ LOG(FATAL) << "While walking " << thread_name << " Reached unexpected frame above what "
+ << "should have been top. Method is " << GetMethod()->PrettyMethod()
+ << " return_pc: " << std::hex << return_pc
+ << " dex pc: " << dex_pc;
+ UNREACHABLE();
+ }
if (kVerboseInstrumentation) {
LOG(INFO) << " Installing exit stub in " << DescribeLocation();
}
@@ -267,22 +283,21 @@
if (kVerboseInstrumentation) {
LOG(INFO) << "Ignoring already instrumented " << frame.Dump();
}
+ } else if (UNLIKELY(reached_existing_instrumentation_frames_)) {
+ // If tracing was enabled we might have had all methods have the instrumentation frame
+ // except the runtime transition method at the very top of the stack. This isn't really a
+ // problem since the transition method just goes back into the runtime and never leaves it
+ // so it can be ignored.
+ should_be_at_top_ = true;
+ DCHECK(m->IsRuntimeMethod()) << "Expected method to be runtime method at start of thread "
+ << "but was " << m->PrettyMethod();
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "reached expected top frame " << m->PrettyMethod();
+ }
+ // Don't bother continuing on the upcalls on non-debug builds.
+ return kIsDebugBuild ? true : false;
} else {
CHECK_NE(return_pc, 0U);
- if (UNLIKELY(reached_existing_instrumentation_frames_)) {
- std::string thread_name;
- GetThread()->GetThreadName(thread_name);
- uint32_t dex_pc = dex::kDexNoIndex;
- if (last_return_pc_ != 0 &&
- GetCurrentOatQuickMethodHeader() != nullptr) {
- dex_pc = GetCurrentOatQuickMethodHeader()->ToDexPc(m, last_return_pc_);
- }
- LOG(FATAL) << "While walking " << thread_name << " found existing instrumentation frames."
- << " method is " << GetMethod()->PrettyMethod()
- << " return_pc is " << std::hex << return_pc
- << " dex pc: " << dex_pc;
- UNREACHABLE();
- }
InstrumentationStackFrame instrumentation_frame(
m->IsRuntimeMethod() ? nullptr : GetThisObject(),
m,
@@ -320,6 +335,7 @@
std::vector<uint32_t> dex_pcs_;
const uintptr_t instrumentation_exit_pc_;
bool reached_existing_instrumentation_frames_;
+ bool should_be_at_top_;
size_t instrumentation_stack_depth_;
uintptr_t last_return_pc_;
};
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 6300038..293e18a 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -1484,7 +1484,8 @@
jweak weak_global = env_->NewWeakGlobalRef(local_ref);
ASSERT_NE(weak_global, nullptr);
env_->DeleteLocalRef(local_ref);
- Runtime::Current()->GetHeap()->CollectGarbage(false); // GC should clear the weak global.
+ // GC should clear the weak global.
+ Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references */ false);
jobject new_global_ref = env_->NewGlobalRef(weak_global);
EXPECT_EQ(new_global_ref, nullptr);
jobject new_local_ref = env_->NewLocalRef(weak_global);
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index d9c4da9..9c3afbb 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -207,7 +207,7 @@
void SignalCatcher::HandleSigUsr1() {
LOG(INFO) << "SIGUSR1 forcing GC (no HPROF) and profile save";
- Runtime::Current()->GetHeap()->CollectGarbage(false);
+ Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references */ false);
ProfileSaver::ForceProcessProfiles();
}
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 7657fa8..895c1a4 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -68,7 +68,7 @@
bool Resume(Thread* thread, SuspendReason reason = SuspendReason::kInternal)
REQUIRES(!Locks::thread_suspend_count_lock_) WARN_UNUSED;
- // Suspends all threads and gets exclusive access to the mutator_lock_.
+ // Suspends all threads and gets exclusive access to the mutator lock.
// If long_suspend is true, then other threads who try to suspend will never timeout.
// long_suspend is currenly used for hprof since large heaps take a long time.
void SuspendAll(const char* cause, bool long_suspend = false)
@@ -240,7 +240,7 @@
DISALLOW_COPY_AND_ASSIGN(ThreadList);
};
-// Helper for suspending all threads and
+// Helper for suspending all threads and getting exclusive access to the mutator lock.
class ScopedSuspendAll : public ValueObject {
public:
explicit ScopedSuspendAll(const char* cause, bool long_suspend = false)
diff --git a/runtime/trace.cc b/runtime/trace.cc
index bdc6757..d97dcb5 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -303,6 +303,12 @@
}
}
{
+ // Avoid a deadlock between a thread doing garbage collection
+ // and the profile sampling thread, by blocking GC when sampling
+ // thread stacks (see b/73624630).
+ gc::ScopedGCCriticalSection gcs(self,
+ art::gc::kGcCauseInstrumentation,
+ art::gc::kCollectorTypeInstrumentation);
ScopedSuspendAll ssa(__FUNCTION__);
MutexLock mu(self, *Locks::thread_list_lock_);
runtime->GetThreadList()->ForEach(GetSample, the_trace);
diff --git a/sigchainlib/Android.bp b/sigchainlib/Android.bp
index 7aab726..1f490cd 100644
--- a/sigchainlib/Android.bp
+++ b/sigchainlib/Android.bp
@@ -16,15 +16,24 @@
cc_library {
name: "libsigchain",
+ cpp_std: "gnu++17",
+
host_supported: true,
defaults: ["art_defaults"],
- shared: {
- srcs: ["sigchain_dummy.cc"],
- },
- static: {
- srcs: ["sigchain.cc"],
- },
target: {
+ linux: {
+ shared: {
+ srcs: ["sigchain_dummy.cc"],
+ },
+ static: {
+ srcs: ["sigchain.cc"],
+ },
+ },
+
+ darwin: {
+ srcs: ["sigchain_dummy.cc"],
+ },
+
android: {
shared_libs: ["liblog"],
},
@@ -51,3 +60,12 @@
},
},
}
+
+art_cc_test {
+ name: "art_sigchain_tests",
+ defaults: [
+ "art_gtest_defaults",
+ ],
+ srcs: ["sigchain_test.cc"],
+ whole_static_libs: ["libsigchain"],
+}
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index b8ab51b..61346b1 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -29,8 +29,10 @@
#include <stdlib.h>
#include <string.h>
+#include <algorithm>
#include <initializer_list>
#include <mutex>
+#include <type_traits>
#include <utility>
#include "sigchain.h"
@@ -78,9 +80,28 @@
#define fatal(...) log(__VA_ARGS__); abort()
-static int sigorset(sigset_t* dest, sigset_t* left, sigset_t* right) {
+#if defined(__BIONIC__) && !defined(__LP64__)
+static int sigismember(const sigset64_t* sigset, int signum) {
+ return sigismember64(sigset, signum);
+}
+
+static int sigemptyset(sigset64_t* sigset) {
+ return sigemptyset64(sigset);
+}
+
+static int sigaddset(sigset64_t* sigset, int signum) {
+ return sigaddset64(sigset, signum);
+}
+
+static int sigdelset(sigset64_t* sigset, int signum) {
+ return sigdelset64(sigset, signum);
+}
+#endif
+
+template<typename SigsetType>
+static int sigorset(SigsetType* dest, SigsetType* left, SigsetType* right) {
sigemptyset(dest);
- for (size_t i = 0; i < sizeof(sigset_t) * CHAR_BIT; ++i) {
+ for (size_t i = 0; i < sizeof(SigsetType) * CHAR_BIT; ++i) {
if (sigismember(left, i) == 1 || sigismember(right, i) == 1) {
sigaddset(dest, i);
}
@@ -93,35 +114,36 @@
static decltype(&sigaction) linked_sigaction;
static decltype(&sigprocmask) linked_sigprocmask;
+#if defined(__BIONIC__)
+static decltype(&sigaction64) linked_sigaction64;
+static decltype(&sigprocmask64) linked_sigprocmask64;
+#endif
+
+template<typename T>
+static void lookup_next_symbol(T* output, T wrapper, const char* name) {
+ void* sym = dlsym(RTLD_NEXT, name);
+ if (sym == nullptr) {
+ sym = dlsym(RTLD_DEFAULT, name);
+ if (sym == wrapper || sym == sigaction) {
+ fatal("Unable to find next %s in signal chain", name);
+ }
+ }
+ *output = reinterpret_cast<T>(sym);
+}
+
__attribute__((constructor)) static void InitializeSignalChain() {
static std::once_flag once;
std::call_once(once, []() {
- void* linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
- if (linked_sigaction_sym == nullptr) {
- linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
- if (linked_sigaction_sym == nullptr ||
- linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
- fatal("Unable to find next sigaction in signal chain");
- }
- }
+ lookup_next_symbol(&linked_sigaction, sigaction, "sigaction");
+ lookup_next_symbol(&linked_sigprocmask, sigprocmask, "sigprocmask");
- void* linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
- if (linked_sigprocmask_sym == nullptr) {
- linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
- if (linked_sigprocmask_sym == nullptr ||
- linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
- fatal("Unable to find next sigprocmask in signal chain");
- }
- }
-
- linked_sigaction =
- reinterpret_cast<decltype(linked_sigaction)>(linked_sigaction_sym);
- linked_sigprocmask =
- reinterpret_cast<decltype(linked_sigprocmask)>(linked_sigprocmask_sym);
+#if defined(__BIONIC__)
+ lookup_next_symbol(&linked_sigaction64, sigaction64, "sigaction64");
+ lookup_next_symbol(&linked_sigprocmask64, sigprocmask64, "sigprocmask64");
+#endif
});
}
-
static pthread_key_t GetHandlingSignalKey() {
static pthread_key_t key;
static std::once_flag once;
@@ -175,19 +197,51 @@
// Register the signal chain with the kernel if needed.
void Register(int signo) {
+#if defined(__BIONIC__)
+ struct sigaction64 handler_action = {};
+ sigfillset64(&handler_action.sa_mask);
+#else
struct sigaction handler_action = {};
+ sigfillset(&handler_action.sa_mask);
+#endif
+
handler_action.sa_sigaction = SignalChain::Handler;
handler_action.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
- sigfillset(&handler_action.sa_mask);
+
+#if defined(__BIONIC__)
+ linked_sigaction64(signo, &handler_action, &action_);
+#else
linked_sigaction(signo, &handler_action, &action_);
+#endif
}
- void SetAction(const struct sigaction* action) {
- action_ = *action;
+ template <typename SigactionType>
+ SigactionType GetAction() {
+ if constexpr (std::is_same_v<decltype(action_), SigactionType>) {
+ return action_;
+ } else {
+ SigactionType result;
+ result.sa_flags = action_.sa_flags;
+ result.sa_handler = action_.sa_handler;
+ result.sa_restorer = action_.sa_restorer;
+ memcpy(&result.sa_mask, &action_.sa_mask,
+ std::min(sizeof(action_.sa_mask), sizeof(result.sa_mask)));
+ return result;
+ }
}
- struct sigaction GetAction() {
- return action_;
+ template <typename SigactionType>
+ void SetAction(const SigactionType* new_action) {
+ if constexpr (std::is_same_v<decltype(action_), SigactionType>) {
+ action_ = *new_action;
+ } else {
+ action_.sa_flags = new_action->sa_flags;
+ action_.sa_handler = new_action->sa_handler;
+ action_.sa_restorer = new_action->sa_restorer;
+ sigemptyset(&action_.sa_mask);
+ memcpy(&action_.sa_mask, &new_action->sa_mask,
+ std::min(sizeof(action_.sa_mask), sizeof(new_action->sa_mask)));
+ }
}
void AddSpecialHandler(SigchainAction* sa) {
@@ -222,11 +276,17 @@
private:
bool claimed_;
+#if defined(__BIONIC__)
+ struct sigaction64 action_;
+#else
struct sigaction action_;
+#endif
SigchainAction special_handlers_[2];
};
-static SignalChain chains[_NSIG];
+// _NSIG is 1 greater than the highest valued signal, but signals start from 1.
+// Leave an empty element at index 0 for convenience.
+static SignalChain chains[_NSIG + 1];
void SignalChain::Handler(int signo, siginfo_t* siginfo, void* ucontext_raw) {
// Try the special handlers first.
@@ -260,12 +320,22 @@
// Forward to the user's signal handler.
int handler_flags = chains[signo].action_.sa_flags;
ucontext_t* ucontext = static_cast<ucontext_t*>(ucontext_raw);
+#if defined(__BIONIC__)
+ sigset64_t mask;
+ sigorset(&mask, &ucontext->uc_sigmask64, &chains[signo].action_.sa_mask);
+#else
sigset_t mask;
sigorset(&mask, &ucontext->uc_sigmask, &chains[signo].action_.sa_mask);
+#endif
if (!(handler_flags & SA_NODEFER)) {
sigaddset(&mask, signo);
}
+
+#if defined(__BIONIC__)
+ linked_sigprocmask64(SIG_SETMASK, &mask, nullptr);
+#else
linked_sigprocmask(SIG_SETMASK, &mask, nullptr);
+#endif
if ((handler_flags & SA_SIGINFO)) {
chains[signo].action_.sa_sigaction(signo, siginfo, ucontext_raw);
@@ -281,20 +351,22 @@
}
}
-extern "C" int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
- InitializeSignalChain();
-
+template <typename SigactionType>
+static int __sigaction(int signal, const SigactionType* new_action,
+ SigactionType* old_action,
+ int (*linked)(int, const SigactionType*,
+ SigactionType*)) {
// If this signal has been claimed as a signal chain, record the user's
// action but don't pass it on to the kernel.
// Note that we check that the signal number is in range here. An out of range signal
// number should behave exactly as the libc sigaction.
- if (signal < 0 || signal >= _NSIG) {
+ if (signal <= 0 || signal >= _NSIG) {
errno = EINVAL;
return -1;
}
if (chains[signal].IsClaimed()) {
- struct sigaction saved_action = chains[signal].GetAction();
+ SigactionType saved_action = chains[signal].GetAction<SigactionType>();
if (new_action != nullptr) {
chains[signal].SetAction(new_action);
}
@@ -306,13 +378,27 @@
// Will only get here if the signal chain has not been claimed. We want
// to pass the sigaction on to the kernel via the real sigaction in libc.
- return linked_sigaction(signal, new_action, old_action);
+ return linked(signal, new_action, old_action);
}
+extern "C" int sigaction(int signal, const struct sigaction* new_action,
+ struct sigaction* old_action) {
+ InitializeSignalChain();
+ return __sigaction(signal, new_action, old_action, linked_sigaction);
+}
+
+#if defined(__BIONIC__)
+extern "C" int sigaction64(int signal, const struct sigaction64* new_action,
+ struct sigaction64* old_action) {
+ InitializeSignalChain();
+ return __sigaction(signal, new_action, old_action, linked_sigaction64);
+}
+#endif
+
extern "C" sighandler_t signal(int signo, sighandler_t handler) {
InitializeSignalChain();
- if (signo < 0 || signo > _NSIG) {
+ if (signo <= 0 || signo >= _NSIG) {
errno = EINVAL;
return SIG_ERR;
}
@@ -326,7 +412,8 @@
// If this signal has been claimed as a signal chain, record the user's
// action but don't pass it on to the kernel.
if (chains[signo].IsClaimed()) {
- oldhandler = reinterpret_cast<sighandler_t>(chains[signo].GetAction().sa_handler);
+ oldhandler = reinterpret_cast<sighandler_t>(
+ chains[signo].GetAction<struct sigaction>().sa_handler);
chains[signo].SetAction(&sa);
return oldhandler;
}
@@ -348,23 +435,23 @@
}
#endif
-extern "C" int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
- InitializeSignalChain();
-
+template <typename SigsetType>
+int __sigprocmask(int how, const SigsetType* new_set, SigsetType* old_set,
+ int (*linked)(int, const SigsetType*, SigsetType*)) {
// When inside a signal handler, forward directly to the actual sigprocmask.
if (GetHandlingSignal()) {
- return linked_sigprocmask(how, bionic_new_set, bionic_old_set);
+ return linked(how, new_set, old_set);
}
- const sigset_t* new_set_ptr = bionic_new_set;
- sigset_t tmpset;
- if (bionic_new_set != nullptr) {
- tmpset = *bionic_new_set;
+ const SigsetType* new_set_ptr = new_set;
+ SigsetType tmpset;
+ if (new_set != nullptr) {
+ tmpset = *new_set;
- if (how == SIG_BLOCK) {
+ if (how == SIG_BLOCK || how == SIG_SETMASK) {
// Don't allow claimed signals in the mask. If a signal chain has been claimed
// we can't allow the user to block that signal.
- for (int i = 0 ; i < _NSIG; ++i) {
+ for (int i = 1; i < _NSIG; ++i) {
if (chains[i].IsClaimed() && sigismember(&tmpset, i)) {
sigdelset(&tmpset, i);
}
@@ -373,9 +460,23 @@
new_set_ptr = &tmpset;
}
- return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
+ return linked(how, new_set_ptr, old_set);
}
+extern "C" int sigprocmask(int how, const sigset_t* new_set,
+ sigset_t* old_set) {
+ InitializeSignalChain();
+ return __sigprocmask(how, new_set, old_set, linked_sigprocmask);
+}
+
+#if defined(__BIONIC__)
+extern "C" int sigprocmask64(int how, const sigset64_t* new_set,
+ sigset64_t* old_set) {
+ InitializeSignalChain();
+ return __sigprocmask(how, new_set, old_set, linked_sigprocmask64);
+}
+#endif
+
extern "C" void AddSpecialSignalHandlerFn(int signal, SigchainAction* sa) {
InitializeSignalChain();
diff --git a/sigchainlib/sigchain_test.cc b/sigchainlib/sigchain_test.cc
new file mode 100644
index 0000000..9f338ad
--- /dev/null
+++ b/sigchainlib/sigchain_test.cc
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <signal.h>
+#include <sys/syscall.h>
+
+#include <functional>
+
+#include <gtest/gtest.h>
+
+#include "sigchain.h"
+
+#if !defined(__BIONIC__)
+typedef sigset_t sigset64_t;
+
+static int sigemptyset64(sigset64_t* set) {
+ return sigemptyset(set);
+}
+
+static int sigismember64(sigset64_t* set, int member) {
+ return sigismember(set, member);
+}
+#endif
+
+static int RealSigprocmask(int how, const sigset64_t* new_sigset, sigset64_t* old_sigset) {
+ // glibc's sigset_t is overly large, so sizeof(*new_sigset) doesn't work.
+ return syscall(__NR_rt_sigprocmask, how, new_sigset, old_sigset, 8);
+}
+
+class SigchainTest : public ::testing::Test {
+ void SetUp() final {
+ art::AddSpecialSignalHandlerFn(SIGSEGV, &action);
+ }
+
+ void TearDown() final {
+ art::RemoveSpecialSignalHandlerFn(SIGSEGV, action.sc_sigaction);
+ }
+
+ art::SigchainAction action = {
+ .sc_sigaction = [](int, siginfo_t*, void*) { return true; },
+ .sc_mask = {},
+ .sc_flags = 0,
+ };
+};
+
+
+static void TestSignalBlocking(std::function<void()> fn) {
+ // Unblock SIGSEGV, make sure it stays unblocked.
+ sigset64_t mask;
+ sigemptyset64(&mask);
+ ASSERT_EQ(0, RealSigprocmask(SIG_SETMASK, &mask, nullptr)) << strerror(errno);
+
+ fn();
+
+ if (testing::Test::HasFatalFailure()) return;
+ ASSERT_EQ(0, RealSigprocmask(SIG_SETMASK, nullptr, &mask));
+ ASSERT_FALSE(sigismember64(&mask, SIGSEGV));
+}
+
+TEST_F(SigchainTest, sigprocmask_setmask) {
+ TestSignalBlocking([]() {
+ sigset_t mask;
+ sigfillset(&mask);
+ ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &mask, nullptr));
+ });
+}
+
+TEST_F(SigchainTest, sigprocmask_block) {
+ TestSignalBlocking([]() {
+ sigset_t mask;
+ sigfillset(&mask);
+ ASSERT_EQ(0, sigprocmask(SIG_BLOCK, &mask, nullptr));
+ });
+}
+
+// bionic-only wide variants for LP32.
+#if defined(__BIONIC__)
+TEST_F(SigchainTest, sigprocmask64_setmask) {
+ TestSignalBlocking([]() {
+ sigset64_t mask;
+ sigfillset64(&mask);
+ ASSERT_EQ(0, sigprocmask64(SIG_SETMASK, &mask, nullptr));
+ });
+}
+
+TEST_F(SigchainTest, sigprocmask64_block) {
+ TestSignalBlocking([]() {
+ sigset64_t mask;
+ sigfillset64(&mask);
+ ASSERT_EQ(0, sigprocmask64(SIG_BLOCK, &mask, nullptr));
+ });
+}
+
+TEST_F(SigchainTest, pthread_sigmask64_setmask) {
+ TestSignalBlocking([]() {
+ sigset64_t mask;
+ sigfillset64(&mask);
+ ASSERT_EQ(0, pthread_sigmask64(SIG_SETMASK, &mask, nullptr));
+ });
+}
+
+TEST_F(SigchainTest, pthread_sigmask64_block) {
+ TestSignalBlocking([]() {
+ sigset64_t mask;
+ sigfillset64(&mask);
+ ASSERT_EQ(0, pthread_sigmask64(SIG_BLOCK, &mask, nullptr));
+ });
+}
+#endif
+
+// glibc doesn't implement most of these in terms of sigprocmask, which we rely on.
+#if defined(__BIONIC__)
+TEST_F(SigchainTest, pthread_sigmask_setmask) {
+ TestSignalBlocking([]() {
+ sigset_t mask;
+ sigfillset(&mask);
+ ASSERT_EQ(0, pthread_sigmask(SIG_SETMASK, &mask, nullptr));
+ });
+}
+
+TEST_F(SigchainTest, pthread_sigmask_block) {
+ TestSignalBlocking([]() {
+ sigset_t mask;
+ sigfillset(&mask);
+ ASSERT_EQ(0, pthread_sigmask(SIG_BLOCK, &mask, nullptr));
+ });
+}
+
+TEST_F(SigchainTest, sigset_mask) {
+ TestSignalBlocking([]() {
+ sigset(SIGSEGV, SIG_HOLD);
+ });
+}
+
+TEST_F(SigchainTest, sighold) {
+ TestSignalBlocking([]() {
+ sighold(SIGSEGV);
+ });
+}
+
+#if defined(__BIONIC__)
+// Not exposed via headers, but the symbols are available if you declare them yourself.
+extern "C" int sigblock(int);
+extern "C" int sigsetmask(int);
+#endif
+
+TEST_F(SigchainTest, sigblock) {
+ TestSignalBlocking([]() {
+ int mask = ~0U;
+ ASSERT_EQ(0, sigblock(mask));
+ });
+}
+
+TEST_F(SigchainTest, sigsetmask) {
+ TestSignalBlocking([]() {
+ int mask = ~0U;
+ ASSERT_EQ(0, sigsetmask(mask));
+ });
+}
+
+#endif
diff --git a/sigchainlib/version-script32.txt b/sigchainlib/version-script32.txt
index 2340785..e8a18e7 100644
--- a/sigchainlib/version-script32.txt
+++ b/sigchainlib/version-script32.txt
@@ -5,8 +5,10 @@
RemoveSpecialSignalHandlerFn;
bsd_signal;
sigaction;
+ sigaction64;
signal;
sigprocmask;
+ sigprocmask64;
local:
*;
};
diff --git a/sigchainlib/version-script64.txt b/sigchainlib/version-script64.txt
index acf3630..72c86a1 100644
--- a/sigchainlib/version-script64.txt
+++ b/sigchainlib/version-script64.txt
@@ -4,8 +4,10 @@
AddSpecialSignalHandlerFn;
RemoveSpecialSignalHandlerFn;
sigaction;
+ sigaction64;
signal;
sigprocmask;
+ sigprocmask64;
local:
*;
};
diff --git a/test/004-SignalTest/signaltest.cc b/test/004-SignalTest/signaltest.cc
index a58a075..67118d5 100644
--- a/test/004-SignalTest/signaltest.cc
+++ b/test/004-SignalTest/signaltest.cc
@@ -99,6 +99,15 @@
static struct sigaction oldaction;
+bool compare_sigaction(const struct sigaction* lhs, const struct sigaction* rhs) {
+ // bionic's definition of `struct sigaction` has internal padding bytes, so we can't just do a
+ // naive memcmp of the entire struct.
+ return memcmp(&lhs->sa_mask, &rhs->sa_mask, sizeof(lhs->sa_mask)) == 0 &&
+ lhs->sa_sigaction == rhs->sa_sigaction &&
+ lhs->sa_flags == rhs->sa_flags &&
+ lhs->sa_restorer == rhs->sa_restorer;
+}
+
extern "C" JNIEXPORT void JNICALL Java_Main_initSignalTest(JNIEnv*, jclass) {
struct sigaction action;
action.sa_sigaction = signalhandler;
@@ -112,8 +121,15 @@
sigaction(SIGSEGV, &action, &oldaction);
struct sigaction check;
sigaction(SIGSEGV, nullptr, &check);
- if (memcmp(&action, &check, sizeof(action)) != 0) {
+ if (!compare_sigaction(&check, &action)) {
printf("sigaction returned different value\n");
+ printf("action.sa_mask = %p, check.sa_mask = %p\n",
+ *reinterpret_cast<void**>(&action.sa_mask),
+ *reinterpret_cast<void**>(&check.sa_mask));
+ printf("action.sa_sigaction = %p, check.sa_sigaction = %p\n",
+ action.sa_sigaction, check.sa_sigaction);
+ printf("action.sa_flags = %x, check.sa_flags = %x\n",
+ action.sa_flags, check.sa_flags);
}
signal(BLOCKED_SIGNAL, blocked_signal);
signal(UNBLOCKED_SIGNAL, unblocked_signal);
diff --git a/test/115-native-bridge/expected.txt b/test/115-native-bridge/expected.txt
index 343a762..06f5979 100644
--- a/test/115-native-bridge/expected.txt
+++ b/test/115-native-bridge/expected.txt
@@ -63,6 +63,8 @@
Getting trampoline for Java_Main_testSignal with shorty I.
NB signal handler with signal 11.
NB signal handler with signal 4.
+NB signal handler with signal 11.
+NB signal handler with signal 4.
Loading invalid library 'libinvalid.so' from Java, which will fail.
Checking for support.
Was to load 'libinvalid.so', force fail.
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index a049b97..3b35209 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -224,6 +224,22 @@
sigaction(SIGILL, &tmp, nullptr);
kill(getpid(), SIGILL);
+#if defined(__BIONIC__)
+ // Do the same again, but with sigaction64.
+ struct sigaction64 tmp2;
+ sigemptyset64(&tmp2.sa_mask);
+ tmp2.sa_sigaction = test_sigaction_handler;
+ tmp2.sa_restorer = nullptr;
+
+ sigaction64(SIGSEGV, &tmp2, nullptr);
+ sigaction64(SIGILL, &tmp2, nullptr);
+#endif
+
+ // Reraise SIGSEGV/SIGILL even on non-bionic, so that the expected output is
+ // the same.
+ raise_sigsegv();
+ kill(getpid(), SIGILL);
+
return 1234;
}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index fe1e31e..cc3d200 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1,17 +1,17 @@
[
{
- "tests": "1934-jvmti-signal-thread",
- "description": ["Disables 1934-jvmti-signal-thread in tracing configurations"],
- "variant": "trace | stream",
- "bug": "http://b/67384421"
- },
- {
"tests": "153-reference-stress",
"description": ["Disable 153-reference-stress temporarily until a fix",
"arrives."],
"bug": "http://b/33389022"
},
{
+ "tests": "579-inline-infinite",
+ "description": ["This test seems to fail often on redefine-stress for unknown reasons"],
+ "variant": "redefine-stress",
+ "bug": "http://b/73871735"
+ },
+ {
"tests": "080-oom-fragmentation",
"description": "Disable 080-oom-fragmentation due to flakes.",
"bug": "http://b/33795328"
@@ -673,5 +673,304 @@
"env_vars": {"SANITIZE_HOST": "address"},
"bug": "b/73060923",
"description": ["ASAN issue"]
+ },
+ {
+ "tests": ["1941-dispose-stress", "522-checker-regression-monitor-exit"],
+ "variant": "jvm",
+ "bug": "b/73888836",
+ "description": ["Hangs forever, times out."]
+ },
+ {
+ "tests": [
+ "004-JniTest",
+ "004-NativeAllocations",
+ "004-ReferenceMap",
+ "004-SignalTest",
+ "004-StackWalk",
+ "004-ThreadStress",
+ "004-UnsafeTest",
+ "005-annotations",
+ "008-exceptions",
+ "020-string",
+ "021-string2",
+ "030-bad-finalizer",
+ "031-class-attributes",
+ "034-call-null",
+ "038-inner-null",
+ "044-proxy",
+ "046-reflect",
+ "064-field-access",
+ "068-classloader",
+ "070-nio-buffer",
+ "071-dexfile",
+ "071-dexfile-get-static-size",
+ "071-dexfile-map-clean",
+ "082-inline-execute",
+ "086-null-super",
+ "087-gc-after-link",
+ "088-monitor-verification",
+ "091-override-package-private-method",
+ "097-duplicate-method",
+ "098-ddmc",
+ "099-vmdebug",
+ "100-reflect2",
+ "104-growth-limit",
+ "111-unresolvable-exception",
+ "115-native-bridge",
+ "116-nodex2oat",
+ "117-nopatchoat",
+ "118-noimage-dex2oat",
+ "119-noimage-patchoat",
+ "127-checker-secondarydex",
+ "129-ThreadGetId",
+ "130-hprof",
+ "1337-gc-coverage",
+ "1338-gc-no-los",
+ "134-nodex2oat-nofallback",
+ "134-reg-promotion",
+ "135-MirandaDispatch",
+ "136-daemon-jni-shutdown",
+ "137-cfi",
+ "138-duplicate-classes-check",
+ "138-duplicate-classes-check2",
+ "140-field-packing",
+ "141-class-unload",
+ "142-classloader2",
+ "143-string-value",
+ "144-static-field-sigquit",
+ "145-alloc-tracking-stress",
+ "146-bad-interface",
+ "147-stripped-dex-fallback",
+ "148-multithread-gc-annotations",
+ "150-loadlibrary",
+ "154-gc-loop",
+ "156-register-dex-file-multi-loader",
+ "157-void-class",
+ "158-app-image-class-table",
+ "159-app-image-fields",
+ "161-final-abstract-class",
+ "162-method-resolution",
+ "163-app-image-methods",
+ "164-resolution-trampoline-dex-cache",
+ "167-visit-locks",
+ "168-vmstack-annotated",
+ "201-built-in-except-detail-messages",
+ "203-multi-checkpoint",
+ "304-method-tracing",
+ "305-other-fault-handler",
+ "412-new-array",
+ "416-optimizing-arith-not",
+ "425-invoke-super",
+ "431-type-propagation",
+ "432-optimizing-cmp",
+ "434-invoke-direct",
+ "435-new-instance",
+ "441-checker-inliner",
+ "442-checker-constant-folding",
+ "448-multiple-returns",
+ "449-checker-bce",
+ "452-multiple-returns2",
+ "453-not-byte",
+ "454-get-vreg",
+ "455-checker-gvn",
+ "457-regs",
+ "458-checker-instruct-simplification",
+ "459-dead-phi",
+ "460-multiple-returns3",
+ "461-get-reference-vreg",
+ "466-get-live-vreg",
+ "467-regalloc-pair",
+ "468-checker-bool-simplif-regression",
+ "471-uninitialized-locals",
+ "472-unreachable-if-regression",
+ "475-regression-inliner-ids",
+ "480-checker-dead-blocks",
+ "496-checker-inlining-class-loader",
+ "498-type-propagation",
+ "501-null-constant-dce",
+ "501-regression-packed-switch",
+ "503-dead-instructions",
+ "504-regression-baseline-entry",
+ "506-verify-aput",
+ "509-pre-header",
+ "510-checker-try-catch",
+ "511-clinit-interface",
+ "515-dce-dominator",
+ "516-dead-move-result",
+ "517-checker-builder-fallthrough",
+ "518-null-array-get",
+ "520-equivalent-phi",
+ "529-checker-unresolved",
+ "530-checker-lse",
+ "530-checker-lse-ctor-fences",
+ "530-checker-lse3",
+ "530-checker-regression-reftyp-final",
+ "536-checker-intrinsic-optimization",
+ "536-checker-needs-access-check",
+ "541-regression-inlined-deopt",
+ "543-env-long-ref",
+ "545-tracing-and-jit",
+ "550-checker-regression-wide-store",
+ "551-invoke-super",
+ "552-checker-primitive-typeprop",
+ "552-invoke-non-existent-super",
+ "553-invoke-super",
+ "556-invoke-super",
+ "559-checker-irreducible-loop",
+ "563-checker-fakestring",
+ "564-checker-irreducible-loop",
+ "565-checker-doublenegbitwise",
+ "565-checker-irreducible-loop",
+ "566-polymorphic-inlining",
+ "567-checker-compare",
+ "569-checker-pattern-replacement",
+ "570-checker-osr",
+ "571-irreducible-loop",
+ "574-irreducible-and-constant-area",
+ "575-checker-string-init-alias",
+ "580-checker-string-fact-intrinsics",
+ "585-inline-unresolved",
+ "586-checker-null-array-get",
+ "587-inline-class-error",
+ "588-checker-irreducib-lifetime-hole",
+ "591-new-instance-string",
+ "592-checker-regression-bool-input",
+ "593-checker-boolean-2-integral-conv",
+ "593-checker-shift-and-simplifier",
+ "594-invoke-super",
+ "595-error-class",
+ "595-profile-saving",
+ "596-app-images",
+ "596-checker-dead-phi",
+ "596-monitor-inflation",
+ "597-deopt-busy-loop",
+ "597-deopt-invoke-stub",
+ "597-deopt-new-string",
+ "599-checker-irreducible-loop",
+ "600-verifier-fails",
+ "601-method-access",
+ "602-deoptimizeable",
+ "605-new-string-from-bytes",
+ "608-checker-unresolved-lse",
+ "612-jit-dex-cache",
+ "613-inlining-dex-cache",
+ "616-cha",
+ "616-cha-abstract",
+ "616-cha-interface",
+ "616-cha-interface-default",
+ "616-cha-miranda",
+ "622-simplifyifs-exception-edges",
+ "624-checker-stringops",
+ "626-const-class-linking",
+ "628-vdex",
+ "629-vdex-speed",
+ "630-safecast-array",
+ "631-checker-fp-abs",
+ "633-checker-rtp-getclass",
+ "634-vdex-duplicate",
+ "636-wrong-static-access",
+ "638-checker-inline-cache-intrinsic",
+ "638-checker-inline-caches",
+ "638-no-line-number",
+ "641-irreducible-inline",
+ "643-checker-bogus-ic",
+ "645-checker-abs-simd",
+ "647-jni-get-field-id",
+ "647-sinking-catch",
+ "648-inline-caches-unresolved",
+ "649-vdex-duplicate-method",
+ "652-deopt-intrinsic",
+ "656-annotation-lookup-generic-jni",
+ "656-checker-simd-opt",
+ "659-unpadded-array",
+ "660-clinit",
+ "660-store-8-16",
+ "661-classloader-allocator",
+ "661-oat-writer-layout",
+ "663-checker-select-generator",
+ "663-odd-dex-size",
+ "663-odd-dex-size2",
+ "663-odd-dex-size3",
+ "663-odd-dex-size4",
+ "667-jit-jni-stub",
+ "667-out-of-bounds",
+ "668-aiobe",
+ "674-hiddenapi",
+ "674-hotness-compiled",
+ "674-vdex-uncompress",
+ "675-checker-unverified-method",
+ "676-proxy-jit-at-first-use",
+ "676-resolve-field-type",
+ "706-checker-scheduler",
+ "707-checker-invalid-profile",
+ "714-invoke-custom-lambda-metafactory",
+ "800-smali",
+ "801-VoidCheckCast",
+ "802-deoptimization",
+ "804-class-extends-itself",
+ "900-hello-plugin",
+ "901-hello-ti-agent",
+ "903-hello-tagging",
+ "904-object-allocation",
+ "906-iterate-heap",
+ "909-attach-agent",
+ "910-methods",
+ "911-get-stack-trace",
+ "912-classes",
+ "913-heaps",
+ "918-fields",
+ "920-objects",
+ "922-properties",
+ "924-threads",
+ "925-threadgroups",
+ "927-timers",
+ "929-search",
+ "931-agent-thread",
+ "933-misc-events",
+ "936-search-onload",
+ "939-hello-transformation-bcp",
+ "944-transform-classloaders",
+ "946-obsolete-throw",
+ "948-change-annotations",
+ "950-redefine-intrinsic",
+ "952-invoke-custom",
+ "954-invoke-polymorphic-verifier",
+ "955-methodhandles-smali",
+ "956-methodhandles",
+ "957-methodhandle-transforms",
+ "958-methodhandle-stackframe",
+ "972-default-imt-collision",
+ "972-iface-super-multidex",
+ "973-default-multidex",
+ "974-verify-interface-super",
+ "975-iface-private",
+ "976-conflict-no-methods",
+ "978-virtual-interface",
+ "980-redefine-object",
+ "981-dedup-original-dex",
+ "983-source-transform-verify",
+ "986-native-method-bind",
+ "988-method-trace",
+ "989-method-trace-throw",
+ "993-breakpoints",
+ "1900-track-alloc",
+ "1906-suspend-list-me-first",
+ "1914-get-local-instance",
+ "1926-missed-frame-pop",
+ "1930-monitor-info",
+ "1932-monitor-events-misc",
+ "1935-get-set-current-frame-jit",
+ "1938-transform-abstract-single-impl",
+ "1939-proxy-frames",
+ "1940-ddms-ext",
+ "1945-proxy-method-arguments",
+ "1946-list-descriptors",
+ "1947-breakpoint-redefine-deopt"
+ ],
+ "variant": "jvm",
+ "bug": "b/73888836",
+ "description": ["Failing on JVM. Needs further investigating."]
}
+
+
]
diff --git a/test/testrunner/device_config.py b/test/testrunner/device_config.py
index c7ed6f7..1fad7d2 100644
--- a/test/testrunner/device_config.py
+++ b/test/testrunner/device_config.py
@@ -1,7 +1,7 @@
device_config = {
# Configuration syntax:
#
-# device: The value of ro.product.name or 'host'
+# device: The value of ro.product.name or 'host' or 'jvm'
# properties: (Use one or more of these).
# * run-test-args: additional run-test-args
#
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 531508e..fcc5505 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -96,15 +96,20 @@
if target.has_key('run-test'):
run_test_command = [os.path.join(env.ANDROID_BUILD_TOP,
'art/test/testrunner/testrunner.py')]
- run_test_command += target.get('run-test', [])
+ test_flags = target.get('run-test', [])
+ run_test_command += test_flags
# Let testrunner compute concurrency based on #cpus.
# b/65822340
# run_test_command += ['-j', str(n_threads)]
+
+ # In the config assume everything will run with --host and on ART.
+ # However for only [--jvm] this is undesirable, so don't pass in ART-specific flags.
+ if ['--jvm'] != test_flags:
+ run_test_command += ['--host']
+ run_test_command += ['--dex2oat-jobs']
+ run_test_command += ['4']
run_test_command += ['-b']
- run_test_command += ['--host']
run_test_command += ['--verbose']
- run_test_command += ['--dex2oat-jobs']
- run_test_command += ['4']
sys.stdout.write(str(run_test_command) + '\n')
sys.stdout.flush()
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 3064c76..9d03775 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -31,12 +31,7 @@
},
'art-test-javac' : {
- 'make' : 'test-art-host-gtest',
- 'run-test' : [],
- 'env' : {
- 'ANDROID_COMPILE_WITH_JACK' : 'false',
- 'ART_USE_READ_BARRIER' : 'true'
- }
+ 'run-test' : ['--jvm']
},
# ART run-test configurations
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 4329ad4..3974ccb 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -136,7 +136,7 @@
# TODO: Avoid duplication of the variant names in different lists.
VARIANT_TYPE_DICT['pictest'] = {'pictest', 'npictest'}
VARIANT_TYPE_DICT['run'] = {'ndebug', 'debug'}
- VARIANT_TYPE_DICT['target'] = {'target', 'host'}
+ VARIANT_TYPE_DICT['target'] = {'target', 'host', 'jvm'}
VARIANT_TYPE_DICT['trace'] = {'trace', 'ntrace', 'stream'}
VARIANT_TYPE_DICT['image'] = {'picimage', 'no-image', 'multipicimage'}
VARIANT_TYPE_DICT['debuggable'] = {'ndebuggable', 'debuggable'}
@@ -193,7 +193,7 @@
_user_input_variants['jvmti'].add('no-jvmti')
# By default we run all 'compiler' variants.
- if not _user_input_variants['compiler']:
+ if not _user_input_variants['compiler'] and _user_input_variants['target'] != 'jvm':
_user_input_variants['compiler'].add('optimizing')
_user_input_variants['compiler'].add('jit')
_user_input_variants['compiler'].add('interpreter')
@@ -215,7 +215,6 @@
if not _user_input_variants['image']: # Default
_user_input_variants['image'].add('picimage')
-
if not _user_input_variants['pictest']: # Default
_user_input_variants['pictest'].add('npictest')
@@ -269,11 +268,10 @@
"""
Gets any extra arguments from the device_config.
"""
- if target == 'host':
- return device_config.get(target, { 'run-test-args' : [] })['run-test-args']
- else:
- device = get_device_name()
- return device_config.get(device, { 'run-test-args' : [] })['run-test-args']
+ device_name = target
+ if target == 'target':
+ device_name = get_device_name()
+ return device_config.get(device_name, { 'run-test-args' : [] })['run-test-args']
def get_device_name():
"""
@@ -308,15 +306,27 @@
tests: The set of tests to be run.
"""
options_all = ''
+
+ # jvm does not run with all these combinations,
+ # or at least it doesn't make sense for most of them.
+ # TODO: support some jvm variants like jvmti ?
+ target_input_variants = _user_input_variants['target']
+ uncombinated_target_input_variants = []
+ if 'jvm' in target_input_variants:
+ _user_input_variants['target'].remove('jvm')
+ uncombinated_target_input_variants.append('jvm')
+
global total_test_count
total_test_count = len(tests)
- for variant_type in VARIANT_TYPE_DICT:
- if not (variant_type == 'target' or 'address_sizes' in variant_type):
- total_test_count *= len(_user_input_variants[variant_type])
+ if target_input_variants:
+ for variant_type in VARIANT_TYPE_DICT:
+ if not (variant_type == 'target' or 'address_sizes' in variant_type):
+ total_test_count *= len(_user_input_variants[variant_type])
target_address_combinations = 0
- for target in _user_input_variants['target']:
+ for target in target_input_variants:
for address_size in _user_input_variants['address_sizes_target'][target]:
target_address_combinations += 1
+ target_address_combinations += len(uncombinated_target_input_variants)
total_test_count *= target_address_combinations
if env.ART_TEST_WITH_STRACE:
@@ -339,17 +349,32 @@
if dex2oat_jobs != -1:
options_all += ' --dex2oat-jobs ' + str(dex2oat_jobs)
- config = itertools.product(tests, _user_input_variants['target'], _user_input_variants['run'],
- _user_input_variants['prebuild'], _user_input_variants['compiler'],
- _user_input_variants['relocate'], _user_input_variants['trace'],
- _user_input_variants['gc'], _user_input_variants['jni'],
- _user_input_variants['image'], _user_input_variants['pictest'],
- _user_input_variants['debuggable'], _user_input_variants['jvmti'],
- _user_input_variants['cdex_level'])
+ def iter_config(tests, input_variants, user_input_variants):
+ config = itertools.product(tests, input_variants, user_input_variants['run'],
+ user_input_variants['prebuild'], user_input_variants['compiler'],
+ user_input_variants['relocate'], user_input_variants['trace'],
+ user_input_variants['gc'], user_input_variants['jni'],
+ user_input_variants['image'], user_input_variants['pictest'],
+ user_input_variants['debuggable'], user_input_variants['jvmti'],
+ user_input_variants['cdex_level'])
+ return config
- for test, target, run, prebuild, compiler, relocate, trace, gc, \
- jni, image, pictest, debuggable, jvmti, cdex_level in config:
- for address_size in _user_input_variants['address_sizes_target'][target]:
+ # [--host, --target] combines with all the other user input variants.
+ config = iter_config(tests, target_input_variants, _user_input_variants)
+ # [--jvm] currently combines with nothing else. most of the extra flags we'd insert
+ # would be unrecognizable by the 'java' binary, so avoid inserting any extra flags for now.
+ uncombinated_config = iter_config(tests, uncombinated_target_input_variants, { 'run': [''],
+ 'prebuild': [''], 'compiler': [''],
+ 'relocate': [''], 'trace': [''],
+ 'gc': [''], 'jni': [''],
+ 'image': [''], 'pictest': [''],
+ 'debuggable': [''], 'jvmti': [''],
+ 'cdex_level': ['']})
+
+ def start_combination(config_tuple, address_size):
+ test, target, run, prebuild, compiler, relocate, trace, gc, \
+ jni, image, pictest, debuggable, jvmti, cdex_level = config_tuple
+
if stop_testrunner:
# When ART_TEST_KEEP_GOING is set to false, then as soon as a test
# fails, stop_testrunner is set to True. When this happens, the method
@@ -384,6 +409,8 @@
if target == 'host':
options_test += ' --host'
+ elif target == 'jvm':
+ options_test += ' --jvm'
if run == 'ndebug':
options_test += ' -O'
@@ -488,6 +515,14 @@
worker.daemon = True
worker.start()
+ for config_tuple in config:
+ target = config_tuple[1]
+ for address_size in _user_input_variants['address_sizes_target'][target]:
+ start_combination(config_tuple, address_size)
+
+ for config_tuple in uncombinated_config:
+ start_combination(config_tuple, "") # no address size
+
while threading.active_count() > 2:
time.sleep(0.1)
@@ -969,6 +1004,8 @@
build_targets += 'test-art-host-run-test-dependencies'
if 'target' in _user_input_variants['target']:
build_targets += 'test-art-target-run-test-dependencies'
+ if 'jvm' in _user_input_variants['target']:
+ build_targets += 'test-art-host-run-test-dependencies'
build_command = 'make'
build_command += ' DX='
build_command += ' -j'