1
1
/*
2
- * Copyright (c) 2013, 2023 , Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 2013, 2025 , 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
34
34
import java .time .format .DateTimeFormatter ;
35
35
import java .util .Comparator ;
36
36
import java .util .List ;
37
+ import java .util .Map ;
37
38
import java .util .Map .Entry ;
38
39
import java .util .Objects ;
39
40
import java .util .concurrent .ConcurrentHashMap ;
40
41
import java .util .concurrent .CopyOnWriteArrayList ;
41
42
import java .util .concurrent .atomic .AtomicInteger ;
43
+ import java .util .concurrent .atomic .AtomicLong ;
42
44
import java .util .function .Consumer ;
43
45
import java .util .function .Function ;
44
46
import java .util .stream .Collectors ;
72
74
import com .oracle .graal .pointsto .flow .StoreFieldTypeFlow .StoreStaticFieldTypeFlow ;
73
75
import com .oracle .graal .pointsto .flow .TypeFlow ;
74
76
import com .oracle .graal .pointsto .flow .builder .TypeFlowBuilder ;
77
+ import com .oracle .graal .pointsto .flow .context .bytecode .ContextSensitiveMultiTypeState ;
78
+ import com .oracle .graal .pointsto .flow .context .bytecode .ContextSensitiveSingleTypeState ;
75
79
import com .oracle .graal .pointsto .meta .AnalysisField ;
76
80
import com .oracle .graal .pointsto .meta .AnalysisType ;
77
81
import com .oracle .graal .pointsto .util .AnalysisError ;
82
+ import com .oracle .graal .pointsto .util .GraalAccess ;
78
83
import com .oracle .svm .util .ClassUtil ;
79
84
80
85
import jdk .graal .compiler .graph .NodeSourcePosition ;
84
89
import jdk .vm .ci .meta .JavaType ;
85
90
import jdk .vm .ci .meta .ResolvedJavaMethod ;
86
91
92
+ /**
93
+ * This class provides methods for collecting and reporting statistics about
94
+ * {@link PointsToAnalysis}. It tracks various metrics such as {@link TypeState} memory footprint,
95
+ * {@link TypeFlow} statistics, and union operation statistics. If the {@link TypeFlow} or
96
+ * {@link TypeState} hierarchy changes, this class might have to be updated to reflect that.
97
+ *
98
+ * @see PointsToAnalysis
99
+ * @see TypeFlow
100
+ * @see TypeState
101
+ */
87
102
public class PointsToStats {
88
103
89
104
static boolean reportStatistics ;
105
+ static boolean reportTypeStateMemoryFootPrint ;
90
106
91
107
public static void init (PointsToAnalysis bb ) {
108
+ reportStatistics = bb .reportAnalysisStatistics ();
109
+ reportTypeStateMemoryFootPrint = bb .reportTypeStateMemoryFootprint ();
92
110
registerTypeState (bb , EmptyTypeState .SINGLETON );
93
111
registerTypeState (bb , NullTypeState .SINGLETON );
94
112
registerTypeState (bb , AnyPrimitiveTypeState .SINGLETON );
95
113
PrimitiveConstantTypeState .registerCachedTypeStates (bb );
96
- reportStatistics = bb .reportAnalysisStatistics ();
97
114
}
98
115
99
116
public static void report (@ SuppressWarnings ("unused" ) BigBang bb , String reportNameRoot ) {
100
-
117
+ assert reportStatistics || reportTypeStateMemoryFootPrint : "At least one of these options should be selected." ;
101
118
try {
102
119
DateTimeFormatter formatter = DateTimeFormatter .ofPattern ("yyyyMMdd_HHmmss" );
103
120
String timeStamp = LocalDateTime .now ().format (formatter );
104
121
Path statsDirectory = Files .createDirectories (FileSystems .getDefault ().getPath ("svmbuild" ).resolve ("stats" ));
105
122
106
- doReport (statsDirectory , reportNameRoot , "type state stats" , timeStamp , PointsToStats ::reportTypeStateStats );
107
- doReport (statsDirectory , reportNameRoot , "union operation stats" , timeStamp , PointsToStats ::reportUnionOpertationsStats );
108
- doReport (statsDirectory , reportNameRoot , "type flow stats" , timeStamp , PointsToStats ::reportTypeFlowStats );
109
- doReport (statsDirectory , reportNameRoot , "pruned type flow stats" , timeStamp , PointsToStats ::reportPrunedTypeFlows );
123
+ /* Both report option include the footprint, so generate it unconditionally. */
124
+ doReport (statsDirectory , reportNameRoot , "type state memory footprint" , timeStamp , PointsToStats ::reportTypeStateMemoryFootprint );
125
+ if (reportStatistics ) {
126
+ /* The rest of reports should only be generated if reportStatistics was enabled. */
127
+ doReport (statsDirectory , reportNameRoot , "detailed type state stats" , timeStamp , PointsToStats ::reportTypeStateStats );
128
+ doReport (statsDirectory , reportNameRoot , "union operation stats" , timeStamp , PointsToStats ::reportUnionOpertationsStats );
129
+ doReport (statsDirectory , reportNameRoot , "type flow stats" , timeStamp , PointsToStats ::reportTypeFlowStats );
130
+ doReport (statsDirectory , reportNameRoot , "pruned type flow stats" , timeStamp , PointsToStats ::reportPrunedTypeFlows );
131
+ }
110
132
111
133
} catch (IOException e ) {
112
134
throw JVMCIError .shouldNotReachHere (e );
@@ -316,7 +338,25 @@ private static void reportTypeFlowStats(BufferedWriter out) {
316
338
private static final AtomicInteger nextStateId = new AtomicInteger ();
317
339
private static ConcurrentHashMap <TypeState , AtomicInteger > typeStateStats = new ConcurrentHashMap <>();
318
340
341
+ /**
342
+ * Contains the count and total size of the given TypeState class.
343
+ *
344
+ * @see #typeStateFootprint
345
+ * @see #reportTypeStateMemoryFootprint
346
+ * @see #registerTypeStateSize
347
+ */
348
+ private static final class TypeStateMemoryStats {
349
+ AtomicInteger frequency = new AtomicInteger ();
350
+ AtomicLong size = new AtomicLong ();
351
+ }
352
+
353
+ private static Map <Class <? extends TypeState >, TypeStateMemoryStats > typeStateFootprint = new ConcurrentHashMap <>();
354
+
319
355
public static <T extends TypeState > T registerTypeState (PointsToAnalysis bb , T state ) {
356
+ if (bb .reportAnalysisStatistics () || bb .reportTypeStateMemoryFootprint ()) {
357
+ /* TypeState memory footprint is measured in both cases. */
358
+ registerTypeStateSize (state );
359
+ }
320
360
321
361
if (!bb .reportAnalysisStatistics ()) {
322
362
return state ;
@@ -343,6 +383,77 @@ private static int typesCount(TypeState state) {
343
383
return state .typesCount ();
344
384
}
345
385
386
+ /**
387
+ * This method is used to track the memory footprint of {@link TypeState} classes. It updates
388
+ * the frequency and total size of the given {@link TypeState} class in the
389
+ * {@link #typeStateFootprint} map.
390
+ *
391
+ * @param <T> the type of the {@link TypeState} instance
392
+ * @param state the {@link TypeState} instance to register
393
+ */
394
+ private static <T extends TypeState > void registerTypeStateSize (T state ) {
395
+ var stats = typeStateFootprint .computeIfAbsent (state .getClass (), __ -> new TypeStateMemoryStats ());
396
+ stats .frequency .incrementAndGet ();
397
+ stats .size .addAndGet (getTypeStateMemorySize (state ));
398
+ }
399
+
400
+ /**
401
+ * In most cases, we use just the shallow size of the object as obtained from the heap dump.
402
+ * However, {@link MultiTypeState} is an exception, because it represents a set of values, so we
403
+ * consider the size of the underlying collection as well.
404
+ */
405
+ private static long getTypeStateMemorySize (TypeState typeState ) {
406
+ var shallowSize = getObjectSize (typeState );
407
+ if (!(typeState instanceof MultiTypeState multi )) {
408
+ return shallowSize ;
409
+ }
410
+ var bitsetSize = getObjectSize (multi .typesBitSet );
411
+ var wordArraySize = getObjectSize (TypeStateUtils .extractBitSetField (multi .typesBitSet ));
412
+ return shallowSize + bitsetSize + wordArraySize ;
413
+ }
414
+
415
+ private static long getObjectSize (Object object ) {
416
+ return GraalAccess .getOriginalProviders ().getMetaAccess ().getMemorySize (GraalAccess .getOriginalProviders ().getSnippetReflection ().forObject (object ));
417
+ }
418
+
419
+ /**
420
+ * Reports the memory footprint of {@link TypeState} classes used by {@link PointsToAnalysis}.
421
+ * <p>
422
+ * This method writes a report to the provided {@link BufferedWriter} containing the frequency
423
+ * and total size of each allocated {@link TypeState} class.
424
+ * <p>
425
+ * The report includes the following information:
426
+ * <ul>
427
+ * <li>Type: the class name of the {@link TypeState}</li>
428
+ * <li>Frequency: the number of instances of the {@link TypeState} class</li>
429
+ * <li>Total Size: the total memory size of all instances of the {@link TypeState} class</li>
430
+ * </ul>
431
+ * <p>
432
+ * The report is written in a tabular format with the columns "Type", "Frequency", and "Total
433
+ * Size".
434
+ *
435
+ * @param out the {@link BufferedWriter} to write the report to
436
+ */
437
+ private static void reportTypeStateMemoryFootprint (BufferedWriter out ) {
438
+ doWrite (out , String .format ("%30s\t %15s\t %15s%n" , "Type" , "Frequency" , "Total Size" ));
439
+ /* Use explicit order for the final report. */
440
+ var typeStateOrder = List .of (EmptyTypeState .class , NullTypeState .class , PrimitiveConstantTypeState .class , AnyPrimitiveTypeState .class , SingleTypeState .class ,
441
+ ContextSensitiveSingleTypeState .class , ConstantTypeState .class ,
442
+ MultiTypeState .class , ContextSensitiveMultiTypeState .class );
443
+ var totalFreq = 0L ;
444
+ var totalSize = 0L ;
445
+ for (var typeStateClass : typeStateOrder ) {
446
+ var stats = typeStateFootprint .remove (typeStateClass );
447
+ if (stats != null ) {
448
+ doWrite (out , String .format ("%30s\t %15d\t %15d%n" , ClassUtil .getUnqualifiedName (typeStateClass ), stats .frequency .get (), stats .size .get ()));
449
+ totalFreq += stats .frequency .get ();
450
+ totalSize += stats .size .get ();
451
+ }
452
+ }
453
+ AnalysisError .guarantee (typeStateFootprint .isEmpty (), "Missing elements in the typeStateOrder list: %s, please update it." , typeStateFootprint .keySet ());
454
+ doWrite (out , String .format ("%30s\t %15d\t %15d%n" , "TOTAL" , totalFreq , totalSize ));
455
+ }
456
+
346
457
private static void reportTypeStateStats (BufferedWriter out ) {
347
458
348
459
doWrite (out , String .format ("%10s\t %10s\t %10s\t %10s\t %10s%n" , "Id" , "Frequency" , "Types#" , "Object#" , "Types" ));
@@ -391,6 +502,11 @@ private static void reportUnionOpertationsStats(BufferedWriter out) {
391
502
});
392
503
}
393
504
505
+ public static void cleanupAfterAnalysis () {
506
+ typeStateStats = null ;
507
+ typeStateFootprint = null ;
508
+ }
509
+
394
510
static class UnionOperation {
395
511
int state1Id ;
396
512
int state2Id ;
0 commit comments