Prevent the JIT from loading classes in debuggable mode

In debuggable mode the timing and placement of class loads is
observable. This causes deopts to be placed in situations where a
class would be loaded by the JIT.

This has negligible impact on performance of compiled code, causing
only a handful of classes to be not loaded.

For example with the it.mvilla.android.fenix2 application of the
~13000 classes loaded the jit was responsible for 19 of them.

Since the jit is only responsible for a small number of class loads
and for the class not to have been loaded by the interpreter the path
must be cold anyway there is no performance impact on standard paths.

Test: ./test.py --host
Test: ./test/testrunner/testrunner.py --with-agent=libjitloadd.so=fatal --host
Test: Attach libjitload.so to running processes and examine jit-load
      counts.

Bug: 70838465
Bug: 73255278
Bug: 112074977
Bug: 116517081

Change-Id: I030bc8116345af506e83ba78427a7fbda7a7b386
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d95f71a..c18f46b 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2501,7 +2501,7 @@
       // the Java-side could still succeed for racy programs if another thread is actively
       // modifying the class loader's path list.
 
-      if (!self->CanCallIntoJava()) {
+      if (self->IsRuntimeThread()) {
         // Oops, we can't call into java so we can't run actual class-loader code.
         // This is true for e.g. for the compiler (jit or aot).
         ObjPtr<mirror::Throwable> pre_allocated =
@@ -2634,6 +2634,17 @@
     }
   }
 
