diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/attach/PosixAttachApiSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/attach/PosixAttachApiSupport.java index 749cae8306a0..4a967df3cce2 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/attach/PosixAttachApiSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/attach/PosixAttachApiSupport.java @@ -67,7 +67,7 @@ public static PosixAttachApiSupport singleton() { } @Override - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+18/src/hotspot/os/posix/attachListener_posix.cpp#L474-L490") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/posix/attachListener_posix.cpp#L344-L360") public void startup() { String path = getSocketFilePath(); try (CCharPointerHolder f = CTypeConversion.toCString(path)) { @@ -76,7 +76,7 @@ public void startup() { } @Override - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+18/src/hotspot/os/posix/attachListener_posix.cpp#L537-L568") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/posix/attachListener_posix.cpp#L409-L440") public boolean isInitTrigger() { String filename = ".attach_pid" + ProcessHandle.current().pid(); if (isInitTrigger0(filename)) { @@ -94,7 +94,7 @@ private static boolean isInitTrigger0(String path) { } @Override - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+18/src/hotspot/os/posix/attachListener_posix.cpp#L501-L523") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/posix/attachListener_posix.cpp#L373-L395") public void initialize() { lock.lock(); try { @@ -130,7 +130,7 @@ private boolean isSocketFileValid() { } @Override - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+18/src/hotspot/os/posix/attachListener_posix.cpp#L170-L181") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/posix/attachListener_posix.cpp#L169-L180") public void shutdown(boolean inTeardownHook) { if (!shutdownRequested.compareAndSet(false, true) && Thread.currentThread() instanceof AttachListenerThread) { /* @@ -186,7 +186,7 @@ private String getSocketFilePath() { return cachedSocketFilePath; } - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+18/src/hotspot/os/posix/attachListener_posix.cpp#L186-L250") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/posix/attachListener_posix.cpp#L185-L249") private boolean createListener() { assert lock.isHeldByCurrentThread(); diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/attach/PosixAttachListenerThread.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/attach/PosixAttachListenerThread.java index 9b309b5d2bc1..55bc6ef7af07 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/attach/PosixAttachListenerThread.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/attach/PosixAttachListenerThread.java @@ -26,13 +26,8 @@ package com.oracle.svm.core.posix.attach; -import java.nio.charset.StandardCharsets; - -import jdk.graal.compiler.word.Word; -import org.graalvm.nativeimage.StackValue; -import org.graalvm.nativeimage.c.type.CCharPointer; -import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.word.Pointer; +import org.graalvm.word.PointerBase; import com.oracle.svm.core.attach.AttachListenerThread; import com.oracle.svm.core.posix.PosixUtils; @@ -40,20 +35,6 @@ import com.oracle.svm.core.util.BasedOnJDKFile; public final class PosixAttachListenerThread extends AttachListenerThread { - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/aix/attachListener_aix.cpp#L82") // - private static final String PROTOCOL_VERSION = "1"; - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/aix/attachListener_aix.cpp#L269") // - private static final int VERSION_SIZE = 8; - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/aix/attachListener_aix.cpp#L85") // - private static final int ATTACH_ERROR_BAD_VERSION = 101; - - /** - * Each attach request consists of a fixed number of zero-terminated UTF-8 strings. - * {@code } - */ - private static final int EXPECTED_STRING_COUNT = 2 + ARG_COUNT_MAX; - private static final int MAX_REQUEST_LEN = (VERSION_SIZE + 1) + (NAME_LENGTH_MAX + 1) + (ARG_COUNT_MAX * (ARG_LENGTH_MAX + 1)); - private final int listener; public PosixAttachListenerThread(int listener) { @@ -61,128 +42,53 @@ public PosixAttachListenerThread(int listener) { } @Override - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+3/src/hotspot/os/posix/attachListener_posix.cpp#L254-L311") - protected PosixAttachOperation dequeue() { + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/posix/attachListener_posix.cpp#L256-L313") + protected AttachOperation dequeue() { while (true) { int socket = AttachHelper.waitForRequest(listener); if (socket == -1) { return null; } - PosixAttachOperation op = readRequest(socket); + PosixAttachSocketChannel channel = new PosixAttachSocketChannel(socket); + AttachOperation op = readRequest(channel); if (op == null) { - /* Close the socket and try again. */ - Unistd.NoTransitions.close(socket); + channel.close(); } else { return op; } } } - /** This method reads and processes a single request from the socket. */ - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/aix/attachListener_aix.cpp#L268-L359") - private static PosixAttachOperation readRequest(int socket) { - int strCount = 0; - int[] stringEnds = new int[EXPECTED_STRING_COUNT]; - Pointer buf = StackValue.get(MAX_REQUEST_LEN); - - /* Read until all expected strings have been read, the buffer is full, or EOF. */ - int offset = 0; - do { - int n = PosixUtils.readUninterruptibly(socket, buf, MAX_REQUEST_LEN, offset); - if (n == -1) { - return null; - } else if (n == 0) { - break; - } - - int end = offset + n; - while (offset < end) { - if (buf.readByte(offset) == 0) { - /* End-of-string found. */ - stringEnds[strCount] = offset; - strCount++; - } - offset++; - } - } while (offset < MAX_REQUEST_LEN && strCount < EXPECTED_STRING_COUNT); - - if (strCount != EXPECTED_STRING_COUNT) { - /* Incomplete or invalid request. */ - return null; - } - - String version = decodeString(buf, stringEnds, 0); - if (!PROTOCOL_VERSION.equals(version)) { - complete(socket, ATTACH_ERROR_BAD_VERSION, null); - return null; - } - - String name = decodeString(buf, stringEnds, 1); - if (name.length() > NAME_LENGTH_MAX) { - return null; - } - - String arg0 = decodeString(buf, stringEnds, 2); - if (arg0.length() > ARG_LENGTH_MAX) { - return null; - } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/posix/attachListener_posix.cpp#L102-L139") + private static class PosixAttachSocketChannel extends AttachSocketChannel { + private int socket; - String arg1 = decodeString(buf, stringEnds, 3); - if (arg1.length() > ARG_LENGTH_MAX) { - return null; + PosixAttachSocketChannel(int socket) { + this.socket = socket; } - String arg2 = decodeString(buf, stringEnds, 4); - if (arg2.length() > ARG_LENGTH_MAX) { - return null; + @Override + public int read(PointerBase buffer, int size) { + return PosixUtils.readUninterruptibly(socket, (Pointer) buffer, size, 0); } - return new PosixAttachOperation(name, arg0, arg1, arg2, socket); - } - - private static String decodeString(Pointer buf, int[] stringEnds, int index) { - int start = index == 0 ? 0 : stringEnds[index - 1] + 1; - int length = stringEnds[index] - start; - assert length >= 0; - return CTypeConversion.toJavaString((CCharPointer) buf.add(start), Word.unsigned(length), StandardCharsets.UTF_8); - } - - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+15/src/hotspot/os/posix/attachListener_posix.cpp#L323-L325") - private static void complete(int socket, int code, String response) { - /* Send the return code. */ - byte[] returnCodeData = Integer.toString(code).getBytes(StandardCharsets.UTF_8); - sendData(socket, returnCodeData); - - byte[] lineBreak = System.lineSeparator().getBytes(StandardCharsets.UTF_8); - sendData(socket, lineBreak); - - /* Send the actual response message. */ - if (response != null && !response.isEmpty()) { - byte[] responseBytes = response.getBytes(StandardCharsets.UTF_8); - sendData(socket, responseBytes); - sendData(socket, lineBreak); + @Override + protected void write(byte[] data) { + PosixUtils.writeUninterruptibly(socket, data); } - AttachHelper.shutdownSocket(socket); - Unistd.NoTransitions.close(socket); - } - - private static void sendData(int socket, byte[] data) { - PosixUtils.writeUninterruptibly(socket, data); - } - - private static class PosixAttachOperation extends AttachOperation { - private final int socket; - - PosixAttachOperation(String name, String arg0, String arg1, String arg2, int socket) { - super(name, arg0, arg1, arg2); - this.socket = socket; + protected boolean isOpen() { + return socket != -1; } @Override - public void complete(int code, String response) { - PosixAttachListenerThread.complete(socket, code, response); + public void close() { + if (isOpen()) { + AttachHelper.shutdownSocket(socket); + Unistd.NoTransitions.close(socket); + socket = -1; + } } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/attach/AttachListenerThread.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/attach/AttachListenerThread.java index 33be9a38a805..3d406fe863d8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/attach/AttachListenerThread.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/attach/AttachListenerThread.java @@ -28,10 +28,22 @@ import java.io.PrintWriter; import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.nativeimage.c.type.CTypeConversion; +import org.graalvm.word.Pointer; +import org.graalvm.word.PointerBase; +import org.graalvm.word.UnsignedWord; import com.oracle.svm.core.dcmd.DCmd; import com.oracle.svm.core.dcmd.DCmdSupport; import com.oracle.svm.core.jni.headers.JNIErrors; +import com.oracle.svm.core.memory.NativeMemory; +import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.option.RuntimeOptionKey; import com.oracle.svm.core.thread.PlatformThreads; import com.oracle.svm.core.util.BasedOnJDKFile; @@ -45,23 +57,27 @@ * requests. At the moment, only jcmd is supported. */ public abstract class AttachListenerThread extends Thread { + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.hpp#L176-L178")// + protected static final int ATTACH_ERROR_BAD_VERSION = 101; + + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.hpp#L171")// + protected static final int NAME_LENGTH_MAX_V1 = 16; + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.hpp#L172")// + protected static final int ARG_LENGTH_MAX_V1 = 1024; + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.hpp#L173")// + protected static final int ARG_COUNT_MAX_V1 = 3; + private static final String JCMD_COMMAND_STRING = "jcmd"; - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+18/src/hotspot/share/services/attachListener.hpp#L142") // - protected static final int NAME_LENGTH_MAX = 16; - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+18/src/hotspot/share/services/attachListener.hpp#L143") // - protected static final int ARG_LENGTH_MAX = 1024; - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+18/src/hotspot/share/services/attachListener.hpp#L144") // - protected static final int ARG_COUNT_MAX = 3; @SuppressWarnings("this-escape") - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+15/src/hotspot/share/services/attachListener.cpp#L667-L681") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.cpp#L668-L682") public AttachListenerThread() { super(PlatformThreads.singleton().systemGroup, "Attach Listener"); this.setDaemon(true); } @Override - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+15/src/hotspot/share/services/attachListener.cpp#L585-L650") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.cpp#L586-L651") public void run() { try { while (true) { @@ -83,19 +99,19 @@ public void run() { } } - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+15/src/hotspot/share/services/attachListener.cpp#L205-L217") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.cpp#L364-L410") private static void handleJcmd(AttachOperation op) { try { /* jcmd only uses the first argument. */ - String response = parseAndExecute(op.arg0); + String response = parseAndExecute(op.getArguments().getFirst()); op.complete(JNIErrors.JNI_OK(), response); } catch (Throwable e) { handleException(op, e); } } - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+15/src/hotspot/share/services/diagnosticFramework.cpp#L382-L418") - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+24/src/hotspot/share/services/diagnosticFramework.cpp#L422-L439") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/diagnosticFramework.cpp#L382-L418") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/diagnosticFramework.cpp#L429-L446") private static String parseAndExecute(String input) throws Throwable { String[] args = StringUtil.split(input, " "); String cmdName = args[0]; @@ -132,20 +148,245 @@ private static void handleException(AttachOperation op, Throwable e) { protected abstract AttachOperation dequeue(); - public abstract static class AttachOperation { + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.cpp#L845-L906") + protected static AttachOperation readRequest(AttachSocketChannel channel) { + int ver = readInt(channel); + if (ver < 0) { + return null; + } + + int bufferSize; + int minStrCount; + int minReadSize = 1; + switch (ver) { + case Version.V1: + /* + * Each request consists of a fixed number of zero-terminated UTF-8 strings: + * + */ + bufferSize = (NAME_LENGTH_MAX_V1 + 1) + ARG_COUNT_MAX_V1 * (ARG_LENGTH_MAX_V1 + 1); + minStrCount = 1 /* name */ + ARG_COUNT_MAX_V1; + break; + case Version.V2: + /* + * Each request consists of a variable number of zero-terminated UTF-8 strings: + * ()* + */ + channel.writeReply(ATTACH_ERROR_BAD_VERSION, "v2 is unsupported"); + return null; + default: + /* Failed to read request: unknown version. */ + channel.writeReply(ATTACH_ERROR_BAD_VERSION, "unknown version"); + return null; + } + + AttachOperation op = readRequestData(channel, bufferSize, minStrCount, minReadSize); + if (op != null && ver == Version.V1) { + /* + * The whole request does not exceed bufferSize, but for v1 also name/arguments should + * not exceed their respective max. length. + */ + if (op.getName().length() > NAME_LENGTH_MAX_V1) { + /* Failed to read request: operation name is too long. */ + return null; + } + for (String arg : op.getArguments()) { + if (arg.length() > ARG_LENGTH_MAX_V1) { + /* Failed to read request: operation argument is too long. */ + return null; + } + } + } + return op; + } + + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.cpp#L735-L803") + private static AttachOperation readRequestData(AttachSocketChannel channel, int bufferSize, int minStringCount, int minReadSize) { + Pointer buffer = NativeMemory.malloc(bufferSize, NmtCategory.Serviceability); + try { + return readRequestData0(channel, buffer, bufferSize, minStringCount, minReadSize); + } finally { + NativeMemory.free(buffer); + } + } + + private static AttachOperation readRequestData0(AttachSocketChannel channel, Pointer buffer, int bufferSize, int minStringCount, int minReadSize) { + int strCount = 0; + int offset = 0; + int left = bufferSize; + + /* + * Read until all (expected) strings or expected bytes have been read, the buffer is full, + * or EOF. + */ + do { + int n = channel.read(buffer.add(offset), left); + if (n < 0) { + return null; + } else if (n == 0) { + break; + } + + if (minStringCount > 0) { + /* Need to count arguments. */ + for (int i = 0; i < n; i++) { + if (buffer.readByte(offset + i) == 0) { + strCount++; + } + } + } + offset += n; + left -= n; + } while (left > 0 && (offset < minReadSize || strCount < minStringCount)); + + if (offset < minReadSize || strCount < minStringCount) { + /* Unexpected EOF. */ + return null; + } else if (buffer.readByte(offset - 1) != 0) { + /* Request must end with '\0'. */ + return null; + } + + /* Parse all strings. This part is very different from HotSpot. */ + ArrayList values = decodeStrings(buffer, buffer.add(offset)); + return createAttachOperation(channel, values); + } + + private static ArrayList decodeStrings(Pointer dataStart, Pointer dataEnd) { + ArrayList result = new ArrayList<>(4); + + Pointer currentStart = dataStart; + Pointer pos = dataStart; + while (pos.belowThan(dataEnd)) { + if (pos.readByte(0) == 0) { + String s = decodeString(currentStart, pos); + result.add(s); + + currentStart = pos.add(1); + } + + pos = pos.add(1); + } + + return result; + } + + private static String decodeString(Pointer start, Pointer end) { + assert end.aboveOrEqual(start); + UnsignedWord length = end.subtract(start); + return CTypeConversion.toJavaString((CCharPointer) start, length, StandardCharsets.UTF_8); + } + + private static AttachOperation createAttachOperation(AttachSocketChannel channel, ArrayList values) { + /* Parse the name and the options. */ + String name; + String nameAndOptions = values.getFirst(); + + int optionStart = nameAndOptions.indexOf(' '); + if (optionStart != -1) { + name = nameAndOptions.substring(0, optionStart); + parseOptions(); + } else { + name = nameAndOptions; + } + + List args = values.subList(1, values.size()); + return new AttachOperation(channel, name, args); + } + + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.cpp#L805-L820") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.cpp#L822-L843") + private static void parseOptions() { + /* "streaming" is the only option at the moment, and we don't support it. */ + } + + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.cpp#L704-L733") + private static int readInt(AttachSocketChannel channel) { + int maxValue = Integer.MAX_VALUE / 20; + CCharPointer chPtr = StackValue.get(CCharPointer.class); + int value = 0; + while (true) { + int n = channel.read(chPtr, 1); + if (n != 1) { + return -1; + } + byte ch = chPtr.read(); + if (ch == 0) { + return value; + } + if (ch < '0' || ch > '9') { + return -1; + } + /* Ensure there is no integer overflow. */ + if (value >= maxValue) { + return -1; + } + value = value * 10 + (ch - '0'); + } + } + + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.hpp#L71-L74") + private interface Version { + int V1 = 1; + int V2 = 2; + } + + public abstract static class AttachSocketChannel { + public abstract int read(PointerBase buffer, int size); + + /** + * This method should only be called directly if we don't have an {@link AttachOperation} + * yet. + */ + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.cpp#L921-L934") + public void writeReply(int result, String message) { + /* Send the return code. */ + byte[] lineBreak = System.lineSeparator().getBytes(StandardCharsets.UTF_8); + byte[] returnCodeData = Integer.toString(result).getBytes(StandardCharsets.UTF_8); + write(returnCodeData); + write(lineBreak); + + /* Send the actual response message. */ + if (message != null && !message.isEmpty()) { + byte[] responseBytes = message.getBytes(StandardCharsets.UTF_8); + write(responseBytes); + write(lineBreak); + } + } + + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.cpp#L908-L919") + protected abstract void write(byte[] data); + + public abstract void close(); + } + + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/services/attachListener.hpp#L167-L304") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/posix/attachListener_posix.cpp#L141-L158") + public static class AttachOperation { + private final AttachSocketChannel channel; private final String name; - private final String arg0; - @SuppressWarnings("unused") private final String arg1; - @SuppressWarnings("unused") private final String arg2; + private final List args; - public AttachOperation(String name, String arg0, String arg1, String arg2) { + public AttachOperation(AttachSocketChannel channel, String name, List args) { + this.channel = channel; this.name = name; - this.arg0 = arg0; - this.arg1 = arg1; - this.arg2 = arg2; + this.args = args; + } + + public String getName() { + return name; + } + + public List getArguments() { + return args; } - public abstract void complete(int code, String response); + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/posix/attachListener_posix.cpp#L323-L325") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/posix/attachListener_posix.cpp#L107-L109") + public void complete(int result, String message) { + channel.writeReply(result, message); + channel.close(); + } } static class Options { 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 index 6fa1a561b664..f0598a0e4655 100644 --- 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 @@ -53,6 +53,8 @@ public enum NmtCategory { NMT("Native Memory Tracking"), /** Profile-guided optimizations. */ PGO("PGO"), + /* Serviceability, e.g., attach API. */ + Serviceability("Serviceability)"), /** Threading. */ Threading("Threading"), /** Memory allocated via Unsafe. */