Remove native printing from test 980 and reliance on print internals
In preparation for a making a CTS test out of this make the test not
rely on internal allocation patterns of the 'System.out' object and
not make use of std::cout.
Test: ./test.py --host -j40
Change-Id: Ib7e874aaec71f93e834cf94ac5fe96663536691a
diff --git a/test/980-redefine-object/check b/test/980-redefine-object/check
index 987066f..07b21b3 100755
--- a/test/980-redefine-object/check
+++ b/test/980-redefine-object/check
@@ -17,4 +17,4 @@
# The number of paused background threads (and therefore InterruptedExceptions)
# can change so we will just delete their lines from the log.
-sed "/Object allocated of type 'Ljava\/lang\/InterruptedException;'/d" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
+sed "/Object allocated of type 'java\.lang\.InterruptedException'/d" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
diff --git a/test/980-redefine-object/expected.txt b/test/980-redefine-object/expected.txt
index 6e9bce0..4c294bc 100644
--- a/test/980-redefine-object/expected.txt
+++ b/test/980-redefine-object/expected.txt
@@ -2,51 +2,31 @@
Allocating an j.l.Object before redefining Object class
Allocating a Transform before redefining Object class
Redefining the Object class to add a hook into the <init> method
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
Allocating an j.l.Object after redefining Object class
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.lang.Object'
Allocating a Transform after redefining Object class
-Object allocated of type 'LTransform;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'Transform'
Allocating an int[] after redefining Object class
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
Allocating an array list
-Object allocated of type 'Ljava/util/ArrayList;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.util.ArrayList'
Adding a bunch of stuff to the array list
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'LTransform;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.lang.Object'
+Object allocated of type 'java.lang.Object'
+Object allocated of type 'Transform'
Allocating a linked list
-Object allocated of type 'Ljava/util/LinkedList;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.util.LinkedList'
Adding a bunch of stuff to the linked list
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'LTransform;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.lang.Object'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.lang.Object'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'Transform'
+Object allocated of type 'java.util.LinkedList$Node'
Throwing from down 4 stack frames
-Object allocated of type 'Ljava/lang/Exception;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.lang.Exception'
Exception caught.
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
Finishing test!
diff --git a/test/980-redefine-object/redefine_object.cc b/test/980-redefine-object/redefine_object.cc
deleted file mode 100644
index 1faf1a1..0000000
--- a/test/980-redefine-object/redefine_object.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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 <inttypes.h>
-#include <iostream>
-
-#include "android-base/stringprintf.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "jni.h"
-#include "jvmti.h"
-#include "scoped_utf_chars.h"
-
-// Test infrastructure
-#include "jni_binder.h"
-#include "jvmti_helper.h"
-#include "test_env.h"
-
-namespace art {
-namespace Test980RedefineObjects {
-
-extern "C" JNIEXPORT void JNICALL Java_Main_bindFunctionsForClass(
- JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass target) {
- BindFunctionsOnClass(jvmti_env, env, target);
-}
-
-extern "C" JNIEXPORT void JNICALL Java_art_test_TestWatcher_NotifyConstructed(
- JNIEnv* env, jclass TestWatcherClass ATTRIBUTE_UNUSED, jobject constructed) {
- char* sig = nullptr;
- char* generic_sig = nullptr;
- if (JvmtiErrorToException(env,
- jvmti_env,
- jvmti_env->GetClassSignature(env->GetObjectClass(constructed),
- &sig,
- &generic_sig))) {
- // Exception.
- return;
- }
- std::cout << "Object allocated of type '" << sig << "'" << std::endl;
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(generic_sig));
-}
-
-} // namespace Test980RedefineObjects
-} // namespace art
diff --git a/test/980-redefine-object/src-ex/TestWatcher.java b/test/980-redefine-object/src-ex/TestWatcher.java
index d15e688..c38e07b 100644
--- a/test/980-redefine-object/src-ex/TestWatcher.java
+++ b/test/980-redefine-object/src-ex/TestWatcher.java
@@ -16,10 +16,60 @@
package art.test;
+import java.util.concurrent.locks.ReentrantLock;
+
public class TestWatcher {
- // NB This function is native since it is called in the Object.<init> method and so cannot cause
- // any java allocations at all. The normal System.out.print* functions will cause allocations to
- // occur so we cannot use them. This means the easiest way to report the object as being created
- // is to go into native code and do it there.
- public static native void NotifyConstructed(Object o);
+ // Lock to synchronize access to the static state of this class.
+ private static final ReentrantLock lock = new ReentrantLock();
+ private static volatile boolean criticalFailure = false;
+ private static boolean reportingEnabled = true;
+ private static boolean doingReport = false;
+
+ private static void MonitorEnter() {
+ lock.lock();
+ }
+
+ private static void MonitorExit() {
+ // Need to do this manually since we need to notify critical failure but would deadlock if
+ // waited for the unlock.
+ if (!lock.isHeldByCurrentThread()) {
+ NotifyCriticalFailure();
+ throw new IllegalMonitorStateException("Locking error!");
+ } else {
+ lock.unlock();
+ }
+ }
+
+ // Stops reporting. Must be paired with an EnableReporting call.
+ public static void DisableReporting() {
+ MonitorEnter();
+ reportingEnabled = false;
+ }
+
+ // Stops reporting. Must be paired with a DisableReporting call.
+ public static void EnableReporting() {
+ reportingEnabled = true;
+ MonitorExit();
+ }
+
+ public static void NotifyCriticalFailure() {
+ criticalFailure = true;
+ }
+
+ public static void NotifyConstructed(Object o) {
+ if (criticalFailure) {
+ // Something went very wrong. We are probably trying to report it so don't get in the way.
+ return;
+ }
+ MonitorEnter();
+ // We could enter an infinite loop if println allocates (which it does) so we disable
+ // reporting while we are doing a report. Since we are synchronized we won't miss any
+ // allocations.
+ if (reportingEnabled && !doingReport) {
+ doingReport = true;
+ System.out.println("Object allocated of type '" + o.getClass().getName() + "'");
+ doingReport = false;
+ }
+ MonitorExit();
+ }
}
diff --git a/test/980-redefine-object/src/Main.java b/test/980-redefine-object/src/Main.java
index a50215e..7a82fde 100644
--- a/test/980-redefine-object/src/Main.java
+++ b/test/980-redefine-object/src/Main.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Base64;
import java.util.LinkedList;
@@ -287,6 +288,31 @@
private static final String LISTENER_LOCATION =
System.getenv("DEX_LOCATION") + "/980-redefine-object-ex.jar";
+ private static Method doEnableReporting;
+ private static Method doDisableReporting;
+
+ private static void DisableReporting() {
+ if (doDisableReporting == null) {
+ return;
+ }
+ try {
+ doDisableReporting.invoke(null);
+ } catch (Exception e) {
+ throw new Error("Unable to disable reporting!");
+ }
+ }
+
+ private static void EnableReporting() {
+ if (doEnableReporting == null) {
+ return;
+ }
+ try {
+ doEnableReporting.invoke(null);
+ } catch (Exception e) {
+ throw new Error("Unable to enable reporting!");
+ }
+ }
+
public static void main(String[] args) {
art.Main.bindAgentJNIForClass(Main.class);
doTest();
@@ -298,8 +324,8 @@
addToBootClassLoader(LISTENER_LOCATION);
// Load TestWatcher from the bootclassloader and make sure it is initialized.
Class<?> testwatcher_class = Class.forName("art.test.TestWatcher", true, null);
- // Bind the native functions of testwatcher_class.
- bindFunctionsForClass(testwatcher_class);
+ doEnableReporting = testwatcher_class.getDeclaredMethod("EnableReporting");
+ doDisableReporting = testwatcher_class.getDeclaredMethod("DisableReporting");
} catch (Exception e) {
throw new Error("Exception while making testwatcher", e);
}
@@ -308,9 +334,9 @@
// NB This function will cause 2 objects of type "Ljava/nio/HeapCharBuffer;" and
// "Ljava/nio/HeapCharBuffer;" to be allocated each time it is called.
private static void safePrintln(Object o) {
- System.out.flush();
- System.out.print("\t" + o + "\n");
- System.out.flush();
+ DisableReporting();
+ System.out.println("\t" + o);
+ EnableReporting();
}
private static void throwFrom(int depth) throws Exception {
@@ -382,8 +408,6 @@
private static native void addToBootClassLoader(String s);
- private static native void bindFunctionsForClass(Class<?> target);
-
// Transforms the class
private static native void doCommonClassRedefinition(Class<?> target,
byte[] class_file,
diff --git a/test/Android.bp b/test/Android.bp
index 8059a2f..538fac4 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -297,7 +297,6 @@
"936-search-onload/search_onload.cc",
"944-transform-classloaders/classloader.cc",
"945-obsolete-native/obsolete_native.cc",
- "980-redefine-object/redefine_object.cc",
"983-source-transform-verify/source_transform.cc",
"984-obsolete-invoke/obsolete_invoke.cc",
],