Skip to content

Commit ab2fbe4

Browse files
committed
[GR-44384] File size warnings for Bundles.
PullRequest: graal/16116
2 parents 7a66c26 + 0144299 commit ab2fbe4

File tree

9 files changed

+89
-11
lines changed

9 files changed

+89
-11
lines changed

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
This changelog summarizes major changes to GraalVM Native Image.
44

55
## GraalVM for JDK 26 (Internal Version 26.0.0)
6+
* (GR-44384) Add size warnings for bundles when individual or cumulative file sizes exceed limits. Configure with options `size-warning-file-limit` and `size-warning-total-limit` to `bundle-create`, sizes in MiB.
67
* (GR-43070) Add a new API flag `-Werror` to treat warnings as errors.
78
* (GR-69280) Allow use of the `graal.` prefix for options without issuing a warning.
89

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ByteFormattingUtil.java renamed to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/ByteFormattingUtil.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -22,7 +22,7 @@
2222
* or visit www.oracle.com if you need additional information or have any
2323
* questions.
2424
*/
25-
package com.oracle.svm.hosted;
25+
package com.oracle.svm.core.util;
2626

2727
public class ByteFormattingUtil {
2828
// "123.12KiB".length() = 9, holds as long as it's not >= 1000GiB
@@ -65,5 +65,4 @@ private static String toHuman(long value, Unit unit) {
6565
assert string.length() <= MAX_WIDTH || value >= 1000L * Unit.GiB.value;
6666
return string;
6767
}
68-
6968
}

substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/BundleSupport.java

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -57,6 +57,7 @@
5757
import com.oracle.svm.core.OS;
5858
import com.oracle.svm.core.option.BundleMember;
5959
import com.oracle.svm.core.util.ArchiveSupport;
60+
import com.oracle.svm.core.util.ByteFormattingUtil;
6061
import com.oracle.svm.driver.BundleOptions.BundleOption;
6162
import com.oracle.svm.driver.BundleOptions.ExtendedOption;
6263
import com.oracle.svm.driver.launcher.BundleLauncher;
@@ -112,12 +113,27 @@ final class BundleSupport {
112113
private static final String DRY_RUN_OPTION = "dry-run";
113114
private static final String CONTAINER_OPTION = "container";
114115
private static final String DOCKERFILE_OPTION = "dockerfile";
116+
private static final String SIZE_WARNING_FILE_LIMIT_OPTION = "size-warning-file-limit";
117+
private static final String SIZE_WARNING_TOTAL_LIMIT_OPTION = "size-warning-total-limit";
115118
static final String BUNDLE_FILE_EXTENSION = ".nib";
116119
static final String BUNDLE_ALIAS = "<BUNDLE>";
117120

118121
ContainerSupport containerSupport;
119122
boolean useContainer;
120123

124+
private long fileSizeWarningFileLimit = 1024 * 1024 * 50; // 50 MB
125+
private long fileSizeWarningTotalLimit = 1024 * 1024 * 500; // 500 MB
126+
127+
/**
128+
* Counter for all the files addd to a bundle (except .nil), to print warnings.
129+
*/
130+
private long cumulativeFileSize = 0;
131+
132+
/**
133+
* Tracks whether a layers file (.nil) is part of the bundle.
134+
*/
135+
private boolean nilFileSeen = false;
136+
121137
private static final String DEFAULT_DOCKERFILE = getDockerfile("Dockerfile");
122138

123139
private static String getDockerfile(String name) {
@@ -249,10 +265,31 @@ private void processExtendedOption(ExtendedOption option) {
249265
throw NativeImage.showError(String.format("native-image option %s requires a dockerfile argument. E.g. %s=path/to/Dockerfile.", option.key(), option.key()));
250266
}
251267
}
268+
case SIZE_WARNING_FILE_LIMIT_OPTION -> {
269+
fileSizeWarningFileLimit = readSizeLimit(option.key(), option.value());
270+
}
271+
case SIZE_WARNING_TOTAL_LIMIT_OPTION -> {
272+
fileSizeWarningTotalLimit = readSizeLimit(option.key(), option.value());
273+
}
252274
default -> throw NativeImage.showError(String.format("Unknown option %s. Use --help-extra for usage instructions.", option.key()));
253275
}
254276
}
255277

278+
private long readSizeLimit(String optionKey, String optionValue) {
279+
if (optionValue != null) {
280+
try {
281+
long limit = Long.parseLong(optionValue);
282+
if (limit >= 0) {
283+
return limit * 1024 * 1024;
284+
} else if (limit == -1) {
285+
return -1; // no limit
286+
}
287+
} catch (NumberFormatException ex) {
288+
}
289+
}
290+
throw NativeImage.showError(String.format("native-image option %s requires a size in MiB, or -1 to deactivate.", optionKey));
291+
}
292+
256293
private BundleSupport(NativeImage nativeImage) {
257294
Objects.requireNonNull(nativeImage);
258295
this.nativeImage = nativeImage;
@@ -556,12 +593,50 @@ private void copyFile(Path sourceFile, Path target, boolean overwrite) {
556593
return;
557594
}
558595
CopyOption[] options = overwrite ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[0];
596+
if (warnSize()) {
597+
trackFileSize(sourceFile);
598+
}
559599
Files.copy(sourceFile, target, options);
560600
} catch (IOException e) {
561601
throw NativeImage.showError("Failed to copy " + sourceFile + " to " + target, e);
562602
}
563603
}
564604

