21
21
* questions.
22
22
*/
23
23
24
-
25
-
26
24
/*
27
25
* @test
28
26
* @summary Fuzz tests for java.lang.Continuation
45
43
// * @run main/othervm/timeout=300 -XX:+UseContinuationLazyCopy -XX:-UseContinuationChunks -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. Fuzz
46
44
// * @run main/othervm/timeout=300 -XX:+UseContinuationLazyCopy -XX:+UseContinuationChunks -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. Fuzz
47
45
46
+ // * @run main/othervm/timeout=3000 -XX:StartFlightRecording=filename=test.jfr,settings=profile -XX:+UseContinuationLazyCopy -XX:-UseContinuationChunks -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. Fuzz
47
+
48
48
// Anything excluded or not compileonly is not compiled; see CompilerOracle::should_exclude
49
49
50
50
// @run driver jdk.test.lib.FileInstaller compilerDirectives.json compilerDirectives.json
51
51
// -XX:CompilerDirectivesFile=compilerDirectives.json
52
52
53
- import java .lang .invoke .MethodHandle ;
54
- import java .lang .invoke .MethodHandles ;
55
- import java .lang .invoke .MethodType ;
56
- import java .lang .reflect .InvocationTargetException ;
57
- import java .lang .reflect .Method ;
58
- import java .lang .reflect .Modifier ;
53
+ import java .lang .invoke .*;
54
+ import java .lang .reflect .*;
59
55
import java .lang .StackWalker .StackFrame ;
60
56
import java .io .IOException ;
61
- import java .nio .file .Files ;
62
- import java .nio .file .Path ;
63
- import java .util .ArrayList ;
64
- import java .util .Arrays ;
65
- import java .util .EnumMap ;
66
- import java .util .EnumSet ;
67
- import java .util .List ;
68
- import java .util .Map ;
69
- import java .util .Objects ;
70
- import java .util .Random ;
71
- import java .util .Set ;
72
- import java .util .HashSet ;
57
+ import java .nio .file .*;
58
+ import java .util .*;
73
59
import java .util .stream .Collectors ;
74
60
import java .util .stream .Stream ;
75
61
import static java .lang .Math .max ;
80
66
import sun .hotspot .WhiteBox ;
81
67
82
68
public class Fuzz implements Runnable {
83
- static final boolean VERIFY_STACK = true ; // could add significant time
69
+ static final boolean VERIFY_STACK = false ; // could add significant time
70
+ static final boolean FILE = true ;
84
71
static final boolean RANDOM = false ;
85
72
static final boolean VERBOSE = false ;
86
73
87
- public static void main (String [] args ) {
88
- for (int compileLevel : new int []{4 })
89
- for (boolean compileRun : new boolean []{true })
90
- test (compileLevel , compileRun );
91
- }
92
-
93
- private static boolean COMPILE_RUN ;
94
- private static int COMPILE_LEVEL ;
95
-
96
- static void test (int compileLevel , boolean compileRun ) {
97
- resetCompilation ();
98
- COMPILE_LEVEL = compileLevel ;
99
- COMPILE_RUN = compileRun ;
74
+ static final String FILENAME = "fuzz.dat" ;
75
+ static final Path TEST_DIR = Path .of (System .getProperty ("test.src" , "." ));
100
76
101
- testFile ();
102
- if (RANDOM )
103
- testRandom ();
104
- }
105
-
106
- static void testFile () {
107
- System .out .println ("-- FILE --" );
108
- try {
109
- testStream (file (Path .of (System .getProperty ("test.src" , "." )).resolve ("fuzz.dat" )));
110
- } catch (IOException e ) { throw new RuntimeException (e ); }
111
- }
112
-
113
- static void testRandom () {
114
- long seed = 1L ; // System.currentTimeMillis();
115
- System .out .println ("-- RANDOM (seed: " + seed + ") --" );
116
- testStream (random (new Random (seed )).limit (50 ));
77
+ public static void main (String [] args ) {
78
+ warmup ();
79
+ for (int compileLevel : new int []{4 }) {
80
+ for (boolean compileRun : new boolean []{true }) {
81
+ COMPILE_LEVEL = compileLevel ;
82
+ COMPILE_RUN = compileRun ;
83
+ resetCompilation ();
84
+ runTests ();
85
+ }
86
+ }
117
87
}
118
88
119
- static Stream <Op []> file (Path file ) throws IOException {
120
- return Files .lines (file ).map (String ::trim ).filter (s -> !s .isBlank () && !s .startsWith ("#" )).map (Fuzz ::parse );
89
+ static void runTests () {
90
+ if (FILE ) testFile (FILENAME );
91
+ if (RANDOM ) testRandom (System .currentTimeMillis (), 50 );
121
92
}
122
93
123
- static Stream <Op []> random (Random rnd ) {
124
- var g = new Generator (rnd );
125
- return Stream .iterate (0 , x ->x +1 ).map (__ -> g .generate ());
126
- }
94
+ static void testStream (Stream <Op []> traces ) { traces .forEach (Fuzz ::testTrace ); }
127
95
128
- static void testStream (Stream <Op []> traces ) {
129
- traces .forEach (Fuzz ::testTrace );
130
- }
96
+ static final int RETRIES = 4 ;
131
97
132
98
static void testTrace (Op [] trace ) {
133
99
System .out .println ();
@@ -144,9 +110,9 @@ static void testTrace(Op[] trace) {
144
110
int yields = fuzz .test ();
145
111
time (start , "Test (" + yields + " yields)" );
146
112
147
- if (!fuzz .checkCompilation ("AFTER" )) {
148
- if (retry ++ < 2 ) {
149
- System .out .println ("RETRYING" );
113
+ if (!fuzz .checkCompilation ()) {
114
+ if (retry ++ < RETRIES ) {
115
+ System .out .println ("RETRYING " + retry );
150
116
continue ;
151
117
}
152
118
}
@@ -180,7 +146,7 @@ enum Op {
180
146
static final Op [] ARRAY = new Op [0 ];
181
147
}
182
148
183
- ///// Trace Gnereation
149
+ ///// Trace Generation
184
150
185
151
static class Generator {
186
152
public Op [] generate () {
@@ -219,6 +185,33 @@ public Op[] generate() {
219
185
private int plusOrMinus (int n ) { return rnd .nextInt (2 *n + 1 ) - n ; }
220
186
}
221
187
188
+ static Stream <Op []> random (Random rnd ) {
189
+ var g = new Generator (rnd );
190
+ return Stream .iterate (0 , x ->x +1 ).map (__ -> g .generate ());
191
+ }
192
+
193
+ static void testRandom (long seed , int number ) {
194
+ System .out .println ("-- RANDOM (seed: " + seed + ") --" );
195
+ testStream (random (new Random (seed )).limit (number ));
196
+ }
197
+
198
+ //// File
199
+
200
+ static void testFile (String fileName ) {
201
+ System .out .println ("-- FILE (" + FILENAME + ") --" );
202
+ try {
203
+ testStream (file (TEST_DIR .resolve (fileName )));
204
+ } catch (IOException e ) { throw new RuntimeException (e ); }
205
+ }
206
+
207
+ static Stream <Op []> file (Path file ) throws IOException {
208
+ return Files .lines (file ).map (String ::trim ).filter (s -> !s .isBlank () && !s .startsWith ("#" )).map (Fuzz ::parse );
209
+ }
210
+
211
+ static Op [] parse (String line ) {
212
+ return Arrays .stream (line .split (", " )).map (s -> Enum .valueOf (Op .class , s ))
213
+ .collect (Collectors .toList ()).toArray (Op .ARRAY );
214
+ }
222
215
223
216
////////////////////////////////////////
224
217
@@ -254,12 +247,14 @@ int test() {
254
247
int count = 0 ;
255
248
try {
256
249
while (true ) {
250
+ var start = time ();
257
251
cont .run ();
258
252
if (cont .isDone ()) break ;
259
253
260
254
assert !shouldThrow ();
261
255
verifyStack (cont );
262
256
count ++;
257
+ time (start , "Iteration" );
263
258
}
264
259
verifyResult (result );
265
260
} catch (FuzzException e ) {
@@ -346,6 +341,7 @@ boolean shouldThrow() {
346
341
switch (trace [i ]) {
347
342
case CALL_I_CTCH , CALL_C_CTCH -> { return false ; }
348
343
case THROW -> { return true ; }
344
+ default -> {}
349
345
}
350
346
}
351
347
return false ;
@@ -360,24 +356,28 @@ void captureStack() {
360
356
361
357
void verifyStack () {
362
358
if (!VERIFY_STACK ) return ;
359
+ var start = time ();
363
360
verifyStack (backtrace );
364
361
verifyStack (backtrace , StackWalkerHelper .toStackTraceElement (fbacktrace ));
365
362
verifyStack (fbacktrace , lfbacktrace );
366
363
367
364
verifyStack (backtrace , Thread .currentThread ().getStackTrace ());
368
365
verifyStack (fbacktrace , StackWalkerHelper .getStackFrames (SCOPE ));
369
366
verifyStack (lfbacktrace , StackWalkerHelper .getLiveStackFrames (SCOPE ));
367
+ time (start , "Verify stack" );
370
368
}
371
369
372
370
void verifyStack (Continuation cont ) {
373
371
if (!VERIFY_STACK ) return ;
372
+ var start = time ();
374
373
verifyStack (backtrace );
375
374
verifyStack (backtrace , StackWalkerHelper .toStackTraceElement (fbacktrace ));
376
375
verifyStack (fbacktrace , lfbacktrace );
377
376
378
377
verifyStack (backtrace , cont .getStackTrace ());
379
378
verifyStack (fbacktrace , StackWalkerHelper .getStackFrames (cont ));
380
379
verifyStack (lfbacktrace , StackWalkerHelper .getLiveStackFrames (cont ));
380
+ time (start , "Verify continuation stack" );
381
381
}
382
382
383
383
static boolean isStackCaptureMechanism (Object sf ) {
@@ -452,7 +452,25 @@ static String sfToString(Object f) {
452
452
return f instanceof StackFrame ? StackWalkerHelper .frameToString ((StackFrame )f ) : Objects .toString (f );
453
453
}
454
454
455
- ////// Static Helpers
455
+ ////// Compilation
456
+
457
+ private static boolean COMPILE_RUN ;
458
+ private static int COMPILE_LEVEL ;
459
+
460
+ static final int WARMUP_ITERS = 15_000 ;
461
+ static final Op [] WARMUP_TRACE = {Op .MH_C_INT , Op .MH_C_MANY , Op .REF_C_INT , Op .REF_C_MANY , Op .CALL_C_INT };
462
+
463
+ static void warmup () {
464
+ final long start = time ();
465
+ warmup (WARMUP_TRACE , WARMUP_ITERS ); // generate (for reflection) and compile method handles
466
+ time (start , "Warmup" );
467
+ }
468
+
469
+ static void warmup (Op [] trace , int times ) {
470
+ for (int i =0 ; i <times ; i ++) {
471
+ new Fuzz (trace ).run ();
472
+ }
473
+ }
456
474
457
475
static void resetCompilation () {
458
476
Set <Method > compile = Op .COMPILED .stream ().map (Fuzz ::method ).collect (Collectors .toCollection (HashSet ::new ));
@@ -502,13 +520,34 @@ static void compile() {
502
520
time (start , "Compile" );
503
521
}
504
522
505
- boolean checkCompilation (String message ) {
523
+ boolean checkContinuationCompilation () {
524
+ for (Method m : Continuation .class .getDeclaredMethods ()) {
525
+ if (!WB .isMethodCompiled (m )) {
526
+ if (!Modifier .isNative (m .getModifiers ())
527
+ && (m .getName ().startsWith ("enter" )
528
+ || m .getName ().startsWith ("yield" ))) {
529
+ return false ;
530
+ }
531
+ }
532
+ }
533
+ return true ;
534
+ }
535
+
536
+ boolean checkCompilation () {
537
+ boolean res = true ;
538
+
539
+ if (!checkContinuationCompilation ()) {
540
+ res = false ;
541
+ System .out .println ("CHANGED CONTINUATION COMPILATION" );
542
+ }
543
+
506
544
Op [] newTrace = Arrays .copyOf (trace , trace .length );
507
- boolean res = checkCompilation (newTrace );
508
- if (! res ) {
509
- System .out .println ("CHANGED COMPILATION " + message );
545
+ if (! checkCompilation (newTrace )) {
546
+ res = false ;
547
+ System .out .println ("CHANGED COMPILATION" );
510
548
printTrace (newTrace );
511
549
}
550
+
512
551
return res ;
513
552
}
514
553
@@ -523,6 +562,8 @@ static boolean checkCompilation(Op[] trace) {
523
562
return ok ;
524
563
}
525
564
565
+ //// Static Helpers
566
+
526
567
static void rethrow (Throwable t ) {
527
568
if (t instanceof Error ) throw (Error )t ;
528
569
if (t instanceof RuntimeException ) throw (RuntimeException )t ;
@@ -537,12 +578,7 @@ static <T> T[] arrayType(T[] array) {
537
578
538
579
static String write (Op [] trace ) {
539
580
return Arrays .stream (trace ).map (Object ::toString ).collect (Collectors .joining (", " ));
540
- }
541
-
542
- static Op [] parse (String line ) {
543
- return Arrays .stream (line .split (", " )).map (s -> Enum .valueOf (Op .class , s ))
544
- .collect (Collectors .toList ()).toArray (Op .ARRAY );
545
- }
581
+ }
546
582
547
583
static Method method (Op op ) { return method .get (op ); }
548
584
static MethodHandle handle (Op op ) { return handle .get (op ); }
0 commit comments