diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/708-jit-cache-churn/expected.txt | 2 | ||||
| -rw-r--r-- | test/708-jit-cache-churn/info.txt | 1 | ||||
| -rw-r--r-- | test/708-jit-cache-churn/jit.cc | 56 | ||||
| -rw-r--r-- | test/708-jit-cache-churn/src/JitCacheChurnTest.java | 279 | ||||
| -rw-r--r-- | test/708-jit-cache-churn/src/Main.java | 31 | ||||
| -rw-r--r-- | test/Android.bp | 1 | ||||
| -rw-r--r-- | test/knownfailures.json | 18 | ||||
| -rw-r--r-- | test/testrunner/target_config.py | 11 | ||||
| -rw-r--r-- | test/ti-stress/stress.cc | 245 |
9 files changed, 544 insertions, 100 deletions
diff --git a/test/708-jit-cache-churn/expected.txt b/test/708-jit-cache-churn/expected.txt new file mode 100644 index 0000000000..77a1486479 --- /dev/null +++ b/test/708-jit-cache-churn/expected.txt @@ -0,0 +1,2 @@ +JNI_OnLoad called +Done diff --git a/test/708-jit-cache-churn/info.txt b/test/708-jit-cache-churn/info.txt new file mode 100644 index 0000000000..4aaa3d48c9 --- /dev/null +++ b/test/708-jit-cache-churn/info.txt @@ -0,0 +1 @@ +Tests JIT cache for page permission updates and CPU cache inconsistencies. Only runs when test runner permits JIT, e.g. --jit. diff --git a/test/708-jit-cache-churn/jit.cc b/test/708-jit-cache-churn/jit.cc new file mode 100644 index 0000000000..1284a8703d --- /dev/null +++ b/test/708-jit-cache-churn/jit.cc @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#include "jni.h" + +#include "art_method.h" +#include "jit/jit.h" +#include "jit/jit_code_cache.h" +#include "jni_internal.h" +#include "mirror/class.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "thread_list.h" + +namespace art { + +extern "C" JNIEXPORT +jboolean +Java_JitCacheChurnTest_removeJitCompiledMethod(JNIEnv* env, + jclass, + jobject javaMethod, + jboolean releaseMemory) { + if (!Runtime::Current()->UseJitCompilation()) { + return JNI_FALSE; + } + + jit::Jit* jit = Runtime::Current()->GetJit(); + jit->WaitForCompilationToFinish(Thread::Current()); + + ScopedObjectAccess soa(env); + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); + + jit::JitCodeCache* code_cache = jit->GetCodeCache(); + + // Drop the shared mutator lock + ScopedThreadSuspension selfSuspension(Thread::Current(), art::ThreadState::kNative); + // Get exclusive mutator lock with suspend all. + ScopedSuspendAll suspend("Removing JIT compiled method", /*long_suspend*/true); + bool removed = code_cache->RemoveMethod(method, static_cast<bool>(releaseMemory)); + return removed ? JNI_TRUE : JNI_FALSE; +} + +} // namespace art diff --git a/test/708-jit-cache-churn/src/JitCacheChurnTest.java b/test/708-jit-cache-churn/src/JitCacheChurnTest.java new file mode 100644 index 0000000000..abc5f35f70 --- /dev/null +++ b/test/708-jit-cache-churn/src/JitCacheChurnTest.java @@ -0,0 +1,279 @@ +/* + * 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. + */ + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.Callable; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * A test driver for JIT compiling methods and looking for JIT + * cache issues. + */ +public class JitCacheChurnTest { + /* The name of methods to JIT */ + private static final String JITTED_METHOD = "$noinline$Call"; + + /* The number of cores to oversubscribe load by. */ + private static final int OVERSUBSCRIBED_CORES = 1; + + /* The number of concurrent executions of methods to be JIT compiled. */ + private static final int CONCURRENCY = + Runtime.getRuntime().availableProcessors() + OVERSUBSCRIBED_CORES; + + /* The number of times the methods to be JIT compiled should be executed per thread. */ + private static final int METHOD_ITERATIONS = 10; + + /* Number of test iterations JIT methods and removing methods from JIT cache. */ + private static final int TEST_ITERATIONS = 512; + + /* Tasks to run and generate compiled code of various sizes */ + private static final BaseTask [] TASKS = { + new TaskOne(), new TaskTwo(), new TaskThree(), new TaskFour(), new TaskFive(), new TaskSix(), + new TaskSeven(), new TaskEight(), new TaskNine(), new TaskTen() + }; + private static final int TASK_BITMASK = (1 << TASKS.length) - 1; + + private final ExecutorService executorService; + private int runMask = 0; + + private JitCacheChurnTest() { + this.executorService = new ThreadPoolExecutor(CONCURRENCY, CONCURRENCY, 5000, + TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>()); + } + + private void shutdown() { + this.executorService.shutdown(); + } + + private void runTasks(Callable<Integer> task) { + // Force JIT compilation of tasks method. + ensureJitCompiled(task.getClass(), JITTED_METHOD); + + // Launch worker threads to run JIT compiled method. + try { + ArrayList<Callable<Integer>> tasks = new ArrayList<>(CONCURRENCY); + for (int i = 0; i < CONCURRENCY; ++i) { + tasks.add(i, task); + } + + List<Future<Integer>> results = executorService.invokeAll(tasks); + for (Future<?> result : results) { + result.get(); + } + } catch (InterruptedException | ExecutionException e) { + System.err.println(e); + System.exit(-1); + } + } + + private static abstract class BaseTask implements Callable<Integer> { + private static CyclicBarrier barrier = new CyclicBarrier(CONCURRENCY); + + public Integer call() throws Exception { + barrier.await(); + int iterations = METHOD_ITERATIONS + 1; + for (int i = 0; i < iterations; ++i) { + $noinline$Call(); + } + return $noinline$Call(); + } + + protected abstract Integer $noinline$Call(); + } + + private static class TaskOne extends BaseTask { + @Override + protected Integer $noinline$Call() { + return null; + } + } + + private static class TaskTwo extends BaseTask { + @Override + protected Integer $noinline$Call() { + return 0; + } + } + + private static class TaskThree extends BaseTask { + @Override + protected Integer $noinline$Call() { + int sum = 0; + for (int i = 0; i < 3; ++i) { + sum = i * (i + 1); + } + return sum; + } + } + + private static class TaskFour extends BaseTask { + @Override + protected Integer $noinline$Call() { + int sum = 0; + for (int i = 0; i < 10; ++i) { + int bits = i; + bits = ((bits >>> 1) & 0x55555555) | ((bits << 1) & 0x55555555); + bits = ((bits >>> 2) & 0x33333333) | ((bits << 2) & 0x33333333); + bits = ((bits >>> 4) & 0x0f0f0f0f) | ((bits << 4) & 0x0f0f0f0f); + bits = ((bits >>> 8) & 0x00ff00ff) | ((bits << 8) & 0x00ff00ff); + bits = (bits >>> 16) | (bits << 16); + sum += bits; + } + return sum; + } + } + + private static class TaskFive extends BaseTask { + static final AtomicInteger instances = new AtomicInteger(0); + int instance; + TaskFive() { + instance = instances.getAndIncrement(); + } + protected Integer $noinline$Call() { + return instance; + } + } + + private static class TaskSix extends TaskFive { + protected Integer $noinline$Call() { + return instance + 1; + } + } + + private static class TaskSeven extends TaskFive { + protected Integer $noinline$Call() { + return 2 * instance + 1; + } + } + + private static class TaskEight extends TaskFive { + protected Integer $noinline$Call() { + double a = Math.cosh(2.22 * instance); + double b = a / 2; + double c = b * 3; + double d = a + b + c; + if (d > 42) { + d *= Math.max(Math.sin(d), Math.sinh(d)); + d *= Math.max(1.33, 0.17 * Math.sinh(d)); + d *= Math.max(1.34, 0.21 * Math.sinh(d)); + d *= Math.max(1.35, 0.32 * Math.sinh(d)); + d *= Math.max(1.36, 0.41 * Math.sinh(d)); + d *= Math.max(1.37, 0.57 * Math.sinh(d)); + d *= Math.max(1.38, 0.61 * Math.sinh(d)); + d *= Math.max(1.39, 0.79 * Math.sinh(d)); + d += Double.parseDouble("3.711e23"); + } + + if (d > 3) { + return (int) a; + } else { + return (int) b; + } + } + } + + private static class TaskNine extends TaskFive { + private final String [] numbers = { "One", "Two", "Three", "Four", "Five", "Six" }; + + protected Integer $noinline$Call() { + String number = numbers[instance % numbers.length]; + return number.length(); + } + } + + private static class TaskTen extends TaskFive { + private final String [] numbers = { "12345", "23451", "34512", "78901", "89012" }; + + protected Integer $noinline$Call() { + int odd = 0; + String number = numbers[instance % numbers.length]; + for (int i = 0; i < number.length(); i += 2) { + odd += Integer.parseInt(numbers[i]); + } + odd *= 3; + + int even = 0; + for (int i = 1; i < number.length(); i += 2) { + even += Integer.parseInt(numbers[i]); + } + return (odd + even) % 10; + } + } + + private void runAndJitMethods(int mask) { + runMask |= mask; + for (int index = 0; mask != 0; mask >>= 1, index++) { + if ((mask & 1) == 1) { + runTasks(TASKS[index]); + } + } + } + + private static void ensureJitCompiled(Class<?> klass, String name) { + Main.ensureJitCompiled(klass, name); + } + + private void removeJittedMethod(Class<?> klass, String name) { + Method method = null; + try { + method = klass.getDeclaredMethod(name); + } catch (NoSuchMethodException e) { + System.err.println(e); + System.exit(-1); + } + removeJitCompiledMethod(method, false); + } + + private void removeJittedMethods(int mask) { + mask = mask & runMask; + runMask ^= mask; + for (int index = 0; mask != 0; mask >>= 1, index++) { + if ((mask & 1) == 1) { + removeJittedMethod(TASKS[index].getClass(), JITTED_METHOD); + } + } + } + + private static int getMethodsAsMask(Random rng) { + return rng.nextInt(TASK_BITMASK) + 1; + } + + public static void run() { + JitCacheChurnTest concurrentExecution = new JitCacheChurnTest(); + Random invokeMethodGenerator = new Random(5); + Random removeMethodGenerator = new Random(7); + try { + for (int i = 0; i < TEST_ITERATIONS; ++i) { + concurrentExecution.runAndJitMethods(getMethodsAsMask(invokeMethodGenerator)); + concurrentExecution.removeJittedMethods(getMethodsAsMask(removeMethodGenerator)); + } + } finally { + concurrentExecution.shutdown(); + } + } + + private static native void removeJitCompiledMethod(Method method, boolean releaseMemory); +} diff --git a/test/708-jit-cache-churn/src/Main.java b/test/708-jit-cache-churn/src/Main.java new file mode 100644 index 0000000000..0595aae506 --- /dev/null +++ b/test/708-jit-cache-churn/src/Main.java @@ -0,0 +1,31 @@ +/* + * 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. + */ + +public class Main { + + public static void main(String[] args) throws Exception { + // Explicit loadLibrary here to pull JNI exports from arttestd. + System.loadLibrary(args[0]); + if (hasJit()) { + JitCacheChurnTest.run(); + } + System.out.println("Done"); + } + + static native boolean hasJit(); + + static native void ensureJitCompiled(Class<?> klass, String methodName); +} diff --git a/test/Android.bp b/test/Android.bp index 35c3d9c332..0937c62469 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -394,6 +394,7 @@ cc_defaults { "626-const-class-linking/clear_dex_cache_types.cc", "642-fp-callees/fp_callees.cc", "647-jni-get-field-id/get_field_id.cc", + "708-jit-cache-churn/jit.cc" ], shared_libs: [ "libbacktrace", diff --git a/test/knownfailures.json b/test/knownfailures.json index f51522669c..b89d457bc8 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -706,16 +706,20 @@ "description": "Test disabled due to redefine-stress disabling intrinsics which changes the trace output slightly." }, { - "tests": "137-cfi", - "description": [ "ASan is reporting out-of-bounds reads in libunwind."], - "variant": "host", - "env_vars": {"SANITIZE_HOST": "address"}, - "bug": "b/62350406" - }, - { "tests": ["137-cfi", "629-vdex-speed"], "description": [ "Tests require speed compilation which is no longer the default for", "no-prebuild or no-image configs."], "variant": "no-prebuild | no-image" + }, + { + "tests": ["059-finalizer-throw", "063-process-manager"], + "description": [ "Tests that take too long on target with gcstress and debug" ], + "variant": "gcstress & target & debug" + }, + { + "tests": ["905-object-free"], + "description": [ "Flake on gcstress" ], + "bug": "b/62562923", + "variant": "gcstress & jit & target" } ] diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 474c25947c..baf7600349 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -324,18 +324,23 @@ target_config = { # ASAN (host) configurations. + # These configurations need detect_leaks=0 to work in non-setup environments like build bots, + # as our build tools leak. b/37751350 + 'art-gtest-asan': { 'make' : 'test-art-host-gtest', 'env': { - 'SANITIZE_HOST' : 'address' + 'SANITIZE_HOST' : 'address', + 'ASAN_OPTIONS' : 'detect_leaks=0' } }, - 'art-run-test-asan': { + 'art-asan': { 'run-test' : ['--interpreter', '--optimizing', '--jit'], 'env': { - 'SANITIZE_HOST' : 'address' + 'SANITIZE_HOST' : 'address', + 'ASAN_OPTIONS' : 'detect_leaks=0' } }, diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc index 497db1cd3e..515a39126d 100644 --- a/test/ti-stress/stress.cc +++ b/test/ti-stress/stress.cc @@ -18,6 +18,7 @@ #include <stdio.h> #include <iostream> #include <fstream> +#include <memory> #include <stdio.h> #include <sstream> #include <strstream> @@ -87,6 +88,142 @@ static bool DoExtractClassFromData(StressData* data, return ReadIntoBuffer(data->out_temp_dex, dex); } +class ScopedThreadInfo { + public: + ScopedThreadInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jthread thread) + : jvmtienv_(jvmtienv), env_(env), free_name_(false) { + memset(&info_, 0, sizeof(info_)); + if (thread == nullptr) { + info_.name = const_cast<char*>("<NULLPTR>"); + } else if (jvmtienv->GetThreadInfo(thread, &info_) != JVMTI_ERROR_NONE) { + info_.name = const_cast<char*>("<UNKNOWN THREAD>"); + } else { + free_name_ = true; + } + } + + ~ScopedThreadInfo() { + if (free_name_) { + jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(info_.name)); + } + env_->DeleteLocalRef(info_.thread_group); + env_->DeleteLocalRef(info_.context_class_loader); + } + + const char* GetName() const { + return info_.name; + } + + private: + jvmtiEnv* jvmtienv_; + JNIEnv* env_; + bool free_name_; + jvmtiThreadInfo info_; +}; + +class ScopedClassInfo { + public: + ScopedClassInfo(jvmtiEnv* jvmtienv, jclass c) + : jvmtienv_(jvmtienv), + class_(c), + name_(nullptr), + generic_(nullptr) {} + + ~ScopedClassInfo() { + jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_)); + jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_)); + } + + bool Init() { + return jvmtienv_->GetClassSignature(class_, &name_, &generic_) == JVMTI_ERROR_NONE; + } + + jclass GetClass() const { + return class_; + } + const char* GetName() const { + return name_; + } + const char* GetGeneric() const { + return generic_; + } + + private: + jvmtiEnv* jvmtienv_; + jclass class_; + char* name_; + char* generic_; +}; + +class ScopedMethodInfo { + public: + ScopedMethodInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jmethodID m) + : jvmtienv_(jvmtienv), + env_(env), + method_(m), + declaring_class_(nullptr), + class_info_(nullptr), + name_(nullptr), + signature_(nullptr), + generic_(nullptr) {} + + ~ScopedMethodInfo() { + env_->DeleteLocalRef(declaring_class_); + jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_)); + jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(signature_)); + jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_)); + } + + bool Init() { + if (jvmtienv_->GetMethodDeclaringClass(method_, &declaring_class_) != JVMTI_ERROR_NONE) { + return false; + } + class_info_.reset(new ScopedClassInfo(jvmtienv_, declaring_class_)); + return class_info_->Init() && + (jvmtienv_->GetMethodName(method_, &name_, &signature_, &generic_) == JVMTI_ERROR_NONE); + } + + const ScopedClassInfo& GetDeclaringClassInfo() const { + return *class_info_; + } + + jclass GetDeclaringClass() const { + return declaring_class_; + } + + const char* GetName() const { + return name_; + } + + const char* GetSignature() const { + return signature_; + } + + const char* GetGeneric() const { + return generic_; + } + + private: + jvmtiEnv* jvmtienv_; + JNIEnv* env_; + jmethodID method_; + jclass declaring_class_; + std::unique_ptr<ScopedClassInfo> class_info_; + char* name_; + char* signature_; + char* generic_; + + friend std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m); +}; + +std::ostream& operator<<(std::ostream &os, const ScopedMethodInfo* m) { + return os << *m; +} + +std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m) { + return os << m.GetDeclaringClassInfo().GetName() << "->" << m.GetName() << m.GetSignature(); +} + static void doJvmtiMethodBind(jvmtiEnv* jvmtienv, JNIEnv* env, jthread thread, @@ -94,38 +231,14 @@ static void doJvmtiMethodBind(jvmtiEnv* jvmtienv, void* address, /*out*/void** out_address) { *out_address = address; - jvmtiThreadInfo info; - if (thread == nullptr) { - info.name = const_cast<char*>("<NULLPTR>"); - } else if (jvmtienv->GetThreadInfo(thread, &info) != JVMTI_ERROR_NONE) { - info.name = const_cast<char*>("<UNKNOWN THREAD>"); - } - char *fname, *fsig, *fgen; - char *cname, *cgen; - jclass klass = nullptr; - if (jvmtienv->GetMethodDeclaringClass(m, &klass) != JVMTI_ERROR_NONE) { - LOG(ERROR) << "Unable to get method declaring class!"; + ScopedThreadInfo thread_info(jvmtienv, env, thread); + ScopedMethodInfo method_info(jvmtienv, env, m); + if (!method_info.Init()) { + LOG(ERROR) << "Unable to get method info!"; return; } - if (jvmtienv->GetMethodName(m, &fname, &fsig, &fgen) != JVMTI_ERROR_NONE) { - LOG(ERROR) << "Unable to get method name!"; - env->DeleteLocalRef(klass); - return; - } - if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) { - LOG(ERROR) << "Unable to get class name!"; - env->DeleteLocalRef(klass); - return; - } - LOG(INFO) << "Loading native method \"" << cname << "->" << fname << fsig << "\". Thread is \"" - << info.name << "\""; - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cname)); - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cgen)); - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fname)); - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fsig)); - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fgen)); - env->DeleteLocalRef(klass); - return; + LOG(INFO) << "Loading native method \"" << method_info << "\". Thread is " + << thread_info.GetName(); } static std::string GetName(jvmtiEnv* jvmtienv, JNIEnv* jnienv, jobject obj) { @@ -197,80 +310,32 @@ void JNICALL MethodExitHook(jvmtiEnv* jvmtienv, jmethodID m, jboolean was_popped_by_exception, jvalue val) { - jvmtiThreadInfo info; - if (thread == nullptr) { - info.name = const_cast<char*>("<NULLPTR>"); - } else if (jvmtienv->GetThreadInfo(thread, &info) != JVMTI_ERROR_NONE) { - // LOG(WARNING) << "Unable to get thread info!"; - info.name = const_cast<char*>("<UNKNOWN THREAD>"); - } - char *fname, *fsig, *fgen; - char *cname, *cgen; - jclass klass = nullptr; - if (jvmtienv->GetMethodDeclaringClass(m, &klass) != JVMTI_ERROR_NONE) { - LOG(ERROR) << "Unable to get method declaring class!"; + ScopedThreadInfo info(jvmtienv, env, thread); + ScopedMethodInfo method_info(jvmtienv, env, m); + if (!method_info.Init()) { + LOG(ERROR) << "Unable to get method info!"; return; } - if (jvmtienv->GetMethodName(m, &fname, &fsig, &fgen) != JVMTI_ERROR_NONE) { - LOG(ERROR) << "Unable to get method name!"; - env->DeleteLocalRef(klass); - return; - } - if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) { - LOG(ERROR) << "Unable to get class name!"; - env->DeleteLocalRef(klass); - return; - } - std::string type(fsig); + std::string type(method_info.GetSignature()); type = type.substr(type.find(")") + 1); std::string out_val(was_popped_by_exception ? "" : GetValOf(jvmtienv, env, type, val)); - LOG(INFO) << "Leaving method \"" << cname << "->" << fname << fsig << "\". Thread is \"" - << info.name << "\"." << std::endl + LOG(INFO) << "Leaving method \"" << method_info << "\". Thread is \"" << info.GetName() << "\"." + << std::endl << " Cause: " << (was_popped_by_exception ? "exception" : "return ") << out_val << "."; - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cname)); - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cgen)); - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fname)); - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fsig)); - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fgen)); - env->DeleteLocalRef(klass); } void JNICALL MethodEntryHook(jvmtiEnv* jvmtienv, JNIEnv* env, jthread thread, jmethodID m) { - jvmtiThreadInfo info; - if (thread == nullptr) { - info.name = const_cast<char*>("<NULLPTR>"); - } else if (jvmtienv->GetThreadInfo(thread, &info) != JVMTI_ERROR_NONE) { - info.name = const_cast<char*>("<UNKNOWN THREAD>"); - } - char *fname, *fsig, *fgen; - char *cname, *cgen; - jclass klass = nullptr; - if (jvmtienv->GetMethodDeclaringClass(m, &klass) != JVMTI_ERROR_NONE) { - LOG(ERROR) << "Unable to get method declaring class!"; - return; - } - if (jvmtienv->GetMethodName(m, &fname, &fsig, &fgen) != JVMTI_ERROR_NONE) { - LOG(ERROR) << "Unable to get method name!"; - env->DeleteLocalRef(klass); + ScopedThreadInfo info(jvmtienv, env, thread); + ScopedMethodInfo method_info(jvmtienv, env, m); + if (!method_info.Init()) { + LOG(ERROR) << "Unable to get method info!"; return; } - if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) { - LOG(ERROR) << "Unable to get class name!"; - env->DeleteLocalRef(klass); - return; - } - LOG(INFO) << "Entering method \"" << cname << "->" << fname << fsig << "\". Thread is \"" - << info.name << "\""; - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cname)); - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cgen)); - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fname)); - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fsig)); - jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fgen)); - env->DeleteLocalRef(klass); + LOG(INFO) << "Entering method \"" << method_info << "\". Thread is \"" << info.GetName() << "\""; } // The hook we are using. |