Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -113,6 +113,7 @@ public abstract class PointsToAnalysis extends AbstractAnalysisEngine {

protected final boolean trackTypeFlowInputs;
protected final boolean reportAnalysisStatistics;
protected final boolean reportTypeStateMemoryFootprint;

private ConcurrentMap<UnsafeLoadTypeFlow, Boolean> unsafeLoads;
private ConcurrentMap<UnsafeStoreTypeFlow, Boolean> unsafeStores;
Expand Down Expand Up @@ -146,7 +147,8 @@ public PointsToAnalysis(OptionValues options, AnalysisUniverse universe, HostVM

trackTypeFlowInputs = PointstoOptions.TrackInputFlows.getValue(options);
reportAnalysisStatistics = PointstoOptions.PrintPointsToStatistics.getValue(options);
if (reportAnalysisStatistics) {
reportTypeStateMemoryFootprint = PointstoOptions.PrintTypeStateMemoryFootprint.getValue(options);
if (reportAnalysisStatistics || reportTypeStateMemoryFootprint) {
PointsToStats.init(this);
}

Expand Down Expand Up @@ -201,6 +203,10 @@ public boolean reportAnalysisStatistics() {
return reportAnalysisStatistics;
}

public boolean reportTypeStateMemoryFootprint() {
return reportTypeStateMemoryFootprint;
}

public MethodTypeFlowBuilder createMethodTypeFlowBuilder(PointsToAnalysis bb, PointsToAnalysisMethod method, MethodFlowsGraph flowsGraph, MethodFlowsGraph.GraphKind graphKind) {
return new MethodTypeFlowBuilder(bb, method, flowsGraph, graphKind);
}
Expand Down Expand Up @@ -299,6 +305,7 @@ public void cleanupAfterAnalysis() {
unsafeStores = null;

ConstantObjectsProfiler.constantTypes.clear();
PointsToStats.cleanupAfterAnalysis();
}

public AnalysisType lookup(JavaType type) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -31,6 +31,8 @@

import org.graalvm.collections.EconomicMap;

import com.oracle.graal.pointsto.typestate.TypeState;

import jdk.graal.compiler.options.Option;
import jdk.graal.compiler.options.OptionKey;

Expand Down Expand Up @@ -149,6 +151,16 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean o
@Option(help = "Report analysis statistics.")//
public static final OptionKey<Boolean> PrintPointsToStatistics = new OptionKey<>(false);

/**
* The {@link TypeState} memory footprint report enabled by this option is also generated as a
* part of {@link #PrintPointsToStatistics}. However, running the full
* {@link #PrintPointsToStatistics} is resource-intensive, so we expose
* {@code PrintTypeStateMemoryFootprint} to allow computing just the footprint if the rest is
* not required.
*/
@Option(help = "Report the memory footprint of TypeState objects used by the analysis.")//
public static final OptionKey<Boolean> PrintTypeStateMemoryFootprint = new OptionKey<>(false);

@Option(help = "Path to the contents of the Inspect web server.")//
public static final OptionKey<String> InspectServerContentPath = new OptionKey<>("inspect");

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2021, Alibaba Group Holding Limited. All rights reserved.
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025, Alibaba Group Holding Limited. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -29,14 +29,14 @@
import java.util.List;
import java.util.stream.Collectors;

import jdk.graal.compiler.options.OptionValues;

import com.oracle.graal.pointsto.BigBang;
import com.oracle.graal.pointsto.api.PointstoOptions;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.typestate.PointsToStats;
import com.oracle.graal.pointsto.typestate.TypeStateUtils;

import jdk.graal.compiler.options.OptionValues;

public class AnalysisReporter {
public static void printAnalysisReports(String imageName, OptionValues options, String reportsPath, BigBang bb) {
if (bb != null) {
Expand All @@ -55,7 +55,7 @@ public static void printAnalysisReports(String imageName, OptionValues options,
AnalysisHeapHistogramPrinter.print(bb, reportsPath, baseImageName);
}

if (PointstoOptions.PrintPointsToStatistics.getValue(options)) {
if (PointstoOptions.PrintPointsToStatistics.getValue(options) || PointstoOptions.PrintTypeStateMemoryFootprint.getValue(options)) {
PointsToStats.report(bb, baseImageName);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -33,7 +33,7 @@
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.util.AnalysisError;

public class MultiTypeState extends TypeState {
public non-sealed class MultiTypeState extends TypeState {

/**
* Keep a bit set for types to easily answer queries like contains type or types count, and
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -34,11 +34,13 @@
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -72,9 +74,12 @@
import com.oracle.graal.pointsto.flow.StoreFieldTypeFlow.StoreStaticFieldTypeFlow;
import com.oracle.graal.pointsto.flow.TypeFlow;
import com.oracle.graal.pointsto.flow.builder.TypeFlowBuilder;
import com.oracle.graal.pointsto.flow.context.bytecode.ContextSensitiveMultiTypeState;
import com.oracle.graal.pointsto.flow.context.bytecode.ContextSensitiveSingleTypeState;
import com.oracle.graal.pointsto.meta.AnalysisField;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.util.AnalysisError;
import com.oracle.graal.pointsto.util.GraalAccess;
import com.oracle.svm.util.ClassUtil;

import jdk.graal.compiler.graph.NodeSourcePosition;
Expand All @@ -84,29 +89,46 @@
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.ResolvedJavaMethod;

/**
* This class provides methods for collecting and reporting statistics about
* {@link PointsToAnalysis}. It tracks various metrics such as {@link TypeState} memory footprint,
* {@link TypeFlow} statistics, and union operation statistics. If the {@link TypeFlow} or
* {@link TypeState} hierarchy changes, this class might have to be updated to reflect that.
*
* @see PointsToAnalysis
* @see TypeFlow
* @see TypeState
*/
public class PointsToStats {

static boolean reportStatistics;
static boolean reportTypeStateMemoryFootPrint;

public static void init(PointsToAnalysis bb) {
reportStatistics = bb.reportAnalysisStatistics();
reportTypeStateMemoryFootPrint = bb.reportTypeStateMemoryFootprint();
registerTypeState(bb, EmptyTypeState.SINGLETON);
registerTypeState(bb, NullTypeState.SINGLETON);
registerTypeState(bb, AnyPrimitiveTypeState.SINGLETON);
PrimitiveConstantTypeState.registerCachedTypeStates(bb);
reportStatistics = bb.reportAnalysisStatistics();
}

public static void report(@SuppressWarnings("unused") BigBang bb, String reportNameRoot) {

assert reportStatistics || reportTypeStateMemoryFootPrint : "At least one of these options should be selected.";
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
String timeStamp = LocalDateTime.now().format(formatter);
Path statsDirectory = Files.createDirectories(FileSystems.getDefault().getPath("svmbuild").resolve("stats"));

doReport(statsDirectory, reportNameRoot, "type state stats", timeStamp, PointsToStats::reportTypeStateStats);
doReport(statsDirectory, reportNameRoot, "union operation stats", timeStamp, PointsToStats::reportUnionOpertationsStats);
doReport(statsDirectory, reportNameRoot, "type flow stats", timeStamp, PointsToStats::reportTypeFlowStats);
doReport(statsDirectory, reportNameRoot, "pruned type flow stats", timeStamp, PointsToStats::reportPrunedTypeFlows);
/* Both report option include the footprint, so generate it unconditionally. */
doReport(statsDirectory, reportNameRoot, "type state memory footprint", timeStamp, PointsToStats::reportTypeStateMemoryFootprint);
if (reportStatistics) {
/* The rest of reports should only be generated if reportStatistics was enabled. */
doReport(statsDirectory, reportNameRoot, "detailed type state stats", timeStamp, PointsToStats::reportTypeStateStats);
doReport(statsDirectory, reportNameRoot, "union operation stats", timeStamp, PointsToStats::reportUnionOpertationsStats);
doReport(statsDirectory, reportNameRoot, "type flow stats", timeStamp, PointsToStats::reportTypeFlowStats);
doReport(statsDirectory, reportNameRoot, "pruned type flow stats", timeStamp, PointsToStats::reportPrunedTypeFlows);
}

} catch (IOException e) {
throw JVMCIError.shouldNotReachHere(e);
Expand Down Expand Up @@ -316,7 +338,25 @@ private static void reportTypeFlowStats(BufferedWriter out) {
private static final AtomicInteger nextStateId = new AtomicInteger();
private static ConcurrentHashMap<TypeState, AtomicInteger> typeStateStats = new ConcurrentHashMap<>();

/**
* Contains the count and total size of the given TypeState class.
*
* @see #typeStateFootprint
* @see #reportTypeStateMemoryFootprint
* @see #registerTypeStateSize
*/
private static final class TypeStateMemoryStats {
AtomicInteger frequency = new AtomicInteger();
AtomicLong size = new AtomicLong();
}

private static Map<Class<? extends TypeState>, TypeStateMemoryStats> typeStateFootprint = new ConcurrentHashMap<>();

public static <T extends TypeState> T registerTypeState(PointsToAnalysis bb, T state) {
if (bb.reportAnalysisStatistics() || bb.reportTypeStateMemoryFootprint()) {
/* TypeState memory footprint is measured in both cases. */
registerTypeStateSize(state);
}

if (!bb.reportAnalysisStatistics()) {
return state;
Expand All @@ -343,6 +383,77 @@ private static int typesCount(TypeState state) {
return state.typesCount();
}

/**
* This method is used to track the memory footprint of {@link TypeState} classes. It updates
* the frequency and total size of the given {@link TypeState} class in the
* {@link #typeStateFootprint} map.
*
* @param <T> the type of the {@link TypeState} instance
* @param state the {@link TypeState} instance to register
*/
private static <T extends TypeState> void registerTypeStateSize(T state) {
var stats = typeStateFootprint.computeIfAbsent(state.getClass(), __ -> new TypeStateMemoryStats());
stats.frequency.incrementAndGet();
stats.size.addAndGet(getTypeStateMemorySize(state));
}

/**
* In most cases, we use just the shallow size of the object as obtained from the heap dump.
* However, {@link MultiTypeState} is an exception, because it represents a set of values, so we
* consider the size of the underlying collection as well.
*/
private static long getTypeStateMemorySize(TypeState typeState) {
var shallowSize = getObjectSize(typeState);
if (!(typeState instanceof MultiTypeState multi)) {
return shallowSize;
}
var bitsetSize = getObjectSize(multi.typesBitSet);
var wordArraySize = getObjectSize(TypeStateUtils.extractBitSetField(multi.typesBitSet));
return shallowSize + bitsetSize + wordArraySize;
}

private static long getObjectSize(Object object) {
return GraalAccess.getOriginalProviders().getMetaAccess().getMemorySize(GraalAccess.getOriginalProviders().getSnippetReflection().forObject(object));
}

/**
* Reports the memory footprint of {@link TypeState} classes used by {@link PointsToAnalysis}.
* <p>
* This method writes a report to the provided {@link BufferedWriter} containing the frequency
* and total size of each allocated {@link TypeState} class.
* <p>
* The report includes the following information:
* <ul>
* <li>Type: the class name of the {@link TypeState}</li>
* <li>Frequency: the number of instances of the {@link TypeState} class</li>
* <li>Total Size: the total memory size of all instances of the {@link TypeState} class</li>
* </ul>
* <p>
* The report is written in a tabular format with the columns "Type", "Frequency", and "Total
* Size".
*
* @param out the {@link BufferedWriter} to write the report to
*/
private static void reportTypeStateMemoryFootprint(BufferedWriter out) {
doWrite(out, String.format("%30s\t%15s\t%15s%n", "Type", "Frequency", "Total Size"));
/* Use explicit order for the final report. */
var typeStateOrder = List.of(EmptyTypeState.class, NullTypeState.class, PrimitiveConstantTypeState.class, AnyPrimitiveTypeState.class, SingleTypeState.class,
ContextSensitiveSingleTypeState.class, ConstantTypeState.class,
MultiTypeState.class, ContextSensitiveMultiTypeState.class);
var totalFreq = 0L;
var totalSize = 0L;
for (var typeStateClass : typeStateOrder) {
var stats = typeStateFootprint.remove(typeStateClass);
if (stats != null) {
doWrite(out, String.format("%30s\t%15d\t%15d%n", ClassUtil.getUnqualifiedName(typeStateClass), stats.frequency.get(), stats.size.get()));
totalFreq += stats.frequency.get();
totalSize += stats.size.get();
}
}
AnalysisError.guarantee(typeStateFootprint.isEmpty(), "Missing elements in the typeStateOrder list: %s, please update it.", typeStateFootprint.keySet());
doWrite(out, String.format("%30s\t%15d\t%15d%n", "TOTAL", totalFreq, totalSize));
}

private static void reportTypeStateStats(BufferedWriter out) {

doWrite(out, String.format("%10s\t%10s\t%10s\t%10s\t%10s%n", "Id", "Frequency", "Types#", "Object#", "Types"));
Expand Down Expand Up @@ -391,6 +502,11 @@ private static void reportUnionOpertationsStats(BufferedWriter out) {
});
}

public static void cleanupAfterAnalysis() {
typeStateStats = null;
typeStateFootprint = null;
}

static class UnionOperation {
int state1Id;
int state2Id;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -32,7 +32,7 @@
import com.oracle.graal.pointsto.flow.context.object.AnalysisObject;
import com.oracle.graal.pointsto.meta.AnalysisType;

public class SingleTypeState extends TypeState {
public non-sealed class SingleTypeState extends TypeState {
protected final AnalysisType type;
/** Can this type state represent the null value? */
protected final boolean canBeNull;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -33,13 +33,23 @@
import com.oracle.graal.pointsto.BigBang;
import com.oracle.graal.pointsto.PointsToAnalysis;
import com.oracle.graal.pointsto.flow.PrimitiveComparison;
import com.oracle.graal.pointsto.flow.TypeFlow;
import com.oracle.graal.pointsto.flow.context.object.AnalysisObject;
import com.oracle.graal.pointsto.meta.AnalysisType;

import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;

public abstract class TypeState {
/**
* This class and its subclasses represent the sets of objects/types/primitive values propagated
* through {@link TypeFlow} nodes during the run of {@link PointsToAnalysis}. If the
* {@link TypeState} hierarchy is changed, {@link PointsToStats} might have to be updated to reflect
* that.
*
* @see TypeFlow
* @see PointsToStats
*/
public abstract sealed class TypeState permits EmptyTypeState, NullTypeState, PrimitiveTypeState, SingleTypeState, MultiTypeState {

/** Get the number of types. */
public abstract int typesCount();
Expand Down