Skip to content

Commit

Permalink
New option ImageBuildID and substitution of java.util.ImmutableCollec…
Browse files Browse the repository at this point in the history
…tions.
  • Loading branch information
Maja Skoko committed Aug 9, 2023
1 parent e2914d1 commit 89009b4
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 0 deletions.
1 change: 1 addition & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This changelog summarizes major changes to GraalVM Native Image.
* (GR-47365) Throw `MissingReflectionRegistrationError` when attempting to create a proxy class without having it registered at build-time, instead of a `VMError`.
* (GR-46064) Add option `-H:±IndirectBranchTargetMarker` to mark indirect branch targets on AMD64 with an endbranch instruction. This is a prerequisite for future Intel CET support.
* (GR-46740) Add support for foreign downcalls (part of "Project Panama") on the AMD64 platform.
* (GR-27034) Add `-H:ImageBuildID` option to generate Image Build ID, which is a 128-bit UUID string generated randomly, once per bundle or digest of input args when bundles are not used.

## Version 23.0.0
* (GR-40187) Report invalid use of SVM specific classes on image class- or module-path as error. As a temporary workaround, `-H:+AllowDeprecatedBuilderClassesOnImageClasspath` allows turning the error into a warning.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.UUID;
import java.util.function.Predicate;

import org.graalvm.collections.EconomicMap;
Expand Down Expand Up @@ -83,6 +84,9 @@ public class SubstrateOptions {
@Option(help = "Constrain debug info generation to the comma-separated list of package prefixes given to this option.")//
public static final HostedOptionKey<LocatableMultiOptionValue.Strings> SourceLevelDebugFilter = new HostedOptionKey<>(LocatableMultiOptionValue.Strings.buildWithCommaDelimiter());

@Option(help = "Image Build ID is a 128-bit UUID string generated randomly, once per bundle or digest of input args when bundles are not used.")//
public static final HostedOptionKey<String> ImageBuildID = new HostedOptionKey<>("");

public static boolean parseOnce() {
/*
* Parsing all graphs before static analysis is work-in-progress and for JIT compilation is
Expand Down Expand Up @@ -167,6 +171,11 @@ public static Predicate<String> getSourceLevelDebugFilter() {
return makeFilter(SourceLevelDebugFilter.getValue().values());
}

@Fold
public static UUID getImageBuildID() {
return UUID.fromString(SubstrateOptions.ImageBuildID.getValue());
}

@Platforms(Platform.HOSTED_ONLY.class)
private static Predicate<String> makeFilter(List<String> definedFilters) {
if (definedFilters.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.math.BigInteger;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -426,4 +428,10 @@ public static String stripPackage(String qualifiedClassName) {
/* Anonymous classes can contain a '/' which can lead to an invalid binary name. */
return qualifiedClassName.substring(qualifiedClassName.lastIndexOf(".") + 1).replace("/", "");
}

public static UUID getUUIDFromString(String digest) {
long mostSigBits = new BigInteger(digest.substring(0, 16), 16).longValue();
long leastSigBits = new BigInteger(digest.substring(16), 16).longValue();
return new UUID(mostSigBits, leastSigBits);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.jdk;

import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.TargetClass;

/**
* This substitution is necessary in order to make native images deterministic. We substitute Java
* safety feature, which based on SALT32L value randomize iteration through a map and set data
* structures to make sure no implementation relies on iteration order.
*
* Depending on salt value constant folding will pick a different branch and eliminate the other so
* the code can look slightly different. Since anyway the SALT32L value is the same for one native
* image this will just help to make native images fully deterministic.
*/
@TargetClass(className = "java.util.ImmutableCollections")
public final class Target_java_util_ImmutableCollections {

@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias, isFinal = true) //
static long SALT32L = ImmutableCollectionsSupport.getSaltValue();

@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias, isFinal = true) //
static boolean REVERSE = (SALT32L & 1) == 0;
}

