1
1
/*
2
- * Copyright (c) 2018, 2020 , Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 2018, 2024 , Oracle and/or its affiliates. All rights reserved.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
23
23
24
24
package jdk .jfr .event .io ;
25
25
26
+ import java .lang .classfile .ClassFile ;
27
+ import java .lang .classfile .CodeBuilder ;
28
+ import java .lang .classfile .CodeElement ;
29
+ import java .lang .classfile .CodeTransform ;
30
+ import java .lang .classfile .MethodModel ;
31
+ import java .lang .classfile .MethodTransform ;
32
+ import java .lang .constant .ClassDesc ;
33
+ import java .lang .constant .MethodTypeDesc ;
26
34
import java .util .Arrays ;
27
35
import java .util .Set ;
28
36
import java .util .HashSet ;
31
39
import java .lang .instrument .ClassFileTransformer ;
32
40
import java .lang .instrument .Instrumentation ;
33
41
import java .lang .instrument .IllegalClassFormatException ;
42
+ import java .util .stream .Collectors ;
43
+ import java .util .stream .Stream ;
34
44
35
- import jdk .internal .org .objectweb .asm .ClassReader ;
36
- import jdk .internal .org .objectweb .asm .ClassVisitor ;
37
- import jdk .internal .org .objectweb .asm .MethodVisitor ;
38
- import jdk .internal .org .objectweb .asm .ClassWriter ;
39
- import jdk .internal .org .objectweb .asm .Opcodes ;
40
- import jdk .internal .org .objectweb .asm .Type ;
41
45
import jdk .test .lib .process .OutputAnalyzer ;
42
46
import jdk .test .lib .process .ProcessTools ;
43
47
48
+ import static java .lang .constant .ConstantDescs .CD_String ;
49
+ import static java .lang .constant .ConstantDescs .CD_void ;
50
+
44
51
/*
45
52
* @test
46
53
* @summary Test that will instrument the same classes that JFR will also instrument.
47
54
* @key jfr
48
55
* @requires vm.hasJFR
49
56
*
50
57
* @library /test/lib /test/jdk
51
- * @modules java.base/jdk.internal.org.objectweb.asm
52
- * java.instrument
58
+ * @modules java.instrument
53
59
* jdk.jartool/sun.tools.jar
54
60
* jdk.jfr
61
+ * @enablePreview
62
+ * @comment update --enable-preview in launchTest() too
55
63
*
56
64
* @run main/othervm jdk.jfr.event.io.TestInstrumentation
57
65
*/
@@ -90,7 +98,7 @@ public class TestInstrumentation implements ClassFileTransformer {
90
98
private static TestInstrumentation testTransformer = null ;
91
99
92
100
// All methods that will be instrumented.
93
- private static final String [] instrMethodKeys = {
101
+ private static final Set < MethodKey > instrMethodKeys = Stream . of (
94
102
"java/io/RandomAccessFile::seek::(J)V" ,
95
103
"java/io/RandomAccessFile::read::()I" ,
96
104
"java/io/RandomAccessFile::read::([B)I" ,
@@ -116,31 +124,24 @@ public class TestInstrumentation implements ClassFileTransformer {
116
124
"java/nio/channels/SocketChannel::read::([Ljava/nio/ByteBuffer;)J" ,
117
125
"java/nio/channels/SocketChannel::write::([Ljava/nio/ByteBuffer;)J" ,
118
126
"sun/nio/ch/FileChannelImpl::read::(Ljava/nio/ByteBuffer;)I" ,
119
- "sun/nio/ch/FileChannelImpl::write::(Ljava/nio/ByteBuffer;)I" ,
120
- };
121
-
122
- private static String getInstrMethodKey (String className , String methodName , String signature ) {
123
- // This key is used to identify a class and method. It is sent to callback(key)
124
- return className + "::" + methodName + "::" + signature ;
125
- }
126
-
127
- private static String getClassFromMethodKey (String methodKey ) {
128
- return methodKey .split ("::" )[0 ];
129
- }
127
+ "sun/nio/ch/FileChannelImpl::write::(Ljava/nio/ByteBuffer;)I"
128
+ ).map (s -> {
129
+ String [] a = s .split ("::" );
130
+ return new MethodKey (a [0 ], a [1 ], a [2 ]);
131
+ }).collect (Collectors .toUnmodifiableSet ());
130
132
131
133
// Set of all classes targeted for instrumentation.
132
- private static Set <String > instrClassesTarget = null ;
134
+ private static Set <ClassDesc > instrClassesTarget = null ;
133
135
134
136
// Set of all classes where instrumentation has been completed.
135
- private static Set <String > instrClassesDone = null ;
137
+ private static Set <ClassDesc > instrClassesDone = null ;
136
138
137
139
static {
138
140
// Split class names from InstrMethodKeys.
139
- instrClassesTarget = new HashSet <String >();
140
- instrClassesDone = new HashSet <String >();
141
- for (String s : instrMethodKeys ) {
142
- String className = getClassFromMethodKey (s );
143
- instrClassesTarget .add (className );
141
+ instrClassesTarget = new HashSet <>();
142
+ instrClassesDone = new HashSet <>();
143
+ for (MethodKey key : instrMethodKeys ) {
144
+ instrClassesTarget .add (key .owner ());
144
145
}
145
146
}
146
147
@@ -164,9 +165,10 @@ public static void main(String[] args) throws Throwable {
164
165
runAllTests (TransformStatus .Transformed );
165
166
166
167
// Retransform all classes and then repeat tests
167
- Set <Class <?>> classes = new HashSet <Class <?>>();
168
- for (String className : instrClassesTarget ) {
169
- Class <?> clazz = Class .forName (className .replaceAll ("/" , "." ));
168
+ Set <Class <?>> classes = new HashSet <>();
169
+ for (ClassDesc className : instrClassesTarget ) {
170
+ var desc = className .descriptorString ();
171
+ Class <?> clazz = Class .forName (desc .substring (1 , desc .length () - 1 ).replace ('/' , '.' ));
170
172
classes .add (clazz );
171
173
log ("Will retransform " + clazz .getName ());
172
174
}
@@ -197,10 +199,10 @@ public static void runAllTests(TransformStatus status) throws Throwable {
197
199
198
200
// Verify that all expected callbacks have been called.
199
201
Set <String > callbackKeys = InstrumentationCallback .getKeysCopy ();
200
- for (String key : instrMethodKeys ) {
201
- boolean gotCallback = callbackKeys .contains (key );
202
+ for (MethodKey key : instrMethodKeys ) {
203
+ boolean gotCallback = callbackKeys .contains (key . toString () );
202
204
boolean expectsCallback = isClassInstrumented (status , key );
203
- String msg = String .format ("key:%s, expects:%b" , key , expectsCallback );
205
+ String msg = String .format ("status:%s, key:%s, expects:%b" , status , key , expectsCallback );
204
206
if (gotCallback != expectsCallback ) {
205
207
throw new Exception ("Wrong callback() for " + msg );
206
208
} else {
@@ -214,14 +216,14 @@ public static void runAllTests(TransformStatus status) throws Throwable {
214
216
}
215
217
}
216
218
217
- private static boolean isClassInstrumented (TransformStatus status , String key ) throws Throwable {
219
+ private static boolean isClassInstrumented (TransformStatus status , MethodKey key ) throws Throwable {
218
220
switch (status ) {
219
221
case Retransformed :
220
222
return true ;
221
223
case Removed :
222
224
return false ;
223
225
case Transformed :
224
- String className = getClassFromMethodKey ( key );
226
+ var className = key . owner ( );
225
227
return instrClassesDone .contains (className );
226
228
}
227
229
throw new Exception ("Test error: Unknown TransformStatus: " + status );
@@ -279,7 +281,7 @@ private static void launchTest() throws Throwable {
279
281
280
282
String [] args = {
281
283
"-Xbootclasspath/a:" + testClassDir + "InstrumentationCallback.jar" ,
282
- "--add-exports" , "java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED " ,
284
+ "--enable-preview " ,
283
285
"-classpath" , classpath ,
284
286
"-javaagent:" + testClassDir + "TestInstrumentation.jar" ,
285
287
"jdk.jfr.event.io.TestInstrumentation$TestMain" };
@@ -305,67 +307,52 @@ public byte[] transform(
305
307
ClassLoader classLoader , String className , Class <?> classBeingRedefined ,
306
308
ProtectionDomain pd , byte [] bytes ) throws IllegalClassFormatException {
307
309
// Check if this class should be instrumented.
308
- if (!instrClassesTarget .contains (className )) {
310
+ ClassDesc target = ClassDesc .ofInternalName (className );
311
+ if (!instrClassesTarget .contains (target )) {
309
312
return null ;
310
313
}
311
314
312
315
boolean isRedefinition = classBeingRedefined != null ;
313
316
log ("instrument class(" + className + ") " + (isRedefinition ? "redef" : "load" ));
314
317
315
- ClassReader reader = new ClassReader (bytes );
316
- ClassWriter writer = new ClassWriter (
317
- reader , ClassWriter .COMPUTE_MAXS | ClassWriter .COMPUTE_FRAMES );
318
- CallbackClassVisitor classVisitor = new CallbackClassVisitor (writer );
319
- reader .accept (classVisitor , 0 );
320
- instrClassesDone .add (className );
321
- return writer .toByteArray ();
322
- }
323
-
324
- private static class CallbackClassVisitor extends ClassVisitor {
325
- private String className ;
326
-
327
- public CallbackClassVisitor (ClassVisitor cv ) {
328
- super (Opcodes .ASM7 , cv );
329
- }
330
-
331
- @ Override
332
- public void visit (
333
- int version , int access , String name , String signature ,
334
- String superName , String [] interfaces ) {
335
- cv .visit (version , access , name , signature , superName , interfaces );
336
- className = name ;
337
- }
318
+ instrClassesDone .add (target );
319
+ var cf = ClassFile .of ();
320
+ return cf .transform (cf .parse (bytes ), (clb , ce ) -> {
321
+ MethodKey key ;
322
+ if (ce instanceof MethodModel mm && instrMethodKeys .contains (key = new MethodKey (
323
+ target , mm .methodName ().stringValue (), mm .methodTypeSymbol ()))) {
324
+ clb .transformMethod (mm , MethodTransform .transformingCode (new CodeTransform () {
325
+ private static final MethodTypeDesc MTD_callback = MethodTypeDesc .of (CD_void , CD_String );
326
+ private static final ClassDesc CD_InstrumentationCallback = InstrumentationCallback .class
327
+ .describeConstable ().orElseThrow ();
328
+
329
+ @ Override
330
+ public void atStart (CodeBuilder cb ) {
331
+ cb .loadConstant (key .toString ());
332
+ cb .invokestatic (CD_InstrumentationCallback , "callback" , MTD_callback );
333
+ log ("instrumented " + key );
334
+ }
338
335
339
- @ Override
340
- public MethodVisitor visitMethod (
341
- int access , String methodName , String desc , String signature , String [] exceptions ) {
342
- String methodKey = getInstrMethodKey (className , methodName , desc );
343
- boolean isInstrumentedMethod = Arrays .asList (instrMethodKeys ).contains (methodKey );
344
- MethodVisitor mv = cv .visitMethod (access , methodName , desc , signature , exceptions );
345
- if (isInstrumentedMethod && mv != null ) {
346
- mv = new CallbackMethodVisitor (mv , methodKey );
347
- log ("instrumented " + methodKey );
336
+ @ Override
337
+ public void accept (CodeBuilder cb , CodeElement ce ) {
338
+ cb .with (ce );
339
+ }
340
+ }));
341
+ } else {
342
+ clb .with (ce );
348
343
}
349
- return mv ;
350
- }
344
+ });
351
345
}
352
346
353
- public static class CallbackMethodVisitor extends MethodVisitor {
354
- private String logMessage ;
355
-
356
- public CallbackMethodVisitor (MethodVisitor mv , String logMessage ) {
357
- super (Opcodes .ASM7 , mv );
358
- this .logMessage = logMessage ;
347
+ public record MethodKey (ClassDesc owner , String name , MethodTypeDesc desc ) {
348
+ public MethodKey (String className , String methodName , String signature ) {
349
+ this (ClassDesc .ofInternalName (className ), methodName , MethodTypeDesc .ofDescriptor (signature ));
359
350
}
360
351
361
352
@ Override
362
- public void visitCode () {
363
- mv .visitCode ();
364
- String methodDescr = Type .getMethodDescriptor (Type .VOID_TYPE , Type .getType (String .class ));
365
- String className = InstrumentationCallback .class .getName ().replace ('.' , '/' );
366
- mv .visitLdcInsn (logMessage );
367
- mv .visitMethodInsn (Opcodes .INVOKESTATIC , className , "callback" , methodDescr );
353
+ public String toString () {
354
+ var ownerDesc = owner .descriptorString ();
355
+ return ownerDesc .substring (1 , ownerDesc .length () - 1 ) + "::" + name + "::" + desc .descriptorString ();
368
356
}
369
357
}
370
-
371
358
}
0 commit comments