blob: 644f4e10ed5f7edb3c8b46815f4c40c8e8ad56ab [file] [log] [blame]
/*
* 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.
*/
package art;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class Test991 {
static List<Field> WATCH_FIELDS = Arrays.asList(TestClass1.class.getDeclaredFields());
static FieldTracer TRACE = null;
static abstract class FieldTracer {
public final void notifyFieldAccess(
Executable method, long location, Class<?> f_klass, Object target, Field f) {
System.out.println("FieldTracer: " + this.getClass());
System.out.println("\tACCESS of " + f + " on object of" +
" type: " + (target == null ? null : target.getClass()) +
" in method " + method);
handleFieldAccess(method, location, f_klass, target, f);
}
public final void notifyFieldModify(
Executable method, long location, Class<?> f_klass, Object target, Field f, Object value) {
System.out.println("FieldTracer: " + this.getClass());
System.out.println("\tMODIFY of " + f + " on object of" +
" type: " + (target == null ? null : target.getClass()) +
" in method " + method +
". New value: " + value + " (type: " + value.getClass() + ")");
handleFieldModify(method, location, f_klass, target, f, value);
}
public void handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f) {}
public void handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v) {}
}
private static class TestError extends Error {
private static final long serialVersionUID = 0;
public TestError(String s) { super(s); }
}
static class DoNothingFieldTracer extends FieldTracer {}
static class ThrowReadFieldTracer extends FieldTracer {
@Override
public void handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f) {
throw new TestError("Throwing error during access");
}
}
static class ThrowWriteFieldTracer extends FieldTracer {
@Override
public void handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v) {
throw new TestError("Throwing error during modify");
}
}
static class ModifyDuringReadAndWriteFieldTracer extends FieldTracer {
@Override
public void handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v) {
// NB This is only safe because the agent doesn't send recursive access/modification events up
// to the java layer here.
((TestClass1)t).xyz += 100;
}
@Override
public void handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f) {
// NB This is only safe because the agent doesn't send recursive access/modification events up
// to the java layer here.
((TestClass1)t).xyz += 10;
}
}
static class ModifyDuringWriteFieldTracer extends FieldTracer {
@Override
public void handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v) {
// NB This is only safe because the agent doesn't send recursive access/modification events up
// to the java layer here.
((TestClass1)t).xyz += 200;
}
}
static class ModifyDuringReadFieldTracer extends FieldTracer {
@Override
public void handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f) {
// NB This is only safe because the agent doesn't send recursive access/modification events up
// to the java layer here.
((TestClass1)t).xyz += 20;
}
}
public static void notifyFieldModify(
Executable m, long location, Class<?> f_klass, Object target, Field f, Object value) {
if (TRACE != null) {
TRACE.notifyFieldModify(m, location, f_klass, target, f, value);
}
}
public static void notifyFieldAccess(
Executable m, long location, Class<?> f_klass, Object target, Field f) {
if (TRACE != null) {
TRACE.notifyFieldAccess(m, location, f_klass, target, f);
}
}
public static class TestClass1 {
public int xyz;
public TestClass1(int xyz) {
this.xyz = xyz;
}
}
public static int readFieldUntraced(TestClass1 target) {
FieldTracer tmp = TRACE;
TRACE = null;
int res = target.xyz;
TRACE = tmp;
return res;
}
public static class JavaReadWrite implements Consumer<TestClass1> {
public void accept(TestClass1 t1) {
int val = t1.xyz;
System.out.println("normal read: xyz = " + val);
t1.xyz = val + 1;
}
}
public static class ReflectiveReadWrite implements Consumer<TestClass1> {
public void accept(TestClass1 t1) {
try {
Field f = t1.getClass().getDeclaredField("xyz");
int val = f.getInt(t1);
System.out.println("reflective read: xyz = " + val);
f.setInt(t1, val + 1);
} catch (IllegalAccessException iae) {
throw new InternalError("Could not set field xyz", iae);
} catch (NoSuchFieldException nsfe) {
throw new InternalError("Could not find field xyz", nsfe);
}
}
}
public static class NativeReadWrite implements Consumer<TestClass1> {
public void accept(TestClass1 t1) {
doNativeReadWrite(t1);
}
}
public static TestClass1 createTestClassNonTraced() {
FieldTracer tmp = TRACE;
TRACE = null;
TestClass1 n = new TestClass1(0);
TRACE = tmp;
return n;
}
public static void run() throws Exception {
Trace.disableTracing(Thread.currentThread());
Trace.enableFieldTracing(
Test991.class,
Test991.class.getDeclaredMethod("notifyFieldAccess",
Executable.class, Long.TYPE, Class.class, Object.class, Field.class),
Test991.class.getDeclaredMethod("notifyFieldModify",
Executable.class, Long.TYPE, Class.class, Object.class, Field.class, Object.class),
Thread.currentThread());
for (Field f : WATCH_FIELDS) {
Trace.watchFieldAccess(f);
Trace.watchFieldModification(f);
}
FieldTracer[] tracers = new FieldTracer[] {
new DoNothingFieldTracer(),
new ThrowReadFieldTracer(),
new ThrowWriteFieldTracer(),
new ModifyDuringReadFieldTracer(),
new ModifyDuringWriteFieldTracer(),
new ModifyDuringReadAndWriteFieldTracer(),
};
Consumer<TestClass1>[] field_modification = new Consumer[] {
new JavaReadWrite(),
new ReflectiveReadWrite(),
new NativeReadWrite(),
};
for (Consumer<TestClass1> c : field_modification) {
for (FieldTracer trace : tracers) {
System.out.println("Test is " + trace.getClass() + " & " + c.getClass());
TestClass1 t1 = createTestClassNonTraced();
TRACE = trace;
System.out.println("Initial state: xyz = " + readFieldUntraced(t1));
try {
c.accept(t1);
} catch (TestError e) {
System.out.println("Caught error. " + e);
} finally {
System.out.println("Final state: xyz = " + readFieldUntraced(t1));
}
}
}
Trace.disableTracing(Thread.currentThread());
}
public static native void doNativeReadWrite(TestClass1 t1);
public static void doPrintNativeNotification(int val) {
System.out.println("native read: xyz = " + val);
}
}