final class ImmutableCollectionsSupport {
public static long getSaltValue() {
/* To generate salt value we are using same approach as in java.util.ImmutableCollections */

long color = 0x243F_6A88_85A3_08D3L; // slice of pi
long seed = SubstrateOptions.getImageBuildID().getMostSignificantBits();
return (int) ((color * seed) >> 16) & 0xFFFF_FFFFL;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.jar.Attributes;
Expand Down Expand Up @@ -190,6 +191,7 @@ private BundleSupport(NativeImage nativeImage) {
try {
rootDir = createBundleRootDir();
bundleProperties = new BundleProperties();
bundleProperties.properties.put(BundleProperties.PROPERTY_KEY_IMAGE_BUILD_ID, UUID.randomUUID().toString());

Path inputDir = rootDir.resolve("input");
stageDir = Files.createDirectories(inputDir.resolve("stage"));
Expand Down Expand Up @@ -220,6 +222,7 @@ private BundleSupport(NativeImage nativeImage, String bundleFilenameArg) {
try {
rootDir = createBundleRootDir();
bundleProperties = new BundleProperties();
bundleProperties.properties.put(BundleProperties.PROPERTY_KEY_IMAGE_BUILD_ID, UUID.randomUUID().toString());

outputDir = rootDir.resolve("output");
String originalOutputDirName = outputDir.getFileName().toString() + ORIGINAL_DIR_EXTENSION;
Expand Down Expand Up @@ -317,6 +320,10 @@ public List<String> getNativeImageArgs() {
return nativeImageArgs;
}

public String getImageBuildID() {
return bundleProperties.properties.get(BundleProperties.PROPERTY_KEY_IMAGE_BUILD_ID);
}

Path recordCanonicalization(Path before, Path after) {
if (before.startsWith(rootDir)) {
nativeImage.showVerboseMessage(nativeImage.isVVerbose(), "RecordCanonicalization Skip: " + before);
Expand Down Expand Up @@ -773,6 +780,7 @@ private final class BundleProperties {
private static final String PROPERTY_KEY_NATIVE_IMAGE_PLATFORM = "NativeImagePlatform";
private static final String PROPERTY_KEY_NATIVE_IMAGE_VENDOR = "NativeImageVendor";
private static final String PROPERTY_KEY_NATIVE_IMAGE_VERSION = "NativeImageVersion";
private static final String PROPERTY_KEY_IMAGE_BUILD_ID = "ImageBuildID";

private final Path bundlePropertiesFile;
private final Map<String, String> properties;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1171,7 +1171,9 @@ private int completeImageBuild() {
updateArgumentEntryValue(imageBuilderArgs, imageNameEntry, imageName);
updateArgumentEntryValue(imageBuilderArgs, imagePathEntry, imagePath.toString());
}
String imageBuildID;
if (useBundle()) {
imageBuildID = bundleSupport.getImageBuildID();
/*
* In creation-mode, we are at the point where we know the final imagePath and imageName
* that we can now use to derive a bundle name in case none was set so far.
Expand All @@ -1183,7 +1185,12 @@ private int completeImageBuild() {
imagePath = bundleSupport.substituteImagePath(imagePath);
/* and we need to adjust the argument that passes the imagePath to the builder */
updateArgumentEntryValue(imageBuilderArgs, imagePathEntry, imagePath.toString());
} else {
String argsDigest = SubstrateUtil.digest(getNativeImageArgs().toString());
assert argsDigest.matches("[0-9a-f]+") && argsDigest.length() >= 32 : "Expecting a hex string";
imageBuildID = SubstrateUtil.getUUIDFromString(argsDigest).toString();
}
addPlainImageBuilderArg(oH(SubstrateOptions.ImageBuildID) + imageBuildID);

if (!leftoverArgs.isEmpty()) {
showError("Unrecognized option(s): " + StringUtil.joinSingleQuoted(leftoverArgs));
Expand Down

0 comments on commit 89009b4

Please sign in to comment.