diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index aa3ad612f8c7..8978a570808d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -1370,6 +1370,11 @@ public Boolean getValue(OptionValues values) { @Option(help = "Create a heap dump and exit.")// public static final RuntimeOptionKey DumpHeapAndExit = new RuntimeOptionKey<>(false, Immutable); + @Option(help = "Name of a csv file into which image heap metadata should be dumped. " + + "This csv file will be located in the same directory as the image. " + + "If this option is an empty string, the metadata will not be dumped.") // + public static final HostedOptionKey ImageHeapMetadataDumpFileName = new HostedOptionKey<>(""); + @Option(help = "Print some VM information and exit.")// public static final RuntimeOptionKey PrintVMInfoAndExit = new RuntimeOptionKey<>(false, Immutable); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java index 803625c25ced..c009d99704b2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java @@ -561,6 +561,7 @@ public void build(String imageName, DebugContext debug) { // We print the heap statistics after the heap was successfully written because this // could modify objects that will be part of the image heap. printHeapStatistics(heap.getLayouter().getPartitions()); + heap.dumpMetadata(); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java index 55c52916324d..8ffcae523613 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java @@ -26,8 +26,13 @@ import static com.oracle.svm.core.util.VMError.shouldNotReachHereUnexpectedInput; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Modifier; +import java.nio.file.Path; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collection; @@ -41,6 +46,7 @@ import java.util.Set; import java.util.function.Predicate; +import com.oracle.svm.core.option.HostedOptionValues; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.function.RelocatedPointer; import org.graalvm.word.UnsignedWord; @@ -792,6 +798,35 @@ public ObjectInfo addLateToImageHeap(Object object, Object reason) { return addToImageHeap(object, (HostedClass) type, getSize(object, type), System.identityHashCode(object), reason); } + /** + * Dumps metadata for every object in the image heap. + */ + public void dumpMetadata() { + String metadataFileName = SubstrateOptions.ImageHeapMetadataDumpFileName.getValue(); + if (metadataFileName == null || metadataFileName.isEmpty()) { + // Do not dump metadata if the file name isn't set + return; + } + + Path metadataFilePath = SubstrateOptions.getImagePath(HostedOptionValues.singleton()).resolve(metadataFileName); + File metadataFile = metadataFilePath.toFile(); + String metadataDir = metadataFile.getParent(); + if (!new File(metadataDir).exists()) { + throw VMError.shouldNotReachHere("Image heap metadata directory does not exist: " + metadataDir); + } + + try (FileWriter metadataOut = new FileWriter(metadataFile); + BufferedWriter metadataBw = new BufferedWriter(metadataOut)) { + metadataBw.write("class-name,partition,offset-in-heap,size\n"); + for (ObjectInfo info : getObjects()) { + String csvLine = info.getClazz().getName() + "," + info.getPartition().getName() + "," + info.getOffset() + "," + info.getSize() + System.lineSeparator(); + metadataBw.write(csvLine); + } + } catch (IOException ex) { + throw new RuntimeException("Failed to dump image heap metadata to " + metadataFile, ex); + } + } + private long getSize(Object object, HostedType type) { if (type.isInstanceClass()) { HostedInstanceClass clazz = (HostedInstanceClass) type;