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'