+  // This is to prevent the calls to ClassLoad and ClassPrepare which can cause java/user-supplied
+  // code to be executed. We put it up here so we can avoid all the allocations associated with
+  // creating the class. This can happen with (eg) jit threads.
+  if (!self->CanLoadClasses()) {
+    // Make sure we don't try to load anything, potentially causing an infinite loop.
+    ObjPtr<mirror::Throwable> pre_allocated =
+        Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
+    self->SetException(pre_allocated);
+    return nullptr;
+  }
+
   if (klass == nullptr) {
     // Allocate a class with the status of not ready.
     // Interface object should get the right size here. Regular class will
@@ -3622,6 +3633,18 @@
   // Identify the underlying component type
   CHECK_EQ('[', descriptor[0]);
   StackHandleScope<2> hs(self);
+
+  // This is to prevent the calls to ClassLoad and ClassPrepare which can cause java/user-supplied
+  // code to be executed. We put it up here so we can avoid all the allocations associated with
+  // creating the class. This can happen with (eg) jit threads.
+  if (!self->CanLoadClasses()) {
+    // Make sure we don't try to load anything, potentially causing an infinite loop.
+    ObjPtr<mirror::Throwable> pre_allocated =
+        Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
+    self->SetException(pre_allocated);
+    return nullptr;
+  }
+
   MutableHandle<mirror::Class> component_type(hs.NewHandle(FindClass(self, descriptor + 1,
                                                                      class_loader)));
   if (component_type == nullptr) {
@@ -3809,6 +3832,7 @@
 ObjPtr<mirror::Class> ClassLinker::InsertClass(const char* descriptor,
                                                ObjPtr<mirror::Class> klass,
                                                size_t hash) {
+  DCHECK(Thread::Current()->CanLoadClasses());
   if (VLOG_IS_ON(class_linker)) {
     ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache();
     std::string source;
@@ -4333,6 +4357,18 @@
                                                     jobjectArray methods,
                                                     jobjectArray throws) {
   Thread* self = soa.Self();
+
+  // This is to prevent the calls to ClassLoad and ClassPrepare which can cause java/user-supplied
+  // code to be executed. We put it up here so we can avoid all the allocations associated with
+  // creating the class. This can happen with (eg) jit-threads.
+  if (!self->CanLoadClasses()) {
+    // Make sure we don't try to load anything, potentially causing an infinite loop.
+    ObjPtr<mirror::Throwable> pre_allocated =
+        Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
+    self->SetException(pre_allocated);
+    return nullptr;
+  }
+
   StackHandleScope<10> hs(self);
   MutableHandle<mirror::Class> temp_klass(hs.NewHandle(
       AllocClass(self, GetClassRoot<mirror::Class>(this), sizeof(mirror::Class))));
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index a51d457..48c172e 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1458,7 +1458,7 @@
   CHECK_EQ(self->GetThreadId(), ThreadList::kMainThreadId);
   CHECK(self != nullptr);
 
-  self->SetCanCallIntoJava(!IsAotCompiler());
+  self->SetIsRuntimeThread(IsAotCompiler());
 
   // Set us to runnable so tools using a runtime can allocate and GC by default
   self->TransitionFromSuspendedToRunnable();
diff --git a/runtime/thread.cc b/runtime/thread.cc
index b6f0965..fd19dac 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1242,7 +1242,7 @@
                                     << "of " << *this;
   // NB This checks the new value! This ensures that we can only set can_be_suspended_by_user_code
   // to false if !CanCallIntoJava().
-  DCHECK(!CanCallIntoJava() || can_be_suspended_by_user_code)
+  DCHECK(IsRuntimeThread() || can_be_suspended_by_user_code)
       << "Threads able to call into java may not be marked as unsuspendable!";
   if (can_be_suspended_by_user_code == CanBeSuspendedByUserCode()) {
     // Don't need to do anything if nothing is changing.
@@ -2156,7 +2156,7 @@
 Thread::Thread(bool daemon)
     : tls32_(daemon),
       wait_monitor_(nullptr),
-      can_call_into_java_(true),
+      is_runtime_thread_(false),
       can_be_suspended_by_user_code_(true) {
   wait_mutex_ = new Mutex("a thread wait mutex");
   wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
@@ -2181,6 +2181,10 @@
   tls32_.is_transitioning_to_runnable = false;
 }
 
+bool Thread::CanLoadClasses() const {
+  return !IsRuntimeThread() || !Runtime::Current()->IsJavaDebuggable();
+}
+
 bool Thread::IsStillStarting() const {
   // You might think you can check whether the state is kStarting, but for much of thread startup,
   // the thread is in kNative; it might also be in kVmWait.
diff --git a/runtime/thread.h b/runtime/thread.h
index aaf6bae..4c9c38a 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -1002,14 +1002,17 @@
       REQUIRES(!Locks::thread_suspend_count_lock_, !Locks::user_code_suspension_lock_);
 
   // Returns true if the thread is allowed to call into java.
-  bool CanCallIntoJava() const {
-    return can_call_into_java_;
+  bool IsRuntimeThread() const {
+    return is_runtime_thread_;
   }
 
-  void SetCanCallIntoJava(bool can_call_into_java) {
-    can_call_into_java_ = can_call_into_java;
+  void SetIsRuntimeThread(bool is_runtime_thread) {
+    is_runtime_thread_ = is_runtime_thread;
   }
 
+  // Returns true if the thread is allowed to load java classes.
+  bool CanLoadClasses() const;
+
   // Activates single step control for debugging. The thread takes the
   // ownership of the given SingleStepControl*. It is deleted by a call
   // to DeactivateSingleStepControl or upon thread destruction.
@@ -1812,9 +1815,8 @@
   // compiled code or entrypoints.
   SafeMap<std::string, std::unique_ptr<TLSData>> custom_tls_ GUARDED_BY(Locks::custom_tls_lock_);
 
-  // True if the thread is allowed to call back into java (for e.g. during class resolution).
-  // By default this is true.
-  bool can_call_into_java_;
+  // True if the thread is some form of runtime thread (ex, GC or JIT).
+  bool is_runtime_thread_;
 
   // True if the thread is subject to user-code suspension. By default this is true. This can only
   // be false for threads where '!can_call_into_java_'.
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 28fc59c..fc9067d 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -102,8 +102,8 @@
                                      nullptr,
                                      worker->thread_pool_->create_peers_));
   worker->thread_ = Thread::Current();
-  // Thread pool workers cannot call into java.
-  worker->thread_->SetCanCallIntoJava(false);
+  // Mark thread pool workers as runtime-threads.
+  worker->thread_->SetIsRuntimeThread(true);
   // Thread pool workers should not be getting paused by user-code.
   worker->thread_->SetCanBeSuspendedByUserCode(false);
   // Do work until its time to shut down.
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 10c8619..72dc165 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -117,6 +117,7 @@
 gdb = False
 gdb_arg = ''
 runtime_option = ''
+with_agent = []
 run_test_option = []
 stop_testrunner = False
 dex2oat_jobs = -1   # -1 corresponds to default threads for dex2oat
@@ -333,6 +334,9 @@
   if runtime_option:
     for opt in runtime_option:
       options_all += ' --runtime-option ' + opt
+  if with_agent:
+    for opt in with_agent:
+      options_all += ' --with-agent ' + opt
 
   if dex2oat_jobs != -1:
     options_all += ' --dex2oat-jobs ' + str(dex2oat_jobs)
@@ -911,6 +915,7 @@
   global timeout
   global dex2oat_jobs
   global run_all_configs
+  global with_agent
 
   parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.")
   parser.add_argument('-t', '--test', action='append', dest='tests', help='name(s) of the test(s)')
@@ -943,6 +948,8 @@
                             This should be enclosed in single-quotes to allow for spaces. The option
                             will be split using shlex.split() prior to invoking run-test.
                             Example \"--run-test-option='--with-agent libtifast.so=MethodExit'\"""")
+  global_group.add_argument('--with-agent', action='append', dest='with_agent',
+                            help="""Pass an agent to be attached to the runtime""")
   global_group.add_argument('--runtime-option', action='append', dest='runtime_option',
                             help="""Pass an option to the runtime. Runtime options
                             starting with a '-' must be separated by a '=', for
@@ -991,6 +998,7 @@
     if options['gdb_arg']:
       gdb_arg = options['gdb_arg']
   runtime_option = options['runtime_option'];
+  with_agent = options['with_agent'];
   run_test_option = sum(map(shlex.split, options['run_test_option']), [])
 
   timeout = options['timeout']
diff --git a/tools/jit-load/Android.bp b/tools/jit-load/Android.bp
new file mode 100644
index 0000000..a57a408
--- /dev/null
+++ b/tools/jit-load/Android.bp
@@ -0,0 +1,85 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Build variants {target,host} x {debug,ndebug} x {32,64}
+
+cc_defaults {
+    name: "jitload-defaults",
+    host_supported: true,
+    srcs: [
+        "jitload.cc",
+    ],
+    defaults: ["art_defaults"],
+
+    // Note that this tool needs to be built for both 32-bit and 64-bit since it requires
+    // to be same ISA as what it is attached to.
+    compile_multilib: "both",
+
+    shared_libs: [
+        "libbase",
+    ],
+    target: {
+        android: {
+        },
+        host: {
+        },
+    },
+    header_libs: [
+        "libopenjdkjvmti_headers",
+    ],
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+    symlink_preferred_arch: true,
+}
+
+art_cc_library {
+    name: "libjitload",
+    defaults: ["jitload-defaults"],
+    shared_libs: [
+        "libart",
+        "libdexfile",
+        "libprofile",
+        "libartbase",
+    ],
+}
+
+art_cc_library {
+    name: "libjitloadd",
+    defaults: [
+        "art_debug_defaults",
+        "jitload-defaults",
+    ],
+    shared_libs: [
+        "libartd",
+        "libdexfiled",
+        "libprofiled",
+        "libartbased",
+    ],
+}
+
+//art_cc_test {
+//    name: "art_titrace_tests",
+//    defaults: [
+//        "art_gtest_defaults",
+//    ],
+//    srcs: ["titrace_test.cc"],
+//}
diff --git a/tools/jit-load/README.md b/tools/jit-load/README.md
new file mode 100644
index 0000000..8aa4513
--- /dev/null
+++ b/tools/jit-load/README.md
@@ -0,0 +1,35 @@
+# jitload
+
+Jitload is an art-specific agent allowing one to count the number of classes
+loaded on the jit-thread or verify that none were.
+
+# Usage
+### Build
+>    `make libjitload`  # or 'make libjitloadd' with debugging checks enabled
+
+The libraries will be built for 32-bit, 64-bit, host and target. Below examples assume you want to use the 64-bit version.
+### Command Line
+
+>    `art -Xplugin:$ANDROID_HOST_OUT/lib64/libopenjdkjvmti.so -agentpath:$ANDROID_HOST_OUT/lib64/libjitload.so -cp tmp/java/helloworld.dex -Xint helloworld`
+
+* `-Xplugin` and `-agentpath` need to be used, otherwise libtitrace agent will fail during init.
+* If using `libartd.so`, make sure to use the debug version of jvmti and agent.
+* Pass the '=fatal' option to the agent to cause it to abort if any classes are
+  loaded on a jit thread. Otherwise a warning will be printed.
+
+>    `art -d -Xplugin:$ANDROID_HOST_OUT/lib64/libopenjdkjvmtid.so -agentpath:$ANDROID_HOST_OUT/lib64/libjitloadd.so=fatal -cp tmp/java/helloworld.dex -Xint helloworld`
+
+* To use with run-test or testrunner.py use the --with-agent argument.
+
+>    `./test/run-test --host --with-agent libtitraced.so=fatal 001-HelloWorld`
+
+
+### Printing the Results
+All statistics gathered during the trace are printed automatically when the
+program normally exits. In the case of Android applications, they are always
+killed, so we need to manually print the results.
+
+>    `kill -SIGQUIT $(pid com.example.android.displayingbitmaps)`
+
+Will initiate a dump of the counts (to logcat).
+
diff --git a/tools/jit-load/jitload.cc b/tools/jit-load/jitload.cc
new file mode 100644
index 0000000..d67eef0
--- /dev/null
+++ b/tools/jit-load/jitload.cc
@@ -0,0 +1,144 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <android-base/logging.h>
+#include <jni.h>
+#include <jvmti.h>
+
+#include "base/runtime_debug.h"
+#include "jit/jit.h"
+#include "runtime-inl.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+
+namespace jitload {
+
+// Special env version that allows JVMTI-like access on userdebug builds.
+static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
+
+#define CHECK_CALL_SUCCESS(c) \
+  do { \
+    auto vc = (c); \
+    CHECK(vc == JNI_OK || vc == JVMTI_ERROR_NONE) << "call " << #c  << " did not succeed\n"; \
+  } while (false)
+
+static jthread GetJitThread() {
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  auto* jit = art::Runtime::Current()->GetJit();
+  if (jit == nullptr) {
+    return nullptr;
+  }
+  auto* thread_pool = jit->GetThreadPool();
+  if (thread_pool == nullptr) {
+    return nullptr;
+  }
+  // Currently we only have a single jit thread so we only look at that one.
+  return soa.AddLocalReference<jthread>(
+          thread_pool->GetWorkers()[0]->GetThread()->GetPeerFromOtherThread());
+}
+
+JNICALL void VmInitCb(jvmtiEnv* jvmti,
+                      JNIEnv* env ATTRIBUTE_UNUSED,
+                      jthread curthread ATTRIBUTE_UNUSED) {
+  jthread jit_thread = GetJitThread();
+  if (jit_thread != nullptr) {
+    CHECK_EQ(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, jit_thread),
+             JVMTI_ERROR_NONE);
+  }
+}
+
+struct AgentOptions {
+  bool fatal;
+  uint64_t cnt;
+};
+
+JNICALL static void DataDumpRequestCb(jvmtiEnv* jvmti) {
+  AgentOptions* ops;
+  CHECK_CALL_SUCCESS(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&ops)));
+  LOG(WARNING) << "Jit thread has loaded " << ops->cnt << " classes";
+}
+
+JNICALL void ClassPrepareJit(jvmtiEnv* jvmti,
+                             JNIEnv* jni_env ATTRIBUTE_UNUSED,
+                             jthread thr ATTRIBUTE_UNUSED,
+                             jclass klass) {
+  AgentOptions* ops;
+  CHECK_CALL_SUCCESS(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&ops)));
+  char* klass_name;
+  CHECK_CALL_SUCCESS(jvmti->GetClassSignature(klass, &klass_name, nullptr));
+  (ops->fatal ? LOG_STREAM(FATAL)
+              : LOG_STREAM(WARNING)) << "Loaded " << klass_name << " on jit thread!";
+  ops->cnt++;
+  CHECK_CALL_SUCCESS(jvmti->Deallocate(reinterpret_cast<unsigned char*>(klass_name)));
+}
+
+JNICALL void VMDeathCb(jvmtiEnv* jvmti, JNIEnv* env ATTRIBUTE_UNUSED) {
+  DataDumpRequestCb(jvmti);
+}
+
+static jvmtiEnv* SetupJvmti(JavaVM* vm, const char* options) {
+  android::base::InitLogging(/* argv */nullptr);
+
+  jvmtiEnv* jvmti = nullptr;
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_0) != JNI_OK &&
+      vm->GetEnv(reinterpret_cast<void**>(&jvmti), kArtTiVersion) != JNI_OK) {
+    LOG(FATAL) << "Unable to setup JVMTI environment!";
+  }
+  jvmtiEventCallbacks cb {
+        .VMInit = VmInitCb,
+        .ClassPrepare = ClassPrepareJit,
+        .DataDumpRequest = DataDumpRequestCb,
+        .VMDeath = VMDeathCb,
+  };
+  AgentOptions* ops;
+  CHECK_CALL_SUCCESS(
+      jvmti->Allocate(sizeof(AgentOptions), reinterpret_cast<unsigned char**>(&ops)));
+  ops->fatal = (strcmp(options, "fatal") == 0);
+  ops->cnt = 0;
+  CHECK_CALL_SUCCESS(jvmti->SetEnvironmentLocalStorage(ops));
+  CHECK_CALL_SUCCESS(jvmti->SetEventCallbacks(&cb, sizeof(cb)));
+  CHECK_CALL_SUCCESS(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, nullptr));
+  CHECK_CALL_SUCCESS(
+      jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DATA_DUMP_REQUEST, nullptr));
+  return jvmti;
+}
+
+// Early attachment (e.g. 'java -agent[lib|path]:filename.so').
+extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* /* reserved */) {
+  SetupJvmti(vm, options);
+  return JNI_OK;
+}
+
+// Late attachment (e.g. 'am attach-agent').
+extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char* options, void* /* reserved */) {
+  jvmtiEnv* jvmti = SetupJvmti(vm, options);
+
+  JNIEnv* jni = nullptr;
+  jthread thr = nullptr;
+  CHECK_CALL_SUCCESS(vm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_6));
+  CHECK_CALL_SUCCESS(jvmti->GetCurrentThread(&thr));
+
+  // Final setup is done in the VmInitCb.
+  VmInitCb(jvmti, jni, thr);
+
+  jni->DeleteLocalRef(thr);
+  return JNI_OK;
+}
+
+#undef CHECK_CALL_SUCCESS
+
+}  // namespace jitload
+