Skip to content

Commit 7869666

Browse files
committed
track the size of files added to a Bundle
1 parent adf4cac commit 7869666

File tree

4 files changed

+82
-8
lines changed

4 files changed

+82
-8
lines changed

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
@@ -64,6 +64,7 @@
6464
import com.oracle.svm.driver.launcher.configuration.BundleArgsParser;
6565
import com.oracle.svm.driver.launcher.configuration.BundleEnvironmentParser;
6666
import com.oracle.svm.driver.launcher.configuration.BundlePathMapParser;
67+
import com.oracle.svm.hosted.ByteFormattingUtil;
6768
import com.oracle.svm.util.ClassUtil;
6869
import com.oracle.svm.util.LogUtils;
6970
import com.oracle.svm.util.StringUtil;
@@ -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/ByteFormattingUtil.java

Lines changed: 1 addition & 2 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
@@ -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
}

0 commit comments

Comments
 (0)