ART: Add GetFrameCount and GetFrameLocation
Add support for GetFrameCount and GetFrameLocation. Add tests.
Bug: 31684812
Test: m test-art-host-run-test-911-get-stack-trace
Change-Id: I7656e243f614eb0ceb5fcd6841128119fad89968
diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt
index 061101c..dad08c9 100644
--- a/test/911-get-stack-trace/expected.txt
+++ b/test/911-get-stack-trace/expected.txt
@@ -773,4 +773,64 @@
doTest ()V 101 54
main ([Ljava/lang/String;)V 38 37
+
+###################
+### Same thread ###
+###################
+4
+JVMTI_ERROR_ILLEGAL_ARGUMENT
+[public static native java.lang.Object[] Frames.getFrameLocation(java.lang.Thread,int), ffffffff]
+[public static void Frames.doTestSameThread(), 38]
+[public static void Frames.doTest() throws java.lang.Exception, 0]
+[public static void Main.main(java.lang.String[]) throws java.lang.Exception, 2e]
+JVMTI_ERROR_NO_MORE_FRAMES
+
+################################
+### Other thread (suspended) ###
+################################
+18
+JVMTI_ERROR_ILLEGAL_ARGUMENT
+[public final native void java.lang.Object.wait() throws java.lang.InterruptedException, ffffffff]
+[private static void Recurse.printOrWait(int,int,ControlData), 18]
+[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 2]
+[private static long Recurse.bar(int,int,int,ControlData), 0]
+[public static int Recurse.foo(int,int,int,ControlData), 0]
+[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9]
+[private static long Recurse.bar(int,int,int,ControlData), 0]
+[public static int Recurse.foo(int,int,int,ControlData), 0]
+[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9]
+[private static long Recurse.bar(int,int,int,ControlData), 0]
+[public static int Recurse.foo(int,int,int,ControlData), 0]
+[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9]
+[private static long Recurse.bar(int,int,int,ControlData), 0]
+[public static int Recurse.foo(int,int,int,ControlData), 0]
+[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9]
+[private static long Recurse.bar(int,int,int,ControlData), 0]
+[public static int Recurse.foo(int,int,int,ControlData), 0]
+[public void Frames$1.run(), 4]
+JVMTI_ERROR_NO_MORE_FRAMES
+
+###########################
+### Other thread (live) ###
+###########################
+17
+JVMTI_ERROR_ILLEGAL_ARGUMENT
+[private static void Recurse.printOrWait(int,int,ControlData), 2c]
+[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 2]
+[private static long Recurse.bar(int,int,int,ControlData), 0]
+[public static int Recurse.foo(int,int,int,ControlData), 0]
+[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9]
+[private static long Recurse.bar(int,int,int,ControlData), 0]
+[public static int Recurse.foo(int,int,int,ControlData), 0]
+[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9]
+[private static long Recurse.bar(int,int,int,ControlData), 0]
+[public static int Recurse.foo(int,int,int,ControlData), 0]
+[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9]
+[private static long Recurse.bar(int,int,int,ControlData), 0]
+[public static int Recurse.foo(int,int,int,ControlData), 0]
+[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9]
+[private static long Recurse.bar(int,int,int,ControlData), 0]
+[public static int Recurse.foo(int,int,int,ControlData), 0]
+[public void Frames$2.run(), 4]
+JVMTI_ERROR_NO_MORE_FRAMES
Done
diff --git a/test/911-get-stack-trace/src/Frames.java b/test/911-get-stack-trace/src/Frames.java
new file mode 100644
index 0000000..a1a11c3
--- /dev/null
+++ b/test/911-get-stack-trace/src/Frames.java
@@ -0,0 +1,133 @@
+/*
+ * 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.util.Arrays;
+
+public class Frames {
+ public static void doTest() throws Exception {
+ doTestSameThread();
+
+ System.out.println();
+
+ doTestOtherThreadWait();
+
+ System.out.println();
+
+ doTestOtherThreadBusyLoop();
+ }
+
+ public static void doTestSameThread() {
+ System.out.println("###################");
+ System.out.println("### Same thread ###");
+ System.out.println("###################");
+
+ Thread t = Thread.currentThread();
+
+ int count = getFrameCount(t);
+ System.out.println(count);
+ try {
+ System.out.println(Arrays.toString(getFrameLocation(t, -1)));
+ } catch (RuntimeException e) {
+ System.out.println(e.getMessage());
+ }
+ for (int i = 0; i < count; i++) {
+ System.out.println(Arrays.toString(getFrameLocation(t, i)));
+ }
+ try {
+ System.out.println(Arrays.toString(getFrameLocation(t, count)));
+ } catch (RuntimeException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ public static void doTestOtherThreadWait() throws Exception {
+ System.out.println("################################");
+ System.out.println("### Other thread (suspended) ###");
+ System.out.println("################################");
+ final ControlData data = new ControlData();
+ data.waitFor = new Object();
+ Thread t = new Thread() {
+ public void run() {
+ Recurse.foo(4, 0, 0, data);
+ }
+ };
+ t.start();
+ data.reached.await();
+ Thread.yield();
+ Thread.sleep(500); // A little bit of time...
+
+ int count = getFrameCount(t);
+ System.out.println(count);
+ try {
+ System.out.println(Arrays.toString(getFrameLocation(t, -1)));
+ } catch (RuntimeException e) {
+ System.out.println(e.getMessage());
+ }
+ for (int i = 0; i < count; i++) {
+ System.out.println(Arrays.toString(getFrameLocation(t, i)));
+ }
+ try {
+ System.out.println(Arrays.toString(getFrameLocation(t, count)));
+ } catch (RuntimeException e) {
+ System.out.println(e.getMessage());
+ }
+
+ // Let the thread make progress and die.
+ synchronized(data.waitFor) {
+ data.waitFor.notifyAll();
+ }
+ t.join();
+ }
+
+ public static void doTestOtherThreadBusyLoop() throws Exception {
+ System.out.println("###########################");
+ System.out.println("### Other thread (live) ###");
+ System.out.println("###########################");
+ final ControlData data = new ControlData();
+ Thread t = new Thread() {
+ public void run() {
+ Recurse.foo(4, 0, 0, data);
+ }
+ };
+ t.start();
+ data.reached.await();
+ Thread.yield();
+ Thread.sleep(500); // A little bit of time...
+
+ int count = getFrameCount(t);
+ System.out.println(count);
+ try {
+ System.out.println(Arrays.toString(getFrameLocation(t, -1)));
+ } catch (RuntimeException e) {
+ System.out.println(e.getMessage());
+ }
+ for (int i = 0; i < count; i++) {
+ System.out.println(Arrays.toString(getFrameLocation(t, i)));
+ }
+ try {
+ System.out.println(Arrays.toString(getFrameLocation(t, count)));
+ } catch (RuntimeException e) {
+ System.out.println(e.getMessage());
+ }
+
+ // Let the thread stop looping and die.
+ data.stop = true;
+ t.join();
+ }
+
+ public static native int getFrameCount(Thread thread);
+ public static native Object[] getFrameLocation(Thread thread, int depth);
+}
diff --git a/test/911-get-stack-trace/src/Main.java b/test/911-get-stack-trace/src/Main.java
index 2df5c53..b199033 100644
--- a/test/911-get-stack-trace/src/Main.java
+++ b/test/911-get-stack-trace/src/Main.java
@@ -36,6 +36,10 @@
ThreadListTraces.doTest();
+ System.out.println();
+
+ Frames.doTest();
+
System.out.println("Done");
}
}
diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc
index f853387..d162e8a 100644
--- a/test/911-get-stack-trace/stack_trace.cc
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -20,6 +20,7 @@
#include "android-base/stringprintf.h"
+#include "android-base/stringprintf.h"
#include "base/logging.h"
#include "base/macros.h"
#include "jni.h"
@@ -202,5 +203,55 @@
return ret;
}
+extern "C" JNIEXPORT jint JNICALL Java_Frames_getFrameCount(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread) {
+ jint count;
+ jvmtiError result = jvmti_env->GetFrameCount(thread, &count);
+ if (JvmtiErrorToException(env, result)) {
+ return -1;
+ }
+ return count;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Frames_getFrameLocation(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint depth) {
+ jmethodID method;
+ jlocation location;
+
+ jvmtiError result = jvmti_env->GetFrameLocation(thread, depth, &method, &location);
+ if (JvmtiErrorToException(env, result)) {
+ return nullptr;
+ }
+
+ auto callback = [&](jint index) -> jobject {
+ switch (index) {
+ case 0:
+ {
+ jclass decl_class;
+ jvmtiError class_result = jvmti_env->GetMethodDeclaringClass(method, &decl_class);
+ if (JvmtiErrorToException(env, class_result)) {
+ return nullptr;
+ }
+ jint modifiers;
+ jvmtiError mod_result = jvmti_env->GetMethodModifiers(method, &modifiers);
+ if (JvmtiErrorToException(env, mod_result)) {
+ return nullptr;
+ }
+ constexpr jint kStatic = 0x8;
+ return env->ToReflectedMethod(decl_class,
+ method,
+ (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
+ }
+ case 1:
+ return env->NewStringUTF(
+ android::base::StringPrintf("%x", static_cast<uint32_t>(location)).c_str());
+ }
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ };
+ jobjectArray ret = CreateObjectArray(env, 2, "java/lang/Object", callback);
+ return ret;
+}
+
} // namespace Test911GetStackTrace
} // namespace art