ART: Refactor run-test 913
Remove references to ART. Filter roots from the JIT. Canonicalize
some thread IDs. Move the test to its own thread, and filter
stack-locals of other threads.
These changes ensure that the test is less dependent on the main
thread and its environment, which is required to use it in CTS.
Also fix a reporting issue for roots.
Bug: 32072923
Test: art/test/testrunner/testrunner.py --host -t 913
Change-Id: I8480ba7751fb6420c256db87cba11b8a65e25ea5
diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc
index 49d9aca..7fc5104 100644
--- a/runtime/openjdkjvmti/ti_heap.cc
+++ b/runtime/openjdkjvmti/ti_heap.cc
@@ -888,8 +888,8 @@
bool add_to_worklist = ReportRoot(root_obj, info);
// We use visited_ to mark roots already so we do not need another set.
if (visited_->find(root_obj) == visited_->end()) {
- visited_->insert(root_obj);
if (add_to_worklist) {
+ visited_->insert(root_obj);
worklist_->push_back(root_obj);
}
}
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index 2a183ee..46e8171 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -1,9 +1,12 @@
---
true true
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
+0@0 --(array-element@0)--> 3000@0 [size=136, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
@@ -40,11 +43,14 @@
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
root@root --(thread)--> 1@1000 [size=16, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
+0@0 --(array-element@0)--> 3000@0 [size=136, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
@@ -79,18 +85,21 @@
5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
6@1000 --(class)--> 1000@0 [size=123, length=-1]
---
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
---
3@1001 --(class)--> 1001@0 [size=123, length=-1]
---
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
---
3@1001 --(class)--> 1001@0 [size=123, length=-1]
---
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
+0@0 --(array-element@0)--> 3000@0 [size=136, length=-1]
---
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
3@1001 --(class)--> 1001@0 [size=123, length=-1]
@@ -99,11 +108,14 @@
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
root@root --(thread)--> 1@1000 [size=16, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
+0@0 --(array-element@0)--> 3000@0 [size=136, length=-1]
---
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
3@1001 --(class)--> 1001@0 [size=123, length=-1]
@@ -182,10 +194,13 @@
---
---
---- untagged objects
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
+0@0 --(array-element@0)--> 3000@0 [size=136, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
@@ -222,11 +237,14 @@
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
root@root --(thread)--> 1@1000 [size=16, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
+0@0 --(array-element@0)--> 3000@0 [size=136, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
@@ -262,8 +280,11 @@
6@1000 --(class)--> 1000@0 [size=123, length=-1]
---
---- tagged classes
-root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
+0@0 --(array-element@0)--> 3000@0 [size=136, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
@@ -286,7 +307,10 @@
5@1002 --(class)--> 1002@0 [size=123, length=-1]
6@1000 --(class)--> 1000@0 [size=123, length=-1]
---
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
+0@0 --(array-element@0)--> 3000@0 [size=136, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 6a06b29..a1b76d5 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -19,40 +19,35 @@
#include <string.h>
#include <iostream>
+#include <sstream>
#include <vector>
#include "android-base/macros.h"
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
-#include "jit/jit.h"
#include "jni.h"
-#include "native_stack_dump.h"
#include "jvmti.h"
-#include "runtime.h"
-#include "scoped_thread_state_change-inl.h"
-#include "thread-inl.h"
-#include "thread_list.h"
// Test infrastructure
#include "jni_helper.h"
#include "jvmti_helper.h"
#include "test_env.h"
+#include "ti_utf.h"
namespace art {
namespace Test913Heaps {
using android::base::StringPrintf;
+#define FINAL final
+#define OVERRIDE override
+#define UNREACHABLE __builtin_unreachable
+
extern "C" JNIEXPORT void JNICALL Java_art_Test913_forceGarbageCollection(
- JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
jvmtiError ret = jvmti_env->ForceGarbageCollection();
- if (ret != JVMTI_ERROR_NONE) {
- char* err;
- jvmti_env->GetErrorName(ret, &err);
- printf("Error forcing a garbage collection: %s\n", err);
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
- }
+ JvmtiErrorToException(env, jvmti_env, ret);
}
class IterationConfig {
@@ -92,7 +87,8 @@
user_data);
}
-static bool Run(jint heap_filter,
+static bool Run(JNIEnv* env,
+ jint heap_filter,
jclass klass_filter,
jobject initial_object,
IterationConfig* config) {
@@ -105,14 +101,7 @@
initial_object,
&callbacks,
config);
- if (ret != JVMTI_ERROR_NONE) {
- char* err;
- jvmti_env->GetErrorName(ret, &err);
- printf("Failure running FollowReferences: %s\n", err);
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
- return false;
- }
- return true;
+ return !JvmtiErrorToException(env, jvmti_env, ret);
}
extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test913_followReferences(
@@ -142,6 +131,23 @@
jint length,
void* user_data ATTRIBUTE_UNUSED) OVERRIDE {
jlong tag = *tag_ptr;
+
+ // Ignore any jni-global roots with untagged classes. These can be from the environment,
+ // or the JIT.
+ if (reference_kind == JVMTI_HEAP_REFERENCE_JNI_GLOBAL && class_tag == 0) {
+ return 0;
+ }
+ // Ignore classes (1000-1002@0) for thread objects. These can be held by the JIT.
+ if (reference_kind == JVMTI_HEAP_REFERENCE_THREAD && class_tag == 0 &&
+ (1000 <= *tag_ptr && *tag_ptr <= 1002)) {
+ return 0;
+ }
+ // Ignore stack-locals of untagged threads. That is the environment.
+ if (reference_kind == JVMTI_HEAP_REFERENCE_STACK_LOCAL &&
+ reference_info->stack_local.thread_tag != 3000) {
+ return 0;
+ }
+
// Only check tagged objects.
if (tag == 0) {
return JVMTI_VISIT_OBJECTS;
@@ -201,10 +207,6 @@
reference_info,
adapted_size,
length));
-
- if (reference_kind == JVMTI_HEAP_REFERENCE_THREAD && *tag_ptr == 1000) {
- DumpStacks();
- }
}
std::vector<std::string> GetLines() const {
@@ -259,9 +261,15 @@
if (info_.jni_local.method != nullptr) {
jvmti_env->GetMethodName(info_.jni_local.method, &name, nullptr, nullptr);
}
+ // Normalize the thread id, as this depends on the number of other threads
+ // and which thread is running the test. Should be:
+ // jlong thread_id = info_.jni_local.thread_id;
+ // TODO: A pre-pass before the test should be able fetch this number, so it can
+ // be compared explicitly.
+ jlong thread_id = 1;
std::string ret = StringPrintf("jni-local[id=%" PRId64 ",tag=%" PRId64 ",depth=%d,"
"method=%s]",
- info_.jni_local.thread_id,
+ thread_id,
info_.jni_local.thread_tag,
info_.jni_local.depth,
name == nullptr ? "<null>" : name);
@@ -284,14 +292,8 @@
jlong size,
jint length,
const jvmtiHeapReferenceInfo* reference_info)
- REQUIRES_SHARED(Locks::mutator_lock_)
: Elem(referrer, referree, size, length) {
memcpy(&info_, reference_info, sizeof(jvmtiHeapReferenceInfo));
- // Debug stack trace for failure condition. Remove when done.
- if (info_.stack_local.depth == 3 && info_.stack_local.slot == 13) {
- DumpNativeStack(std::cerr, GetTid());
- Thread::Current()->DumpJavaStack(std::cerr, false, false);
- }
}
protected:
@@ -300,9 +302,15 @@
if (info_.stack_local.method != nullptr) {
jvmti_env->GetMethodName(info_.stack_local.method, &name, nullptr, nullptr);
}
+ // Normalize the thread id, as this depends on the number of other threads
+ // and which thread is running the test. Should be:
+ // jlong thread_id = info_.stack_local.thread_id;
+ // TODO: A pre-pass before the test should be able fetch this number, so it can
+ // be compared explicitly.
+ jlong thread_id = 1;
std::string ret = StringPrintf("stack-local[id=%" PRId64 ",tag=%" PRId64 ",depth=%d,"
"method=%s,vreg=%d,location=% " PRId64 "]",
- info_.stack_local.thread_id,
+ thread_id,
info_.stack_local.thread_tag,
info_.stack_local.depth,
name == nullptr ? "<null>" : name,
@@ -361,7 +369,13 @@
tmp));
}
case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT: {
- std::string tmp = StringPrintf("array-element@%d", reference_info->array.index);
+ jint index = reference_info->array.index;
+ // Normalize if it's "0@0" -> "3000@1".
+ // TODO: A pre-pass could probably give us this index to check explicitly.
+ if (referrer == "0@0" && referree == "3000@0") {
+ index = 0;
+ }
+ std::string tmp = StringPrintf("array-element@%d", index);
return std::unique_ptr<Elem>(new StringElement(referrer,
referree,
size,
@@ -459,16 +473,6 @@
UNREACHABLE();
}
- static void DumpStacks() NO_THREAD_SAFETY_ANALYSIS {
- auto dump_function = [](art::Thread* t, void* data ATTRIBUTE_UNUSED) {
- std::string name;
- t->GetThreadName(name);
- LOG(ERROR) << name;
- art::DumpNativeStack(LOG_STREAM(ERROR), t->GetTid());
- };
- art::Runtime::Current()->GetThreadList()->ForEach(dump_function, nullptr);
- }
-
jint counter_;
const jint stop_after_;
const jint follow_set_;
@@ -476,8 +480,6 @@
std::vector<std::unique_ptr<Elem>> lines_;
};
- jit::ScopedJitSuspend sjs; // Wait to avoid JIT influence (e.g., JNI globals).
-
// If jniRef isn't null, add a local and a global ref.
ScopedLocalRef<jobject> jni_local_ref(env, nullptr);
jobject jni_global_ref = nullptr;
@@ -487,7 +489,9 @@
}
PrintIterationConfig config(stop_after, follow_set);
- Run(heap_filter, klass_filter, initial_object, &config);
+ if (!Run(env, heap_filter, klass_filter, initial_object, &config)) {
+ return nullptr;
+ }
std::vector<std::string> lines = config.GetLines();
jobjectArray ret = CreateObjectArray(env,
@@ -528,10 +532,10 @@
void* user_data) {
FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data);
if (*tag_ptr != 0) {
- size_t utf_byte_count = CountUtf8Bytes(value, value_length);
+ size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length);
std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]);
memset(mod_utf.get(), 0, utf_byte_count + 1);
- ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
+ ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
p->data.push_back(android::base::StringPrintf("%" PRId64 "@%" PRId64 " (%" PRId64 ", '%s')",
*tag_ptr,
class_tag,
diff --git a/test/913-heaps/src/art/Test913.java b/test/913-heaps/src/art/Test913.java
index c54ecb0..2eb05ef 100644
--- a/test/913-heaps/src/art/Test913.java
+++ b/test/913-heaps/src/art/Test913.java
@@ -21,12 +21,34 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.concurrent.CountDownLatch;
public class Test913 {
public static void run() throws Exception {
Main.bindAgentJNIForClass(Test913.class);
doTest();
+
+ // Use a countdown latch for synchronization, as join() will introduce more roots.
+ final CountDownLatch cdl1 = new CountDownLatch(1);
+
+ // Run the follow-references tests on a dedicated thread so we know the specific Thread type.
+ Thread t = new Thread() {
+ @Override
+ public void run() {
+ try {
+ Test913.runFollowReferences();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ cdl1.countDown();
+ }
+ };
+ t.start();
+ cdl1.await();
+ }
+
+ public static void runFollowReferences() throws Exception {
new TestConfig().doFollowReferencesTest();
Runtime.getRuntime().gc();
@@ -481,7 +503,8 @@
if (currentHead == null) {
currentHead = referrer;
} else {
- if (!currentHead.equals(referrer)) {
+ // Ignore 0@0, as it can happen at any time (as it stands for all other objects).
+ if (!currentHead.equals(referrer) && !referrer.equals("0@0")) {
completedReferrers.add(currentHead);
currentHead = referrer;
if (completedReferrers.contains(referrer)) {
diff --git a/test/Android.bp b/test/Android.bp
index 538fac4..033c4fc 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -260,6 +260,7 @@
"908-gc-start-finish/gc_callbacks.cc",
"910-methods/methods.cc",
"911-get-stack-trace/stack_trace.cc",
+ "913-heaps/heaps.cc",
"918-fields/fields.cc",
"920-objects/objects.cc",
"922-properties/properties.cc",
@@ -293,7 +294,6 @@
"901-hello-ti-agent/basics.cc",
"909-attach-agent/attach.cc",
"912-classes/classes.cc",
- "913-heaps/heaps.cc",
"936-search-onload/search_onload.cc",
"944-transform-classloaders/classloader.cc",
"945-obsolete-native/obsolete_native.cc",