Skip to content

Commit 915b43a

Browse files
committed
Add thread name to debug logs, @HunterDebugClassImpl, skip empty methods in timing
- #64: Debug plugin now includes thread name in logs for non-main threads - #44: Add @HunterDebugClassImpl annotation for class-level custom logger instrumentation - #41: Timing plugin skips empty methods (deferred probe injection) - Bump version to 1.3.1
1 parent 9f3ca1e commit 915b43a

9 files changed

Lines changed: 165 additions & 31 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ All libraries and plugins share the same version number. Replace `LATEST_VERSION
2121
READMEs with the latest version shown in the badge above.
2222

2323
```groovy
24-
def hunterVersion = '1.3.0'
24+
def hunterVersion = '1.3.1'
2525
```
2626

2727
## Compatibility

docs/README_hunter_debug.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ has some advantages over hugo.
1111
| support kotlin | no | yes |
1212
| custom logger | no | yes |
1313
| object toString | no | yes |
14+
| thread name | yes | yes |
1415
| compile speed | normal | fast |
1516

1617

@@ -63,10 +64,37 @@ private String appendIntAndString(int a, String b) {
6364
```xml
6465

6566
MainActivity: ⇢ appendIntAndString[a="5", b="billions"]
66-
⇠ appendIntAndString[0ms]="5 billions"
67+
⇠ appendIntAndString[100ms]="5 billions"
6768

