Skip to content

Commit ea0537b

Browse files
committed
Generate connected components reports for the image heap objects
1 parent e47ac0f commit ea0537b

File tree

8 files changed

+897
-78
lines changed

8 files changed

+897
-78
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/ImageHeap.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
public interface ImageHeap {
3030
Collection<? extends ImageHeapObject> getObjects();
3131

32-
ImageHeapObject addLateToImageHeap(Object object, String reason);
32+
ImageHeapObject addLateToImageHeap(Object object, Object reason);
3333

3434
ImageHeapObject addFillerObject(int size);
3535

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.hosted;
26+
27+
public class ByteFormattingUtil {
28+
private static final double BYTES_TO_KiB = 1024d;
29+
private static final double BYTES_TO_MiB = 1024d * 1024d;
30+
private static final double BYTES_TO_GiB = 1024d * 1024d * 1024d;
31+
32+
public static String bytesToHuman(long bytes) {
33+
return bytesToHuman("%4.2f", bytes);
34+
}
35+
36+
public static String bytesToHuman(String format, long bytes) {
37+
if (bytes < BYTES_TO_KiB) {
38+
return String.format(format, (double) bytes) + "B";
39+
} else if (bytes < BYTES_TO_MiB) {
40+
return String.format(format, bytesToKiB(bytes)) + "KB";
41+
} else if (bytes < BYTES_TO_GiB) {
42+
return String.format(format, bytesToMiB(bytes)) + "MB";
43+
} else {
44+
return String.format(format, bytesToGiB(bytes)) + "GB";
45+
}
46+
}
47+
48+
static double bytesToKiB(long bytes) {
49+
return bytes / BYTES_TO_KiB;
50+
}
51+
52+
static double bytesToGiB(long bytes) {
53+
return bytes / BYTES_TO_GiB;
54+
}
55+
56+
static double bytesToMiB(long bytes) {
57+
return bytes / BYTES_TO_MiB;
58+
}
59+
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java

Lines changed: 26 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ public void printInitializeEnd() {
300300
String gcName = Heap.getHeap().getGC().getName();
301301
recordJsonMetric(GeneralInfo.GC, gcName);
302302
long maxHeapSize = SubstrateGCOptions.MaxHeapSize.getValue();
303-
String maxHeapValue = maxHeapSize == 0 ? "unlimited" : Utils.bytesToHuman(maxHeapSize);
303+
String maxHeapValue = maxHeapSize == 0 ? "unlimited" : ByteFormattingUtil.bytesToHuman(maxHeapSize);
304304
l().a(" ").doclink("Garbage collector", "#glossary-gc").a(": ").a(gcName).a(" (").doclink("max heap size", "#glossary-gc-max-heap-size").a(": ").a(maxHeapValue).a(")").println();
305305
}
306306

@@ -447,16 +447,17 @@ public void printCreationEnd(int imageFileSize, int numHeapObjects, long imageHe
447447
stagePrinter.end(imageTimer.getTotalTime() + writeTimer.getTotalTime());
448448
creationStageEndCompleted = true;
449449
String format = "%9s (%5.2f%%) for ";
450-
l().a(format, Utils.bytesToHuman(codeAreaSize), codeAreaSize / (double) imageFileSize * 100)
450+
l().a(format, ByteFormattingUtil.bytesToHuman(codeAreaSize), codeAreaSize / (double) imageFileSize * 100)
451451
.doclink("code area", "#glossary-code-area").a(":%,10d compilation units", numCompilations).println();
452452
EconomicMap<Pair<String, String>, ResourceStorageEntry> resources = Resources.singleton().resources();
453453
int numResources = resources.size();
454454
recordJsonMetric(ImageDetailKey.IMAGE_HEAP_RESOURCE_COUNT, numResources);
455-
l().a(format, Utils.bytesToHuman(imageHeapSize), imageHeapSize / (double) imageFileSize * 100)
455+
l().a(format, ByteFormattingUtil.bytesToHuman(imageHeapSize), imageHeapSize / (double) imageFileSize * 100)
456456
.doclink("image heap", "#glossary-image-heap").a(":%,9d objects and %,d resources", numHeapObjects, numResources).println();
457457
if (debugInfoSize > 0) {
458458
recordJsonMetric(ImageDetailKey.DEBUG_INFO_SIZE, debugInfoSize); // Optional metric
459-
DirectPrinter l = l().a(format, Utils.bytesToHuman(debugInfoSize), debugInfoSize / (double) imageFileSize * 100)
459+
DirectPrinter l = l().a(format, ByteFormattingUtil.bytesToHuman(debugInfoSize), debugInfoSize / (double) imageFileSize * 100)
460+
460461
.doclink("debug info", "#glossary-debug-info");
461462
if (debugInfoTimer != null) {
462463
l.a(" generated in %.1fs", Utils.millisToSeconds(debugInfoTimer.getTotalTime()));
@@ -468,9 +469,9 @@ public void printCreationEnd(int imageFileSize, int numHeapObjects, long imageHe
468469
recordJsonMetric(ImageDetailKey.TOTAL_SIZE, imageFileSize);
469470
recordJsonMetric(ImageDetailKey.CODE_AREA_SIZE, codeAreaSize);
470471
recordJsonMetric(ImageDetailKey.NUM_COMP_UNITS, numCompilations);
471-
l().a(format, Utils.bytesToHuman(otherBytes), otherBytes / (double) imageFileSize * 100)
472+
l().a(format, ByteFormattingUtil.bytesToHuman(otherBytes), otherBytes / (double) imageFileSize * 100)
472473
.doclink("other data", "#glossary-other-data").println();
473-
l().a("%9s in total", Utils.bytesToHuman(imageFileSize)).println();
474+
l().a("%9s in total", ByteFormattingUtil.bytesToHuman(imageFileSize)).println();
474475
printBreakdowns();
475476
}
476477

@@ -601,7 +602,7 @@ private void printBreakdowns() {
601602
if (packagesBySize.hasNext()) {
602603
Entry<String, Long> e = packagesBySize.next();
603604
String className = Utils.truncateClassOrPackageName(e.getKey());
604-
codeSizePart = String.format("%9s %s", Utils.bytesToHuman(e.getValue()), className);
605+
codeSizePart = String.format("%9s %s", ByteFormattingUtil.bytesToHuman(e.getValue()), className);
605606
printedCodeBytes += e.getValue();
606607
printedCodeItems++;
607608
}
@@ -614,7 +615,7 @@ private void printBreakdowns() {
614615
if (!className.startsWith(BREAKDOWN_BYTE_ARRAY_PREFIX)) {
615616
className = Utils.truncateClassOrPackageName(className);
616617
}
617-
heapSizePart = String.format("%9s %s", Utils.bytesToHuman(e.getValue()), className);
618+
heapSizePart = String.format("%9s %s", ByteFormattingUtil.bytesToHuman(e.getValue()), className);
618619
printedHeapBytes += e.getValue();
619620
printedHeapItems++;
620621
}
@@ -628,9 +629,10 @@ private void printBreakdowns() {
628629
int numHeapItems = heapBreakdown.size();
629630
long totalCodeBytes = codeBreakdown.values().stream().collect(Collectors.summingLong(Long::longValue));
630631
long totalHeapBytes = heapBreakdown.values().stream().collect(Collectors.summingLong(Long::longValue));
631-
p.l().a(String.format("%9s for %s more ", Utils.bytesToHuman(totalCodeBytes - printedCodeBytes), numCodeItems - printedCodeItems)).doclink("origins", "#glossary-code-area-origins")
632+
633+
p.l().a(String.format("%9s for %s more packages", ByteFormattingUtil.bytesToHuman(totalCodeBytes - printedCodeBytes), numCodeItems - printedCodeItems))
632634
.jumpToMiddle()
633-
.a(String.format("%9s for %s more object types", Utils.bytesToHuman(totalHeapBytes - printedHeapBytes), numHeapItems - printedHeapItems)).flushln();
635+
.a(String.format("%9s for %s more object types", ByteFormattingUtil.bytesToHuman(totalHeapBytes - printedHeapBytes), numHeapItems - printedHeapItems)).flushln();
634636
}
635637

636638
public void printEpilog(String imageName, NativeImageGenerator generator, FeatureHandler featureHandler, ImageClassLoader classLoader, Throwable error, OptionValues parsedHostedOptions) {
@@ -727,11 +729,11 @@ private static Path reportImageBuildStatistics(String imageName, BigBang bb) {
727729
String description = "image build statistics";
728730
if (ImageBuildStatistics.Options.ImageBuildStatisticsFile.hasBeenSet(bb.getOptions())) {
729731
final File file = new File(ImageBuildStatistics.Options.ImageBuildStatisticsFile.getValue(bb.getOptions()));
730-
return ReportUtils.report(description, file.getAbsoluteFile().toPath(), statsReporter, false);
732+
return com.oracle.graal.pointsto.reports.ReportUtils.report(description, file.getAbsoluteFile().toPath(), statsReporter, false);
731733
} else {
732-
String name = "image_build_statistics_" + ReportUtils.extractImageName(imageName);
734+
String name = "image_build_statistics_" + com.oracle.graal.pointsto.reports.ReportUtils.extractImageName(imageName);
733735
String path = SubstrateOptions.Path.getValue() + File.separatorChar + "reports";
734-
return ReportUtils.report(description, path, name, "json", statsReporter, false);
736+
return com.oracle.graal.pointsto.reports.ReportUtils.report(description, path, name, "json", statsReporter, false);
735737
}
736738
}
737739

@@ -746,7 +748,7 @@ private void printResourceStatistics() {
746748
.doclink("GCs", "#glossary-garbage-collections");
747749
long peakRSS = ProgressReporterCHelper.getPeakRSS();
748750
if (peakRSS >= 0) {
749-
p.a(" | ").doclink("Peak RSS", "#glossary-peak-rss").a(": ").a("%.2fGB", Utils.bytesToGiB(peakRSS));
751+
p.a(" | ").doclink("Peak RSS", "#glossary-peak-rss").a(": ").a("%.2fGB", ByteFormattingUtil.bytesToGiB(peakRSS));
750752
}
751753
recordJsonMetric(ResourceUsageKey.PEAK_RSS, (peakRSS >= 0 ? peakRSS : UNAVAILABLE_METRIC));
752754
OperatingSystemMXBean osMXBean = ManagementFactory.getOperatingSystemMXBean();
@@ -772,7 +774,7 @@ private void checkForExcessiveGarbageCollection() {
772774
.a(": %.1fs spent in %d GCs during the last stage, taking up %.2f%% of the time.",
773775
Utils.millisToSeconds(gcTimeDeltaMillis), currentGCStats.totalCount - lastGCStats.totalCount, ratio * 100)
774776
.println();
775-
l().a(" Please ensure more than %.2fGB of memory is available for Native Image", Utils.bytesToGiB(ProgressReporterCHelper.getPeakRSS())).println();
777+
l().a(" Please ensure more than %.2fGB of memory is available for Native Image", ByteFormattingUtil.bytesToGiB(ProgressReporterCHelper.getPeakRSS())).println();
776778
l().a(" to reduce GC overhead and improve image build time.").println();
777779
}
778780
lastGCStats = currentGCStats;
@@ -787,52 +789,11 @@ private void recordJsonMetric(JsonMetric metric, Object value) {
787789
/*
788790
* HELPERS
789791
*/
790-
791-
private static Timer getTimer(TimerCollection.Registry type) {
792-
return TimerCollection.singleton().get(type);
793-
}
794-
795-
private static void resetANSIMode() {
796-
NativeImageSystemIOWrappers.singleton().getOut().print(ANSI.RESET);
797-
}
798-
799792
private static class Utils {
800793
private static final double MILLIS_TO_SECONDS = 1000d;
801794
private static final double NANOS_TO_SECONDS = 1000d * 1000d * 1000d;
802-
private static final double BYTES_TO_KiB = 1024d;
803-
private static final double BYTES_TO_MiB = 1024d * 1024d;
804-
private static final double BYTES_TO_GiB = 1024d * 1024d * 1024d;
805-
806795
private static final Field STRING_VALUE = ReflectionUtil.lookupField(String.class, "value");
807796

808-
private static String bytesToHuman(long bytes) {
809-
return bytesToHuman("%4.2f", bytes);
810-
}
811-
812-
private static String bytesToHuman(String format, long bytes) {
813-
if (bytes < BYTES_TO_KiB) {
814-
return String.format(format, (double) bytes) + "B";
815-
} else if (bytes < BYTES_TO_MiB) {
816-
return String.format(format, bytesToKiB(bytes)) + "KB";
817-
} else if (bytes < BYTES_TO_GiB) {
818-
return String.format(format, bytesToMiB(bytes)) + "MB";
819-
} else {
820-
return String.format(format, bytesToGiB(bytes)) + "GB";
821-
}
822-
}
823-
824-
private static double bytesToKiB(long bytes) {
825-
return bytes / BYTES_TO_KiB;
826-
}
827-
828-
private static double bytesToGiB(long bytes) {
829-
return bytes / BYTES_TO_GiB;
830-
}
831-
832-
private static double bytesToMiB(long bytes) {
833-
return bytes / BYTES_TO_MiB;
834-
}
835-
836797
private static double millisToSeconds(double millis) {
837798
return millis / MILLIS_TO_SECONDS;
838799
}
@@ -850,7 +811,7 @@ private static int getInternalByteArrayLength(String string) {
850811
}
851812

852813
private static double getUsedMemory() {
853-
return bytesToGiB(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
814+
return ByteFormattingUtil.bytesToGiB(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
854815
}
855816

856817
private static String stringFilledWith(int size, String fill) {
@@ -891,6 +852,14 @@ private static String truncateClassOrPackageName(String classOrPackageName) {
891852
}
892853
}
893854

855+
private static Timer getTimer(TimerCollection.Registry type) {
856+
return TimerCollection.singleton().get(type);
857+
}
858+
859+
private static void resetANSIMode() {
860+
NativeImageSystemIOWrappers.singleton().getOut().print(ANSI.RESET);
861+
}
862+
894863
private static class GCStats {
895864
private final long totalCount;
896865
private final long totalTimeMillis;

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/HeapHistogram.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525
package com.oracle.svm.hosted.image;
2626

27+
import java.io.PrintWriter;
2728
import java.util.Arrays;
2829
import java.util.Comparator;
2930
import java.util.HashMap;
@@ -39,6 +40,7 @@ public class HeapHistogram {
3940
protected static boolean PrintStrings = false;
4041

4142
private final Map<HostedClass, HistogramEntry> data = new HashMap<>();
43+
private final PrintWriter out;
4244

4345
static class HistogramEntry {
4446
protected final HostedClass clazz;
@@ -50,6 +52,14 @@ static class HistogramEntry {
5052
}
5153
}
5254

55+
public HeapHistogram() {
56+
this.out = new PrintWriter(System.out);
57+
}
58+
59+
public HeapHistogram(PrintWriter out) {
60+
this.out = out;
61+
}
62+
5363
private static final Comparator<HistogramEntry> SIZE_COMPARATOR = (o1, o2) -> {
5464
// Larger sizes first
5565
int result = Long.compare(o2.size, o1.size);
@@ -77,18 +87,18 @@ public void add(ObjectInfo objectInfo, long size) {
7787
entry.size += size;
7888

7989
if (PrintStrings && objectInfo.getObject() instanceof String) {
80-
String reason = String.valueOf(objectInfo.reason);
90+
String reason = String.valueOf(objectInfo.getMainReason());
8191
String value = ((String) objectInfo.getObject()).replace("\n", "");
8292
if (!reason.startsWith("com.oracle.svm.core.hub.DynamicHub")) {
83-
System.out.format("%120s ::: %s\n", value, reason);
93+
out.format("%120s ::: %s\n", value, reason);
8494
}
8595
}
8696
}
8797

8898
public void printHeadings(final String title) {
8999
assert NativeImageOptions.PrintHeapHistogram.getValue();
90-
System.out.format("\n%s\n", title);
91-
System.out.format(headerFormat, "Count", "Size", "Size%", "Cum%", "Class");
100+
out.format("%s\n", title);
101+
out.format(headerFormat, "Count", "Size", "Size%", "Cum%", "Class");
92102
}
93103

94104
public void print() {
@@ -101,7 +111,7 @@ public void print() {
101111
long printedSize = 0;
102112
for (HistogramEntry entry : entries) {
103113
printedSize += entry.size;
104-
System.out.format(entryFormat, entry.count, entry.size, entry.size * 100d / totalSize, printedSize * 100d / totalSize, entry.clazz.toJavaName());
114+
out.format(entryFormat, entry.count, entry.size, entry.size * 100d / totalSize, printedSize * 100d / totalSize, entry.clazz.toJavaName());
105115
}
106116
}
107117

0 commit comments

Comments
 (0)