605+
/**
606+
* Tracks the size of a file added to a bundle. Prints a log warning if the individual size
607+
* exceeds a certain limit. Also prints a warning when the cumulative size of all files added so
608+
* far exceed a limit, and repeats that at every multiple of that limit (maximum once per file).
609+
*
610+
* Layers files (.nil) are ignored for individual warnings and don't contribute to the
611+
* cumulative limit.
612+
*
613+
* @param file the file to track the size of
614+
* @throws IOException
615+
*/
616+
private void trackFileSize(Path file) throws IOException {
617+
if (!warnSize()) {
618+
return;
619+
}
620+
if (file.getFileName().endsWith(".nil")) {
621+
nilFileSeen = true;
622+
}
623+
long fileSize = Files.size(file);
624+
if (fileSizeWarningFileLimit >= 0 && fileSize > fileSizeWarningFileLimit) {
625+
LogUtils.warning(file + " adds " + ByteFormattingUtil.bytesToHuman(fileSize) + " to the Native Image bundle.");
626+
}
627+
if (fileSizeWarningTotalLimit >= 0) {
628+
long nextLimitMultiple = cumulativeFileSize + fileSizeWarningTotalLimit - (cumulativeFileSize % fileSizeWarningTotalLimit);
629+
cumulativeFileSize += fileSize;
630+
if (cumulativeFileSize > nextLimitMultiple) {
631+
LogUtils.warning("Native Image bundle has grown to " + ByteFormattingUtil.bytesToHuman(cumulativeFileSize) + (nilFileSeen ? " (excluding .nil files)." : "."));
632+
}
633+
}
634+
}
635+
636+
private boolean warnSize() {
637+
return fileSizeWarningFileLimit >= 0 || fileSizeWarningTotalLimit >= 0;
638+
}
639+
565640
void complete() {
566641
boolean writeOutput;
567642
try (Stream<Path> pathOutputFiles = Files.list(imagePathOutputDir); Stream<Path> auxiliaryOutputFiles = Files.list(auxiliaryOutputDir)) {

substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/CmdLineOptionHandler.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -134,6 +134,10 @@ private boolean consume(ArgumentQueue args, String headArg) {
134134
}
135135

136136
if (headArg.startsWith(BundleSupport.BUNDLE_OPTION)) {
137+
// warning should be early, before any other output of the feature
138+
if (nativeImage.bundleSupport == null) {
139+
LogUtils.warning("Native Image Bundles are an experimental feature.");
140+
}
137141
nativeImage.bundleSupport = BundleSupport.create(nativeImage, args.poll(), args);
138142
return true;
139143
}

substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,10 +1732,6 @@ protected int buildImage(List<String> javaArgs, LinkedHashSet<Path> cp, LinkedHa
17321732
arguments.addAll(strings);
17331733
}
17341734

1735-
if (useBundle()) {
1736-
LogUtils.warning("Native Image Bundles are an experimental feature.");
1737-
}
1738-
17391735
BiFunction<Path, BundleMember.Role, Path> substituteAuxiliaryPath = useBundle() ? bundleSupport::substituteAuxiliaryPath : (a, b) -> a;
17401736
Function<String, String> imageArgsTransformer = rawArg -> apiOptionHandler.transformBuilderArgument(rawArg, substituteAuxiliaryPath);
17411737
List<String> finalImageArgs = imageArgs.stream().map(imageArgsTransformer).collect(Collectors.toList());

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
import com.oracle.svm.core.traits.BuiltinTraits.NoLayeredCallbacks;
9393
import com.oracle.svm.core.traits.SingletonLayeredInstallationKind.Independent;
9494
import com.oracle.svm.core.traits.SingletonTraits;
95+
import com.oracle.svm.core.util.ByteFormattingUtil;
9596
import com.oracle.svm.core.util.TimeUtils;
9697
import com.oracle.svm.core.util.VMError;
9798
import com.oracle.svm.hosted.ProgressReporterFeature.UserRecommendation;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
package com.oracle.svm.hosted;
2727

28+
import com.oracle.svm.core.util.ByteFormattingUtil;
29+
2830
import java.net.URL;
2931
import java.security.CodeSource;
3032

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
import java.util.stream.Collectors;
4242

4343
import com.oracle.graal.pointsto.BigBang;
44+
import com.oracle.svm.core.util.ByteFormattingUtil;
4445
import com.oracle.svm.core.util.VMError;
45-
import com.oracle.svm.hosted.ByteFormattingUtil;
4646
import com.oracle.svm.hosted.image.NativeImageHeap.HeapInclusionReason;
4747
import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo;
4848
import com.oracle.svm.hosted.image.NativeImageHeap.ObjectReachabilityGroup;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,9 @@
103103
import com.oracle.svm.core.option.SubstrateOptionsParser;
104104
import com.oracle.svm.core.os.ImageHeapProvider;
105105
import com.oracle.svm.core.reflect.SubstrateAccessor;
106+
import com.oracle.svm.core.util.ByteFormattingUtil;
106107
import com.oracle.svm.core.util.UserError;
107108
import com.oracle.svm.core.util.VMError;
108-
import com.oracle.svm.hosted.ByteFormattingUtil;
109109
import com.oracle.svm.hosted.DeadlockWatchdog;
110110
import com.oracle.svm.hosted.FeatureImpl;
111111
import com.oracle.svm.hosted.NativeImageOptions;

0 commit comments

Comments
 (0)