diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/UnmanagedMemory.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/UnmanagedMemory.java index a9d475d4ed0f..3551cf7f486a 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/UnmanagedMemory.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/UnmanagedMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -62,8 +62,7 @@ private UnmanagedMemory() { /** * Allocates {@code size} bytes of unmanaged memory. The content of the memory is undefined. *

- * If {@code size} is 0, the method is allowed but not required to return the null pointer. This - * method never returns a the null pointer, but instead throws a {@link OutOfMemoryError} when + * This method never returns a null pointer, but instead throws an {@link OutOfMemoryError} when * allocation fails. * * @since 19.0 @@ -79,8 +78,7 @@ public static T malloc(UnsignedWord size) { /** * Allocates {@code size} bytes of unmanaged memory. The content of the memory is undefined. *

- * If {@code size} is 0, the method is allowed but not required to return the null pointer. This - * method never returns a the null pointer, but instead throws a {@link OutOfMemoryError} when + * This method never returns a null pointer, but instead throws an {@link OutOfMemoryError} when * allocation fails. * * @since 19.0 @@ -92,8 +90,7 @@ public static T malloc(int size) { /** * Allocates {@code size} bytes of unmanaged memory. The content of the memory is set to 0. *

- * If {@code size} is 0, the method is allowed but not required to return the null pointer. This - * method never returns a the null pointer, but instead throws a {@link OutOfMemoryError} when + * This method never returns a null pointer, but instead throws an {@link OutOfMemoryError} when * allocation fails. * * @since 19.0 @@ -109,8 +106,7 @@ public static T calloc(UnsignedWord size) { /** * Allocates {@code size} bytes of unmanaged memory. The content of the memory is set to 0. *

- * If {@code size} is 0, the method is allowed but not required to return the null pointer. This - * method never returns a the null pointer, but instead throws a {@link OutOfMemoryError} when + * This method never returns a null pointer, but instead throws an {@link OutOfMemoryError} when * allocation fails. * * @since 19.0 @@ -124,9 +120,8 @@ public static T calloc(int size) { * If the new size is larger than the old size, the content of the additional memory is * undefined. *

- * If {@code size} is 0, the method is allowed but not required to return the null pointer. This - * method never returns a the null pointer, but instead throws a {@link OutOfMemoryError} when - * allocation fails. + * This method never returns a null pointer, but instead throws an {@link OutOfMemoryError} when + * allocation fails. In that case, the old data is not deallocated and remains unchanged. * * @since 19.0 */ @@ -139,7 +134,8 @@ public static T realloc(T ptr, UnsignedWord size) { } /** - * Frees unmanaged memory that was previously allocated using methods of this class. + * Frees unmanaged memory that was previously allocated using methods of this class. This method + * is a no-op if the given pointer is {@code null}. * * @since 19.0 */ diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/UnmanagedMemorySupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/UnmanagedMemorySupport.java index c18ab87631e8..dc013c8aa826 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/UnmanagedMemorySupport.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/UnmanagedMemorySupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -43,7 +43,7 @@ import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; -/** Implemented by operating-system specific code. */ +/** Implemented by platform or operating-system specific code. */ public interface UnmanagedMemorySupport { T malloc(UnsignedWord size); diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index ba7c99edda4e..f61bbea50cf3 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -6,6 +6,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-51106) Fields that are accessed via a `VarHandle` or `MethodHandle` are no longer marked as "unsafe accessed" when the `VarHandle`/`MethodHandle` can be fully intrinsified. * (GR-49996) Ensure explicitly set image name (e.g., via `-o imagename`) is not accidentally overwritten by `-jar jarfile` option. * (GR-48683) Together with Red Hat, we added partial support for the JFR event `OldObjectSample`. +* (GR-51851) Together with Red Hat, we added initial support for native memory tracking (`--enable-monitoring=nmt`). * (GR-47109) Together with Red Hat, we added support for JFR event throttling and the event `ObjectAllocationSample`. * (GR-52030) Add a stable name for `Proxy` types in Native Image. The name `$Proxy[id]` is replaced by `$Proxy.s[hashCode]` where `hashCode` is computed using the names of the `Proxy` interfaces, the name of the class loader and the name of the module if it is not a dynamic module. diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java index ee11c2f4a8b3..5e72875f8fa1 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java @@ -61,9 +61,6 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; -import com.oracle.svm.core.jni.headers.JNIMode; -import jdk.graal.compiler.core.common.NumUtil; -import jdk.graal.compiler.java.LambdaUtils; import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.function.CEntryPoint; @@ -88,6 +85,7 @@ import com.oracle.svm.core.jni.headers.JNIEnvironment; import com.oracle.svm.core.jni.headers.JNIFieldId; import com.oracle.svm.core.jni.headers.JNIMethodId; +import com.oracle.svm.core.jni.headers.JNIMode; import com.oracle.svm.core.jni.headers.JNINativeMethod; import com.oracle.svm.core.jni.headers.JNIObjectHandle; import com.oracle.svm.core.jni.headers.JNIValue; @@ -106,6 +104,9 @@ import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiInterface; import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiLocationFormat; +import jdk.graal.compiler.core.common.NumUtil; +import jdk.graal.compiler.java.LambdaUtils; + /** * Intercepts events of interest via breakpoints in Java code. *

diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixProcessPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixProcessPropertiesSupport.java index f3d2b586e41e..0a8ef5e06db5 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixProcessPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixProcessPropertiesSupport.java @@ -41,7 +41,7 @@ import com.oracle.svm.core.BaseProcessPropertiesSupport; import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue; -import com.oracle.svm.core.headers.LibC; +import com.oracle.svm.core.memory.UntrackedNullableNativeMemory; import com.oracle.svm.core.posix.headers.Dlfcn; import com.oracle.svm.core.posix.headers.Signal; import com.oracle.svm.core.posix.headers.Stdlib; @@ -103,7 +103,7 @@ public String getObjectFile(PointerBase symbolAddress) { try { return CTypeConversion.toJavaString(realpath); } finally { - LibC.free(realpath); + UntrackedNullableNativeMemory.free(realpath); } } @@ -157,14 +157,14 @@ protected static String realpath(String path) { * pointer to it, so I have to free it. */ try (CCharPointerHolder pathHolder = CTypeConversion.toCString(path)) { - final CCharPointer realpathPointer = Stdlib.realpath(pathHolder.get(), WordFactory.nullPointer()); - if (realpathPointer.isNull()) { + CCharPointer realpath = Stdlib.realpath(pathHolder.get(), WordFactory.nullPointer()); + if (realpath.isNull()) { /* Failure to find a real path. */ return null; } else { /* Success */ - final String result = CTypeConversion.toJavaString(realpathPointer); - LibC.free(realpathPointer); + String result = CTypeConversion.toJavaString(realpath); + UntrackedNullableNativeMemory.free(realpath); return result; } } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixRawFileOperationSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixRawFileOperationSupport.java index b318b1b93cd9..af5da227d5fd 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixRawFileOperationSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixRawFileOperationSupport.java @@ -32,7 +32,6 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.SignedWord; import org.graalvm.word.UnsignedWord; @@ -42,6 +41,7 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.headers.LibC; +import com.oracle.svm.core.memory.UntrackedNullableNativeMemory; import com.oracle.svm.core.os.AbstractRawFileOperationSupport; import com.oracle.svm.core.os.AbstractRawFileOperationSupport.RawFileOperationSupportHolder; import com.oracle.svm.core.posix.headers.Errno; @@ -58,7 +58,7 @@ public PosixRawFileOperationSupport(boolean useNativeByteOrder) { @Override public CCharPointer allocateCPath(String path) { byte[] data = path.getBytes(); - CCharPointer filename = ImageSingletons.lookup(UnmanagedMemorySupport.class).malloc(WordFactory.unsigned(data.length + 1)); + CCharPointer filename = UntrackedNullableNativeMemory.malloc(WordFactory.unsigned(data.length + 1)); if (filename.isNull()) { return WordFactory.nullPointer(); } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java index fe0e9ccbbc9a..283f30947ae1 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java @@ -37,8 +37,8 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue; -import com.oracle.svm.core.headers.LibC; import com.oracle.svm.core.jdk.SystemPropertiesSupport; +import com.oracle.svm.core.memory.UntrackedNullableNativeMemory; import com.oracle.svm.core.posix.PosixSystemPropertiesSupport; import com.oracle.svm.core.posix.headers.Limits; import com.oracle.svm.core.posix.headers.Stdlib; @@ -105,7 +105,7 @@ protected String osVersionValue() { CCharPointer osVersionStr = Foundation.systemVersionPlatform(); if (osVersionStr.isNonNull()) { osVersionValue = CTypeConversion.toJavaString(osVersionStr); - LibC.free(osVersionStr); + UntrackedNullableNativeMemory.free(osVersionStr); return osVersionValue; } } else { @@ -120,7 +120,7 @@ protected String osVersionValue() { CCharPointer osVersionStr = Foundation.systemVersionPlatformFallback(); if (osVersionStr.isNonNull()) { osVersionValue = CTypeConversion.toJavaString(osVersionStr); - LibC.free(osVersionStr); + UntrackedNullableNativeMemory.free(osVersionStr); return osVersionValue; } return osVersionValue = "Unknown"; diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/jvmstat/PosixPerfMemoryProvider.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/jvmstat/PosixPerfMemoryProvider.java index 1942cde8ee0d..fffe6ebe1034 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/jvmstat/PosixPerfMemoryProvider.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/jvmstat/PosixPerfMemoryProvider.java @@ -41,15 +41,12 @@ import java.nio.ByteBuffer; -import jdk.graal.compiler.core.common.NumUtil; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.StackValue; -import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.WordFactory; @@ -64,6 +61,8 @@ import com.oracle.svm.core.jvmstat.PerfManager; import com.oracle.svm.core.jvmstat.PerfMemoryPrologue; import com.oracle.svm.core.jvmstat.PerfMemoryProvider; +import com.oracle.svm.core.memory.NullableNativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.os.RawFileOperationSupport; import com.oracle.svm.core.os.RawFileOperationSupport.RawFileDescriptor; import com.oracle.svm.core.os.VirtualMemoryProvider; @@ -80,6 +79,8 @@ import com.oracle.svm.core.posix.headers.Signal; import com.oracle.svm.core.posix.headers.Unistd; +import jdk.graal.compiler.core.common.NumUtil; + /** * This class uses high-level JDK features at the moment. In the future, we will need to rewrite * this code so that it can be executed during the isolate startup (i.e., in uninterruptible code), @@ -157,7 +158,7 @@ private static String getUserName(int uid) { } /* Retrieve the username and copy it to a String object. */ - CCharPointer pwBuf = ImageSingletons.lookup(UnmanagedMemorySupport.class).malloc(WordFactory.unsigned(bufSize)); + CCharPointer pwBuf = NullableNativeMemory.malloc(WordFactory.unsigned(bufSize), NmtCategory.JvmStat); if (pwBuf.isNull()) { return null; } @@ -182,7 +183,7 @@ private static String getUserName(int uid) { return CTypeConversion.toJavaString(pwName); } finally { - UnmanagedMemory.free(pwBuf); + NullableNativeMemory.free(pwBuf); } } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/thread/PosixPlatformThreads.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/thread/PosixPlatformThreads.java index ef05aa4fb2b4..93d375f39bf1 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/thread/PosixPlatformThreads.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/thread/PosixPlatformThreads.java @@ -24,19 +24,16 @@ */ package com.oracle.svm.core.posix.thread; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platform.HOSTED_ONLY; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.StackValue; -import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.function.CFunctionPointer; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder; import org.graalvm.nativeimage.c.type.VoidPointer; import org.graalvm.nativeimage.c.type.WordPointer; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; @@ -50,6 +47,8 @@ import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue; +import com.oracle.svm.core.memory.NativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.posix.PosixUtils; import com.oracle.svm.core.posix.headers.Errno; import com.oracle.svm.core.posix.headers.Pthread; @@ -321,7 +320,7 @@ final class PosixParker extends Parker { // Allocate mutex and condition in a single step so that they are adjacent in memory. UnsignedWord mutexSize = SizeOf.unsigned(pthread_mutex_t.class); UnsignedWord condSize = SizeOf.unsigned(pthread_cond_t.class); - Pointer memory = UnmanagedMemory.malloc(mutexSize.add(condSize.multiply(2))); + Pointer memory = NativeMemory.malloc(mutexSize.add(condSize.multiply(2)), NmtCategory.Threading); mutex = (pthread_mutex_t) memory; relativeCond = (pthread_cond_t) memory.add(mutexSize); absoluteCond = (pthread_cond_t) memory.add(mutexSize).add(condSize); @@ -437,7 +436,7 @@ protected void release() { status = Pthread.pthread_mutex_destroy(mutex); assert status == 0; - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(mutex); + NativeMemory.free(mutex); mutex = WordFactory.nullPointer(); } } diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSystemPropertiesSupport.java index fc6e0012e97e..10a35b294b30 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSystemPropertiesSupport.java @@ -45,8 +45,9 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue; -import com.oracle.svm.core.headers.LibC; import com.oracle.svm.core.jdk.SystemPropertiesSupport; +import com.oracle.svm.core.memory.NullableNativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.util.VMError; import com.oracle.svm.core.windows.headers.FileAPI; import com.oracle.svm.core.windows.headers.LibLoaderAPI; @@ -245,29 +246,29 @@ public Pair getOsNameAndVersion() { break; } - VoidPointer versionInfo = LibC.malloc(WordFactory.unsigned(versionSize)); + VoidPointer versionInfo = NullableNativeMemory.malloc(WordFactory.unsigned(versionSize), NmtCategory.Internal); if (versionInfo.isNull()) { break; } + try { + if (WinVer.GetFileVersionInfoW(kernel32Path, 0, versionSize, versionInfo) == 0) { + break; + } - if (WinVer.GetFileVersionInfoW(kernel32Path, 0, versionSize, versionInfo) == 0) { - LibC.free(versionInfo); - break; - } + WindowsLibC.WCharPointer rootPath = NonmovableArrays.addressOf(NonmovableArrays.fromImageHeap(ROOT_PATH), 0); + WordPointer fileInfoPointer = UnsafeStackValue.get(WordPointer.class); + CIntPointer lengthPointer = UnsafeStackValue.get(CIntPointer.class); + if (WinVer.VerQueryValueW(versionInfo, rootPath, fileInfoPointer, lengthPointer) == 0) { + break; + } - WindowsLibC.WCharPointer rootPath = NonmovableArrays.addressOf(NonmovableArrays.fromImageHeap(ROOT_PATH), 0); - WordPointer fileInfoPointer = UnsafeStackValue.get(WordPointer.class); - CIntPointer lengthPointer = UnsafeStackValue.get(CIntPointer.class); - if (WinVer.VerQueryValueW(versionInfo, rootPath, fileInfoPointer, lengthPointer) == 0) { - LibC.free(versionInfo); - break; + VerRsrc.VS_FIXEDFILEINFO fileInfo = fileInfoPointer.read(); + majorVersion = (short) (fileInfo.dwProductVersionMS() >> 16); // HIWORD + minorVersion = (short) fileInfo.dwProductVersionMS(); // LOWORD + buildNumber = (short) (fileInfo.dwProductVersionLS() >> 16); // HIWORD + } finally { + NullableNativeMemory.free(versionInfo); } - - VerRsrc.VS_FIXEDFILEINFO fileInfo = fileInfoPointer.read(); - majorVersion = (short) (fileInfo.dwProductVersionMS() >> 16); // HIWORD - minorVersion = (short) fileInfo.dwProductVersionMS(); // LOWORD - buildNumber = (short) (fileInfo.dwProductVersionLS() >> 16); // HIWORD - LibC.free(versionInfo); } while (false); String osVersion = majorVersion + "." + minorVersion; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java index bf1d2a4e5946..55c96fa1234a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java @@ -30,10 +30,6 @@ import java.util.Set; import org.graalvm.collections.EconomicMap; -import jdk.graal.compiler.api.replacements.Fold; -import jdk.graal.compiler.options.Option; -import jdk.graal.compiler.options.OptionKey; -import jdk.graal.compiler.options.OptionType; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platform.WINDOWS; import org.graalvm.nativeimage.Platforms; @@ -43,10 +39,16 @@ import com.oracle.svm.core.option.APIOption; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.LocatableMultiOptionValue; +import com.oracle.svm.core.option.RuntimeOptionKey; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.UserError; import com.oracle.svm.util.LogUtils; +import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.options.Option; +import jdk.graal.compiler.options.OptionKey; +import jdk.graal.compiler.options.OptionType; + public final class VMInspectionOptions { private static final String ENABLE_MONITORING_OPTION = "enable-monitoring"; private static final String MONITORING_DEFAULT_NAME = ""; @@ -57,12 +59,13 @@ public final class VMInspectionOptions { private static final String MONITORING_JMXCLIENT_NAME = "jmxclient"; private static final String MONITORING_JMXSERVER_NAME = "jmxserver"; private static final String MONITORING_THREADDUMP_NAME = "threaddump"; + private static final String MONITORING_NMT_NAME = "nmt"; private static final List MONITORING_ALL_VALUES = List.of(MONITORING_HEAPDUMP_NAME, MONITORING_JFR_NAME, MONITORING_JVMSTAT_NAME, MONITORING_JMXCLIENT_NAME, MONITORING_JMXSERVER_NAME, - MONITORING_THREADDUMP_NAME, MONITORING_ALL_NAME, MONITORING_DEFAULT_NAME); + MONITORING_THREADDUMP_NAME, MONITORING_NMT_NAME, MONITORING_ALL_NAME, MONITORING_DEFAULT_NAME); private static final String MONITORING_ALLOWED_VALUES_TEXT = "'" + MONITORING_HEAPDUMP_NAME + "', '" + MONITORING_JFR_NAME + "', '" + MONITORING_JVMSTAT_NAME + "', '" + MONITORING_JMXSERVER_NAME + - "' (experimental), '" + MONITORING_JMXCLIENT_NAME + "' (experimental), '" + MONITORING_THREADDUMP_NAME + "', or '" + MONITORING_ALL_NAME + - "' (deprecated behavior: defaults to '" + MONITORING_ALL_NAME + "' if no argument is provided)"; + "' (experimental), '" + MONITORING_JMXCLIENT_NAME + "' (experimental), '" + MONITORING_THREADDUMP_NAME + "', '" + MONITORING_NMT_NAME + "' (experimental), or '" + + MONITORING_ALL_NAME + "' (deprecated behavior: defaults to '" + MONITORING_ALL_NAME + "' if no argument is provided)"; static { assert MONITORING_ALL_VALUES.stream().allMatch(v -> MONITORING_DEFAULT_NAME.equals(v) || MONITORING_ALLOWED_VALUES_TEXT.contains(v)) : "A value is missing in the user-facing help text"; @@ -74,6 +77,12 @@ public final class VMInspectionOptions { public static final HostedOptionKey EnableMonitoringFeatures = new HostedOptionKey<>(LocatableMultiOptionValue.Strings.buildWithCommaDelimiter(), VMInspectionOptions::validateEnableMonitoringFeatures); + @Option(help = "Dumps all runtime compiled methods on SIGUSR2.", type = OptionType.User) // + public static final HostedOptionKey DumpRuntimeCompilationOnSignal = new HostedOptionKey<>(false); + + @Option(help = "Print native memory tracking statistics on shutdown if native memory tracking is enabled.", type = OptionType.User) // + public static final RuntimeOptionKey PrintNMTStatistics = new RuntimeOptionKey<>(false); + @Platforms(Platform.HOSTED_ONLY.class) public static void validateEnableMonitoringFeatures(@SuppressWarnings("unused") OptionKey optionKey) { Set enabledFeatures = getEnabledMonitoringFeatures(); @@ -163,8 +172,10 @@ public static boolean hasThreadDumpSupport() { return hasAllOrKeywordMonitoringSupport(MONITORING_THREADDUMP_NAME) || DeprecatedOptions.DumpThreadStacksOnSignal.getValue(); } - @Option(help = "Dumps all runtime compiled methods on SIGUSR2.", type = OptionType.User) // - public static final HostedOptionKey DumpRuntimeCompilationOnSignal = new HostedOptionKey<>(false); + @Fold + public static boolean hasNativeMemoryTrackingSupport() { + return hasAllOrKeywordMonitoringSupport(MONITORING_NMT_NAME); + } static class DeprecatedOptions { @Option(help = "Enables features that allow the VM to be inspected during run time.", type = OptionType.User, // diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/NonmovableArrays.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/NonmovableArrays.java index 1b765e534da2..b0390c59a113 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/NonmovableArrays.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/NonmovableArrays.java @@ -28,11 +28,9 @@ import java.nio.ByteBuffer; import java.util.Arrays; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.type.CTypeConversion; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; @@ -51,6 +49,8 @@ import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.LayoutEncoding; import com.oracle.svm.core.jdk.UninterruptibleUtils; +import com.oracle.svm.core.memory.NullableNativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.util.VMError; @@ -84,7 +84,7 @@ public final class NonmovableArrays { @SuppressWarnings("unchecked") @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - private static > T createArray(int length, Class arrayType) { + private static > T createArray(int length, Class arrayType, NmtCategory nmtCategory) { if (SubstrateUtil.HOSTED) { Class componentType = arrayType.getComponentType(); Object array = Array.newInstance(componentType, length); @@ -93,7 +93,7 @@ private static > T createArray(int length, Class DynamicHub hub = SubstrateUtil.cast(arrayType, DynamicHub.class); assert LayoutEncoding.isArray(hub.getLayoutEncoding()); UnsignedWord size = LayoutEncoding.getArrayAllocationSize(hub.getLayoutEncoding(), length); - Pointer array = ImageSingletons.lookup(UnmanagedMemorySupport.class).calloc(size); + Pointer array = NullableNativeMemory.calloc(size, nmtCategory); if (array.isNull()) { throw OUT_OF_MEMORY_ERROR; } @@ -181,8 +181,8 @@ public static > T nullArray() { * array, it is not a Java object and must never be referenced as an object. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static NonmovableArray createByteArray(int nbytes) { - return createArray(nbytes, byte[].class); + public static NonmovableArray createByteArray(int nbytes, NmtCategory nmtCategory) { + return createArray(nbytes, byte[].class, nmtCategory); } /** @@ -193,8 +193,8 @@ public static NonmovableArray createByteArray(int nbytes) { * of a Java array, it is not a Java object and must never be referenced as an object. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static NonmovableArray createIntArray(int length) { - return createArray(length, int[].class); + public static NonmovableArray createIntArray(int length, NmtCategory nmtCategory) { + return createArray(length, int[].class, nmtCategory); } /** @@ -205,8 +205,8 @@ public static NonmovableArray createIntArray(int length) { * of a Java array, it is not a Java object and must never be referenced as an object. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static NonmovableArray createWordArray(int length) { - return createArray(length, WordBase[].class); + public static NonmovableArray createWordArray(int length, NmtCategory nmtCategory) { + return createArray(length, WordBase[].class, nmtCategory); } /** @@ -220,16 +220,16 @@ public static NonmovableArray createWordArray(int length * that of a Java array, it is not a Java object and must never be referenced as an object. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static NonmovableObjectArray createObjectArray(Class arrayType, int length) { + public static NonmovableObjectArray createObjectArray(Class arrayType, int length, NmtCategory nmtCategory) { assert (SubstrateUtil.HOSTED ? (arrayType.isArray() && !arrayType.getComponentType().isPrimitive()) : LayoutEncoding.isObjectArray(SubstrateUtil.cast(arrayType, DynamicHub.class).getLayoutEncoding())) : "must be an object array type"; - return createArray(length, arrayType); + return createArray(length, arrayType, nmtCategory); } /** @see java.util.Arrays#copyOf */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static NonmovableObjectArray copyOfObjectArray(T[] source, int newLength) { - NonmovableObjectArray array = createArray(newLength, source.getClass()); + public static NonmovableObjectArray copyOfObjectArray(T[] source, int newLength, NmtCategory nmtCategory) { + NonmovableObjectArray array = createArray(newLength, source.getClass(), nmtCategory); int copyLength = (source.length < newLength) ? source.length : newLength; for (int i = 0; i < copyLength; i++) { setObject(array, i, source[i]); @@ -239,8 +239,8 @@ public static NonmovableObjectArray copyOfObjectArray(T[] source, int new /** Same as {@link #copyOfObjectArray} with a {@code newLength} of the array length. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static NonmovableObjectArray copyOfObjectArray(T[] source) { - return copyOfObjectArray(source, source.length); + public static NonmovableObjectArray copyOfObjectArray(T[] source, NmtCategory nmtCategory) { + return copyOfObjectArray(source, source.length, nmtCategory); } public static byte[] heapCopyOfByteArray(NonmovableArray source) { @@ -299,7 +299,7 @@ private static T arraycopyToHeap(NonmovableArray src, int srcPos, T dest, /** Releases an array created at runtime. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static void releaseUnmanagedArray(NonmovableArray array) { - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(array); + NullableNativeMemory.free(array); untrackUnmanagedArray(array); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/UnmanagedPrimitiveArrays.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/UnmanagedPrimitiveArrays.java index a23112ad2cce..251b914c27ec 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/UnmanagedPrimitiveArrays.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/UnmanagedPrimitiveArrays.java @@ -24,6 +24,11 @@ */ package com.oracle.svm.core.c; +import org.graalvm.word.Pointer; +import org.graalvm.word.PointerBase; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; + import com.oracle.svm.core.JavaMemoryUtil; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.Uninterruptible; @@ -32,17 +37,13 @@ import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.LayoutEncoding; import com.oracle.svm.core.jdk.UninterruptibleUtils; +import com.oracle.svm.core.memory.NullableNativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.util.VMError; import jdk.graal.compiler.nodes.java.ArrayLengthNode; import jdk.graal.compiler.word.Word; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; -import org.graalvm.word.Pointer; -import org.graalvm.word.PointerBase; -import org.graalvm.word.UnsignedWord; -import org.graalvm.word.WordFactory; /** * Support for allocating and accessing primitive element arrays created in unmanaged memory. They @@ -57,11 +58,11 @@ public final class UnmanagedPrimitiveArrays { @SuppressWarnings("unchecked") @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static > T createArray(int length, Class arrayType) { + public static > T createArray(int length, Class arrayType, NmtCategory nmtCategory) { DynamicHub hub = SubstrateUtil.cast(arrayType, DynamicHub.class); VMError.guarantee(LayoutEncoding.isPrimitiveArray(hub.getLayoutEncoding())); UnsignedWord size = WordFactory.unsigned(length).shiftLeft(LayoutEncoding.getArrayIndexShift(hub.getLayoutEncoding())); - Pointer array = ImageSingletons.lookup(UnmanagedMemorySupport.class).calloc(size); + Pointer array = NullableNativeMemory.calloc(size, nmtCategory); if (array.isNull()) { throw OUT_OF_MEMORY_ERROR; } @@ -88,7 +89,7 @@ public static void untrackUnmanagedArray(UnmanagedPrimitiveArray array) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static void releaseUnmanagedArray(UnmanagedPrimitiveArray array) { untrackUnmanagedArray(array); - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(array); + NullableNativeMemory.free(array); } /** Returns a pointer to the address of the given index of an array. */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/IsolateSupportImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/IsolateSupportImpl.java index 5a47755a8c91..a64bc2ac4104 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/IsolateSupportImpl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/IsolateSupportImpl.java @@ -31,7 +31,6 @@ import org.graalvm.nativeimage.Isolates.CreateIsolateParameters; import org.graalvm.nativeimage.Isolates.IsolateException; import org.graalvm.nativeimage.Isolates.ProtectionDomain; -import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CCharPointerPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; @@ -42,6 +41,8 @@ import com.oracle.svm.core.c.function.CEntryPointNativeFunctions.IsolateThreadPointer; import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue; +import com.oracle.svm.core.memory.NativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.os.MemoryProtectionProvider; import com.oracle.svm.core.os.MemoryProtectionProvider.UnsupportedDomainException; @@ -89,7 +90,7 @@ public static IsolateThread createIsolate(CreateIsolateParameters parameters, bo // Internally, we use C-style arguments, i.e., the first argument is reserved for // the name of the binary. We use null when isolates are created manually. argc = isolateArgCount + 1; - argv = UnmanagedMemory.malloc(SizeOf.unsigned(CCharPointerPointer.class).multiply(argc)); + argv = NativeMemory.malloc(SizeOf.unsigned(CCharPointerPointer.class).multiply(argc), NmtCategory.Internal); argv.write(0, WordFactory.nullPointer()); pointerHolders = new CTypeConversion.CCharPointerHolder[isolateArgCount]; @@ -121,7 +122,7 @@ public static IsolateThread createIsolate(CreateIsolateParameters parameters, bo for (CTypeConversion.CCharPointerHolder ph : pointerHolders) { ph.close(); } - UnmanagedMemory.free(params.getArgv()); + NativeMemory.free(params.getArgv()); } throwOnError(result); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java index c58ab2d74eda..3d3cfb28896e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java @@ -58,6 +58,7 @@ import com.oracle.svm.core.meta.SharedField; import com.oracle.svm.core.meta.SharedMethod; import com.oracle.svm.core.meta.SharedType; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.util.ByteArrayReader; import com.oracle.svm.core.util.Counter; @@ -129,12 +130,10 @@ private void encodeAllAndInstall(CodeInfo target, ReferenceAdjuster adjuster) { } @Uninterruptible(reason = "Nonmovable object arrays are not visible to GC until installed in target.") - private static void install(CodeInfo target, JavaConstant[] objectConstantsArray, Class[] sourceClassesArray, - String[] sourceMethodNamesArray, ReferenceAdjuster adjuster) { - - NonmovableObjectArray frameInfoObjectConstants = adjuster.copyOfObjectConstantArray(objectConstantsArray); - NonmovableObjectArray> frameInfoSourceClasses = (sourceClassesArray != null) ? adjuster.copyOfObjectArray(sourceClassesArray) : NonmovableArrays.nullArray(); - NonmovableObjectArray frameInfoSourceMethodNames = (sourceMethodNamesArray != null) ? adjuster.copyOfObjectArray(sourceMethodNamesArray) : NonmovableArrays.nullArray(); + private static void install(CodeInfo target, JavaConstant[] objectConstants, Class[] sourceClasses, String[] sourceMethodNames, ReferenceAdjuster adjuster) { + NonmovableObjectArray frameInfoObjectConstants = adjuster.copyOfObjectConstantArray(objectConstants, NmtCategory.Code); + NonmovableObjectArray> frameInfoSourceClasses = (sourceClasses != null) ? adjuster.copyOfObjectArray(sourceClasses, NmtCategory.Code) : NonmovableArrays.nullArray(); + NonmovableObjectArray frameInfoSourceMethodNames = (sourceMethodNames != null) ? adjuster.copyOfObjectArray(sourceMethodNames, NmtCategory.Code) : NonmovableArrays.nullArray(); CodeInfoAccess.setEncodings(target, frameInfoObjectConstants, frameInfoSourceClasses, frameInfoSourceMethodNames); } @@ -339,9 +338,9 @@ private void encodeIPData() { writeEncodedFrameInfo(encodingBuffer, data, entryFlags); } - codeInfoIndex = NonmovableArrays.createByteArray(TypeConversion.asU4(indexBuffer.getBytesWritten())); + codeInfoIndex = NonmovableArrays.createByteArray(TypeConversion.asU4(indexBuffer.getBytesWritten()), NmtCategory.Code); indexBuffer.toByteBuffer(NonmovableArrays.asByteBuffer(codeInfoIndex)); - codeInfoEncodings = NonmovableArrays.createByteArray(TypeConversion.asU4(encodingBuffer.getBytesWritten())); + codeInfoEncodings = NonmovableArrays.createByteArray(TypeConversion.asU4(encodingBuffer.getBytesWritten()), NmtCategory.Code); encodingBuffer.toByteBuffer(NonmovableArrays.asByteBuffer(codeInfoEncodings)); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/DeoptimizationSourcePositionEncoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/DeoptimizationSourcePositionEncoder.java index 5c77dc245957..19908dd5523f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/DeoptimizationSourcePositionEncoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/DeoptimizationSourcePositionEncoder.java @@ -29,17 +29,19 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.collections.EconomicSet; import org.graalvm.collections.Equivalence; -import jdk.graal.compiler.core.common.util.FrequencyEncoder; -import jdk.graal.compiler.core.common.util.TypeConversion; -import jdk.graal.compiler.core.common.util.UnsafeArrayTypeWriter; -import jdk.graal.compiler.graph.NodeSourcePosition; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.c.NonmovableArray; import com.oracle.svm.core.c.NonmovableArrays; import com.oracle.svm.core.c.NonmovableObjectArray; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.util.ByteArrayReader; +import jdk.graal.compiler.core.common.util.FrequencyEncoder; +import jdk.graal.compiler.core.common.util.TypeConversion; +import jdk.graal.compiler.core.common.util.UnsafeArrayTypeWriter; +import jdk.graal.compiler.graph.NodeSourcePosition; + public class DeoptimizationSourcePositionEncoder { private final FrequencyEncoder objectConstants; @@ -54,10 +56,10 @@ public void encodeAndInstall(List deoptSourcePositions, Code UnsafeArrayTypeWriter encodingBuffer = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); EconomicMap sourcePositionStartOffsets = EconomicMap.create(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE); - NonmovableArray deoptStartOffsets = NonmovableArrays.createIntArray(deoptSourcePositions.size()); + NonmovableArray deoptStartOffsets = NonmovableArrays.createIntArray(deoptSourcePositions.size(), NmtCategory.Code); encodeSourcePositions(deoptSourcePositions, sourcePositionStartOffsets, deoptStartOffsets, encodingBuffer); - NonmovableArray deoptEncodings = NonmovableArrays.createByteArray(TypeConversion.asS4(encodingBuffer.getBytesWritten())); + NonmovableArray deoptEncodings = NonmovableArrays.createByteArray(TypeConversion.asS4(encodingBuffer.getBytesWritten()), NmtCategory.Code); encodingBuffer.toByteBuffer(NonmovableArrays.asByteBuffer(deoptEncodings)); install(target, deoptStartOffsets, deoptEncodings, encodedObjectConstants, deoptSourcePositions, adjuster); @@ -67,7 +69,7 @@ public void encodeAndInstall(List deoptSourcePositions, Code private static void install(CodeInfo target, NonmovableArray deoptStartOffsets, NonmovableArray deoptEncodings, Object[] encodedObjectConstants, List deoptSourcePositions, ReferenceAdjuster adjuster) { - NonmovableObjectArray deoptObjectConstants = adjuster.copyOfObjectArray(encodedObjectConstants); + NonmovableObjectArray deoptObjectConstants = adjuster.copyOfObjectArray(encodedObjectConstants, NmtCategory.Code); RuntimeCodeInfoAccess.setDeoptimizationMetadata(target, deoptStartOffsets, deoptEncodings, deoptObjectConstants); afterInstallation(deoptStartOffsets, deoptEncodings, deoptSourcePositions, deoptObjectConstants, adjuster); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoEncoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoEncoder.java index 5e06b89ba6ca..1227003e734a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoEncoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoEncoder.java @@ -56,6 +56,7 @@ import com.oracle.svm.core.meta.SharedField; import com.oracle.svm.core.meta.SharedMethod; import com.oracle.svm.core.meta.SharedType; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.sampler.CallStackFrameMethodData; import com.oracle.svm.core.sampler.CallStackFrameMethodInfo; import com.oracle.svm.core.util.ByteArrayReader; @@ -909,7 +910,7 @@ private NonmovableArray encodeFrameDatas() { assert frameMetadata.writeFrameVerificationInfo(data, encoders); } } - NonmovableArray frameInfoEncodings = NonmovableArrays.createByteArray(TypeConversion.asS4(encodingBuffer.getBytesWritten())); + NonmovableArray frameInfoEncodings = NonmovableArrays.createByteArray(TypeConversion.asS4(encodingBuffer.getBytesWritten()), NmtCategory.Code); encodingBuffer.toByteBuffer(NonmovableArrays.asByteBuffer(frameInfoEncodings)); return frameInfoEncodings; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ImageCodeInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ImageCodeInfo.java index fec3fde15470..4c8140249588 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ImageCodeInfo.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ImageCodeInfo.java @@ -41,6 +41,7 @@ import com.oracle.svm.core.c.NonmovableObjectArray; import com.oracle.svm.core.heap.UnknownObjectField; import com.oracle.svm.core.heap.UnknownPrimitiveField; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.util.VMError; import jdk.graal.compiler.word.Word; @@ -71,7 +72,7 @@ public class ImageCodeInfo { @Platforms(Platform.HOSTED_ONLY.class) ImageCodeInfo() { - NonmovableObjectArray objfields = NonmovableArrays.createObjectArray(Object[].class, CodeInfoImpl.OBJFIELDS_COUNT); + NonmovableObjectArray objfields = NonmovableArrays.createObjectArray(Object[].class, CodeInfoImpl.OBJFIELDS_COUNT, NmtCategory.Code); NonmovableArrays.setObject(objfields, CodeInfoImpl.NAME_OBJFIELD, CODE_INFO_NAME); // The image code info is never invalidated, so we consider it as always tethered. NonmovableArrays.setObject(objfields, CodeInfoImpl.TETHER_OBJFIELD, new CodeInfoTether(true)); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/InstalledCodeObserverSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/InstalledCodeObserverSupport.java index f2c7f21523a7..63a53f6691ca 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/InstalledCodeObserverSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/InstalledCodeObserverSupport.java @@ -27,8 +27,6 @@ import java.util.ArrayList; import java.util.List; -import jdk.graal.compiler.code.CompilationResult; -import jdk.graal.compiler.debug.DebugContext; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.Pointer; @@ -40,6 +38,10 @@ import com.oracle.svm.core.code.InstalledCodeObserver.InstalledCodeObserverHandle; import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.core.nmt.NmtCategory; + +import jdk.graal.compiler.code.CompilationResult; +import jdk.graal.compiler.debug.DebugContext; @AutomaticallyRegisteredImageSingleton public final class InstalledCodeObserverSupport { @@ -74,7 +76,7 @@ public static NonmovableArray installObservers(Inst if (observers.length == 0) { return NonmovableArrays.nullArray(); } - NonmovableArray observerHandles = NonmovableArrays.createWordArray(observers.length); + NonmovableArray observerHandles = NonmovableArrays.createWordArray(observers.length, NmtCategory.Code); for (int i = 0; i < observers.length; i++) { InstalledCodeObserverHandle handle = observers[i].install(); NonmovableArrays.setWord(observerHandles, i, handle); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/InstantReferenceAdjuster.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/InstantReferenceAdjuster.java index bc577142320c..c7bafc4dfe7c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/InstantReferenceAdjuster.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/InstantReferenceAdjuster.java @@ -31,6 +31,7 @@ import com.oracle.svm.core.c.NonmovableArrays; import com.oracle.svm.core.c.NonmovableObjectArray; import com.oracle.svm.core.meta.DirectSubstrateObjectConstant; +import com.oracle.svm.core.nmt.NmtCategory; import jdk.vm.ci.meta.JavaConstant; @@ -59,8 +60,8 @@ public void setConstantTargetAt(PointerBase address, int length, JavaConstant co @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public NonmovableObjectArray copyOfObjectArray(T[] source) { - return NonmovableArrays.copyOfObjectArray(source); + public NonmovableObjectArray copyOfObjectArray(T[] source, NmtCategory nmtCategory) { + return NonmovableArrays.copyOfObjectArray(source, nmtCategory); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ReferenceAdjuster.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ReferenceAdjuster.java index 55c69b6fc7f3..124e31840391 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ReferenceAdjuster.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ReferenceAdjuster.java @@ -26,7 +26,6 @@ import java.nio.ByteOrder; -import jdk.graal.compiler.api.replacements.Fold; import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; @@ -35,7 +34,9 @@ import com.oracle.svm.core.c.NonmovableObjectArray; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.heap.ReferenceAccess; +import com.oracle.svm.core.nmt.NmtCategory; +import jdk.graal.compiler.api.replacements.Fold; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaConstant; @@ -53,8 +54,8 @@ public interface ReferenceAdjuster { void setConstantTargetAt(PointerBase address, int length, JavaConstant constant); @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - default NonmovableObjectArray copyOfObjectConstantArray(T[] constants) { - NonmovableObjectArray objects = NonmovableArrays.createObjectArray(Object[].class, constants.length); + default NonmovableObjectArray copyOfObjectConstantArray(T[] constants, NmtCategory nmtCategory) { + NonmovableObjectArray objects = NonmovableArrays.createObjectArray(Object[].class, constants.length, nmtCategory); for (int i = 0; i < constants.length; i++) { setConstantTargetInArray(objects, i, (JavaConstant) constants[i]); } @@ -62,7 +63,7 @@ default NonmovableObjectArray copyOfObjectConstantA } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - NonmovableObjectArray copyOfObjectArray(T[] source); + NonmovableObjectArray copyOfObjectArray(T[] source, NmtCategory nmtCategory); /** Indicates whether all object references have been written. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeCache.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeCache.java index 50a389d42d1e..6d2af1753d59 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeCache.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeCache.java @@ -46,6 +46,7 @@ import com.oracle.svm.core.deopt.Deoptimizer; import com.oracle.svm.core.deopt.SubstrateInstalledCode; import com.oracle.svm.core.heap.RestrictHeapAccess; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.option.RuntimeOptionKey; import com.oracle.svm.core.stack.JavaStackWalker; import com.oracle.svm.core.stack.StackFrameVisitor; @@ -192,7 +193,7 @@ private void enlargeTable() { if (newTableSize < INITIAL_TABLE_SIZE) { newTableSize = INITIAL_TABLE_SIZE; } - NonmovableArray newCodeInfos = NonmovableArrays.createWordArray(newTableSize); + NonmovableArray newCodeInfos = NonmovableArrays.createWordArray(newTableSize, NmtCategory.Code); if (codeInfos.isNonNull()) { NonmovableArrays.arraycopy(codeInfos, 0, newCodeInfos, 0, NonmovableArrays.lengthOf(codeInfos)); NonmovableArrays.releaseUnmanagedArray(codeInfos); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java index 334672f9fc6a..4b5d4b0a5d13 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java @@ -26,10 +26,7 @@ import java.util.EnumSet; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.function.CodePointer; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; @@ -45,6 +42,9 @@ import com.oracle.svm.core.heap.CodeReferenceMapDecoder; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.ObjectReferenceVisitor; +import com.oracle.svm.core.memory.NativeMemory; +import com.oracle.svm.core.memory.NullableNativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.os.CommittedMemoryProvider; import com.oracle.svm.core.util.DuplicatedInNativeCode; import com.oracle.svm.core.util.VMError; @@ -203,12 +203,12 @@ public static boolean walkObjectFields(CodeInfo info, ObjectReferenceVisitor vis } public static CodeInfo allocateMethodInfo() { - NonmovableObjectArray objectFields = NonmovableArrays.createObjectArray(Object[].class, CodeInfoImpl.OBJFIELDS_COUNT); + NonmovableObjectArray objectFields = NonmovableArrays.createObjectArray(Object[].class, CodeInfoImpl.OBJFIELDS_COUNT, NmtCategory.Code); return allocateMethodInfo(objectFields); } public static CodeInfo allocateMethodInfo(NonmovableObjectArray objectData) { - CodeInfoImpl info = UnmanagedMemory.calloc(CodeInfoAccess.getSizeOfCodeInfo()); + CodeInfoImpl info = NativeMemory.calloc(CodeInfoAccess.getSizeOfCodeInfo(), NmtCategory.Code); assert objectData.isNonNull() && NonmovableArrays.lengthOf(objectData) == CodeInfoImpl.OBJFIELDS_COUNT; info.setObjectFields(objectData); @@ -279,7 +279,7 @@ public static void free(CodeInfo info) { } impl.setState(CodeInfo.STATE_FREED); - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(info); + NullableNativeMemory.free(info); } private static final NonmovableArrayAction GUARANTEE_ALL_OBJECTS_IN_IMAGE_HEAP_ACTION = new NonmovableArrayAction() { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoMemory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoMemory.java index 677a26df2e17..761481723d48 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoMemory.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoMemory.java @@ -42,6 +42,7 @@ import com.oracle.svm.core.deopt.SubstrateInstalledCode; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.log.Log; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.util.VMError; @@ -235,7 +236,7 @@ public boolean removeDuringGC(CodeInfo info) { private void add0(CodeInfo info) { addToSizeCounters(info); if (table.isNull()) { - table = NonmovableArrays.createWordArray(32); + table = NonmovableArrays.createWordArray(32, NmtCategory.Code); } int index; boolean resized; @@ -271,7 +272,7 @@ private boolean resize(int newLength) { return false; } NonmovableArray oldTable = table; - table = NonmovableArrays.createWordArray(newLength); + table = NonmovableArrays.createWordArray(newLength, NmtCategory.Code); for (int i = 0; i < oldLength; i++) { UntetheredCodeInfo tag = NonmovableArrays.getWord(oldTable, i); if (tag.isNonNull()) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/AbstractUninterruptibleHashtable.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/AbstractUninterruptibleHashtable.java index 8eb7cd7b84ac..f5b915281843 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/AbstractUninterruptibleHashtable.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/AbstractUninterruptibleHashtable.java @@ -24,16 +24,16 @@ */ package com.oracle.svm.core.collections; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.UnmanagedMemoryUtil; +import com.oracle.svm.core.memory.NullableNativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; /** * An uninterruptible hashtable with a fixed size that uses chaining in case of a collision. @@ -41,17 +41,19 @@ public abstract class AbstractUninterruptibleHashtable implements UninterruptibleHashtable { private static final int DEFAULT_TABLE_LENGTH = 2053; + private final NmtCategory nmtCategory; private final UninterruptibleEntry[] table; private int size; @Platforms(Platform.HOSTED_ONLY.class) - public AbstractUninterruptibleHashtable() { - this(DEFAULT_TABLE_LENGTH); + public AbstractUninterruptibleHashtable(NmtCategory nmtCategory) { + this(nmtCategory, DEFAULT_TABLE_LENGTH); } @Platforms(Platform.HOSTED_ONLY.class) @SuppressWarnings("this-escape") - public AbstractUninterruptibleHashtable(int primeLength) { + public AbstractUninterruptibleHashtable(NmtCategory nmtCategory, int primeLength) { + this.nmtCategory = nmtCategory; this.table = createTable(primeLength); this.size = 0; } @@ -67,7 +69,7 @@ public AbstractUninterruptibleHashtable(int primeLength) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) protected UninterruptibleEntry copyToHeap(UninterruptibleEntry pointerOnStack, UnsignedWord sizeToAlloc) { - UninterruptibleEntry pointerOnHeap = ImageSingletons.lookup(UnmanagedMemorySupport.class).malloc(sizeToAlloc); + UninterruptibleEntry pointerOnHeap = NullableNativeMemory.malloc(sizeToAlloc, nmtCategory); if (pointerOnHeap.isNonNull()) { UnmanagedMemoryUtil.copy((Pointer) pointerOnStack, (Pointer) pointerOnHeap, sizeToAlloc); return pointerOnHeap; @@ -78,7 +80,7 @@ protected UninterruptibleEntry copyToHeap(UninterruptibleEntry pointerOnStack, U @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) protected void free(UninterruptibleEntry entry) { size--; - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(entry); + NullableNativeMemory.free(entry); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @@ -95,6 +97,27 @@ protected UninterruptibleEntry insertEntry(UninterruptibleEntry valueOnStack) { return WordFactory.nullPointer(); } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public boolean remove(UninterruptibleEntry valueOnStack) { + int index = Integer.remainderUnsigned(valueOnStack.getHash(), DEFAULT_TABLE_LENGTH); + UninterruptibleEntry entry = table[index]; + UninterruptibleEntry prev = WordFactory.nullPointer(); + while (entry.isNonNull()) { + if (isEqual(valueOnStack, entry)) { + if (prev.isNull()) { + table[index] = entry.getNext(); + } else { + prev.setNext(entry.getNext()); + } + free(entry); + return true; + } + prev = entry; + entry = entry.getNext(); + } + return false; + } + @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public int getSize() { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/GrowableWordArrayAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/GrowableWordArrayAccess.java index f9d7e0e3e337..6b3142d625bd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/GrowableWordArrayAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/GrowableWordArrayAccess.java @@ -24,16 +24,17 @@ */ package com.oracle.svm.core.collections; -import jdk.graal.compiler.api.replacements.Fold; -import jdk.graal.compiler.word.Word; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.type.WordPointer; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.WordFactory; import com.oracle.svm.core.UnmanagedMemoryUtil; import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.memory.NullableNativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; + +import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.word.Word; public class GrowableWordArrayAccess { private static final int INITIAL_CAPACITY = 10; @@ -49,8 +50,8 @@ public static Word get(GrowableWordArray array, int i) { return array.getData().addressOf(i).read(); } - public static boolean add(GrowableWordArray array, Word element) { - if (array.getSize() == array.getCapacity() && !grow(array)) { + public static boolean add(GrowableWordArray array, Word element, NmtCategory nmtCategory) { + if (array.getSize() == array.getCapacity() && !grow(array, nmtCategory)) { return false; } @@ -61,14 +62,14 @@ public static boolean add(GrowableWordArray array, Word element) { public static void freeData(GrowableWordArray array) { if (array.isNonNull()) { - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(array.getData()); + NullableNativeMemory.free(array.getData()); array.setData(WordFactory.nullPointer()); array.setSize(0); array.setCapacity(0); } } - private static boolean grow(GrowableWordArray array) { + private static boolean grow(GrowableWordArray array, NmtCategory nmtCategory) { int newCapacity = computeNewCapacity(array); if (newCapacity < 0) { /* Overflow. */ @@ -77,13 +78,13 @@ private static boolean grow(GrowableWordArray array) { assert newCapacity >= INITIAL_CAPACITY; WordPointer oldData = array.getData(); - WordPointer newData = ImageSingletons.lookup(UnmanagedMemorySupport.class).malloc(WordFactory.unsigned(newCapacity).multiply(wordSize())); + WordPointer newData = NullableNativeMemory.malloc(WordFactory.unsigned(newCapacity).multiply(wordSize()), nmtCategory); if (newData.isNull()) { return false; } UnmanagedMemoryUtil.copyForward((Pointer) oldData, (Pointer) newData, WordFactory.unsigned(array.getSize()).multiply(wordSize())); - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(oldData); + NullableNativeMemory.free(oldData); array.setData(newData); array.setCapacity(newCapacity); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/headers/LibC.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/headers/LibC.java index 663a97993d84..65681458609d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/headers/LibC.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/headers/LibC.java @@ -35,6 +35,7 @@ import jdk.graal.compiler.api.replacements.Fold; +/** Platform-independent LibC support. */ public class LibC { public static final int EXIT_CODE_ABORT = 99; @@ -68,26 +69,6 @@ public static T memset(T s, SignedWord c, UnsignedWord n return libc().memset(s, c, n); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static T malloc(UnsignedWord size) { - return libc().malloc(size); - } - - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static T calloc(UnsignedWord nmemb, UnsignedWord size) { - return libc().calloc(nmemb, size); - } - - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static T realloc(PointerBase ptr, UnsignedWord size) { - return libc().realloc(ptr, size); - } - - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static void free(PointerBase ptr) { - libc().free(ptr); - } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static void exit(int status) { libc().exit(status); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/headers/LibCSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/headers/LibCSupport.java index 6965ee9b356d..b7ed0b46fdae 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/headers/LibCSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/headers/LibCSupport.java @@ -31,7 +31,9 @@ import org.graalvm.word.UnsignedWord; import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.memory.NativeMemory; +/** Platform-independent LibC support. Don't use this class directly, use {@link LibC} instead. */ public interface LibCSupport { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) int errno(); @@ -51,15 +53,19 @@ public interface LibCSupport { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) T memset(T s, SignedWord c, UnsignedWord n); + /** Don't call this directly, see {@link NativeMemory} for more details. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) T malloc(UnsignedWord size); + /** Don't call this directly, see {@link NativeMemory} for more details. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) T calloc(UnsignedWord nmemb, UnsignedWord size); + /** Don't call this directly, see {@link NativeMemory} for more details. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) T realloc(PointerBase ptr, UnsignedWord size); + /** Don't call this directly, see {@link NativeMemory} for more details. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) void free(PointerBase ptr); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceMapEncoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceMapEncoder.java index e1f987790055..edaf84a07a39 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceMapEncoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceMapEncoder.java @@ -33,13 +33,14 @@ import java.util.PrimitiveIterator; import java.util.Set; -import jdk.graal.compiler.core.common.util.TypeConversion; -import jdk.graal.compiler.core.common.util.UnsafeArrayTypeWriter; - import com.oracle.svm.core.c.NonmovableArray; import com.oracle.svm.core.c.NonmovableArrays; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.util.ByteArrayReader; +import jdk.graal.compiler.core.common.util.TypeConversion; +import jdk.graal.compiler.core.common.util.UnsafeArrayTypeWriter; + public abstract class ReferenceMapEncoder { public interface OffsetIterator extends PrimitiveIterator.OfInt { @Override @@ -99,7 +100,7 @@ public NonmovableArray encodeAll() { encodeAll(sortedEntries); int length = TypeConversion.asS4(writeBuffer.getBytesWritten()); - NonmovableArray array = NonmovableArrays.createByteArray(length); + NonmovableArray array = NonmovableArrays.createByteArray(length, NmtCategory.Code); writeBuffer.toByteBuffer(NonmovableArrays.asByteBuffer(array)); return array; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpMetadata.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpMetadata.java index 04ef357f2423..942812612c05 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpMetadata.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpMetadata.java @@ -24,7 +24,6 @@ */ package com.oracle.svm.core.heap.dump; -import jdk.graal.compiler.api.replacements.Fold; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -34,7 +33,6 @@ import org.graalvm.nativeimage.c.struct.RawStructure; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CCharPointer; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; @@ -47,11 +45,15 @@ import com.oracle.svm.core.heap.ObjectVisitor; import com.oracle.svm.core.heap.UnknownObjectField; import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.memory.NullableNativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.util.coder.ByteStream; import com.oracle.svm.core.util.coder.ByteStreamAccess; import com.oracle.svm.core.util.coder.NativeCoder; import com.oracle.svm.core.util.coder.Pack200Coder; +import jdk.graal.compiler.api.replacements.Fold; + /** * Provides access to the encoded heap dump metadata that was prepared at image build-time. * @@ -140,19 +142,19 @@ public boolean initialize() { * structures so that we can abort right away in case that an allocation fails. */ UnsignedWord classInfosSize = WordFactory.unsigned(classInfoCount).multiply(SizeOf.get(ClassInfo.class)); - classInfos = ImageSingletons.lookup(UnmanagedMemorySupport.class).calloc(classInfosSize); + classInfos = NullableNativeMemory.calloc(classInfosSize, NmtCategory.HeapDump); if (classInfos.isNull()) { return false; } UnsignedWord fieldStartsSize = WordFactory.unsigned(totalFieldCount).multiply(SizeOf.get(FieldInfoPointer.class)); - fieldInfoTable = ImageSingletons.lookup(UnmanagedMemorySupport.class).calloc(fieldStartsSize); + fieldInfoTable = NullableNativeMemory.calloc(fieldStartsSize, NmtCategory.HeapDump); if (fieldInfoTable.isNull()) { return false; } UnsignedWord fieldNameTableSize = WordFactory.unsigned(fieldNameCount).multiply(SizeOf.get(FieldNamePointer.class)); - fieldNameTable = ImageSingletons.lookup(UnmanagedMemorySupport.class).calloc(fieldNameTableSize); + fieldNameTable = NullableNativeMemory.calloc(fieldNameTableSize, NmtCategory.HeapDump); if (fieldNameTable.isNull()) { return false; } @@ -214,13 +216,13 @@ public boolean initialize() { * Must always be called, regardless if {@link #initialize} returned true or false. */ public void teardown() { - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(classInfos); + NullableNativeMemory.free(classInfos); classInfos = WordFactory.nullPointer(); - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(fieldInfoTable); + NullableNativeMemory.free(fieldInfoTable); fieldInfoTable = WordFactory.nullPointer(); - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(fieldNameTable); + NullableNativeMemory.free(fieldNameTable); fieldNameTable = WordFactory.nullPointer(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java index 95cffbb587bd..4f6db2db3570 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java @@ -28,8 +28,6 @@ import java.io.IOException; -import jdk.graal.compiler.api.replacements.Fold; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.StackValue; @@ -37,7 +35,6 @@ import org.graalvm.nativeimage.c.struct.RawStructure; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CCharPointer; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.WordFactory; @@ -47,6 +44,7 @@ import com.oracle.svm.core.heap.RestrictHeapAccess; import com.oracle.svm.core.heap.VMOperationInfos; import com.oracle.svm.core.log.Log; +import com.oracle.svm.core.memory.UntrackedNullableNativeMemory; import com.oracle.svm.core.os.RawFileOperationSupport; import com.oracle.svm.core.os.RawFileOperationSupport.FileCreationMode; import com.oracle.svm.core.os.RawFileOperationSupport.RawFileDescriptor; @@ -55,6 +53,8 @@ import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.util.TimeUtils; +import jdk.graal.compiler.api.replacements.Fold; + public class HeapDumpSupportImpl extends HeapDumping { private final HeapDumpWriter writer; private final HeapDumpOperation heapDumpOperation; @@ -77,7 +77,7 @@ public void initializeDumpHeapOnOutOfMemoryError() { @Override public void teardownDumpHeapOnOutOfMemoryError() { - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(outOfMemoryHeapDumpPath); + UntrackedNullableNativeMemory.free(outOfMemoryHeapDumpPath); outOfMemoryHeapDumpPath = WordFactory.nullPointer(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpWriter.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpWriter.java index 63338d5f2ee2..30fb293d6c67 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpWriter.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpWriter.java @@ -71,6 +71,7 @@ import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.LayoutEncoding; import com.oracle.svm.core.log.Log; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.os.BufferedFileOperationSupport; import com.oracle.svm.core.os.BufferedFileOperationSupport.BufferedFile; import com.oracle.svm.core.os.RawFileOperationSupport.RawFileDescriptor; @@ -443,7 +444,7 @@ private boolean dumpHeap0(RawFileDescriptor fd) { private boolean initialize(RawFileDescriptor fd) { assert topLevelRecordBegin == -1 && subRecordBegin == -1 && !error; - this.f = file().allocate(fd); + this.f = file().allocate(fd, NmtCategory.HeapDump); if (f.isNull()) { return false; } @@ -1340,7 +1341,7 @@ public void initialize(GrowableWordArray largeObjects) { @RestrictHeapAccess(access = NO_ALLOCATION, reason = "Heap dumping must not allocate.") public boolean visitObject(Object obj) { if (isLarge(obj)) { - boolean added = GrowableWordArrayAccess.add(largeObjects, Word.objectToUntrackedPointer(obj)); + boolean added = GrowableWordArrayAccess.add(largeObjects, Word.objectToUntrackedPointer(obj), NmtCategory.HeapDump); if (!added) { Log.log().string("Failed to add an element to the large object list. Heap dump will be incomplete.").newline(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java index a20bee43c883..0d8a683fcc28 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java @@ -29,9 +29,7 @@ import java.lang.reflect.Method; import java.security.ProtectionDomain; -import jdk.graal.compiler.nodes.extended.MembarNode; import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.impl.UnsafeMemorySupport; import org.graalvm.word.WordFactory; @@ -46,26 +44,30 @@ import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.LayoutEncoding; import com.oracle.svm.core.hub.PredefinedClassesSupport; +import com.oracle.svm.core.memory.NativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.os.VirtualMemoryProvider; import com.oracle.svm.core.util.VMError; +import jdk.graal.compiler.nodes.extended.MembarNode; + @TargetClass(className = "jdk.internal.misc.Unsafe") @SuppressWarnings({"static-method", "unused"}) final class Target_jdk_internal_misc_Unsafe_Core { @Substitute private long allocateMemory0(long bytes) { - return UnmanagedMemory.malloc(WordFactory.unsigned(bytes)).rawValue(); + return NativeMemory.malloc(WordFactory.unsigned(bytes), NmtCategory.Unsafe).rawValue(); } @Substitute private long reallocateMemory0(long address, long bytes) { - return UnmanagedMemory.realloc(WordFactory.unsigned(address), WordFactory.unsigned(bytes)).rawValue(); + return NativeMemory.realloc(WordFactory.unsigned(address), WordFactory.unsigned(bytes), NmtCategory.Unsafe).rawValue(); } @Substitute private void freeMemory0(long address) { - UnmanagedMemory.free(WordFactory.unsigned(address)); + NativeMemory.free(WordFactory.unsigned(address)); } @Substitute diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java index 6875c685d0c2..925f43e24b0f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java @@ -32,11 +32,8 @@ import java.util.TimeZone; import org.graalvm.collections.EconomicMap; -import jdk.graal.compiler.options.Option; -import jdk.graal.compiler.options.OptionKey; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.nativeimage.impl.InternalPlatform; @@ -51,9 +48,13 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.handles.PrimitiveArrayView; +import com.oracle.svm.core.memory.UntrackedNullableNativeMemory; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.util.VMError; +import jdk.graal.compiler.options.Option; +import jdk.graal.compiler.options.OptionKey; + /** * The following classes aim to provide full support for time zones for native-image. This * substitution is necessary due to the reliance on JAVA_HOME in the JDK. @@ -100,7 +101,7 @@ private static String getSystemTimeZoneID(String javaHome) { CCharPointer tzId = LibCHelper.SVM_FindJavaTZmd(tzMappingsPtr, contentLen); String result = CTypeConversion.toJavaString(tzId); // SVM_FindJavaTZmd returns a newly allocated string - UnmanagedMemory.free(tzId); + UntrackedNullableNativeMemory.free(tzId); return result; } finally { if (refContent != null) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrBufferAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrBufferAccess.java index 31da845e8aa1..8c3a4389bbf8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrBufferAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrBufferAccess.java @@ -24,19 +24,20 @@ */ package com.oracle.svm.core.jfr; -import jdk.graal.compiler.api.replacements.Fold; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.struct.SizeOf; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.memory.NullableNativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.util.UnsignedUtils; +import jdk.graal.compiler.api.replacements.Fold; + /** * Used to access the raw memory of a {@link JfrBuffer}. */ @@ -61,7 +62,7 @@ public static JfrBuffer allocate(JfrBufferType bufferType) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static JfrBuffer allocate(UnsignedWord dataSize, JfrBufferType bufferType) { UnsignedWord headerSize = JfrBufferAccess.getHeaderSize(); - JfrBuffer result = ImageSingletons.lookup(UnmanagedMemorySupport.class).malloc(headerSize.add(dataSize)); + JfrBuffer result = NullableNativeMemory.malloc(headerSize.add(dataSize), NmtCategory.JFR); if (result.isNonNull()) { result.setSize(dataSize); result.setBufferType(bufferType); @@ -74,7 +75,7 @@ public static JfrBuffer allocate(UnsignedWord dataSize, JfrBufferType bufferType @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static void free(JfrBuffer buffer) { - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(buffer); + NullableNativeMemory.free(buffer); } @Uninterruptible(reason = "Prevent safepoints as those could change the flushed position.") diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrBufferList.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrBufferList.java index 17c202d7693a..320a0922a66c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrBufferList.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrBufferList.java @@ -26,10 +26,8 @@ package com.oracle.svm.core.jfr; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.WordFactory; import com.oracle.svm.core.Uninterruptible; @@ -75,7 +73,7 @@ public void teardown() { } JfrBufferNode next = node.getNext(); - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(node); + JfrBufferNodeAccess.free(node); node = next; } head = WordFactory.nullPointer(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrBufferNodeAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrBufferNodeAccess.java index 759db4b69ce4..a72e5118208c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrBufferNodeAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrBufferNodeAccess.java @@ -27,14 +27,14 @@ package com.oracle.svm.core.jfr; import org.graalvm.nativeimage.CurrentIsolate; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CIntPointer; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.WordFactory; import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.memory.NullableNativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.thread.NativeSpinLockUtils; import com.oracle.svm.core.thread.VMOperation; @@ -47,7 +47,7 @@ private JfrBufferNodeAccess() { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static JfrBufferNode allocate(JfrBuffer buffer) { - JfrBufferNode node = ImageSingletons.lookup(UnmanagedMemorySupport.class).malloc(SizeOf.unsigned(JfrBufferNode.class)); + JfrBufferNode node = NullableNativeMemory.malloc(SizeOf.unsigned(JfrBufferNode.class), NmtCategory.JFR); if (node.isNonNull()) { node.setBuffer(buffer); node.setNext(WordFactory.nullPointer()); @@ -59,7 +59,7 @@ public static JfrBufferNode allocate(JfrBuffer buffer) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static void free(JfrBufferNode node) { - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(node); + NullableNativeMemory.free(node); } /** Should be used instead of {@link JfrBufferNode#getBuffer}. */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackTraceRepository.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackTraceRepository.java index 689c3c37f9a0..c8b98185ca65 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackTraceRepository.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackTraceRepository.java @@ -33,7 +33,6 @@ import org.graalvm.nativeimage.c.struct.RawStructure; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CIntPointer; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; @@ -51,6 +50,8 @@ import com.oracle.svm.core.jfr.traceid.JfrTraceIdEpoch; import com.oracle.svm.core.jfr.utils.JfrVisited; import com.oracle.svm.core.locks.VMMutex; +import com.oracle.svm.core.memory.NullableNativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.sampler.SamplerSampleWriter; import com.oracle.svm.core.sampler.SamplerSampleWriterData; import com.oracle.svm.core.sampler.SamplerSampleWriterDataAccess; @@ -193,7 +194,7 @@ private JfrStackTraceTableEntry getOrPutStackTrace0(Pointer start, UnsignedWord * the thread-local buffer to the C heap because the thread-local buffer will be * overwritten or freed at some point. */ - Pointer to = ImageSingletons.lookup(UnmanagedMemorySupport.class).malloc(size); + Pointer to = NullableNativeMemory.malloc(size, NmtCategory.JFR); if (to.isNonNull()) { UnmanagedMemoryUtil.copy(start, to, size); entry.setRawStackTrace(to); @@ -205,7 +206,8 @@ private JfrStackTraceTableEntry getOrPutStackTrace0(Pointer start, UnsignedWord } /* Hashtable entry allocation failed. */ - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(to); + NullableNativeMemory.free(to); + to = WordFactory.nullPointer(); } /* Some allocation failed. */ @@ -303,6 +305,11 @@ public interface JfrStackTraceTableEntry extends JfrVisited { public static final class JfrStackTraceTable extends AbstractUninterruptibleHashtable { private static long nextId; + @Platforms(Platform.HOSTED_ONLY.class) + JfrStackTraceTable() { + super(NmtCategory.JFR); + } + @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) protected JfrStackTraceTableEntry[] createTable(int size) { @@ -333,7 +340,7 @@ protected UninterruptibleEntry copyToHeap(UninterruptibleEntry valueOnStack) { protected void free(UninterruptibleEntry entry) { JfrStackTraceTableEntry stackTraceEntry = (JfrStackTraceTableEntry) entry; /* The base method will free only the entry itself, not the pointer with stacktrace. */ - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(stackTraceEntry.getRawStackTrace()); + NullableNativeMemory.free(stackTraceEntry.getRawStackTrace()); super.free(entry); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrSymbolRepository.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrSymbolRepository.java index 8949a12c50cb..a5b6f6bee8e9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrSymbolRepository.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrSymbolRepository.java @@ -24,8 +24,6 @@ */ package com.oracle.svm.core.jfr; -import jdk.graal.compiler.core.common.SuppressFBWarnings; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.StackValue; @@ -43,6 +41,10 @@ import com.oracle.svm.core.jdk.UninterruptibleUtils.CharReplacer; import com.oracle.svm.core.jfr.traceid.JfrTraceIdEpoch; import com.oracle.svm.core.locks.VMMutex; +import com.oracle.svm.core.nmt.NmtCategory; + +import jdk.graal.compiler.core.common.SuppressFBWarnings; +import jdk.graal.compiler.word.Word; /** * In Native Image, we use {@link java.lang.String} objects that live in the image heap as symbols. @@ -179,6 +181,11 @@ private interface JfrSymbol extends UninterruptibleEntry { private static class JfrSymbolHashtable extends AbstractUninterruptibleHashtable { private static long nextId; + @Platforms(Platform.HOSTED_ONLY.class) + JfrSymbolHashtable() { + super(NmtCategory.JFR); + } + @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) protected JfrSymbol[] createTable(int size) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/utils/JfrVisitedTable.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/utils/JfrVisitedTable.java index 0aa3e41ff71c..593c54d2d56f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/utils/JfrVisitedTable.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/utils/JfrVisitedTable.java @@ -25,13 +25,20 @@ package com.oracle.svm.core.jfr.utils; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.struct.SizeOf; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.collections.AbstractUninterruptibleHashtable; import com.oracle.svm.core.collections.UninterruptibleEntry; +import com.oracle.svm.core.nmt.NmtCategory; public final class JfrVisitedTable extends AbstractUninterruptibleHashtable { + @Platforms(Platform.HOSTED_ONLY.class) + public JfrVisitedTable() { + super(NmtCategory.JFR); + } @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIJavaVMList.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIJavaVMList.java index 24131c95471d..8966b02ff48c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIJavaVMList.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIJavaVMList.java @@ -29,8 +29,6 @@ import static org.graalvm.word.WordFactory.unsigned; import static org.graalvm.word.WordFactory.zero; -import jdk.graal.compiler.word.Word; -import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CIntPointer; import org.graalvm.nativeimage.c.type.WordPointer; @@ -42,6 +40,10 @@ import com.oracle.svm.core.c.CGlobalDataFactory; import com.oracle.svm.core.jni.headers.JNIJavaVM; import com.oracle.svm.core.jni.headers.JNIJavaVMPointer; +import com.oracle.svm.core.memory.NativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; + +import jdk.graal.compiler.word.Word; /** * A process-global, lock-free list of JavaVM pointers. Implemented as arrays in native memory which @@ -76,7 +78,7 @@ public static void addJavaVM(JNIJavaVM newEntry) { Pointer p = nextPointer.readWord(0); if (p.isNull()) { // No empty slots, create new array UnsignedWord newCapacity = capacity.notEqual(0) ? capacity.multiply(2) : INITIAL_CAPACITY; - Pointer newArray = UnmanagedMemory.calloc(newCapacity.add(2 /* capacity and next */).multiply(wordSize)); + Pointer newArray = NativeMemory.calloc(newCapacity.add(2 /* capacity and next */).multiply(wordSize), NmtCategory.JNI); newArray.writeWord(0, newCapacity); newArray.writeWord(wordSize, newEntry); p = nextPointer.compareAndSwapWord(0, nullPointer(), newArray, ANY_LOCATION); @@ -84,7 +86,7 @@ public static void addJavaVM(JNIJavaVM newEntry) { return; } // Another thread already created and linked a new array, continue in that array - UnmanagedMemory.free(newArray); + NativeMemory.free(newArray); } capacity = p.readWord(0); p = p.add(wordSize); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java index 60d6d3b04e1e..730742e77080 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIInvocationInterface.java @@ -24,8 +24,6 @@ */ package com.oracle.svm.core.jni.functions; -import jdk.graal.compiler.serviceprovider.IsolateUtil; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Isolate; import org.graalvm.nativeimage.LogHandler; import org.graalvm.nativeimage.StackValue; @@ -36,7 +34,6 @@ import org.graalvm.nativeimage.c.type.CCharPointerPointer; import org.graalvm.nativeimage.c.type.CIntPointer; import org.graalvm.nativeimage.c.type.WordPointer; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; @@ -73,6 +70,7 @@ import com.oracle.svm.core.jni.headers.JNIJavaVMPointer; import com.oracle.svm.core.jni.headers.JNIVersion; import com.oracle.svm.core.log.FunctionPointerLogHandler; +import com.oracle.svm.core.memory.UntrackedNullableNativeMemory; import com.oracle.svm.core.monitor.MonitorInflationCause; import com.oracle.svm.core.monitor.MonitorSupport; import com.oracle.svm.core.snippets.ImplicitExceptions; @@ -80,6 +78,8 @@ import com.oracle.svm.core.thread.PlatformThreads; import com.oracle.svm.core.util.Utf8; +import jdk.graal.compiler.serviceprovider.IsolateUtil; + /** * Implementation of the JNI invocation API for interacting with a Java VM without having an * existing context and without linking against the Java VM library beforehand. @@ -128,7 +128,7 @@ static int enter(JNIJavaVMPointer vmBuf, JNIEnvironmentPointer penv, JNIJavaVMIn int vmArgc = vmArgs.getNOptions(); if (vmArgc > 0) { UnsignedWord size = SizeOf.unsigned(CCharPointerPointer.class).multiply(vmArgc + 1); - CCharPointerPointer argv = ImageSingletons.lookup(UnmanagedMemorySupport.class).malloc(size); + CCharPointerPointer argv = UntrackedNullableNativeMemory.malloc(size); if (argv.isNull()) { return JNIErrors.JNI_ENOMEM(); } @@ -169,7 +169,7 @@ static int enter(JNIJavaVMPointer vmBuf, JNIEnvironmentPointer penv, JNIJavaVMIn int code = CEntryPointActions.enterCreateIsolate(params); if (params.isNonNull()) { - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(params.getArgv()); + UntrackedNullableNativeMemory.free(params.getArgv()); params = WordFactory.nullPointer(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/CHeapPerfMemoryProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/CHeapPerfMemoryProvider.java index d7ba95251947..ac1417b0e57b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/CHeapPerfMemoryProvider.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/CHeapPerfMemoryProvider.java @@ -26,11 +26,12 @@ import java.nio.ByteBuffer; -import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.word.Pointer; import org.graalvm.word.WordFactory; import com.oracle.svm.core.jdk.DirectByteBufferUtil; +import com.oracle.svm.core.memory.NativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; /** * Allocates a buffer with a minimal size that only contains the performance data header (see @@ -42,14 +43,14 @@ public class CHeapPerfMemoryProvider implements PerfMemoryProvider { @Override public ByteBuffer create() { int size = PerfMemoryPrologue.getPrologueSize(); - memory = UnmanagedMemory.calloc(size); + memory = NativeMemory.calloc(size, NmtCategory.JvmStat); return DirectByteBufferUtil.allocate(memory.rawValue(), size); } @Override public void teardown() { if (memory.isNonNull()) { - UnmanagedMemory.free(memory); + NativeMemory.free(memory); memory = WordFactory.nullPointer(); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/PerfMemory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/PerfMemory.java index 223f2481a1a3..75b4dfb96d96 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/PerfMemory.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/PerfMemory.java @@ -29,12 +29,10 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.word.LocationIdentity; import org.graalvm.word.Pointer; import org.graalvm.word.WordFactory; @@ -45,6 +43,10 @@ import com.oracle.svm.core.c.CGlobalDataFactory; import com.oracle.svm.core.jdk.DirectByteBufferUtil; import com.oracle.svm.core.jdk.Target_java_nio_Buffer; +import com.oracle.svm.core.memory.NativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; + +import jdk.graal.compiler.word.Word; /** * Provides access to the underlying OS-specific memory that stores the performance data. @@ -148,7 +150,7 @@ public synchronized Word allocate(int size) { if (used + size >= capacity) { PerfMemoryPrologue.addOverflow(rawMemory, size); // Always return a valid pointer (external tools won't see this memory though). - Word result = UnmanagedMemory.calloc(size); + Word result = NativeMemory.calloc(size, NmtCategory.JvmStat); addOverflowMemory(result); return result; } @@ -198,7 +200,7 @@ public void teardown() { if (overflowMemory != null) { for (int i = 0; i < overflowMemory.length; i++) { - UnmanagedMemory.free(overflowMemory[i]); + NativeMemory.free(overflowMemory[i]); } overflowMemory = null; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/memory/NativeMemory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/memory/NativeMemory.java new file mode 100644 index 000000000000..31929d623cd1 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/memory/NativeMemory.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, Red Hat Inc. 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.memory; + +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; + +import org.graalvm.nativeimage.UnmanagedMemory; +import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; +import org.graalvm.word.PointerBase; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.nmt.NmtCategory; + +/** + * Internal API for managing native memory. This class supports native memory tracking (NMT) and is + * therefore preferred over the public API class {@link UnmanagedMemory} and its + * {@link UnmanagedMemorySupport implementations}. + * + * All methods that allocate native memory throw an {@link OutOfMemoryError} if the memory + * allocation fails. If native memory needs to be allocated from uninterruptible code, use + * {@link NullableNativeMemory} instead. + * + * Note that NMT may cause segfaults in certain scenarios: + *
    + *
  • Native memory that was allocated outside of Java (e.g., in a C library) or via some API that + * does not support NMT (e.g., {@link UnmanagedMemory}) may only be freed with + * {@link UntrackedNullableNativeMemory#free}. This is necessary because NMT assumes that each + * allocation has a custom header, which isn't true for such memory.
  • + *
  • NMT accesses the image heap. If native memory needs to be allocated before the image heap is + * mapped or after it was unmapped, {@link UntrackedNullableNativeMemory} must be used.
  • + *
+ */ +public class NativeMemory { + /** + * Allocates {@code size} bytes of native memory. The content of the memory is undefined. + *

+ * This method never returns a null pointer but instead throws an {@link OutOfMemoryError} when + * allocation fails. + */ + public static T malloc(UnsignedWord size, NmtCategory category) { + T result = NullableNativeMemory.malloc(size, category); + if (result.isNull()) { + throw new OutOfMemoryError("Memory allocation failed: malloc returned null."); + } + return result; + } + + /** + * Allocates {@code size} bytes of native memory. The content of the memory is undefined. + *

+ * This method never returns a null pointer but instead throws an {@link OutOfMemoryError} when + * allocation fails. + */ + public static T malloc(int size, NmtCategory category) { + assert size >= 0; + return malloc(WordFactory.unsigned(size), category); + } + + /** + * Allocates {@code size} bytes of native memory. The content of the memory is set to 0. + *

+ * This method never returns a null pointer, but instead throws an {@link OutOfMemoryError} when + * allocation fails. + */ + public static T calloc(UnsignedWord size, NmtCategory category) { + T result = NullableNativeMemory.calloc(size, category); + if (result.isNull()) { + throw new OutOfMemoryError("Memory allocation failed: calloc returned null."); + } + return result; + } + + /** + * Allocates {@code size} bytes of native memory. The content of the memory is set to 0. + *

+ * This method never returns a null pointer, but instead throws an {@link OutOfMemoryError} when + * allocation fails. + */ + public static T calloc(int size, NmtCategory category) { + assert size >= 0; + return calloc(WordFactory.unsigned(size), category); + } + + /** + * Changes the size of the provided native memory to {@code size} bytes. If the new size is + * larger than the old size, the content of the additional memory is undefined. + *

+ * This method never returns a null pointer, but instead throws an {@link OutOfMemoryError} when + * allocation fails. In that case, the old data is not deallocated and remains unchanged. + */ + public static T realloc(T ptr, UnsignedWord size, NmtCategory category) { + T result = NullableNativeMemory.realloc(ptr, size, category); + if (result.isNull()) { + throw new OutOfMemoryError("Memory allocation failed: realloc returned null."); + } + return result; + } + + /** + * Changes the size of the provided native memory to {@code size} bytes. If the new size is + * larger than the old size, the content of the additional memory is undefined. + *

+ * This method never returns a null pointer, but instead throws an {@link OutOfMemoryError} when + * allocation fails. In that case, the old data is not deallocated and remains unchanged. + */ + public static T realloc(T ptr, int size, NmtCategory category) { + assert size >= 0; + return realloc(ptr, WordFactory.unsigned(size), category); + } + + /** + * Frees native memory that was previously allocated using methods of this class. This method is + * a no-op if the given pointer is {@code null}. + *

+ * Note that this method must NOT be used to free memory that was allocated via other + * classes (e.g., {@link UnmanagedMemorySupport}) or outside of Native Image code (e.g., in a C + * library). + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static void free(PointerBase ptr) { + NullableNativeMemory.free(ptr); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/memory/NullableNativeMemory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/memory/NullableNativeMemory.java new file mode 100644 index 000000000000..7fa50b7615f6 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/memory/NullableNativeMemory.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, Red Hat Inc. 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.memory; + +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; + +import org.graalvm.nativeimage.UnmanagedMemory; +import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; +import org.graalvm.word.PointerBase; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.VMInspectionOptions; +import com.oracle.svm.core.nmt.NativeMemoryTracking; +import com.oracle.svm.core.nmt.NmtCategory; +import com.oracle.svm.core.nmt.NmtMallocHeader; + +/** + * Internal API for managing native memory. This class supports native memory tracking (NMT) and is + * therefore preferred over the public API class {@link UnmanagedMemory} and its + * {@link UnmanagedMemorySupport implementations}. + * + * All methods that allocate native memory return {@code null} if the memory allocation fails. + */ +public class NullableNativeMemory { + /** + * Allocates {@code size} bytes of native memory. The content of the memory is undefined. + *

+ * This method returns a null pointer when allocation fails. + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static T malloc(UnsignedWord size, NmtCategory category) { + T outerPointer = UntrackedNullableNativeMemory.malloc(getAllocationSize(size)); + return track(outerPointer, size, category); + } + + /** + * Allocates {@code size} bytes of native memory. The content of the memory is undefined. + *

+ * This method returns a null pointer when allocation fails. + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static T malloc(int size, NmtCategory category) { + assert size >= 0; + return malloc(WordFactory.unsigned(size), category); + } + + /** + * Allocates {@code size} bytes of native memory. The content of the memory is set to 0. + *

+ * This method returns a null pointer when allocation fails. + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static T calloc(UnsignedWord size, NmtCategory category) { + T outerPointer = UntrackedNullableNativeMemory.calloc(getAllocationSize(size)); + return track(outerPointer, size, category); + } + + /** + * Allocates {@code size} bytes of native memory. The content of the memory is set to 0. + *

+ * This method returns a null pointer when allocation fails. + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static T calloc(int size, NmtCategory category) { + assert size >= 0; + return calloc(WordFactory.unsigned(size), category); + } + + /** + * Changes the size of the provided native memory to {@code size} bytes. If the new size is + * larger than the old size, the content of the additional memory is undefined. + *

+ * This method returns a null pointer when allocation fails. In that case, the old data is not + * deallocated and remains unchanged. + */ + @SuppressWarnings("unchecked") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static T realloc(T ptr, UnsignedWord size, NmtCategory category) { + if (ptr.isNull()) { + return malloc(size, category); + } else if (!VMInspectionOptions.hasNativeMemoryTrackingSupport()) { + return UntrackedNullableNativeMemory.realloc(ptr, getAllocationSize(size)); + } + + /* Query the NMT information for the old allocation. */ + NmtMallocHeader header = NativeMemoryTracking.getHeader(ptr); + T oldOuterPointer = (T) header; + int oldCategory = header.getCategory(); + UnsignedWord oldSize = header.getAllocationSize(); + + /* Try to realloc. */ + T newOuterPointer = UntrackedNullableNativeMemory.realloc(oldOuterPointer, getAllocationSize(size)); + if (newOuterPointer.isNull()) { + return WordFactory.nullPointer(); + } + + oldOuterPointer = WordFactory.nullPointer(); + + /* Only untrack the old block if the allocation was successful. */ + NativeMemoryTracking.singleton().untrack(oldSize, oldCategory); + return track(newOuterPointer, size, category); + } + + /** + * Changes the size of the provided native memory to {@code size} bytes. If the new size is + * larger than the old size, the content of the additional memory is undefined. + *

+ * This method returns a null pointer when allocation fails. In that case, the old data is not + * deallocated and remains unchanged. + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static T realloc(T ptr, int size, NmtCategory category) { + assert size >= 0; + return realloc(ptr, WordFactory.unsigned(size), category); + } + + /** + * Frees native memory that was previously allocated using methods of this class. This method is + * a no-op if the given pointer is {@code null}. + *

+ * Note that this method must NOT be used to free memory that was allocated via other + * classes (e.g., {@link UnmanagedMemorySupport}) or outside of Native Image code (e.g., in a C + * library). + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static void free(PointerBase ptr) { + if (ptr.isNull()) { + return; + } + + PointerBase outerPtr = untrack(ptr); + UntrackedNullableNativeMemory.free(outerPtr); + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private static UnsignedWord getAllocationSize(UnsignedWord size) { + if (VMInspectionOptions.hasNativeMemoryTrackingSupport()) { + return size.add(NativeMemoryTracking.sizeOfNmtHeader()); + } + return size; + } + + @SuppressWarnings("unchecked") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private static T track(T outerPtr, UnsignedWord size, NmtCategory category) { + if (VMInspectionOptions.hasNativeMemoryTrackingSupport() && outerPtr.isNonNull()) { + T innerPtr = (T) NativeMemoryTracking.singleton().initializeHeader(outerPtr, size, category); + NativeMemoryTracking.singleton().track(innerPtr); + return innerPtr; + } + return outerPtr; + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private static PointerBase untrack(PointerBase innerPtr) { + if (VMInspectionOptions.hasNativeMemoryTrackingSupport()) { + return NativeMemoryTracking.singleton().untrack(innerPtr); + } + return innerPtr; + } +} diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UnmanagedMemorySupportImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/memory/UnmanagedMemorySupportImpl.java similarity index 59% rename from substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UnmanagedMemorySupportImpl.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/memory/UnmanagedMemorySupportImpl.java index 6c56453a7466..9c41d7ce1b7c 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UnmanagedMemorySupportImpl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/memory/UnmanagedMemorySupportImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, 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 @@ -22,40 +22,68 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.posix; +package com.oracle.svm.core.memory; +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; import com.oracle.svm.core.Uninterruptible; -import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; -import com.oracle.svm.core.headers.LibC; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.headers.LibCSupport; +import com.oracle.svm.core.util.UnsignedUtils; -@AutomaticallyRegisteredImageSingleton(UnmanagedMemorySupport.class) +import jdk.graal.compiler.api.replacements.Fold; + +/** + * Delegates to the libc-specific memory management functions. Some platforms use a different + * implementation. + */ class UnmanagedMemorySupportImpl implements UnmanagedMemorySupport { @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public T malloc(UnsignedWord size) { - return LibC.malloc(size); + /* + * Some libc implementations may return a nullptr when the size is 0. We use a minimum size + * of 1 to ensure that we always get a unique pointer if the allocation succeeds. + */ + UnsignedWord allocationSize = UnsignedUtils.max(size, WordFactory.unsigned(1)); + return libc().malloc(allocationSize); } @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public T calloc(UnsignedWord size) { - return LibC.calloc(WordFactory.unsigned(1), size); + return libc().calloc(WordFactory.unsigned(1), size); } @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public T realloc(T ptr, UnsignedWord size) { - return LibC.realloc(ptr, size); + return libc().realloc(ptr, size); } @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public void free(PointerBase ptr) { - LibC.free(ptr); + libc().free(ptr); + } + + @Fold + static LibCSupport libc() { + return ImageSingletons.lookup(LibCSupport.class); + } +} + +@AutomaticallyRegisteredFeature +class UnmanagedMemorySupportFeature implements InternalFeature { + @Override + public void duringSetup(DuringSetupAccess access) { + if (!ImageSingletons.contains(UnmanagedMemorySupport.class)) { + ImageSingletons.add(UnmanagedMemorySupport.class, new UnmanagedMemorySupportImpl()); + } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/memory/UntrackedNullableNativeMemory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/memory/UntrackedNullableNativeMemory.java new file mode 100644 index 000000000000..d05fa8041444 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/memory/UntrackedNullableNativeMemory.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2024, 2024, 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.memory; + +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; +import org.graalvm.word.PointerBase; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.Uninterruptible; + +import jdk.graal.compiler.api.replacements.Fold; + +/** + * Manages native memory. This class explicitly does NOT support native memory tracking + * (NMT). It can therefore be used for the following use cases: + *

    + *
  • Allocate native memory that is later freed outside of Native Image code (e.g., in a C + * library).
  • + *
  • Free native memory that was allocated outside of Native Image code (e.g., in a C + * library).
  • + *
+ */ +public class UntrackedNullableNativeMemory { + /** + * Allocates {@code size} bytes of native memory. The content of the memory is undefined. + *

+ * This method returns a null pointer when allocation fails. + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static T malloc(UnsignedWord size) { + return memory().malloc(size); + } + + /** + * Allocates {@code size} bytes of native memory. The content of the memory is undefined. + *

+ * This method returns a null pointer when allocation fails. + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static T malloc(int size) { + assert size >= 0; + return malloc(WordFactory.unsigned(size)); + } + + /** + * Allocates {@code size} bytes of native memory. The content of the memory is set to 0. + *

+ * This method returns a null pointer when allocation fails. + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static T calloc(UnsignedWord size) { + return memory().calloc(size); + } + + /** + * Allocates {@code size} bytes of native memory. The content of the memory is set to 0. + *

+ * This method returns a null pointer when allocation fails. + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static T calloc(int size) { + assert size >= 0; + return calloc(WordFactory.unsigned(size)); + } + + /** + * Changes the size of the provided native memory to {@code size} bytes. If the new size is + * larger than the old size, the content of the additional memory is undefined. + *

+ * This method returns a null pointer when allocation fails. In that case, the old data is not + * deallocated and remains unchanged. + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static T realloc(T ptr, UnsignedWord size) { + return memory().realloc(ptr, size); + } + + /** + * Changes the size of the provided native memory to {@code size} bytes. If the new size is + * larger than the old size, the content of the additional memory is undefined. + *

+ * This method returns a null pointer when allocation fails. In that case, the old data is not + * deallocated and remains unchanged. + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static T realloc(T ptr, int size) { + assert size >= 0; + return realloc(ptr, WordFactory.unsigned(size)); + } + + /** + * Frees native memory that was previously allocated using methods of this class or that was + * allocated outside of Java (e.g., in a C library). This method is a no-op if the given pointer + * is {@code null}. + *

+ * Note that this method must NOT be used to free memory that was allocated via classes + * that support native memory tracking (e.g., {@link NativeMemory} or + * {@link NullableNativeMemory}). + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static void free(PointerBase ptr) { + memory().free(ptr); + } + + @Fold + static UnmanagedMemorySupport memory() { + return ImageSingletons.lookup(UnmanagedMemorySupport.class); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NativeMemoryTracking.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NativeMemoryTracking.java new file mode 100644 index 000000000000..616cbe1944d0 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NativeMemoryTracking.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, Red Hat Inc. 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.nmt; + +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.c.struct.SizeOf; +import org.graalvm.word.Pointer; +import org.graalvm.word.PointerBase; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.VMInspectionOptions; +import com.oracle.svm.core.jdk.RuntimeSupport; +import com.oracle.svm.core.memory.NativeMemory; +import com.oracle.svm.core.util.UnsignedUtils; + +import jdk.graal.compiler.api.replacements.Fold; + +/** + * This class implements native memory tracking (NMT). There are two components to NMT: tracking + * memory allocations (malloc/realloc/calloc), and tracking virtual memory usage (not supported + * yet). + * + * For tracking memory allocations, we have an internal API (see {@link NativeMemory}) that adds a + * custom {@link NmtMallocHeader header} to each allocation if NMT is enabled. This header stores + * data that is needed to properly untrack the memory when it is freed. + */ +public class NativeMemoryTracking { + private static final UnsignedWord ALIGNMENT = WordFactory.unsigned(16); + private static final int MAGIC = 0xF0F1F2F3; + private final NmtMallocMemorySnapshot mallocMemorySnapshot = new NmtMallocMemorySnapshot(); + + @Platforms(Platform.HOSTED_ONLY.class) + public NativeMemoryTracking() { + } + + @Fold + public static NativeMemoryTracking singleton() { + return ImageSingletons.lookup(NativeMemoryTracking.class); + } + + @Fold + public static UnsignedWord sizeOfNmtHeader() { + /* + * Align the header to 16 bytes to preserve platform-specific malloc alignments up to 16 + * bytes (i.e., the allocation payload is aligned to 16 bytes if the platform-specific + * malloc implementation returns a pointer that is aligned to at least 16 bytes). + */ + return UnsignedUtils.roundUp(SizeOf.unsigned(NmtMallocHeader.class), ALIGNMENT); + } + + /** + * Initializes the NMT header and returns a pointer to the allocation payload (i.e., the inner + * pointer). + */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + @SuppressWarnings("static-method") + public Pointer initializeHeader(PointerBase outerPtr, UnsignedWord size, NmtCategory category) { + NmtMallocHeader mallocHeader = (NmtMallocHeader) outerPtr; + mallocHeader.setAllocationSize(size); + mallocHeader.setCategory(category.ordinal()); + assert setMagic(mallocHeader); + return getInnerPointer(mallocHeader); + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private static boolean setMagic(NmtMallocHeader mallocHeader) { + mallocHeader.setMagic(MAGIC); + return true; + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public void track(PointerBase innerPtr) { + if (innerPtr.isNull()) { + return; + } + + NmtMallocHeader header = getHeader(innerPtr); + UnsignedWord nmtHeaderSize = sizeOfNmtHeader(); + UnsignedWord allocationSize = header.getAllocationSize(); + UnsignedWord totalSize = allocationSize.add(nmtHeaderSize); + + mallocMemorySnapshot.getInfoByCategory(header.getCategory()).track(allocationSize); + mallocMemorySnapshot.getInfoByCategory(NmtCategory.NMT).track(nmtHeaderSize); + mallocMemorySnapshot.getTotalInfo().track(totalSize); + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public PointerBase untrack(PointerBase innerPtr) { + if (innerPtr.isNull()) { + return WordFactory.nullPointer(); + } + + NmtMallocHeader header = getHeader(innerPtr); + untrack(header.getAllocationSize(), header.getCategory()); + return header; + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public void untrack(UnsignedWord size, int category) { + mallocMemorySnapshot.getInfoByCategory(category).untrack(size); + mallocMemorySnapshot.getInfoByCategory(NmtCategory.NMT).untrack(sizeOfNmtHeader()); + mallocMemorySnapshot.getTotalInfo().untrack(size.add(sizeOfNmtHeader())); + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static NmtMallocHeader getHeader(PointerBase innerPtr) { + NmtMallocHeader result = (NmtMallocHeader) ((Pointer) innerPtr).subtract(sizeOfNmtHeader()); + assert result.getMagic() == MAGIC : "bad NMT malloc header"; + return result; + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private static Pointer getInnerPointer(NmtMallocHeader mallocHeader) { + return ((Pointer) mallocHeader).add(sizeOfNmtHeader()); + } + + public long getUsedMemory(NmtCategory category) { + return mallocMemorySnapshot.getInfoByCategory(category).getUsed(); + } + + public static RuntimeSupport.Hook shutdownHook() { + return isFirstIsolate -> NativeMemoryTracking.singleton().printStatistics(); + } + + public void printStatistics() { + if (VMInspectionOptions.PrintNMTStatistics.getValue()) { + System.out.println(); + System.out.println("Native memory tracking"); + System.out.println(" Total used memory: " + mallocMemorySnapshot.getTotalInfo().getUsed() + " bytes"); + System.out.println(" Total alive allocations: " + mallocMemorySnapshot.getTotalInfo().getCount()); + + for (int i = 0; i < NmtCategory.values().length; i++) { + String name = NmtCategory.values()[i].getName(); + NmtMallocMemoryInfo info = mallocMemorySnapshot.getInfoByCategory(i); + + System.out.println(" " + name + " used memory: " + info.getUsed() + " bytes"); + System.out.println(" " + name + " alive allocations: " + info.getCount()); + } + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtCategory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtCategory.java new file mode 100644 index 000000000000..ff71cdfabaed --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtCategory.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, Red Hat Inc. 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.nmt; + +import com.oracle.svm.core.Uninterruptible; + +/** Categories for native memory tracking. */ +public enum NmtCategory { + /** JIT compiler. */ + Compiler("Compiler"), + /** JIT compiled code. */ + Code("Code"), + /** Garbage collector. */ + GC("GC"), + /** Heap dumping infrastructure. */ + HeapDump("Heap Dump"), + /** Java Flight Recorder. */ + JFR("JFR"), + /** Java Native Interface. */ + JNI("JNI"), + /** JVM stat / perf data. */ + JvmStat("jvmstat"), + /** NMT itself. */ + NMT("Native Memory Tracking"), + /** Profile-guided optimizations. */ + PGO("PGO"), + /** Threading. */ + Threading("Threading"), + /** Memory allocated via Unsafe. */ + Unsafe("Unsafe"), + + /** Some other, VM internal reason - avoid if possible, better to add a new category. */ + Internal("Internal"); + + private final String name; + + NmtCategory(String name) { + this.name = name; + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public String getName() { + return name; + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtFeature.java new file mode 100644 index 000000000000..9b7786681fe4 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtFeature.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, Red Hat Inc. 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.nmt; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.svm.core.VMInspectionOptions; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.jdk.RuntimeSupport; + +@AutomaticallyRegisteredFeature +public class NmtFeature implements InternalFeature { + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return VMInspectionOptions.hasNativeMemoryTrackingSupport(); + } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(NativeMemoryTracking.class, new NativeMemoryTracking()); + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + RuntimeSupport.getRuntimeSupport().addShutdownHook(NativeMemoryTracking.shutdownHook()); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtMallocHeader.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtMallocHeader.java new file mode 100644 index 000000000000..5f2462bb07b7 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtMallocHeader.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, Red Hat Inc. 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.nmt; + +import org.graalvm.nativeimage.c.struct.RawField; +import org.graalvm.nativeimage.c.struct.RawStructure; +import org.graalvm.word.PointerBase; +import org.graalvm.word.UnsignedWord; + +/** + * A "malloc header" stores metadata about the native allocation (malloc/calloc/realloc). To do + * this, a small amount of additional space is requested contiguous to the user allocation. This + * metadata is used to update the memory tracking once the block is freed. + */ +@RawStructure +public interface NmtMallocHeader extends PointerBase { + @RawField + UnsignedWord getAllocationSize(); + + @RawField + void setAllocationSize(UnsignedWord value); + + @RawField + int getCategory(); + + @RawField + void setCategory(int value); + + @RawField + int getMagic(); + + @RawField + void setMagic(int value); +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtMallocMemoryInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtMallocMemoryInfo.java new file mode 100644 index 000000000000..7a54c3b491ca --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtMallocMemoryInfo.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, Red Hat Inc. 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.nmt; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.word.UnsignedWord; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicLong; + +class NmtMallocMemoryInfo { + private final AtomicLong count = new AtomicLong(0); + private final AtomicLong used = new AtomicLong(0); + + @Platforms(Platform.HOSTED_ONLY.class) + NmtMallocMemoryInfo() { + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + void track(UnsignedWord allocationSize) { + count.incrementAndGet(); + used.addAndGet(allocationSize.rawValue()); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + void untrack(UnsignedWord allocationSize) { + long lastCount = count.decrementAndGet(); + long lastSize = used.addAndGet(-allocationSize.rawValue()); + assert lastSize >= 0 && lastCount >= 0; + } + + long getUsed() { + return used.get(); + } + + long getCount() { + return count.get(); + } +} diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUnmanagedMemorySupportImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtMallocMemorySnapshot.java similarity index 58% rename from substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUnmanagedMemorySupportImpl.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtMallocMemorySnapshot.java index 7ee93b64d4e7..d5a8bca94a31 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUnmanagedMemorySupportImpl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtMallocMemorySnapshot.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, Red Hat Inc. 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 @@ -22,40 +23,40 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.windows; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; -import org.graalvm.word.PointerBase; -import org.graalvm.word.UnsignedWord; -import org.graalvm.word.WordFactory; +package com.oracle.svm.core.nmt; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.Uninterruptible; -import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; -import com.oracle.svm.core.headers.LibC; -@AutomaticallyRegisteredImageSingleton(UnmanagedMemorySupport.class) -class WindowsUnmanagedMemorySupportImpl implements UnmanagedMemorySupport { - @Override - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public T malloc(UnsignedWord size) { - return LibC.malloc(size); +class NmtMallocMemorySnapshot { + private final NmtMallocMemoryInfo[] categories; + private final NmtMallocMemoryInfo total; + + @Platforms(Platform.HOSTED_ONLY.class) + NmtMallocMemorySnapshot() { + total = new NmtMallocMemoryInfo(); + categories = new NmtMallocMemoryInfo[NmtCategory.values().length]; + for (int i = 0; i < categories.length; i++) { + categories[i] = new NmtMallocMemoryInfo(); + } } - @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public T calloc(UnsignedWord size) { - return LibC.calloc(WordFactory.unsigned(1), size); + NmtMallocMemoryInfo getInfoByCategory(NmtCategory category) { + return getInfoByCategory(category.ordinal()); } - @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public T realloc(T ptr, UnsignedWord size) { - return LibC.realloc(ptr, size); + NmtMallocMemoryInfo getInfoByCategory(int category) { + assert category < categories.length; + return categories[category]; } - @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public void free(PointerBase ptr) { - LibC.free(ptr); + NmtMallocMemoryInfo getTotalInfo() { + return total; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractRawFileOperationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractRawFileOperationSupport.java index af9d461fb5c1..753e63da36d0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractRawFileOperationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractRawFileOperationSupport.java @@ -26,8 +26,6 @@ import java.io.File; -import jdk.graal.compiler.api.replacements.Fold; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -41,6 +39,9 @@ import com.oracle.svm.core.hub.LayoutEncoding; import com.oracle.svm.core.snippets.KnownIntrinsics; +import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.word.Word; + public abstract class AbstractRawFileOperationSupport implements RawFileOperationSupport { private final boolean useNativeByteOrder; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/BufferedFileOperationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/BufferedFileOperationSupport.java index f2638b6de817..c96df4dd61cb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/BufferedFileOperationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/BufferedFileOperationSupport.java @@ -26,15 +26,12 @@ import java.nio.ByteOrder; -import jdk.graal.compiler.api.replacements.Fold; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.struct.RawField; import org.graalvm.nativeimage.c.struct.RawStructure; import org.graalvm.nativeimage.c.struct.SizeOf; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; @@ -47,10 +44,15 @@ import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.LayoutEncoding; import com.oracle.svm.core.jdk.JavaLangSubstitutions; +import com.oracle.svm.core.memory.NullableNativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.os.BufferedFileOperationSupport.BufferedFileOperationSupportHolder; import com.oracle.svm.core.os.RawFileOperationSupport.RawFileDescriptor; import com.oracle.svm.core.snippets.KnownIntrinsics; +import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.word.Word; + /** * Provides buffered, OS-independent operations on files. Most of the code is implemented in a way * that it can be used from uninterruptible code. @@ -99,7 +101,7 @@ protected BufferedFileOperationSupport(boolean useNativeByteOrder) { * Returns a null pointer otherwise. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public BufferedFile allocate(RawFileDescriptor fd) { + public BufferedFile allocate(RawFileDescriptor fd, NmtCategory nmtCategory) { if (!rawFiles().isValid(fd)) { return WordFactory.nullPointer(); } @@ -110,7 +112,7 @@ public BufferedFile allocate(RawFileDescriptor fd) { /* Use a single allocation for the struct and the corresponding buffer. */ UnsignedWord totalSize = SizeOf.unsigned(BufferedFile.class).add(WordFactory.unsigned(BUFFER_SIZE)); - BufferedFile result = ImageSingletons.lookup(UnmanagedMemorySupport.class).malloc(totalSize); + BufferedFile result = NullableNativeMemory.malloc(totalSize, nmtCategory); if (result.isNull()) { return WordFactory.nullPointer(); } @@ -127,7 +129,7 @@ public BufferedFile allocate(RawFileDescriptor fd) { */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public void free(BufferedFile f) { - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(f); + NullableNativeMemory.free(f); } /** diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/RawFileOperationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/RawFileOperationSupport.java index 139af6afa1b6..5ce50b5673fd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/RawFileOperationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/RawFileOperationSupport.java @@ -26,17 +26,18 @@ import java.io.File; -import jdk.graal.compiler.api.replacements.Fold; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.type.CCharPointer; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.Pointer; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordBase; import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.memory.UntrackedNullableNativeMemory; import com.oracle.svm.core.os.AbstractRawFileOperationSupport.RawFileOperationSupportHolder; +import jdk.graal.compiler.api.replacements.Fold; + /** * Provides unbuffered, OS-independent operations on files. Most of the code is implemented in a way * that it can be used from uninterruptible code. @@ -75,7 +76,7 @@ static RawFileOperationSupport nativeByteOrder() { /** * Tries to allocate a platform-dependent C string for the given path. Note that the returned * value needs to be freed manually once it is no longer needed (see - * {@link UnmanagedMemorySupport#free}). + * {@link UntrackedNullableNativeMemory#free}). * * @return If the allocation is successful, a non-null value is returned. */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerBufferPool.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerBufferPool.java index a7d2221ff925..b5f9643c1ef0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerBufferPool.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerBufferPool.java @@ -28,7 +28,6 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; @@ -37,6 +36,8 @@ import com.oracle.svm.core.jfr.SubstrateJVM; import com.oracle.svm.core.jfr.sampler.JfrExecutionSampler; import com.oracle.svm.core.locks.VMMutex; +import com.oracle.svm.core.memory.NullableNativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; /** * Keeps track of {@link #availableBuffers available} and {@link #fullBuffers full} buffers. If @@ -160,7 +161,7 @@ private SamplerBuffer tryAllocateBuffer0() { UnsignedWord headerSize = SamplerBufferAccess.getHeaderSize(); UnsignedWord dataSize = WordFactory.unsigned(SubstrateJVM.getThreadLocal().getThreadLocalBufferSize()); - SamplerBuffer result = ImageSingletons.lookup(UnmanagedMemorySupport.class).malloc(headerSize.add(dataSize)); + SamplerBuffer result = NullableNativeMemory.malloc(headerSize.add(dataSize), NmtCategory.JFR); if (result.isNonNull()) { bufferCount++; result.setSize(dataSize); @@ -183,7 +184,7 @@ private boolean popAndFree() { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) private void free(SamplerBuffer buffer) { - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(buffer); + NullableNativeMemory.free(buffer); bufferCount--; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/PlatformThreads.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/PlatformThreads.java index ee0355ce92b1..3e33f5c0feed 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/PlatformThreads.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/PlatformThreads.java @@ -56,7 +56,6 @@ import org.graalvm.nativeimage.ObjectHandles; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.function.CEntryPoint; import org.graalvm.nativeimage.c.function.CEntryPointLiteral; import org.graalvm.nativeimage.c.function.CFunctionPointer; @@ -95,7 +94,9 @@ import com.oracle.svm.core.jfr.HasJfrSupport; import com.oracle.svm.core.locks.VMMutex; import com.oracle.svm.core.log.Log; +import com.oracle.svm.core.memory.NativeMemory; import com.oracle.svm.core.monitor.MonitorSupport; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.nodes.CFunctionEpilogueNode; import com.oracle.svm.core.nodes.CFunctionPrologueNode; import com.oracle.svm.core.stack.StackFrameVisitor; @@ -719,14 +720,14 @@ protected T prepareStart(Thread thread, int startDat T startData = WordFactory.nullPointer(); ObjectHandle threadHandle = WordFactory.zero(); try { - startData = UnmanagedMemory.malloc(startDataSize); + startData = NativeMemory.malloc(startDataSize, NmtCategory.Threading); threadHandle = ObjectHandles.getGlobal().create(thread); startData.setIsolate(CurrentIsolate.getIsolate()); startData.setThreadHandle(threadHandle); } catch (Throwable e) { if (startData.isNonNull()) { - UnmanagedMemory.free(startData); + freeStartData(startData); } if (threadHandle.notEqual(WordFactory.zero())) { ObjectHandles.getGlobal().destroy(threadHandle); @@ -784,7 +785,7 @@ private static void decrementNonDaemonThreadsAndNotify() { } protected static void freeStartData(ThreadStartData startData) { - UnmanagedMemory.free(startData); + NativeMemory.free(startData); } void startThread(Thread thread, long stackSize) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java index a0f5ae79647c..db22367483b3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java @@ -32,7 +32,6 @@ import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.c.function.CFunction; import org.graalvm.nativeimage.c.type.CCharPointer; -import org.graalvm.nativeimage.impl.UnmanagedMemorySupport; import org.graalvm.word.ComparableWord; import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; @@ -53,6 +52,7 @@ import com.oracle.svm.core.locks.VMLockSupport; import com.oracle.svm.core.locks.VMMutex; import com.oracle.svm.core.log.Log; +import com.oracle.svm.core.memory.UntrackedNullableNativeMemory; import com.oracle.svm.core.nodes.CFunctionEpilogueNode; import com.oracle.svm.core.nodes.CFunctionPrologueNode; import com.oracle.svm.core.threadlocal.FastThreadLocal; @@ -254,7 +254,7 @@ public IsolateThread allocateIsolateThread(int isolateThreadSize) { UnsignedWord alignment = WordFactory.unsigned(64); UnsignedWord memorySize = WordFactory.unsigned(isolateThreadSize).add(alignment); - Pointer memory = ImageSingletons.lookup(UnmanagedMemorySupport.class).calloc(memorySize); + Pointer memory = UntrackedNullableNativeMemory.calloc(memorySize); if (memory.isNull()) { return WordFactory.nullPointer(); } @@ -274,7 +274,7 @@ public void freeCurrentIsolateThread() { @Uninterruptible(reason = "Thread state no longer set up.") protected void freeIsolateThread(IsolateThread thread) { Pointer memory = unalignedIsolateThreadMemoryTL.get(thread); - ImageSingletons.lookup(UnmanagedMemorySupport.class).free(memory); + UntrackedNullableNativeMemory.free(memory); } /** diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolatedReferenceAdjuster.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolatedReferenceAdjuster.java index 453fbe54c1cf..664db15b0826 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolatedReferenceAdjuster.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolatedReferenceAdjuster.java @@ -25,7 +25,6 @@ package com.oracle.svm.graal.isolated; import org.graalvm.nativeimage.ObjectHandle; -import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; @@ -38,7 +37,9 @@ import com.oracle.svm.core.code.ReferenceAdjuster; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.handles.ThreadLocalHandles; +import com.oracle.svm.core.memory.NativeMemory; import com.oracle.svm.core.meta.DirectSubstrateObjectConstant; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.util.VMError; import jdk.vm.ci.meta.JavaConstant; @@ -98,9 +99,9 @@ public void setConstantTargetAt(PointerBase address, int length, JavaConstant co @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public NonmovableObjectArray copyOfObjectArray(T[] source) { + public NonmovableObjectArray copyOfObjectArray(T[] source, NmtCategory nmtCategory) { @SuppressWarnings("unchecked") - NonmovableObjectArray copy = NonmovableArrays.createObjectArray((Class) source.getClass(), source.length); + NonmovableObjectArray copy = NonmovableArrays.createObjectArray((Class) source.getClass(), source.length, nmtCategory); for (int i = 0; i < source.length; i++) { setObjectInArray(copy, i, source[i]); } @@ -114,14 +115,12 @@ public boolean isFinished() { } public ForeignIsolateReferenceAdjusterData exportData() { - ForeignIsolateReferenceAdjusterData data = UnmanagedMemory.malloc(SizeOf.get(ForeignIsolateReferenceAdjusterData.class)); + ForeignIsolateReferenceAdjusterData data = NativeMemory.malloc(SizeOf.get(ForeignIsolateReferenceAdjusterData.class), NmtCategory.Compiler); data.setCount(count); data.setAddresses(addresses); data.setHandles(handles); - NonmovableArrays.untrackUnmanagedArray(addresses); addresses = WordFactory.nullPointer(); - NonmovableArrays.untrackUnmanagedArray(handles); handles = WordFactory.nullPointer(); count = 0; @@ -129,9 +128,6 @@ public ForeignIsolateReferenceAdjusterData exportData() { } public static void adjustAndDispose(ForeignIsolateReferenceAdjusterData data, ThreadLocalHandles handleSet) { - NonmovableArrays.trackUnmanagedArray(data.getAddresses()); - NonmovableArrays.trackUnmanagedArray(data.getHandles()); - int count = data.getCount(); for (int i = 0; i < count; i++) { @SuppressWarnings("unchecked") @@ -143,7 +139,7 @@ public static void adjustAndDispose(ForeignIsolateRefer NonmovableArrays.releaseUnmanagedArray(data.getAddresses()); NonmovableArrays.releaseUnmanagedArray(data.getHandles()); - UnmanagedMemory.free(data); + NativeMemory.free(data); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @@ -166,8 +162,8 @@ private void growIfFull() { int oldSize = addresses.isNonNull() ? NonmovableArrays.lengthOf(addresses) : 0; if (count == oldSize) { int newSize = (oldSize != 0) ? (2 * oldSize) : 32; - NonmovableArray newAddresses = NonmovableArrays.createWordArray(newSize); - NonmovableArray newHandles = NonmovableArrays.createWordArray(newSize); + NonmovableArray newAddresses = NonmovableArrays.createWordArray(newSize, NmtCategory.Compiler); + NonmovableArray newHandles = NonmovableArrays.createWordArray(newSize, NmtCategory.Compiler); if (addresses.isNonNull()) { NonmovableArrays.arraycopy(addresses, 0, newAddresses, 0, oldSize); NonmovableArrays.releaseUnmanagedArray(addresses); diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolatedRuntimeCodeInstaller.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolatedRuntimeCodeInstaller.java index 9e1bb36ba2f5..2ad0fd3830fd 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolatedRuntimeCodeInstaller.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolatedRuntimeCodeInstaller.java @@ -24,10 +24,7 @@ */ package com.oracle.svm.graal.isolated; -import jdk.graal.compiler.code.CompilationResult; -import jdk.graal.compiler.core.common.CompilationIdentifier; import org.graalvm.nativeimage.IsolateThread; -import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.function.CEntryPoint; import org.graalvm.nativeimage.c.function.CodePointer; import org.graalvm.nativeimage.c.struct.SizeOf; @@ -40,11 +37,16 @@ import com.oracle.svm.core.code.RuntimeCodeInfoAccess; import com.oracle.svm.core.deopt.SubstrateInstalledCode; import com.oracle.svm.core.graal.meta.SharedRuntimeMethod; +import com.oracle.svm.core.memory.NativeMemory; import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.graal.meta.RuntimeCodeInstaller; import com.oracle.svm.graal.meta.SubstrateInstalledCodeImpl; import com.oracle.svm.graal.meta.SubstrateMethod; +import jdk.graal.compiler.code.CompilationResult; +import jdk.graal.compiler.core.common.CompilationIdentifier; + public final class IsolatedRuntimeCodeInstaller extends RuntimeCodeInstaller { /** @@ -114,23 +116,23 @@ private CodeInstallInfo doPrepareInstall() { id = proxy.getHandle(); } - CodeInstallInfo installInfo = UnmanagedMemory.malloc(SizeOf.get(CodeInstallInfo.class)); + CodeInstallInfo installInfo = NativeMemory.malloc(SizeOf.get(CodeInstallInfo.class), NmtCategory.Compiler); installInfo.setCodeInfo(codeInfo); installInfo.setAdjusterData(adjuster.exportData()); installInfo.setCompilationId(id); - IsolatedRuntimeMethodInfoAccess.untrackInCurrentIsolate(installInfo.getCodeInfo()); + IsolatedRuntimeMethodInfoAccess.untrackInCurrentIsolate(installInfo); return installInfo; } private static void installPrepared(SharedMethod method, CodeInstallInfo installInfo, SubstrateInstalledCode installedCode) { - IsolatedRuntimeMethodInfoAccess.startTrackingInCurrentIsolate(installInfo.getCodeInfo()); + IsolatedRuntimeMethodInfoAccess.startTrackingInCurrentIsolate(installInfo); IsolatedReferenceAdjuster.adjustAndDispose(installInfo.getAdjusterData(), IsolatedCompileClient.get().getHandleSet()); installInfo.setAdjusterData(WordFactory.nullPointer()); doInstallPrepared(method, installInfo.getCodeInfo(), installedCode); - UnmanagedMemory.free(installInfo); + NativeMemory.free(installInfo); } private final IsolateThread targetIsolate; diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolatedRuntimeMethodInfoAccess.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolatedRuntimeMethodInfoAccess.java index 54330d27bcbf..a2dbf8c5cf63 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolatedRuntimeMethodInfoAccess.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolatedRuntimeMethodInfoAccess.java @@ -25,25 +25,57 @@ package com.oracle.svm.graal.isolated; import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.VMInspectionOptions; import com.oracle.svm.core.c.NonmovableArray; import com.oracle.svm.core.c.NonmovableArrays; import com.oracle.svm.core.code.CodeInfo; import com.oracle.svm.core.code.InstalledCodeObserverSupport; import com.oracle.svm.core.code.RuntimeCodeInfoAccess; import com.oracle.svm.core.code.RuntimeCodeInfoMemory; +import com.oracle.svm.core.nmt.NativeMemoryTracking; final class IsolatedRuntimeMethodInfoAccess { - public static void startTrackingInCurrentIsolate(CodeInfo info) { - RuntimeCodeInfoAccess.forEachArray(info, TRACK_ACTION); + public static void startTrackingInCurrentIsolate(CodeInstallInfo installInfo) { + CodeInfo info = installInfo.getCodeInfo(); InstalledCodeObserverSupport.attachToCurrentIsolate(RuntimeCodeInfoAccess.getCodeObserverHandles(info)); RuntimeCodeInfoMemory.singleton().add(info); + + /* NonmovableArray tracking and native memory tracking. */ + RuntimeCodeInfoAccess.forEachArray(info, TRACK_ACTION); + + ForeignIsolateReferenceAdjusterData adjusterData = installInfo.getAdjusterData(); + NonmovableArrays.trackUnmanagedArray(adjusterData.getAddresses()); + NonmovableArrays.trackUnmanagedArray(adjusterData.getHandles()); + + if (VMInspectionOptions.hasNativeMemoryTrackingSupport()) { + NativeMemoryTracking.singleton().track(installInfo); + NativeMemoryTracking.singleton().track(installInfo.getAdjusterData()); + NativeMemoryTracking.singleton().track(installInfo.getCodeInfo()); + NativeMemoryTracking.singleton().track(adjusterData.getAddresses()); + NativeMemoryTracking.singleton().track(adjusterData.getHandles()); + } } - public static void untrackInCurrentIsolate(CodeInfo info) { + public static void untrackInCurrentIsolate(CodeInstallInfo installInfo) { + CodeInfo info = installInfo.getCodeInfo(); RuntimeCodeInfoMemory.singleton().remove(info); InstalledCodeObserverSupport.detachFromCurrentIsolate(RuntimeCodeInfoAccess.getCodeObserverHandles(info)); + + /* NonmovableArray tracking and native memory tracking. */ RuntimeCodeInfoAccess.forEachArray(info, UNTRACK_ACTION); + + ForeignIsolateReferenceAdjusterData adjusterData = installInfo.getAdjusterData(); + NonmovableArrays.untrackUnmanagedArray(adjusterData.getAddresses()); + NonmovableArrays.untrackUnmanagedArray(adjusterData.getHandles()); + + if (VMInspectionOptions.hasNativeMemoryTrackingSupport()) { + NativeMemoryTracking.singleton().untrack(installInfo); + NativeMemoryTracking.singleton().untrack(installInfo.getAdjusterData()); + NativeMemoryTracking.singleton().untrack(installInfo.getCodeInfo()); + NativeMemoryTracking.singleton().untrack(adjusterData.getAddresses()); + NativeMemoryTracking.singleton().untrack(adjusterData.getHandles()); + } } private static final RuntimeCodeInfoAccess.NonmovableArrayAction TRACK_ACTION = new RuntimeCodeInfoAccess.NonmovableArrayAction() { @@ -51,6 +83,9 @@ public static void untrackInCurrentIsolate(CodeInfo info) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public void apply(NonmovableArray array) { NonmovableArrays.trackUnmanagedArray(array); + if (VMInspectionOptions.hasNativeMemoryTrackingSupport()) { + NativeMemoryTracking.singleton().track(array); + } } }; private static final RuntimeCodeInfoAccess.NonmovableArrayAction UNTRACK_ACTION = new RuntimeCodeInfoAccess.NonmovableArrayAction() { @@ -58,6 +93,9 @@ public void apply(NonmovableArray array) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public void apply(NonmovableArray array) { NonmovableArrays.untrackUnmanagedArray(array); + if (VMInspectionOptions.hasNativeMemoryTrackingSupport()) { + NativeMemoryTracking.singleton().untrack(array); + } } }; diff --git a/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties b/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties index 095bac6acbb2..e1470477f070 100644 --- a/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties +++ b/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties @@ -8,5 +8,5 @@ Args= \ --features=com.oracle.svm.test.jfr.JfrTestFeature \ --add-opens=java.base/java.lang=ALL-UNNAMED \ --add-exports=org.graalvm.nativeimage.base/com.oracle.svm.util=ALL-UNNAMED \ - --enable-monitoring=jvmstat,jfr,jmxserver,jmxclient \ + --enable-monitoring=jvmstat,jfr,jmxserver,jmxclient,nmt \ -J--enable-preview \ No newline at end of file diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/nmt/NativeMemoryTrackingTests.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/nmt/NativeMemoryTrackingTests.java new file mode 100644 index 000000000000..7c0a5ce10e37 --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/nmt/NativeMemoryTrackingTests.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, Red Hat Inc. 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.test.nmt; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.graalvm.word.Pointer; +import org.graalvm.word.WordFactory; +import org.junit.Test; + +import com.oracle.svm.core.memory.NativeMemory; +import com.oracle.svm.core.nmt.NativeMemoryTracking; +import com.oracle.svm.core.nmt.NmtCategory; + +public class NativeMemoryTrackingTests { + private static final int ALLOCATION_SIZE = 1024 * 16; + private static final int REALLOC_SIZE = ALLOCATION_SIZE / 2; + + @Test + public void testMalloc() { + assertEquals(0, getUsedMemory()); + + Pointer ptr = NativeMemory.malloc(WordFactory.unsigned(ALLOCATION_SIZE), NmtCategory.Code); + assertEquals(ALLOCATION_SIZE, getUsedMemory()); + assertTrue(getUsedMemory() > 0); + + NativeMemory.free(ptr); + + assertEquals(0, getUsedMemory()); + } + + @Test + public void testCalloc() { + assertEquals(0, getUsedMemory()); + Pointer ptr = NativeMemory.calloc(WordFactory.unsigned(ALLOCATION_SIZE), NmtCategory.Code); + + assertEquals(ALLOCATION_SIZE, getUsedMemory()); + assertTrue(getUsedMemory() > 0); + + NativeMemory.free(ptr); + + assertEquals(0, getUsedMemory()); + } + + @Test + public void testRealloc() { + assertEquals(0, getUsedMemory()); + Pointer ptr = NativeMemory.malloc(WordFactory.unsigned(ALLOCATION_SIZE), NmtCategory.Code); + + assertEquals(getUsedMemory(), ALLOCATION_SIZE); + assertTrue(getUsedMemory() > 0); + + Pointer reallocPtr = NativeMemory.realloc(ptr, WordFactory.unsigned(REALLOC_SIZE), NmtCategory.Code); + + assertEquals(REALLOC_SIZE, getUsedMemory()); + + NativeMemory.free(reallocPtr); + assertEquals(0, getUsedMemory()); + } + + private static long getUsedMemory() { + return NativeMemoryTracking.singleton().getUsedMemory(NmtCategory.Code); + } +}