package com.oracle.graal.pointsto;
import static com.oracle.graal.pointsto.meta.AnalysisUniverse.ESTIMATED_NUMBER_OF_TYPES;
import static jdk.vm.ci.common.JVMCIError.shouldNotReachHere;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicLongArray;
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
import org.graalvm.compiler.core.common.SuppressFBWarnings;
import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.DebugContext.Builder;
import org.graalvm.compiler.debug.DebugHandlersFactory;
import org.graalvm.compiler.debug.Indent;
import org.graalvm.compiler.graph.NodeSourcePosition;
import org.graalvm.compiler.nodes.spi.Replacements;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
import com.oracle.graal.pointsto.ObjectScanner.ReusableSet;
import com.oracle.graal.pointsto.api.HostVM;
import com.oracle.graal.pointsto.api.PointstoOptions;
import com.oracle.graal.pointsto.constraints.UnsupportedFeatures;
import com.oracle.graal.pointsto.flow.AllSynchronizedTypeFlow;
import com.oracle.graal.pointsto.flow.MethodTypeFlow;
import com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder;
import com.oracle.graal.pointsto.flow.OffsetLoadTypeFlow.AbstractUnsafeLoadTypeFlow;
import com.oracle.graal.pointsto.flow.OffsetStoreTypeFlow.AbstractUnsafeStoreTypeFlow;
import com.oracle.graal.pointsto.flow.TypeFlow;
import com.oracle.graal.pointsto.flow.UnknownTypeFlow;
import com.oracle.graal.pointsto.flow.context.AnalysisContext;
import com.oracle.graal.pointsto.flow.context.AnalysisContextPolicy;
import com.oracle.graal.pointsto.meta.AnalysisField;
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
import com.oracle.graal.pointsto.meta.HostedProviders;
import com.oracle.graal.pointsto.typestate.PointsToStats;
import com.oracle.graal.pointsto.typestate.TypeState;
import com.oracle.graal.pointsto.util.CompletionExecutor;
import com.oracle.graal.pointsto.util.CompletionExecutor.DebugContextRunnable;
import com.oracle.graal.pointsto.util.Timer;
import com.oracle.graal.pointsto.util.Timer.StopTimer;
import com.oracle.svm.util.ImageGeneratorThreadMarker;
import jdk.vm.ci.code.BytecodePosition;
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.meta.ConstantReflectionProvider;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaType;
public abstract class BigBang {
private final OptionValues options;
private final List<DebugHandlersFactory> debugHandlerFactories;
private final DebugContext debug;
private final HostedProviders providers;
private final Replacements replacements;
private final HeapScanningPolicy heapScanningPolicy;
private final AnalysisType objectType;
private TypeFlow<?> allSynchronizedTypeFlow;
private UnknownTypeFlow unknownTypeFlow;
protected final AnalysisUniverse universe;
protected final AnalysisMetaAccess metaAccess;
protected final HostVM hostVM;
private final UnsupportedFeatures unsupportedFeatures;
protected final boolean trackTypeFlowInputs;
protected final boolean reportAnalysisStatistics;
private final CompletionExecutor executor;
private final Runnable heartbeatCallback;
private ConcurrentMap<AbstractUnsafeLoadTypeFlow, Boolean> unsafeLoads;
private ConcurrentMap<AbstractUnsafeStoreTypeFlow, Boolean> unsafeStores;
public final AtomicLong numParsedGraphs = new AtomicLong();
private final CompletionExecutor.Timing timing;
public final Timer typeFlowTimer;
public final Timer checkObjectsTimer;
public final Timer processFeaturesTimer;
public final Timer analysisTimer;
public BigBang(OptionValues options, AnalysisUniverse universe, HostedProviders providers, HostVM hostVM, ForkJoinPool executorService, Runnable heartbeatCallback,
UnsupportedFeatures unsupportedFeatures) {
this.options = options;
this.debugHandlerFactories = Collections.singletonList(new GraalDebugHandlersFactory(providers.getSnippetReflection()));
this.debug = new Builder(options, debugHandlerFactories).build();
this.hostVM = hostVM;
String imageName = hostVM.getImageName();
this.typeFlowTimer = new Timer(imageName, "(typeflow)", false);
this.checkObjectsTimer = new Timer(imageName, "(objects)", false);
this.processFeaturesTimer = new Timer(imageName, "(features)", false);
this.analysisTimer = new Timer(imageName, "analysis", true);
this.universe = universe;
this.metaAccess = (AnalysisMetaAccess) providers.getMetaAccess();
this.replacements = providers.getReplacements();
this.unsupportedFeatures = unsupportedFeatures;
this.providers = providers;
this.objectType = metaAccess.lookupJavaType(Object.class);
objectType.getTypeFlow(this, true);
allSynchronizedTypeFlow = new AllSynchronizedTypeFlow();
unknownTypeFlow = new UnknownTypeFlow();
trackTypeFlowInputs = PointstoOptions.TrackInputFlows.getValue(options);
reportAnalysisStatistics = PointstoOptions.PrintPointsToStatistics.getValue(options);
if (reportAnalysisStatistics) {
PointsToStats.init(this);
}
unsafeLoads = new ConcurrentHashMap<>();
unsafeStores = new ConcurrentHashMap<>();
timing = PointstoOptions.ProfileAnalysisOperations.getValue(options) ? new AnalysisTiming() : null;
executor = new CompletionExecutor(this, executorService, heartbeatCallback);
executor.init(timing);
this.heartbeatCallback = heartbeatCallback;
heapScanningPolicy = PointstoOptions.ExhaustiveHeapScan.getValue(options)
? HeapScanningPolicy.scanAll()
: HeapScanningPolicy.skipTypes(skippedHeapTypes());
}
public AnalysisType[] skippedHeapTypes() {
return new AnalysisType[]{metaAccess.lookupJavaType(String.class)};
}
public Runnable getHeartbeatCallback() {
return heartbeatCallback;
}
public boolean trackTypeFlowInputs() {
return trackTypeFlowInputs;
}
public boolean reportAnalysisStatistics() {
return reportAnalysisStatistics;
}
public OptionValues getOptions() {
return options;
}
public List<DebugHandlersFactory> getDebugHandlerFactories() {
return debugHandlerFactories;
}
public DebugContext getDebug() {
return debug;
}
public MethodTypeFlowBuilder createMethodTypeFlowBuilder(BigBang bb, MethodTypeFlow methodFlow) {
return new MethodTypeFlowBuilder(bb, methodFlow);
}
public void registerUnsafeLoad(AbstractUnsafeLoadTypeFlow unsafeLoad) {
unsafeLoads.putIfAbsent(unsafeLoad, true);
}
public void registerUnsafeStore(AbstractUnsafeStoreTypeFlow unsafeStore) {
unsafeStores.putIfAbsent(unsafeStore, true);
}
public void reportIllegalUnknownUse(AnalysisMethod method, BytecodePosition source, String message) {
String trace = "Location: " + (source == null ? "[unknown]" : source.getMethod().asStackTraceElement(source.getBCI()).toString()) + "\n";
trace += "Call path:";
getUnsupportedFeatures().addMessage(method.format("%H.%n(%p)"), method, message, trace);
}
public void forceUnsafeUpdate(AnalysisField field) {
for (AbstractUnsafeLoadTypeFlow unsafeLoad : unsafeLoads.keySet()) {
unsafeLoad.initClone(this);
this.postFlow(unsafeLoad.receiver());
}
for (AbstractUnsafeStoreTypeFlow unsafeStore : unsafeStores.keySet()) {
unsafeStore.initClone(this);
this.postFlow(unsafeStore.receiver());
}
}
public boolean trackConcreteAnalysisObjects(@SuppressWarnings("unused") AnalysisType type) {
return true;
}
@SuppressWarnings("unused")
public boolean isCallAllowed(BigBang bb, AnalysisMethod caller, AnalysisMethod target, NodeSourcePosition srcPosition) {
return true;
}
public void cleanupAfterAnalysis() {
allSynchronizedTypeFlow = null;
unsafeLoads = null;
unsafeStores = null;
unknownTypeFlow = null;
scannedObjects = null;
ConstantObjectsProfiler.constantTypes.clear();
universe.getTypes().forEach(AnalysisType::cleanupAfterAnalysis);
universe.getFields().forEach(AnalysisField::cleanupAfterAnalysis);
universe.getMethods().forEach(AnalysisMethod::cleanupAfterAnalysis);
}
public AnalysisPolicy analysisPolicy() {
return universe.analysisPolicy();
}
public AnalysisContextPolicy<AnalysisContext> contextPolicy() {
return universe.analysisPolicy().getContextPolicy();
}
public AnalysisUniverse getUniverse() {
return universe;
}
public HostedProviders getProviders() {
return providers;
}
public AnalysisMetaAccess getMetaAccess() {
return metaAccess;
}
public Replacements getReplacements() {
return replacements;
}
public UnsupportedFeatures getUnsupportedFeatures() {
return unsupportedFeatures;
}
public AnalysisType lookup(JavaType type) {
return universe.lookup(type);
}
public AnalysisType getObjectType() {
return metaAccess.lookupJavaType(Object.class);
}
public AnalysisType getObjectArrayType() {
return metaAccess.lookupJavaType(Object[].class);
}
public AnalysisType getGraalNodeType() {
return metaAccess.lookupJavaType(org.graalvm.compiler.graph.Node.class);
}
public AnalysisType getGraalNodeListType() {
return metaAccess.lookupJavaType(org.graalvm.compiler.graph.NodeList.class);
}
public AnalysisType getThrowableType() {
return metaAccess.lookupJavaType(Throwable.class);
}
public AnalysisType getThreadType() {
return metaAccess.lookupJavaType(Thread.class);
}
public AnalysisType forClass(Class<?> clazz) {
return metaAccess.lookupJavaType(clazz);
}
public AnalysisType forClass(String className) {
try {
return forClass(Class.forName(className));
} catch (ClassNotFoundException e) {
throw JVMCIError.shouldNotReachHere(e);
}
}
public TypeFlow<?> getAllInstantiatedTypeFlow() {
return objectType.getTypeFlow(this, true);
}
public TypeFlow<?> getUnknownTypeFlow() {
return unknownTypeFlow;
}
public TypeFlow<?> getAllSynchronizedTypeFlow() {
return allSynchronizedTypeFlow;
}
public TypeState getAllSynchronizedTypeState() {
if (allSynchronizedTypeFlow.isSaturated()) {
return getAllInstantiatedTypeFlow().getState();
}
return allSynchronizedTypeFlow.getState();
}
public boolean executorIsStarted() {
return executor.isStarted();
}
public AnalysisMethod addRootMethod(Executable method) {
AnalysisMethod aMethod = metaAccess.lookupJavaMethod(method);
addRootMethod(aMethod);
return aMethod;
}
@SuppressWarnings("try")
public AnalysisMethod addRootMethod(AnalysisMethod aMethod) {
if (aMethod.isRootMethod()) {
return aMethod;
}
aMethod.registerAsRootMethod();
final MethodTypeFlow methodFlow = aMethod.getTypeFlow();
try (Indent indent = debug.logAndIndent("add root method %s", aMethod.getName())) {
boolean isStatic = Modifier.isStatic(aMethod.getModifiers());
int paramCount = aMethod.getSignature().getParameterCount(!isStatic);
int offset = 0;
if (!isStatic) {
methodFlow.setInitialReceiverFlow(this, aMethod.getDeclaringClass());
offset = 1;
}
for (int i = offset; i < paramCount; i++) {
AnalysisType declaredParamType = (AnalysisType) aMethod.getSignature().getParameterType(i - offset, aMethod.getDeclaringClass());
if (declaredParamType.getJavaKind() == JavaKind.Object) {
methodFlow.setInitialParameterFlow(this, declaredParamType, i);
}
}
}
postTask(new DebugContextRunnable() {
@Override
public void run(DebugContext ignore) {
methodFlow.addContext(BigBang.this, BigBang.this.contextPolicy().emptyContext(), null);
}
@Override
public DebugContext getDebug(OptionValues opts, List<DebugHandlersFactory> factories) {
assert opts == getOptions();
return DebugContext.disabled(opts);
}
});
return aMethod;
}
public AnalysisType addSystemClass(Class<?> clazz, boolean addFields, boolean addArrayClass) {
AnalysisType type = metaAccess.lookupJavaType(clazz);
type.registerAsReachable();
return addSystemClass(type, addFields, addArrayClass);
}
@SuppressWarnings({"try"})
private AnalysisType addSystemClass(AnalysisType type, boolean addFields, boolean addArrayClass) {
try (Indent indent = debug.logAndIndent("add system class %s", type.getName())) {
for (AnalysisField field : type.getInstanceFields(false)) {
if (addFields) {
field.registerAsAccessed();
}
TypeFlow<?> fieldDeclaredTypeFlow = field.getType().getTypeFlow(this, true);
fieldDeclaredTypeFlow.addUse(this, type.getContextInsensitiveAnalysisObject().getInstanceFieldFlow(this, field, true));
}
if (type.getSuperclass() != null) {
addSystemClass(type.getSuperclass(), addFields, addArrayClass);
}
if (addArrayClass) {
addSystemClass(type.getArrayClass(), false, false);
}
}
return type;
}
@SuppressWarnings("try")
public AnalysisType addSystemField(Class<?> clazz, String fieldName) {
AnalysisType type = addSystemClass(clazz, false, false);
for (AnalysisField field : type.getInstanceFields(true)) {
if (field.getName().equals(fieldName)) {
try (Indent indent = debug.logAndIndent("add system field %s in class %s", fieldName, clazz.getName())) {
field.registerAsAccessed();
TypeFlow<?> fieldDeclaredTypeFlow = field.getType().getTypeFlow(this, true);
fieldDeclaredTypeFlow.addUse(this, type.getContextInsensitiveAnalysisObject().getInstanceFieldFlow(this, field, true));
}
return field.getType();
}
}
throw shouldNotReachHere("field not found: " + fieldName);
}
@SuppressWarnings("try")
public AnalysisType addSystemStaticField(Class<?> clazz, String fieldName) {
addSystemClass(clazz, false, false);
Field reflectField;
try {
try (Indent indent = debug.logAndIndent("add system static field %s in class %s", fieldName, clazz.getName())) {
reflectField = clazz.getField(fieldName);
AnalysisField field = metaAccess.lookupJavaField(reflectField);
field.registerAsAccessed();
TypeFlow<?> fieldFlow = field.getType().getTypeFlow(this, true);
fieldFlow.addUse(this, field.getStaticFieldFlow());
return field.getType();
}
} catch (NoSuchFieldException e) {
throw shouldNotReachHere("field not found: " + fieldName);
}
}
public void addSystemMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
try {
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
addRootMethod(method);
} catch (NoSuchMethodException ex) {
throw shouldNotReachHere(ex);
}
}
public final SnippetReflectionProvider getSnippetReflectionProvider() {
return providers.getSnippetReflection();
}
public final ConstantReflectionProvider getConstantReflectionProvider() {
return providers.getConstantReflection();
}
public ConstantFieldProvider getConstantFieldProvider() {
return providers.getConstantFieldProvider();
}
public CompletionExecutor getExecutor() {
return executor;
}
public void checkUserLimitations() {
}
public interface TypeFlowRunnable extends DebugContextRunnable {
TypeFlow<?> getTypeFlow();
}
public void postFlow(final TypeFlow<?> operation) {
if (operation.inQueue) {
return;
}
operation.inQueue = true;
executor.execute(new TypeFlowRunnable() {
@Override
public void run(DebugContext ignored) {
PointsToStats.registerTypeFlowQueuedUpdate(BigBang.this, operation);
operation.inQueue = false;
operation.update(BigBang.this);
}
@Override
public String toString() {
return "Operation: " + operation.toString();
}
@Override
public TypeFlow<?> getTypeFlow() {
return operation;
}
@Override
public DebugContext getDebug(OptionValues opts, List<DebugHandlersFactory> factories) {
assert opts == getOptions();
return DebugContext.disabled(opts);
}
});
}
public void postTask(final DebugContextRunnable task) {
executor.execute(task);
}
@SuppressWarnings("try")
public boolean finish() throws InterruptedException {
try (Indent indent = debug.logAndIndent("starting analysis in BigBang.finish")) {
universe.setAnalysisDataValid(this, false);
boolean didSomeWork = false;
int numTypes;
do {
didSomeWork |= doTypeflow();
assert executor.getPostedOperations() == 0;
numTypes = universe.getTypes().size();
try (StopTimer t = checkObjectsTimer.start()) {
checkObjectGraph();
}
} while (executor.getPostedOperations() != 0 || numTypes != universe.getTypes().size());
universe.setAnalysisDataValid(this, true);
return didSomeWork;
}
}
@SuppressWarnings("try")
public boolean doTypeflow() throws InterruptedException {
boolean didSomeWork;
try (StopTimer ignored = typeFlowTimer.start()) {
executor.start();
executor.complete();
didSomeWork = (executor.getPostedOperations() > 0);
executor.shutdown();
}
executor.init(timing);
return didSomeWork;
}
private ReusableSet scannedObjects = new ReusableSet();
@SuppressWarnings("try")
private void checkObjectGraph() throws InterruptedException {
scannedObjects.reset();
ObjectScanner objectScanner = new AnalysisObjectScanner(this, scannedObjects);
checkObjectGraph(objectScanner);
if (PointstoOptions.ScanObjectsParallel.getValue(options)) {
executor.start();
objectScanner.scanBootImageHeapRoots(executor);
executor.complete();
executor.shutdown();
executor.init(timing);
} else {
objectScanner.scanBootImageHeapRoots(null);
}
AnalysisType.updateAssignableTypes(this);
}
public HeapScanningPolicy scanningPolicy() {
return heapScanningPolicy;
}
protected void checkObjectGraph(ObjectScanner objectScanner) {
}
public HostVM getHostVM() {
return hostVM;
}
@SuppressFBWarnings(value = "NP_NONNULL_PARAM_VIOLATION", justification = "ForkJoinPool does support null for the exception handler.")
public static ForkJoinPool createExecutor(DebugContext debug, int numberOfThreads) {
ForkJoinPool.ForkJoinWorkerThreadFactory factory = debugThreadFactory(debug.areScopesEnabled() || debug.areMetricsEnabled() ? debug : null);
return new ForkJoinPool(numberOfThreads, factory, null, false);
}
private static ForkJoinPool.ForkJoinWorkerThreadFactory debugThreadFactory(DebugContext debug) {
return pool -> new SubstrateWorkerThread(pool, debug);
}
public static class ConstantObjectsProfiler {
static final ConcurrentHashMap<AnalysisType, MyInteger> constantTypes = new ConcurrentHashMap<>(ESTIMATED_NUMBER_OF_TYPES);
static final int PROCESSED_CONSTANTS_DUMP_THRESHOLD = 100000;
static final int CONSTANT_COUNTER_DUMP_THRESHOLD = 1000;
static int processedConstants;
static class ConstantCounterEntry {
protected AnalysisType type;
protected int counter;
ConstantCounterEntry(AnalysisType type, int counter) {
this.type = type;
this.counter = counter;
}
}
static class MyInteger {
int myInt = 0;
protected void increment() {
myInt++;
}
protected int value() {
return myInt;
}
@Override
public String toString() {
return myInt + "";
}
}
static class ConstantCounterEntryComparator implements Comparator<ConstantCounterEntry> {
@Override
public int compare(ConstantCounterEntry o1, ConstantCounterEntry o2) {
return Integer.compare(o2.counter, o1.counter);
}
}
static final ConstantCounterEntryComparator CONSTANT_COUNTER_COMPARATOR = new ConstantCounterEntryComparator();
public static void registerConstant(AnalysisType type) {
processedConstants++;
MyInteger counter = constantTypes.get(type);
if (counter == null) {
MyInteger newValue = new MyInteger();
MyInteger oldValue = constantTypes.putIfAbsent(type, newValue);
counter = oldValue != null ? oldValue : newValue;
}
counter.increment();
}
public static void maybeDumpConstantHistogram() {
if (processedConstants > PROCESSED_CONSTANTS_DUMP_THRESHOLD) {
processedConstants = 0;
List<ConstantCounterEntry> constantCounters = new ArrayList<>();
for (Map.Entry<AnalysisType, MyInteger> entry : constantTypes.entrySet()) {
AnalysisType type = entry.getKey();
Integer counter = entry.getValue().value();
if (counter > CONSTANT_COUNTER_DUMP_THRESHOLD) {
constantCounters.add(new ConstantCounterEntry(type, counter));
}
}
Collections.sort(constantCounters, CONSTANT_COUNTER_COMPARATOR);
System.out.println(" - - - - - - - - - - - - - - - - - - - - - - - - ");
System.out.println(" CONSTANT HISTOGRAM ");
for (ConstantCounterEntry constantCounter : constantCounters) {
System.out.format("%d : %s %n", constantCounter.counter, constantCounter.type.getName());
}
System.out.println(" - - - - - - - - - - - - - - - - - - - - - - - - ");
}
}
}
protected abstract static class BucketTiming implements CompletionExecutor.Timing {
private static final int NUM_BUCKETS = 10;
private final AtomicLong numOperations = new AtomicLong();
private final AtomicLong numAdded = new AtomicLong();
private final AtomicLong numDone = new AtomicLong();
private final AtomicLong numInQueue = new AtomicLong();
private final AtomicLong totalTime = new AtomicLong();
private final AtomicLongArray timeBuckets = new AtomicLongArray(NUM_BUCKETS);
@Override
public long getPrintIntervalNanos() {
return 1_000_000_000;
}
@Override
public void addScheduled(DebugContextRunnable r) {
numOperations.incrementAndGet();
numInQueue.incrementAndGet();
numAdded.incrementAndGet();
}
@Override
public void addCompleted(DebugContextRunnable r, long nanos) {
numInQueue.decrementAndGet();
numDone.incrementAndGet();
totalTime.addAndGet(nanos);
int bucket = 0;
long bucketTime = nanos / 1000;
while (bucketTime != 0 && bucket < NUM_BUCKETS - 1) {
bucketTime /= 10;
bucket++;
}
timeBuckets.incrementAndGet(bucket);
if (nanos > 500_000_000L && r instanceof TypeFlowRunnable) {
TypeFlow<?> tf = ((TypeFlowRunnable) r).getTypeFlow();
String source = String.valueOf(tf.getSource());
System.out.format("LONG RUNNING %.2f %s %x %s state %s %x uses %d observers %d%n", (double) nanos / 1_000_000_000, tf.getClass().getSimpleName(), System.identityHashCode(tf),
source, PointsToStats.asString(tf.getState()), System.identityHashCode(tf.getState()), tf.getUses().size(), tf.getObservers().size());
}
}
@Override
public void () {
System.out.format("%9s %6s %6s %6s %10s %8s %5s %5s %5s %5s %5s %5s %5s %5s %5s %5s |", "total", "qlen", "added", "done", "total us", "avg us", "<1us", ">1us", "10", "100", ">1ms",
"10", "100", ">1s", "10", "100");
}
@Override
public void print() {
long operations = numOperations.get();
long queued = numInQueue.get();
long scheduled = numAdded.getAndSet(0);
long completed = numDone.getAndSet(0);
long time = totalTime.getAndSet(0);
long[] buckets = new long[NUM_BUCKETS];
for (int i = 0; i < NUM_BUCKETS; i++) {
buckets[i] = timeBuckets.getAndSet(i, 0);
}
System.out.format("%9d %6d %6d %6d %10d %8d ", operations, queued, scheduled, completed, time / 1000, completed != 0 ? time / 1000 / completed : 0L);
for (int i = 0; i < NUM_BUCKETS; i++) {
System.out.format("%5d ", buckets[i]);
}
System.out.print(" |");
}
}
protected class AnalysisTiming extends BucketTiming {
@Override
public void () {
System.out.format("%5s %5s %5s |", "graphs", "types", "nid");
super.printHeader();
System.out.println();
}
@Override
public void print() {
System.out.format("%5d %5d %5d |", numParsedGraphs.get(), getAllInstantiatedTypeFlow().getState().typesCount(), universe.getNextTypeId());
super.print();
System.out.println();
}
}
private static class SubstrateWorkerThread extends ForkJoinWorkerThread
implements ImageGeneratorThreadMarker {
private final DebugContext debug;
SubstrateWorkerThread(ForkJoinPool pool, DebugContext debug) {
super(pool);
this.debug = debug;
}
@Override
protected void onTermination(Throwable exception) {
if (debug != null) {
debug.closeDumpHandlers(true);
}
}
}
}