6869
```
6970

71+
When running on a non-main thread, the thread name is automatically included:
72+
73+
```xml
74+
75+
MainActivity: ⇢ [worker-1] appendIntAndString[a="5", b="billions"]
76+
⇠ [worker-1] appendIntAndString[100ms]="5 billions"
77+
78+
```
79+
80+
### Class-level annotations
81+
82+
Use `@HunterDebugClass` to instrument all methods in a class (with standard `Log.i`), or `@HunterDebugClassImpl` to instrument all methods with your custom logger. You can use `@HunterDebugSkip` on individual methods to opt them out.
83+
84+
```java
85+
@HunterDebugClassImpl
86+
public class MyRepository {
87+
// All methods in this class will be instrumented with custom logger
88+
89+
@HunterDebugSkip
90+
public void frequentlyCalledMethod() {
91+
// This method is excluded
92+
}
93+
}
94+
```
95+
96+
### Custom logger
97+
7098
If you want to print the debug log with your custom logger. You can use `@HunterDebugImpl` instead of `@HunterDebug`, and
7199
install a custom HunterLoggerHandler to receive the log message, and send it to your custom logger.
72100
(You can use both `@HunterDebug` and `@HunterDebugImpl` at the same time)

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ android.nonTransitiveRClass=true
1313
android.suppressUnsupportedCompileSdk=34
1414

1515
GROUP=cn.quinnchen.hunter
16-
VERSION_NAME=1.3.0
16+
VERSION_NAME=1.3.1
1717
POM_NAME=Hunter
1818
POM_DESCRIPTION=A fast, incremental, concurrent framework to develop compile plugin for android project to manipulate bytecode.
1919
POM_INCEPTION_YEAR=2022
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.hunter.library.debug;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.CLASS)
9+
public @interface HunterDebugClassImpl {
10+
}

hunter-debug-library/src/main/java/com/hunter/library/debug/ParameterPrinter.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ public class ParameterPrinter {
1717

1818
public ParameterPrinter(String tag, String methodName){
1919
this.tag = tag;
20-
result.append("⇢ ").append(methodName).append("[");
20+
String threadName = Thread.currentThread().getName();
21+
result.append("⇢ ");
22+
if (!"main".equals(threadName)) {
23+
result.append("[").append(threadName).append("] ");
24+
}
25+
result.append(methodName).append("[");
2126
}
2227

2328
public ParameterPrinter append(String name, int val) {

hunter-debug-library/src/main/java/com/hunter/library/debug/ResultPrinter.java

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,83 +6,91 @@
66

77
public class ResultPrinter {
88

9+
private static String formatReturn(String methodName, long costedMillis, String returnVal) {
10+
String threadName = Thread.currentThread().getName();
11+
if (!"main".equals(threadName)) {
12+
return "⇠ [" + threadName + "] " + methodName + "[" + costedMillis + "ms]=\"" + returnVal + "\"";
13+
}
14+
return String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMillis + "", returnVal);
15+
}
16+
917
public static void print(String className, String methodName, long costedMilles, byte returnVal) {
10-
Log.i(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
18+
Log.i(className, formatReturn(methodName, costedMilles, returnVal + ""));
1119
}
1220

1321
public static void print(String className, String methodName, long costedMilles, char returnVal) {
14-
Log.i(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
22+
Log.i(className, formatReturn(methodName, costedMilles, returnVal + ""));
1523
}
1624

1725
public static void print(String className, String methodName, long costedMilles, short returnVal) {
18-
Log.i(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
26+
Log.i(className, formatReturn(methodName, costedMilles, returnVal + ""));
1927
}
2028

2129
public static void print(String className, String methodName, long costedMilles, int returnVal) {
22-
Log.i(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
30+
Log.i(className, formatReturn(methodName, costedMilles, returnVal + ""));
2331
}
2432

2533
public static void print(String className, String methodName, long costedMilles, boolean returnVal) {
26-
Log.i(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
34+
Log.i(className, formatReturn(methodName, costedMilles, returnVal + ""));
2735
}
2836

2937
public static void print(String className, String methodName, long costedMilles, long returnVal) {
30-
Log.i(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
38+
Log.i(className, formatReturn(methodName, costedMilles, returnVal + ""));
3139
}
3240

3341
public static void print(String className, String methodName, long costedMilles, float returnVal) {
34-
Log.i(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
42+
Log.i(className, formatReturn(methodName, costedMilles, returnVal + ""));
3543
}
3644

3745
public static void print(String className, String methodName, long costedMilles, double returnVal) {
38-
Log.i(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
46+
Log.i(className, formatReturn(methodName, costedMilles, returnVal + ""));
3947
}
4048

4149
public static void print(String className, String methodName, long costedMilles, Object returnVal) {
4250
if(returnVal != null && returnVal.getClass().isArray()){
43-
Log.i(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", arrayToString(returnVal)));
51+
Log.i(className, formatReturn(methodName, costedMilles, arrayToString(returnVal)));
4452
} else {
45-
Log.i(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal));
53+
Log.i(className, formatReturn(methodName, costedMilles, String.valueOf(returnVal)));
4654
}
4755
}
4856

4957
public static void printWithCustomLogger(String className, String methodName, long costedMilles, byte returnVal) {
50-
HunterLoggerHandler.CUSTOM_IMPL.log(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
58+
HunterLoggerHandler.CUSTOM_IMPL.log(className, formatReturn(methodName, costedMilles, returnVal + ""));
5159
}
5260

5361
public static void printWithCustomLogger(String className, String methodName, long costedMilles, char returnVal) {
54-
HunterLoggerHandler.CUSTOM_IMPL.log(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
62+
HunterLoggerHandler.CUSTOM_IMPL.log(className, formatReturn(methodName, costedMilles, returnVal + ""));
5563
}
5664

5765
public static void printWithCustomLogger(String className, String methodName, long costedMilles, short returnVal) {
58-
HunterLoggerHandler.CUSTOM_IMPL.log(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
66+
HunterLoggerHandler.CUSTOM_IMPL.log(className, formatReturn(methodName, costedMilles, returnVal + ""));
5967
}
6068

6169
public static void printWithCustomLogger(String className, String methodName, long costedMilles, int returnVal) {
62-
HunterLoggerHandler.CUSTOM_IMPL.log(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
70+
HunterLoggerHandler.CUSTOM_IMPL.log(className, formatReturn(methodName, costedMilles, returnVal + ""));
6371
}
6472

6573
public static void printWithCustomLogger(String className, String methodName, long costedMilles, boolean returnVal) {
66-
HunterLoggerHandler.CUSTOM_IMPL.log(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
74+
HunterLoggerHandler.CUSTOM_IMPL.log(className, formatReturn(methodName, costedMilles, returnVal + ""));
6775
}
6876

6977
public static void printWithCustomLogger(String className, String methodName, long costedMilles, long returnVal) {
70-
HunterLoggerHandler.CUSTOM_IMPL.log(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
78+
HunterLoggerHandler.CUSTOM_IMPL.log(className, formatReturn(methodName, costedMilles, returnVal + ""));
7179
}
7280

7381
public static void printWithCustomLogger(String className, String methodName, long costedMilles, float returnVal) {
74-
HunterLoggerHandler.CUSTOM_IMPL.log(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
82+
HunterLoggerHandler.CUSTOM_IMPL.log(className, formatReturn(methodName, costedMilles, returnVal + ""));
7583
}
7684

7785
public static void printWithCustomLogger(String className, String methodName, long costedMilles, double returnVal) {
78-
HunterLoggerHandler.CUSTOM_IMPL.log(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal + ""));
86+
HunterLoggerHandler.CUSTOM_IMPL.log(className, formatReturn(methodName, costedMilles, returnVal + ""));
7987
}
8088

8189
public static void printWithCustomLogger(String className, String methodName, long costedMilles, Object returnVal) {
8290
if(returnVal != null && returnVal.getClass().isArray()){
83-
HunterLoggerHandler.CUSTOM_IMPL.log(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", arrayToString(returnVal)));
91+
HunterLoggerHandler.CUSTOM_IMPL.log(className, formatReturn(methodName, costedMilles, arrayToString(returnVal)));
8492
} else {
85-
HunterLoggerHandler.CUSTOM_IMPL.log(className, String.format(Constants.RETURN_PRINT_FORMAT, methodName, costedMilles + "", returnVal));
93+
HunterLoggerHandler.CUSTOM_IMPL.log(className, formatReturn(methodName, costedMilles, String.valueOf(returnVal)));
8694
}
8795
}
8896

hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/bytecode/prego/DebugPreGoClassAdapter.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public final class DebugPreGoClassAdapter extends ClassVisitor{
2222
private boolean needParameter = false;
2323

2424
private boolean classDebug = false;
25+
private boolean classDebugImpl = false;
2526
private List<String> includes = new ArrayList<>();
2627
private List<String> impls = new ArrayList<>();
2728

@@ -34,6 +35,9 @@ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
3435
AnnotationVisitor orgin = super.visitAnnotation(desc, visible);
3536
if("Lcom/hunter/library/debug/HunterDebugClass;".equals(desc) ) {
3637
classDebug = true;
38+
} else if("Lcom/hunter/library/debug/HunterDebugClassImpl;".equals(desc) ) {
39+
classDebug = true;
40+
classDebugImpl = true;
3741
}
3842
return orgin;
3943
}
@@ -43,7 +47,7 @@ public MethodVisitor visitMethod(final int access, final String name,
4347
final String desc, final String signature, final String[] exceptions) {
4448
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
4549
String methodUniqueKey = name + desc;
46-
debugPreGoMethodAdapter = new DebugPreGoMethodAdapter(name, methodUniqueKey, methodParametersMap, mv, classDebug, new MethodCollector() {
50+
debugPreGoMethodAdapter = new DebugPreGoMethodAdapter(name, methodUniqueKey, methodParametersMap, mv, classDebug, classDebugImpl, new MethodCollector() {
4751
@Override
4852
public void onIncludeMethod(String methodName, boolean useImpl) {
4953
if(useImpl){

hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/bytecode/prego/DebugPreGoMethodAdapter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ public class DebugPreGoMethodAdapter extends MethodVisitor implements Opcodes {
2727
private boolean useImpl = false;
2828

2929

30-
public DebugPreGoMethodAdapter(String methodName,String methodKey, Map<String, List<Parameter>> methodParametersMap, MethodVisitor mv, boolean needParameter, DebugPreGoClassAdapter.MethodCollector methodCollector) {
30+
public DebugPreGoMethodAdapter(String methodName,String methodKey, Map<String, List<Parameter>> methodParametersMap, MethodVisitor mv, boolean needParameter, boolean useImpl, DebugPreGoClassAdapter.MethodCollector methodCollector) {
3131
super(Opcodes.ASM9, mv);
3232
this.methodName = methodName;
3333
this.methodKey = methodKey;
3434
this.methodParametersMap = methodParametersMap;
3535
this.needParameter = needParameter;
36+
this.useImpl = useImpl;
3637
this.methodCollector = methodCollector;
3738
}
3839

hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/bytecode/TimingMethodAdapter.java

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ public final class TimingMethodAdapter extends LocalVariablesSorter implements O
1616
// *outermost* INVOKESPECIAL <init>.
1717
private int newDepth = 0;
1818
private boolean superCallSeen = false;
19+
// Defer start probe until the first real instruction so that empty methods
20+
// (e.g. empty overrides with just a RETURN) are not instrumented.
21+
private boolean startProbeDeferred = false;
1922

2023
public TimingMethodAdapter(String name, int access, String desc, MethodVisitor mv, boolean isConstructor) {
2124
super(Opcodes.ASM9, access, desc, mv);
@@ -27,6 +30,13 @@ public TimingMethodAdapter(String name, int access, String desc, MethodVisitor m
2730
public void visitCode() {
2831
super.visitCode();
2932
if (!isConstructor) {
33+
startProbeDeferred = true;
34+
}
35+
}
36+
37+
private void emitDeferredStartProbe() {
38+
if (startProbeDeferred) {
39+
startProbeDeferred = false;
3040
emitProbeStart();
3141
}
3242
}
@@ -35,30 +45,36 @@ public void visitCode() {
3545
public void visitTypeInsn(int opcode, String type) {
3646
if (isConstructor && !superCallSeen && opcode == NEW) {
3747
newDepth++;
48+
} else {
49+
emitDeferredStartProbe();
3850
}
3951
super.visitTypeInsn(opcode, type);
4052
}
4153

4254
@Override
4355
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
44-
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
4556
if (isConstructor && !superCallSeen
4657
&& opcode == INVOKESPECIAL && "<init>".equals(name)) {
58+
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
4759
if (newDepth > 0) {
4860
newDepth--;
4961
} else {
50-
// This is the mandatory super(...)/this(...) call; safe to probe now.
62+
// This is the mandatory super(...)/this(...) call; defer probe
63+
// so that empty constructors (just super + return) are skipped.
5164
superCallSeen = true;
52-
emitProbeStart();
65+
startProbeDeferred = true;
5366
}
67+
return;
5468
}
69+
emitDeferredStartProbe();
70+
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
5571
}
5672

5773
@Override
5874
public void visitInsn(int opcode) {
5975
if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
60-
// If we never managed to install the start probe (e.g. exotic
61-
// bytecode), skip the end probe to avoid VerifyError.
76+
// If we never managed to install the start probe (e.g. empty method
77+
// or exotic bytecode), skip the end probe to avoid VerifyError.
6278
if (startVarIndex >= 0) {
6379
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
6480
mv.visitVarInsn(LLOAD, startVarIndex);
@@ -70,10 +86,72 @@ public void visitInsn(int opcode) {
7086
mv.visitMethodInsn(INVOKESTATIC, "com/hunter/library/timing/BlockManager",
7187
"timingMethod", "(Ljava/lang/String;J)V", false);
7288
}
89+
} else {
90+
emitDeferredStartProbe();
7391
}
7492
super.visitInsn(opcode);
7593
}
7694

95+
@Override
96+
public void visitIntInsn(int opcode, int operand) {
97+
emitDeferredStartProbe();
98+
super.visitIntInsn(opcode, operand);
99+
}
100+
101+
@Override
102+
public void visitVarInsn(int opcode, int var) {
103+
emitDeferredStartProbe();
104+
super.visitVarInsn(opcode, var);
105+
}
106+
107+
@Override
108+
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
109+
emitDeferredStartProbe();
110+
super.visitFieldInsn(opcode, owner, name, descriptor);
111+
}
112+
113+
@Override
114+
public void visitJumpInsn(int opcode, org.objectweb.asm.Label label) {
115+
emitDeferredStartProbe();
116+
super.visitJumpInsn(opcode, label);
117+
}
118+
119+
@Override
120+
public void visitLdcInsn(Object value) {
121+
emitDeferredStartProbe();
122+
super.visitLdcInsn(value);
123+
}
124+
125+
@Override
126+
public void visitIincInsn(int var, int increment) {
127+
emitDeferredStartProbe();
128+
super.visitIincInsn(var, increment);
129+
}
130+
131+
@Override
132+
public void visitInvokeDynamicInsn(String name, String descriptor, org.objectweb.asm.Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {
133+
emitDeferredStartProbe();
134+
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
135+
}
136+
137+
@Override
138+
public void visitTableSwitchInsn(int min, int max, org.objectweb.asm.Label dflt, org.objectweb.asm.Label... labels) {
139+
emitDeferredStartProbe();
140+
super.visitTableSwitchInsn(min, max, dflt, labels);
141+
}
142+
143+
@Override
144+
public void visitLookupSwitchInsn(org.objectweb.asm.Label dflt, int[] keys, org.objectweb.asm.Label[] labels) {
145+
emitDeferredStartProbe();
146+
super.visitLookupSwitchInsn(dflt, keys, labels);
147+
}
148+
149+
@Override
150+
public void visitMultiANewArrayInsn(String descriptor, int numDimensions) {
151+
emitDeferredStartProbe();
152+
super.visitMultiANewArrayInsn(descriptor, numDimensions);
153+
}
154+
77155
private void emitProbeStart() {
78156
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
79157
startVarIndex = newLocal(Type.LONG_TYPE);

0 commit comments

Comments
 (0)