From a67619d574d96655fd51f43949ecacd62d8198dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=88=9F?= Date: Fri, 23 Apr 2021 20:34:15 +0800 Subject: [PATCH] add verbose log for omni constructor --- .../transformer/TestableClassTransformer.java | 3 +- .../testable/core/tool/OmniConstructor.java | 40 ++++++++++--------- .../alibaba/testable/core/util/LogUtil.java | 18 +++++++-- .../testable/core/util/StringUtil.java | 13 ++++++ 4 files changed, 51 insertions(+), 23 deletions(-) create mode 100644 testable-core/src/main/java/com/alibaba/testable/core/util/StringUtil.java diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java b/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java index 697ffb3..49b2ab8 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java @@ -45,12 +45,11 @@ public class TestableClassTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classFileBuffer) { + // className is in slash-separated format if (isSystemClass(className)) { // Ignore system class and reloaded class return null; } - // className is in slash-separated format - LogUtil.verbose("Handle class: " + className); byte[] bytes = shouldOmniEnhance(className) ? new OmniClassHandler().getBytes(classFileBuffer) : classFileBuffer; ClassNode cn = ClassUtil.getClassNode(className); if (cn != null) { diff --git a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java index 7119dcb..b23fa15 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java @@ -2,7 +2,7 @@ package com.alibaba.testable.core.tool; import com.alibaba.testable.core.exception.ClassConstructionException; import com.alibaba.testable.core.model.TestableNull; -import com.alibaba.testable.core.util.CollectionUtil; +import com.alibaba.testable.core.util.LogUtil; import com.alibaba.testable.core.util.TypeUtil; import javax.lang.model.type.NullType; @@ -17,6 +17,7 @@ import static com.alibaba.testable.core.constant.ConstPool.DOLLAR; public class OmniConstructor { private static final int INITIAL_CAPACITY = 6; + private static final int ZERO = 0; private OmniConstructor() {} @@ -27,7 +28,7 @@ public class OmniConstructor { * @return 返回新创建的对象 */ public static T newInstance(Class clazz) { - return handleCircleReference(newInstance(clazz, new HashSet>(INITIAL_CAPACITY))); + return handleCircleReference(newInstance(clazz, new HashSet>(INITIAL_CAPACITY), ZERO)); } /** @@ -38,10 +39,11 @@ public class OmniConstructor { * @return 返回新创建的对象数组 */ public static T[] newArray(Class clazz, int size) { - return (T[])handleCircleReference(newArray(clazz, size, new HashSet>(INITIAL_CAPACITY))); + return (T[])handleCircleReference(newArray(clazz, size, new HashSet>(INITIAL_CAPACITY), ZERO)); } - private static T newInstance(Class clazz, Set> classPool) { + private static T newInstance(Class clazz, Set> classPool, int level) { + LogUtil.verbose(level, "creating %s", clazz.getSimpleName()); if (classPool.contains(clazz)) { return null; } @@ -50,7 +52,7 @@ public class OmniConstructor { if (clazz.isPrimitive()) { return newPrimitive(clazz); } else if (clazz.isArray()) { - return (T)newArray(clazz.getComponentType(), 0, classPool); + return (T)newArray(clazz.getComponentType(), 0, classPool, level); } else if (clazz.isEnum()) { return newEnum(clazz); } else if (clazz.isInterface()) { @@ -58,7 +60,7 @@ public class OmniConstructor { } else if (Modifier.isAbstract(clazz.getModifiers())) { return newAbstractClass(clazz); } - return newObject(clazz, classPool); + return newObject(clazz, classPool, level); } catch (NoSuchMethodException e) { throw new ClassConstructionException("Failed to find constructor", e); } catch (IllegalAccessException e) { @@ -74,26 +76,26 @@ public class OmniConstructor { } } - private static T newObject(Class clazz, Set> classPool) + private static T newObject(Class clazz, Set> classPool, int level) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { - Object ins = createInstance(getBestConstructor(clazz), classPool); + Object ins = createInstance(getBestConstructor(clazz), classPool, level); if (!TypeUtil.isBasicType(clazz)) { for (Field f : TypeUtil.getAllFields(clazz)) { f.setAccessible(true); // skip "$jacocoData" field added by jacoco if (f.get(ins) == null && !f.getName().startsWith(DOLLAR)) { - f.set(ins, newInstance(f.getType(), classPool)); + f.set(ins, newInstance(f.getType(), classPool, level + 1)); } } } return (T)ins; } - private static Object newArray(Class clazz, int size, Set> classPool) { + private static Object newArray(Class clazz, int size, Set> classPool, int level) { // primary[] cannot be cast to Object[], have to use Object instead of T[] Object array = Array.newInstance(clazz, size); for (int i = 0; i < size; i++) { - Array.set(array, i, newInstance(clazz, classPool)); + Array.set(array, i, newInstance(clazz, classPool, level + 1)); } return array; } @@ -144,10 +146,10 @@ public class OmniConstructor { try { if (instance.getClass().isArray()) { for (int i = 0; i < Array.getLength(instance); i++) { - handleCircleReference(Array.get(instance, i), new HashMap, Object>(INITIAL_CAPACITY)); + handleCircleReference(Array.get(instance, i), new HashMap, Object>(INITIAL_CAPACITY), ZERO); } } else { - handleCircleReference(instance, new HashMap, Object>(INITIAL_CAPACITY)); + handleCircleReference(instance, new HashMap, Object>(INITIAL_CAPACITY), ZERO); } } catch (IllegalAccessException e) { throw new ClassConstructionException("Failed to access field", e); @@ -155,11 +157,13 @@ public class OmniConstructor { return instance; } - private static void handleCircleReference(T instance, Map, Object> classPool) + private static void handleCircleReference(T instance, Map, Object> classPool, int level) throws IllegalAccessException { if (instance == null) { + // don't travel null object return; } + LogUtil.verbose(level, "verifying %s", instance.getClass().getSimpleName()); classPool.put(instance.getClass(), instance); for (Field f : TypeUtil.getAllFields(instance.getClass())) { f.setAccessible(true); @@ -168,21 +172,21 @@ public class OmniConstructor { if (fieldType.isArray()) { if (fieldIns != null) { for (int i = 0; i < Array.getLength(fieldIns); i++) { - handleCircleReference(Array.get(fieldIns, i), classPool); + handleCircleReference(Array.get(fieldIns, i), classPool, level + 1); } } } else if (!fieldType.isPrimitive() && !TypeUtil.isBasicType(fieldType)) { if (fieldIns == null && classPool.containsKey(fieldType)) { f.set(instance, classPool.get(fieldType)); } else if (!classPool.containsKey(fieldType)) { - handleCircleReference(fieldIns, classPool); + handleCircleReference(fieldIns, classPool, level + 1); } } } classPool.remove(instance.getClass()); } - private static Object createInstance(Constructor constructor, Set> classPool) + private static Object createInstance(Constructor constructor, Set> classPool, int level) throws InstantiationException, IllegalAccessException, InvocationTargetException { constructor.setAccessible(true); Class[] types = constructor.getParameterTypes(); @@ -191,7 +195,7 @@ public class OmniConstructor { } else { Object[] args = new Object[types.length]; for (int i = 0; i < types.length; i++) { - args[i] = newInstance(types[i], classPool); + args[i] = newInstance(types[i], classPool, level + 1); } return constructor.newInstance(args); } diff --git a/testable-core/src/main/java/com/alibaba/testable/core/util/LogUtil.java b/testable-core/src/main/java/com/alibaba/testable/core/util/LogUtil.java index 6aa1676..f4493c7 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/util/LogUtil.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/util/LogUtil.java @@ -17,8 +17,16 @@ public class LogUtil { private static FileOutputStream logFileStream = null; public static void verbose(String msg, Object... args) { + verbose(0, msg, args); + } + + public static void diagnose(String msg, Object... args) { + diagnose(0, msg, args); + } + + public static void verbose(int indent, String msg, Object... args) { if (isVerboseEnabled()) { - String text = String.format(msg + "\n", args); + String text = String.format(space(indent) + msg + "\n", args); System.out.print("[VERBOSE] "); System.out.print(text); write("[TIP] "); @@ -26,8 +34,8 @@ public class LogUtil { } } - public static void diagnose(String msg, Object... args) { - String text = String.format(msg + "\n", args); + public static void diagnose(int indent, String msg, Object... args) { + String text = String.format(space(indent) + msg + "\n", args); if (currentLogLevel.level >= LogLevel.ENABLE.level) { System.out.print("[DIAGNOSE] "); System.out.print(text); @@ -93,6 +101,10 @@ public class LogUtil { } } + private static String space(int indent) { + return StringUtil.repeat(" ", indent); + } + private static void write(String text) { try { if (logFileStream != null) { diff --git a/testable-core/src/main/java/com/alibaba/testable/core/util/StringUtil.java b/testable-core/src/main/java/com/alibaba/testable/core/util/StringUtil.java new file mode 100644 index 0000000..31d0941 --- /dev/null +++ b/testable-core/src/main/java/com/alibaba/testable/core/util/StringUtil.java @@ -0,0 +1,13 @@ +package com.alibaba.testable.core.util; + +public class StringUtil { + + public static String repeat(String str, int n) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < n; i++) { + sb.append(str); + } + return sb.toString(); + } + +}