// 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 #include #include #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( 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(&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(&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(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(&jvmti), JVMTI_VERSION_1_0) != JNI_OK && vm->GetEnv(reinterpret_cast(&jvmti), kArtTiVersion) != JNI_OK) { LOG(FATAL) << "Unable to setup JVMTI environment!"; } jvmtiEventCallbacks cb { .VMInit = VmInitCb, .VMDeath = VMDeathCb, .ClassPrepare = ClassPrepareJit, .DataDumpRequest = DataDumpRequestCb, }; AgentOptions* ops; CHECK_CALL_SUCCESS( jvmti->Allocate(sizeof(AgentOptions), reinterpret_cast(&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(&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