summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/708-jit-cache-churn/expected.txt2
-rw-r--r--test/708-jit-cache-churn/info.txt1
-rw-r--r--test/708-jit-cache-churn/jit.cc56
-rw-r--r--test/708-jit-cache-churn/src/JitCacheChurnTest.java279
-rw-r--r--test/708-jit-cache-churn/src/Main.java31
-rw-r--r--test/Android.bp1
-rw-r--r--test/knownfailures.json18
-rw-r--r--test/testrunner/target_config.py11
-rw-r--r--test/ti-stress/stress.cc245
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.