From f10fc7e51fdf09de0d574312ae85029c1e81e36c Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Tue, 31 Mar 2026 17:55:49 +0200 Subject: [PATCH 1/2] chore(crashtracking): Report VM argument flags --- .../datadog/crashtracking/CrashUploader.java | 24 +- .../crashtracking/dto/Experimental.java | 14 +- .../parsers/HotspotCrashLogParser.java | 38 ++- .../parsers/J9JavacoreParser.java | 14 +- .../crashtracking/parsers/RuntimeArgs.java | 237 ++++++++++++++++++ .../crashtracking/CrashUploaderTest.java | 33 ++- .../parsers/HotspotCrashLogParserTest.java | 35 +++ .../parsers/J9JavacoreParserTest.java | 3 + .../parsers/RuntimeArgsTest.java | 130 ++++++++++ .../redacted-truncated-ibmj9-8-javacore.txt | 138 ++++++++++ 10 files changed, 648 insertions(+), 18 deletions(-) create mode 100644 dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/RuntimeArgs.java create mode 100644 dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/RuntimeArgsTest.java create mode 100644 dd-java-agent/agent-crashtracking/src/test/resources/redacted-truncated-ibmj9-8-javacore.txt diff --git a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/CrashUploader.java b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/CrashUploader.java index f1691e85ebd..36955928693 100644 --- a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/CrashUploader.java +++ b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/CrashUploader.java @@ -575,15 +575,27 @@ private RequestBody makeErrorTrackingRequestBody(@Nonnull CrashLog payload, bool writer.endObject(); } // experimental - if (payload.experimental != null && payload.experimental.ucontext != null) { + if (payload.experimental != null + && (payload.experimental.ucontext != null + || payload.experimental.runtimeArgs != null)) { writer.name("experimental"); writer.beginObject(); - writer.name("ucontext"); - writer.beginObject(); - for (Map.Entry entry : payload.experimental.ucontext.entrySet()) { - writer.name(entry.getKey()).value(entry.getValue()); + if (payload.experimental.ucontext != null) { + writer.name("ucontext"); + writer.beginObject(); + for (Map.Entry entry : payload.experimental.ucontext.entrySet()) { + writer.name(entry.getKey()).value(entry.getValue()); + } + writer.endObject(); + } + if (payload.experimental.runtimeArgs != null) { + writer.name("runtime_args"); + writer.beginArray(); + for (String arg : payload.experimental.runtimeArgs) { + writer.value(arg); + } + writer.endArray(); } - writer.endObject(); writer.endObject(); } // files (e.g. /proc/self/maps or dynamic_libraries) diff --git a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/dto/Experimental.java b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/dto/Experimental.java index 687a8db5511..914c6640dd4 100644 --- a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/dto/Experimental.java +++ b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/dto/Experimental.java @@ -1,24 +1,34 @@ package datadog.crashtracking.dto; +import com.squareup.moshi.Json; +import java.util.List; import java.util.Map; import java.util.Objects; public final class Experimental { public final Map ucontext; + @Json(name = "runtime_args") + public final List runtimeArgs; + public Experimental(Map ucontext) { + this(ucontext, null); + } + + public Experimental(Map ucontext, List runtimeArgs) { this.ucontext = ucontext; + this.runtimeArgs = runtimeArgs; } @Override public boolean equals(Object o) { if (!(o instanceof Experimental)) return false; Experimental that = (Experimental) o; - return Objects.equals(ucontext, that.ucontext); + return Objects.equals(ucontext, that.ucontext) && Objects.equals(runtimeArgs, that.runtimeArgs); } @Override public int hashCode() { - return Objects.hash(ucontext); + return Objects.hash(ucontext, runtimeArgs); } } diff --git a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/HotspotCrashLogParser.java b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/HotspotCrashLogParser.java index 0e7f571168f..ff89656ab26 100644 --- a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/HotspotCrashLogParser.java +++ b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/HotspotCrashLogParser.java @@ -43,6 +43,7 @@ * resulting {@link datadog.crashtracking.dto.CrashLog} will be marked {@code incomplete}. */ public final class HotspotCrashLogParser { + private static final String HOTSPOT_JVM_ARGS_PREFIX = "jvm_args:"; private static final DateTimeFormatter ZONED_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("EEE MMM ppd HH:mm:ss yyyy zzz", Locale.getDefault()); private static final DateTimeFormatter OFFSET_DATE_TIME_FORMATTER = @@ -62,7 +63,8 @@ enum State { THREAD, STACKTRACE, REGISTERS, - SEEK_DYNAMIC_LIBRARIES, + PROCESS, + VM_ARGUMENTS, DYNAMIC_LIBRARIES, SYSTEM, DONE @@ -332,6 +334,13 @@ static String parseCurrentThreadName(String line) { return threadDescriptor; } + private static List parseHotspotJvmArgs(String line) { + if (line == null || !line.startsWith(HOTSPOT_JVM_ARGS_PREFIX)) { + return null; + } + return RuntimeArgs.parseVmArgs(line.substring(HOTSPOT_JVM_ARGS_PREFIX.length())); + } + public CrashLog parse(String uuid, String crashLog) { SigInfo sigInfo = null; String pid = null; @@ -342,6 +351,7 @@ public CrashLog parse(String uuid, String crashLog) { boolean incomplete = false; String oomMessage = null; Map registers = null; + List runtimeArgs = null; List dynamicLibraryLines = null; String dynamicLibraryKey = null; @@ -420,7 +430,7 @@ public CrashLog parse(String uuid, String crashLog) { registers = new LinkedHashMap<>(); state = State.REGISTERS; } else if (line.contains("P R O C E S S")) { - state = State.SEEK_DYNAMIC_LIBRARIES; + state = State.PROCESS; } else { // Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) final StackFrame frame = parseLine(line); @@ -440,8 +450,10 @@ public CrashLog parse(String uuid, String crashLog) { } } break; - case SEEK_DYNAMIC_LIBRARIES: - if (line.startsWith("Dynamic libraries:")) { + case PROCESS: + if (runtimeArgs == null && line.startsWith("VM Arguments:")) { + state = State.VM_ARGUMENTS; + } else if (line.startsWith("Dynamic libraries:")) { state = State.DYNAMIC_LIBRARIES; } else if (line.contains("S Y S T E M")) { state = State.SYSTEM; @@ -449,9 +461,16 @@ public CrashLog parse(String uuid, String crashLog) { state = State.DONE; } break; + case VM_ARGUMENTS: + if (line.isEmpty()) { + state = State.PROCESS; + } else if (runtimeArgs == null && line.startsWith(HOTSPOT_JVM_ARGS_PREFIX)) { + runtimeArgs = parseHotspotJvmArgs(line); + } + break; case DYNAMIC_LIBRARIES: if (line.isEmpty()) { - state = State.SEEK_DYNAMIC_LIBRARIES; + state = State.PROCESS; } else { if (dynamicLibraryKey == null) { dynamicLibraryKey = detectDynamicLibrariesKey(line); @@ -491,8 +510,8 @@ public CrashLog parse(String uuid, String crashLog) { } } - // SEEK_DYNAMIC_LIBRARIES and SYSTEM sections are late enough that all critical data is captured - if (state != State.DONE && state != State.SEEK_DYNAMIC_LIBRARIES && state != State.SYSTEM) { + // PROCESS and SYSTEM sections are late enough that all critical data is captured + if (state != State.DONE && state != State.PROCESS && state != State.SYSTEM) { // incomplete crash log incomplete = true; } @@ -553,7 +572,10 @@ public CrashLog parse(String uuid, String crashLog) { Integer parsedPid = safelyParseInt(pid); ProcInfo procInfo = parsedPid != null ? new ProcInfo(parsedPid) : null; Experimental experimental = - (registers != null && !registers.isEmpty()) ? new Experimental(registers) : null; + (registers != null && !registers.isEmpty()) + || (runtimeArgs != null && !runtimeArgs.isEmpty()) + ? new Experimental(registers, runtimeArgs) + : null; DynamicLibs files = (dynamicLibraryLines != null && !dynamicLibraryLines.isEmpty()) ? new DynamicLibs(dynamicLibraryKey, dynamicLibraryLines) diff --git a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/J9JavacoreParser.java b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/J9JavacoreParser.java index b71556b2b3c..1d7214dde6d 100644 --- a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/J9JavacoreParser.java +++ b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/J9JavacoreParser.java @@ -45,6 +45,7 @@ * */ public final class J9JavacoreParser { + private static final String J9_USER_ARG_PREFIX = "2CIUSERARG"; private final BuildIdCollector buildIdCollector; @@ -117,10 +118,17 @@ public CrashLog parse(String uuid, String javacoreContent) { boolean foundThreadSection = false; Map registers = null; + RuntimeArgs j9UserArgs = new RuntimeArgs(); String[] lines = NEWLINE_SPLITTER.split(javacoreContent); for (String line : lines) { + // Full command line is available under 1CICMDLINE, but it's harder to parse properly, + // than adding them from 2CIUSERARGS + if (line.startsWith(J9_USER_ARG_PREFIX)) { + j9UserArgs.addArg(line.substring(J9_USER_ARG_PREFIX.length()).trim()); + } + // Track section changes if (line.startsWith(SECTION_MARKER)) { currentSection = detectSection(line); @@ -290,8 +298,12 @@ public CrashLog parse(String uuid, String javacoreContent) { Metadata metadata = new Metadata("dd-trace-java", VersionInfo.VERSION, "java", null); Integer parsedPid = safelyParseInt(pid); ProcInfo procInfo = parsedPid != null ? new ProcInfo(parsedPid) : null; + List runtimeArgs = j9UserArgs.build(); Experimental experimental = - (registers != null && !registers.isEmpty()) ? new Experimental(registers) : null; + (registers != null && !registers.isEmpty()) + || (runtimeArgs != null && !runtimeArgs.isEmpty()) + ? new Experimental(registers, runtimeArgs) + : null; return new CrashLog( uuid, diff --git a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/RuntimeArgs.java b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/RuntimeArgs.java new file mode 100644 index 00000000000..e11f2dad9e7 --- /dev/null +++ b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/RuntimeArgs.java @@ -0,0 +1,237 @@ +package datadog.crashtracking.parsers; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Utility for normalizing and filtering JVM runtime arguments captured from crash artifacts. + * + *

The helper supports two input shapes: + * + *

    + *
  • a raw JVM-args record, such as HotSpot {@code jvm_args: ...} + *
  • pre-split J9 user arguments, such as individual {@code 2CIUSERARG} records + *
+ * + *

Only a curated subset of arguments is retained for telemetry: {@code -Ddd.*}, {@code -Djdk.*}, + * {@code -Djava.*}, {@code -Dsun.*}, {@code -javaagent:}, {@code -agentlib:}, {@code -X*}, and + * module/native-access options. + */ +final class RuntimeArgs { + private static final String[] SECRET_PROPERTY_KEYWORDS = { + "password", + "passwd", + "secret", + "token", + "apikey", + "api-key", + "accesskey", + "access-key", + "privatekey", + "private-key", + "credential" + }; + private static final String[] MODULE_OPTIONS = { + "--add-modules", + "--add-exports", + "--add-opens", + "--add-reads", + "--patch-module", + "--limit-modules", + "--module-path", + "--upgrade-module-path", + "--enable-native-access", + "--illegal-native-access", + "--sun-misc-unsafe-memory-access" + }; + + private final List args = new ArrayList<>(); + + /** Returns a filtered args list from the raw JVM-arguments. */ + static List parseVmArgs(String raw) { + return filterArgs(joinArgumentTokens(splitArgs(raw))); + } + + void addArg(String arg) { + if (arg != null && !arg.isEmpty()) { + args.add(arg); + } + } + + List build() { + return filterArgs(args); + } + + private static List filterArgs(List args) { + if (args.isEmpty()) { + return Collections.emptyList(); + } + List filtered = new ArrayList<>(); + for (String arg : args) { + if (arg == null || arg.isEmpty()) { + continue; + } + if (isAllowedSystemProperty(arg) + || arg.startsWith("-javaagent:") + || arg.startsWith("-agentlib:") + || arg.startsWith("-X") + || isModuleOrNativeAccessOption(arg)) { + filtered.add(arg); + } + } + return filtered; + } + + private static boolean isModuleOrNativeAccessOption(String arg) { + for (String option : MODULE_OPTIONS) { + if (arg.equals(option) || arg.startsWith(option + "=") || arg.startsWith(option + " ")) { + return true; + } + } + return false; + } + + private static boolean isAllowedSystemProperty(String arg) { + if (hasSecretLikePropertyName(arg)) { + return false; + } + if (arg.startsWith("-Ddd.") || arg.startsWith("-Djdk.") || arg.startsWith("-Dosgi.")) { + return true; + } + // J9 lists them as vm args. + if (arg.startsWith("-Djava.class.path=") || arg.startsWith("-Dsun.java.command=")) { + return false; + } + return arg.startsWith("-Djava.") || arg.startsWith("-Dsun."); + } + + private static boolean hasSecretLikePropertyName(String arg) { + if (!arg.startsWith("-D")) { + return false; + } + int separator = arg.indexOf('='); + String propertyName = separator >= 0 ? arg.substring(2, separator) : arg.substring(2); + String normalizedPropertyName = propertyName.toLowerCase(); + for (String keyword : SECRET_PROPERTY_KEYWORDS) { + if (normalizedPropertyName.contains(keyword)) { + return true; + } + } + return false; + } + + private static List splitArgs(String raw) { + if (raw == null || raw.isEmpty()) { + return Collections.emptyList(); + } + List tokens = new ArrayList<>(); + StringBuilder current = new StringBuilder(raw.length()); + boolean inSingleQuote = false; + boolean inDoubleQuote = false; + boolean escaped = false; + + for (int i = 0; i < raw.length(); i++) { + char c = raw.charAt(i); + // Keep the escaped character verbatim and clear the escape state. + if (escaped) { + current.append(c); + escaped = false; + continue; + } + // Backslashes escape the next character unless we are inside single quotes. + if (c == '\\' && !inSingleQuote) { + escaped = true; + continue; + } + // Single quotes only toggle quoting when not already inside double quotes. + if (c == '\'' && !inDoubleQuote) { + inSingleQuote = !inSingleQuote; + continue; + } + // Double quotes only toggle quoting when not already inside single quotes. + if (c == '"' && !inSingleQuote) { + inDoubleQuote = !inDoubleQuote; + continue; + } + // Outside of quotes, whitespace terminates the current token. + if (Character.isWhitespace(c) && !inSingleQuote && !inDoubleQuote) { + if (current.length() > 0) { + tokens.add(current.toString()); + current.setLength(0); + } + continue; + } + current.append(c); + } + if (current.length() > 0) { + tokens.add(current.toString()); + } + return tokens; + } + + /** + * Joins shell-like tokens into argument-shaped units. + * + *

This pass reconstructs options that span multiple tokens, such as module options whose value + * is separated by whitespace and {@code -XX:OnError=} style options that carry shell fragments + * over multiple tokens. + */ + private static List joinArgumentTokens(List tokens) { + if (tokens.isEmpty()) { + return Collections.emptyList(); + } + List joinedArgs = new ArrayList<>(); + boolean canContinuePreviousArg = false; + for (int i = 0; i < tokens.size(); i++) { + String token = tokens.get(i); + if (token.isEmpty()) { + continue; + } + if (!token.startsWith("-")) { + if (canContinuePreviousArg + && !joinedArgs.isEmpty() + && isContinuationToken(token) + && acceptsContinuation(joinedArgs.get(joinedArgs.size() - 1))) { + int last = joinedArgs.size() - 1; + joinedArgs.set(last, joinedArgs.get(last) + " " + token); + continue; + } + canContinuePreviousArg = false; + continue; + } + + String arg = token; + if (requiresSeparateValue(token) && i + 1 < tokens.size()) { + arg = token + " " + tokens.get(++i); + } + while (i + 1 < tokens.size() + && isContinuationToken(tokens.get(i + 1)) + && acceptsContinuation(arg)) { + arg = arg + " " + tokens.get(++i); + } + joinedArgs.add(arg); + canContinuePreviousArg = acceptsContinuation(arg); + } + return joinedArgs; + } + + private static boolean requiresSeparateValue(String token) { + for (String option : MODULE_OPTIONS) { + if (token.equals(option)) { + return true; + } + } + return false; + } + + private static boolean acceptsContinuation(String arg) { + return arg.startsWith("-XX:OnError=") + || arg.startsWith("-XX:OnOutOfMemoryError=") + || arg.startsWith("-Xdump:"); + } + + private static boolean isContinuationToken(String token) { + return !token.isEmpty() && !token.startsWith("-"); + } +} diff --git a/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/CrashUploaderTest.java b/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/CrashUploaderTest.java index c2e91c11737..763cbd01a53 100644 --- a/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/CrashUploaderTest.java +++ b/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/CrashUploaderTest.java @@ -294,7 +294,7 @@ public void testTelemetryHappyPath(String log) throws Exception { CrashLog extracted = CrashLog.fromJson(message); assertThatJson(extracted.toJson()) - .whenIgnoringPaths("os_info", "metadata") + .whenIgnoringPaths("os_info", "metadata", "experimental") .isEqualTo(expected.toJson()); assertEquals("severity:crash", event.get("payload").get(0).get("tags").asText()); assertCommonPayload(event); @@ -358,9 +358,40 @@ public void testErrorTrackingHappyPath(String log) throws Exception { // assert platform independent equality assertThatJson(mapper.writeValueAsString(extracted)) + .whenIgnoringPaths("experimental") .isEqualTo(mapper.writeValueAsString(expected)); } + @Test + public void testErrorTrackingSerializesRuntimeArgs() throws Exception { + ConfigManager.StoredConfig crashConfig = + new ConfigManager.StoredConfig.Builder(config) + .reportUUID(SAMPLE_UUID) + .processTags("a:b") + .runtimeId("1234") + .tags(ConfigManager.getMergedTagsForSerialization(Config.get())) + .build(); + + uploader = new CrashUploader(config, crashConfig); + server.enqueue(new MockResponse().setResponseCode(200)); + uploader.remoteUpload(readFileAsString("sample-crash-macos-aarch64.txt"), false, true); + + final RecordedRequest recordedRequest = server.takeRequest(5, TimeUnit.SECONDS); + final ObjectMapper mapper = new ObjectMapper(); + final JsonNode event = mapper.readTree(recordedRequest.getBody().readUtf8()); + + final JsonNode runtimeArgs = event.at("/experimental/runtime_args"); + assertTrue(runtimeArgs.isArray()); + boolean found = false; + for (JsonNode runtimeArg : runtimeArgs) { + if ("--enable-native-access=ALL-UNNAMED".equals(runtimeArg.asText())) { + found = true; + break; + } + } + assertTrue(found); + } + private void assertCommonHeader(JsonNode event) { assertEquals(TELEMETRY_API_VERSION, event.get("api_version").asText()); assertEquals("logs", event.get("request_type").asText()); diff --git a/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/HotspotCrashLogParserTest.java b/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/HotspotCrashLogParserTest.java index c83abcb9e21..b23937a41d0 100644 --- a/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/HotspotCrashLogParserTest.java +++ b/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/HotspotCrashLogParserTest.java @@ -1,6 +1,7 @@ package datadog.crashtracking.parsers; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -64,6 +65,13 @@ public void testRegisterParsingMacosAarch64() throws Exception { assertEquals("0x000000016feee0f0", crashLog.experimental.ucontext.get("sp")); assertEquals("0x000000010f8ac794", crashLog.experimental.ucontext.get("pc")); assertEquals("0x0000000060001000", crashLog.experimental.ucontext.get("cpsr")); + + assertNotNull(crashLog.experimental.runtimeArgs); + assertTrue(crashLog.experimental.runtimeArgs.contains("--enable-native-access=ALL-UNNAMED")); + assertTrue(crashLog.experimental.runtimeArgs.contains("--add-modules=ALL-DEFAULT")); + assertFalse( + crashLog.experimental.runtimeArgs.stream() + .anyMatch(arg -> arg.contains("SourceLauncher") || arg.endsWith("CrashTest.java"))); } /** Linux aarch64 uses uppercase register names: R0-R30 */ @@ -81,6 +89,33 @@ public void testRegisterParsingLinuxAarch64() throws Exception { assertEquals("0x0000ffff9efa168c", crashLog.experimental.ucontext.get("R30")); // "Register to memory mapping:" section must NOT be included assertEquals(31, crashLog.experimental.ucontext.size(), "R0-R30 = 31 registers"); + + assertNotNull(crashLog.experimental.runtimeArgs); + assertTrue(crashLog.experimental.runtimeArgs.contains("--add-modules=ALL-DEFAULT")); + } + + @Test + public void testRuntimeArgsFilteringFromHotspotJvmArgs() throws Exception { + final CrashLog crashLog = + new HotspotCrashLogParser() + .parse( + UUID.randomUUID().toString(), readFileAsString("sample-crash-for-telemetry.txt")); + + assertNotNull(crashLog.experimental); + assertNotNull(crashLog.experimental.runtimeArgs); + assertTrue( + crashLog.experimental.runtimeArgs.contains( + "-javaagent:/opt/REDACT_THIS/datadog-apm-agent/dd-java-agent.jar")); + assertTrue(crashLog.experimental.runtimeArgs.contains("-Ddd.profiling.enabled=true")); + assertTrue(crashLog.experimental.runtimeArgs.contains("-Ddd.service=REDACT_THIS")); + assertTrue( + crashLog.experimental.runtimeArgs.stream().anyMatch(arg -> arg.startsWith("--add-opens="))); + assertFalse( + crashLog.experimental.runtimeArgs.stream() + .anyMatch(arg -> arg.startsWith("-Djavax.xml.ws.spi.Provider="))); + assertTrue( + crashLog.experimental.runtimeArgs.stream() + .anyMatch(arg -> arg.startsWith("-Djava.util.logging.config.file="))); } @TableTest({ diff --git a/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/J9JavacoreParserTest.java b/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/J9JavacoreParserTest.java index 2bbdbab4487..63ffe0fbe10 100644 --- a/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/J9JavacoreParserTest.java +++ b/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/J9JavacoreParserTest.java @@ -91,6 +91,9 @@ public void testParseRealGpfCrash(String resource, String pcRegister, String spR assertFalse(crashLog.experimental.ucontext.isEmpty()); assertTrue(crashLog.experimental.ucontext.containsKey(pcRegister)); assertTrue(crashLog.experimental.ucontext.containsKey(spRegister)); + assertNotNull(crashLog.experimental.runtimeArgs); + assertTrue( + crashLog.experimental.runtimeArgs.stream().anyMatch(arg -> arg.startsWith("-Xdump:java:"))); } @Test diff --git a/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/RuntimeArgsTest.java b/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/RuntimeArgsTest.java new file mode 100644 index 00000000000..83d71c99407 --- /dev/null +++ b/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/RuntimeArgsTest.java @@ -0,0 +1,130 @@ +package datadog.crashtracking.parsers; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import org.tabletest.junit.TableTest; + +public class RuntimeArgsTest { + private static final String HOTSPOT_JVM_ARGS_PREFIX = "jvm_args:"; + private static final String J9_USER_ARG_PREFIX = "2CIUSERARG"; + + @TableTest({ + "scenario | resource | isIncluded | vmArg ", + "telemetry fixture logging property | sample-crash-for-telemetry.txt | true | -Djava.util.logging.config.file=/opt/REDACT_THIS/REDACT_THIS/etc/java.util.logging.properties", + "telemetry fixture dd property | sample-crash-for-telemetry.txt | true | -Ddd.profiling.enabled=true ", + "telemetry fixture excluded property | sample-crash-for-telemetry.txt | false | -Dkaraf.startRemoteShell=REDACT_THIS ", + "telemetry fixture ws provider | sample-crash-for-telemetry.txt | false | -Djavax.xml.ws.spi.Provider=com.sun.xml.ws.spi.ProviderImpl ", + "telemetry with OnError | sample-crash-for-telemetry-2.txt | true | -XX:OnError=/tmp/dd_crash_uploader.sh %p ", + "telemetry jdk8 | sample-crash-for-telemetry-3.txt | true | -Ddd.trace.enabled=false ", + "linux aarch64 | sample-crash-linux-aarch64.txt | true | --add-modules=ALL-DEFAULT ", + "macos aarch64 | sample-crash-macos-aarch64.txt | true | --enable-native-access=ALL-UNNAMED ", + "jdk8 zip | sample-crash-jdk8-zip-getentry.txt | true | -Dsun.zip.disableMemoryMapping=false " + }) + public void testParseVmArgsHotspotArgs(String resource, boolean isIncluded, String vmArg) + throws Exception { + List runtimeArgs = RuntimeArgs.parseVmArgs(extractHotspotJvmArgs(resource)); + + assertThat(runtimeArgs).isNotNull().isNotEmpty(); + if (isIncluded) { + assertThat(runtimeArgs).as("Expected included arg in %s", resource).contains(vmArg); + } else { + assertThat(runtimeArgs) + .as("Expected excluded arg to be absent in %s", resource) + .doesNotContain(vmArg); + } + } + + @TableTest({ + "scenario | raw | expectedIncluded ", + "quoted onerror unix | -XX:OnError=\"gcore %p;gdb -p %p\" | -XX:OnError=gcore %p;gdb -p %p ", + "quoted onerror windows | -XX:OnError=\"userdump.exe %p\" | -XX:OnError=userdump.exe %p ", + "quoted module path | --module-path \"/opt/app-modules:/opt/other-modules\" | --module-path /opt/app-modules:/opt/other-modules" + }) + public void testParseVmArgsHandlesQuotedArguments(String raw, String expectedIncluded) { + List runtimeArgs = RuntimeArgs.parseVmArgs(raw); + + assertThat(runtimeArgs).isNotNull().contains(expectedIncluded); + } + + @TableTest({ + "scenario | raw | isIncluded | vmArg ", + "java password excluded | -Djava.net.password=hunter2 | false | -Djava.net.password=hunter2 ", + "sun token excluded | -Dsun.auth.token=abc123 | false | -Dsun.auth.token=abc123 ", + "dd api key excluded | -Ddd.api-key=deadbeef | false | -Ddd.api-key=deadbeef ", + "java logging kept | -Djava.util.logging.config.file=x | true | -Djava.util.logging.config.file=x", + "osgi install kept | -Dosgi.install.area=/opt/app | true | -Dosgi.install.area=/opt/app " + }) + public void testParseVmArgsExcludesSecretLikeSystemProperties( + String raw, boolean isIncluded, String vmArg) { + List runtimeArgs = RuntimeArgs.parseVmArgs(raw); + + assertThat(runtimeArgs).isNotNull(); + if (isIncluded) { + assertThat(runtimeArgs).contains(vmArg); + } else { + assertThat(runtimeArgs).doesNotContain(vmArg); + } + } + + @TableTest({ + "scenario | resource | isIncluded | vmArg ", + "truncated ibmj9 optionsfile | redacted-truncated-ibmj9-8-javacore.txt | true | -Xoptionsfile=/opt/REDACTED/java/8.0/jre/lib/ppc64/compressedrefs/options.default ", + "truncated ibmj9 dd arg | redacted-truncated-ibmj9-8-javacore.txt | true | -Ddd.service=REDACTED ", + "truncated ibmj9 osgi arg | redacted-truncated-ibmj9-8-javacore.txt | true | -Dosgi.install.area=/opt/REDACTED ", + "truncated ibmj9 status arg | redacted-truncated-ibmj9-8-javacore.txt | false | -Dwas.status.socket=REDACTED ", + "truncated ibmj9 xtq arg | redacted-truncated-ibmj9-8-javacore.txt | false | -Dcom.ibm.xtq.processor.overrideSecureProcessing=true ", + "truncated ibmj9 command arg | redacted-truncated-ibmj9-8-javacore.txt | false | -Dsun.java.command=com.ibm.wsspi.bootstrap.WSPreLauncher -nosplash -application com.ibm.ws.bootstrap.WSLauncher com.ibm.ws.runtime.WsServer" + }) + public void testBuildFromJ9UserArgs(String resource, boolean isIncluded, String vmArg) + throws Exception { + RuntimeArgs runtimeArgsBuilder = new RuntimeArgs(); + for (String arg : extractJ9UserArgs(resource)) { + runtimeArgsBuilder.addArg(arg); + } + List runtimeArgs = runtimeArgsBuilder.build(); + + assertThat(runtimeArgs).isNotNull(); + if (isIncluded) { + assertThat(runtimeArgs) + .as("Expected included arg fragment in %s", resource) + .anyMatch(arg -> arg.contains(vmArg)); + } else { + assertThat(runtimeArgs) + .as("Expected excluded arg to be absent in %s", resource) + .doesNotContain(vmArg); + } + } + + private String extractHotspotJvmArgs(String resource) throws IOException { + for (String line : readFileAsString(resource).split("\n")) { + if (line.startsWith(HOTSPOT_JVM_ARGS_PREFIX)) { + return line.substring(HOTSPOT_JVM_ARGS_PREFIX.length()).trim(); + } + } + throw new IllegalArgumentException("Missing jvm_args line in " + resource); + } + + private List extractJ9UserArgs(String resource) throws IOException { + return java.util.Arrays.stream(readFileAsString(resource).split("\n")) + .filter(line -> line.startsWith(J9_USER_ARG_PREFIX)) + .map(line -> line.substring(J9_USER_ARG_PREFIX.length()).trim()) + .collect(Collectors.toList()); + } + + private String readFileAsString(String resource) throws IOException { + try (InputStream stream = getClass().getClassLoader().getResourceAsStream(resource)) { + return new BufferedReader( + new InputStreamReader(Objects.requireNonNull(stream), StandardCharsets.UTF_8)) + .lines() + .collect(Collectors.joining("\n")); + } + } +} diff --git a/dd-java-agent/agent-crashtracking/src/test/resources/redacted-truncated-ibmj9-8-javacore.txt b/dd-java-agent/agent-crashtracking/src/test/resources/redacted-truncated-ibmj9-8-javacore.txt new file mode 100644 index 00000000000..d51d42d8504 --- /dev/null +++ b/dd-java-agent/agent-crashtracking/src/test/resources/redacted-truncated-ibmj9-8-javacore.txt @@ -0,0 +1,138 @@ +0SECTION TITLE subcomponent dump routine +NULL =============================== +1TICHARSET ISO8859-1 +1TISIGINFO Dump Event "user" (00004000) received +1TIDATETIMEUTC Date: 2001/01/01 at 00:00:00:000 (UTC) +1TIDATETIME Date: 2001/01/01 at 00:00:00:000 +1TITIMEZONE Timezone: UTC-0 (UTC) +1TINANOTIME System nanotime: 0 +1TIFILENAME Javacore filename: /opt/REDACTED/REDACTED/javacore.20010101.000000.12345678.0001.txt +1TIREQFLAGS Request Flags: 0x41 (exclusive+preempt) +1TIPREPSTATE Prep State: 0x80 (trace_disabled) +1TIPREPINFO Exclusive VM access not taken: data may not be consistent across javacore sections +NULL ------------------------------------------------------------------------ +0SECTION GPINFO subcomponent dump routine +NULL ================================ +2XHOSLEVEL OS Level : AIX 7.2 +2XHCPUS Processors - +3XHCPUARCH Architecture : ppc64 +3XHNUMCPUS How Many : 16 +3XHNUMASUP NUMA is either not supported or has been disabled by user +NULL +1XHERROR2 Register dump section only produced for SIGSEGV, SIGILL or SIGFPE. +NULL +NULL ------------------------------------------------------------------------ +0SECTION ENVINFO subcomponent dump routine +NULL ================================= +1CIJAVAVERSION JRE 1.8.0 AIX ppc64-64 (build 8.0.8.6 - pap6480sr8fp6-REDACTED(SR8 FP6)) +1CIVMVERSION REDACTED +1CIJ9VMVERSION REDACTED +1CIJITVERSION REDACTED +1CIOMRVERSION REDACTED +1CIIBMVERSION REDACTED +1CIVENDOR IBM Corporation +1CIJITMODES JIT enabled, AOT enabled, FSD disabled, HCR enabled +1CIRUNNINGAS Running as a standalone JVM +1CIVMIDLESTATE VM Idle State: ACTIVE +1CICONTINFO Running in container : FALSE +1CICGRPINFO JVM support for cgroups enabled : FALSE +1CISTARTTIME JVM start time: 2001/01/01 at 00:00:00:000 +1CISTARTNANO JVM start nanotime: 0 +1CIPROCESSID Process ID: 12345678 (0xBC614E) +1CICMDLINE /opt/REDACTED/java/8.0/bin/java -Dosgi.install.area=/opt/REDACTED -Dwas.status.socket=REDACTED -Dosgi.configuration.area=/opt/REDACTED/REDACTED/REDACTED/configuration -Djava.awt.headless=true -Dosgi.framework.extensions=com.ibm.cds,com.ibm.ws.eclipse.adaptors -Xshareclasses:name=webspherev9_8.0_64_%g,nonFatal -Dcom.ibm.xtq.processor.overrideSecureProcessing=true -Xcheck:dump -Djava.security.properties=/opt/REDACTED/properties/java.security -Djava.security.policy=/opt/REDACTED/properties/java.policy -Dcom.ibm.CORBA.ORBPropertyFilePath=/opt/REDACTED/properties -Xbootclasspath/p:/opt/REDACTED/java/8.0/jre/lib/ibmorb.jar -classpath /opt/REDACTED/REDACTED/properties:/opt/REDACTED/properties:/opt/REDACTED/lib/startup.jar:/opt/REDACTED/lib/bootstrap.jar:/opt/REDACTED/lib/lmproxy.jar:/opt/REDACTED/lib/urlprotocols.jar:/opt/REDACTED/deploytool/itp/batchboot.jar:/opt/REDACTED/deploytool/itp/batch2.jar:/opt/REDACTED/java/8.0/lib/tools.jar -Dibm.websphere.internalClassAccessMode=allow -verbose:gc -Xverbosegclog:/opt/REDACTED/REDACTED/logs/REDACTED/verbosegc.%seq.log,10,7000 -Xms3072m -Xmx4096m -Xcompressedrefs -Xscmaxaot16M -Xscmx120M -Dws.ext.dirs=/opt/REDACTED/java/8.0/lib:/opt/REDACTED/REDACTED/classes:/opt/REDACTED/classes:/opt/REDACTED/lib:/opt/REDACTED/installedChannels:/opt/REDACTED/lib/ext:/opt/REDACTED/web/help:/opt/REDACTED/deploytool/itp/plugins/com.ibm.etools.ejbdeploy/runtime -Dcom.ibm.itp.location=/opt/REDACTED/bin -Djava.util.logging.configureByServer=true -Duser.install.root=/opt/REDACTED/REDACTED -Djava.ext.dirs=/opt/REDACTED/tivoli/tam:/opt/REDACTED/javaext:/opt/REDACTED/java/8.0/jre/lib/ext -Dpython.cachedir=/opt/REDACTED/REDACTED/temp/cachedir -Dwas.install.root=/opt/REDACTED -Djava.util.logging.manager=com.ibm.ws.bootstrap.WsLogManager -Dserver.root=/opt/REDACTED/REDACTED -Dcom.ibm.security.jgss.debug=off -Dcom.ibm.security.krb5.Krb5Debug=off -DTEMAGCCollector.gclog.path=None -Dcom.ibm.tivoli.itcam.toolkit.ai.runtimebuilder.enable.rebuild=true -DdisableWSAddressCaching=true -Dlog4j2.formatMsgNoLookups=true -Ddd.integration.jax-ws.enabled=true -Ddd.trace.split-by-tags=servlet.context -Ddd.jmxfetch.enabled=true -Ddd.jmxfetch.start-delay=60 -Ddd.trace.report-hostname=true -Ddd.integration.jms.enabled=true -Ddd.app.customjmxbuilder=false -Ddd.tags=REDACTED:REDACTED, REDACTED:REDACTED,node:REDACTED,cluster:REDACTED -Dcom.sun.management.jmxremote.port=REDACTED -Djavax.management.builder.initial= -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote=true -Dcom.ibm.team.server.monitoring.mbean.server=WebSphere -Dcom.sun.management.jmxremote.authenticate=false -Dappdynamics.agent.nodeName=REDACTED -Ddd.env=REDACTED -Dagent.apm.enabled=true -Ddd.version=REDACTED -Ddd.service=REDACTED -javaagent:/opt/redacted/lib/dd-java-agent.jar -agentlib:hprof=cpu=samples,depth=100,interval=10,lineno=y,thread=y,file=out.hprof.txt -Ddd.profiling.enabled=false -Ddd.trace.classloaders.exclude=com.ibm.xml.xlxp2.jaxb.codegen.AbstractGeneratedStubFactory\$RootStubClassLoader -XX:FlightRecorderOptions=stackdepth=256 -agentlib:am_ibm_16=REDACTED -verbosegc -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -Dsun.rmi.transport.connectionTimeout=300000 -Dcom.ibm.jsse2.disableSSLv3=false -Dsun.net.inetaddr.ttl=15 -Dsun.net.inetaddr.negative.ttl=5 -Djava.library.path=/opt/REDACTED/lib/native/aix/ppc_64/:/opt/REDACTED/java/8.0/jre/lib/ppc64/compressedrefs:/opt/REDACTED/java/8.0/jre/lib/ppc64:/opt/REDACTED/java/8.0/jre/lib/ppc64/j9vm:/opt/REDACTED/java/8.0/jre/lib/ppc64:/opt/REDACTED/java/8.0/jre/../lib/ppc64:/opt/REDACTED/java/8.0/jre//lib/icc:/opt/REDACTED/bin:/opt/REDACTED/nulldllsdir:/usr/lib64:/usr/lib:/lib:/opt/REDACTED/toolkit/lib/aix536:/opt/REDACTED/toolkit/lib/aix536/ttapi: -Djava.endorsed.dirs=/opt/REDACTED/endorsed_apis:/opt/REDACTED/java/8.0/jre/lib/endorsed -Djava.security.auth.login.config=/opt/REDACTED/REDACTED/properties/wsjaas.conf -Djava.security.policy=/opt/REDACTED/REDACTED/properties/server.policy com.ibm.wsspi.bootstrap.WSPreLauncher -nosplash -application com.ibm.ws.bootstrap.WSLauncher com.ibm.ws.runtime.WsServer +1CIJAVAHOMEDIR Java Home Dir: /opt/REDACTED/java/8.0/jre +1CIJAVADLLDIR Java DLL Dir: /opt/REDACTED/java/8.0/jre/bin +1CISYSCP Sys Classpath: /opt/REDACTED/endorsed_apis/jaxws-api.jar;/opt/REDACTED/endorsed_apis/jaxb-api.jar;/opt/REDACTED/endorsed_apis/javax.j2ee.annotation.jar;/opt/REDACTED/java/8.0/jre/lib/ibmorb.jar;/opt/REDACTED/java/8.0/jre/lib/ppc64/compressedrefs/jclSC180/vm.jar;/opt/REDACTED/java/8.0/jre/lib/se-service.jar;/opt/REDACTED/java/8.0/jre/lib/math.jar;/opt/REDACTED/java/8.0/jre/lib/ibmorb.jar;/opt/REDACTED/java/8.0/jre/lib/ibmorbapi.jar;/opt/REDACTED/java/8.0/jre/lib/ibmcfw.jar;/opt/REDACTED/java/8.0/jre/lib/ibmpkcs.jar;/opt/REDACTED/java/8.0/jre/lib/ibmcertpathfw.jar;/opt/REDACTED/java/8.0/jre/lib/ibmjgssfw.jar;/opt/REDACTED/java/8.0/jre/lib/ibmjssefw.jar;/opt/REDACTED/java/8.0/jre/lib/ibmsaslfw.jar;/opt/REDACTED/java/8.0/jre/lib/ibmjcefw.jar;/opt/REDACTED/java/8.0/jre/lib/ibmjgssprovider.jar;/opt/REDACTED/java/8.0/jre/lib/ibmjsseprovider2.jar;/opt/REDACTED/java/8.0/jre/lib/ibmcertpathprovider.jar;/opt/REDACTED/java/8.0/jre/lib/xmldsigfw.jar;/opt/REDACTED/java/8.0/jre/lib/xml.jar;/opt/REDACTED/java/8.0/jre/lib/charsets.jar;/opt/REDACTED/java/8.0/jre/lib/resources.jar;/opt/REDACTED/java/8.0/jre/lib/rt.jar;/opt/REDACTED/java/8.0/jre/lib/dataaccess.jar;/opt/redacted/lib/dd-java-agent.jar; +1CIUSERARGS UserArgs: +2CIUSERARG -Xoptionsfile=/opt/REDACTED/java/8.0/jre/lib/ppc64/compressedrefs/options.default +2CIUSERARG -Xlockword:mode=default,noLockword=java/lang/String,noLockword=java/util/MapEntry,noLockword=java/util/HashMap$Entry,noLockword=org/apache/harmony/luni/util/ModifiedMap$Entry,noLockword=java/util/Hashtable$Entry,noLockword=java/lang/invoke/MethodType,noLockword=java/lang/invoke/MethodHandle,noLockword=java/lang/invoke/CollectHandle,noLockword=java/lang/invoke/ConstructorHandle,noLockword=java/lang/invoke/ConvertHandle,noLockword=java/lang/invoke/ArgumentConversionHandle,noLockword=java/lang/invoke/AsTypeHandle,noLockword=java/lang/invoke/ExplicitCastHandle,noLockword=java/lang/invoke/FilterReturnHandle,noLockword=java/lang/invoke/DirectHandle,noLockword=java/lang/invoke/ReceiverBoundHandle,noLockword=java/lang/invoke/DynamicInvokerHandle,noLockword=java/lang/invoke/FieldHandle,noLockword=java/lang/invoke/FieldGetterHandle,noLockword=java/lang/invoke/FieldSetterHandle,noLockword=java/lang/invoke/StaticFieldGetterHandle,noLockword=java/lang/invoke/StaticFieldSetterHandle,noLockword=java/lang/invoke/IndirectHandle,noLockword=java/lang/invoke/InterfaceHandle,noLockword=java/lang/invoke/VirtualHandle,noLockword=java/lang/invoke/PrimitiveHandle,noLockword=java/lang/invoke/InvokeExactHandle,noLockword=java/lang/invoke/InvokeGenericHandle,noLockword=java/lang/invoke/VarargsCollectorHandle,noLockword=java/lang/invoke/ThunkTuple +2CIUSERARG -Djava.lang.stringBuffer.growAggressively=false +2CIUSERARG -XX:+OriginalJDK8HeapSizeCompatibilityMode +2CIUSERARG -XX:+LegacyXlogOption +2CIUSERARG -XX:+EnsureHashed:java/lang/Class,java/lang/Thread +2CIUSERARG -Xjcl:jclse29 +2CIUSERARG -Dcom.ibm.oti.vm.bootstrap.library.path=/opt/REDACTED/java/8.0/jre/lib/ppc64/compressedrefs:/opt/REDACTED/java/8.0/jre/lib/ppc64 +2CIUSERARG -Dsun.boot.library.path=/opt/REDACTED/java/8.0/jre/lib/ppc64/compressedrefs:/opt/REDACTED/java/8.0/jre/lib/ppc64 +2CIUSERARG -Djava.library.path=/opt/REDACTED/java/8.0/jre/lib/ppc64/compressedrefs:/opt/REDACTED/java/8.0/jre/lib/ppc64:/opt/REDACTED/java/8.0/jre/lib/ppc64/j9vm:/opt/REDACTED/java/8.0/jre/lib/ppc64:/opt/REDACTED/java/8.0/jre/../lib/ppc64:/opt/REDACTED/java/8.0/jre//lib/icc:/lib:/opt/REDACTED/toolkit/lib/aix536:/opt/REDACTED/toolkit/lib/aix536/ttapi:/opt/REDACTED/java/8.0/jre/lib/ppc64/j9vm:/opt/REDACTED/java/8.0/jre/lib/ppc64:/opt/REDACTED/java/8.0/jre/../lib/ppc64:/opt/REDACTED/java/8.0/jre//lib/icc:/opt/REDACTED/lib/native/aix/ppc_64/:/opt/REDACTED/bin:/opt/REDACTED/nulldllsdir:/usr/lib:/usr/lib64:/usr/lib +2CIUSERARG -Djava.home=/opt/REDACTED/java/8.0/jre +2CIUSERARG -Djava.ext.dirs=/opt/REDACTED/java/8.0/jre/fips140-2/lib/ext:/opt/REDACTED/java/8.0/jre/lib/ext +2CIUSERARG -Duser.dir=/opt/REDACTED/REDACTED +2CIUSERARG -Djava.class.path=. +2CIUSERARG -Dosgi.install.area=/opt/REDACTED +2CIUSERARG -Dwas.status.socket=REDACTED +2CIUSERARG -Dosgi.configuration.area=/opt/REDACTED/REDACTED/REDACTED/configuration +2CIUSERARG -Djava.awt.headless=true +2CIUSERARG -Dosgi.framework.extensions=com.ibm.cds,com.ibm.ws.eclipse.adaptors +2CIUSERARG -Xshareclasses:name=webspherev9_8.0_64_%g,nonFatal +2CIUSERARG -Dcom.ibm.xtq.processor.overrideSecureProcessing=true +2CIUSERARG -Xcheck:dump +2CIUSERARG -Djava.security.properties=/opt/REDACTED/properties/java.security +2CIUSERARG -Djava.security.policy=/opt/REDACTED/properties/java.policy +2CIUSERARG -Dcom.ibm.CORBA.ORBPropertyFilePath=/opt/REDACTED/properties +2CIUSERARG -Xbootclasspath/p:/opt/REDACTED/java/8.0/jre/lib/ibmorb.jar +2CIUSERARG -Djava.class.path=/opt/REDACTED/REDACTED/properties:/opt/REDACTED/properties:/opt/REDACTED/lib/startup.jar:/opt/REDACTED/lib/bootstrap.jar:/opt/REDACTED/lib/lmproxy.jar:/opt/REDACTED/lib/urlprotocols.jar:/opt/REDACTED/deploytool/itp/batchboot.jar:/opt/REDACTED/deploytool/itp/batch2.jar:/opt/REDACTED/java/8.0/lib/tools.jar +2CIUSERARG -Dibm.websphere.internalClassAccessMode=allow +2CIUSERARG -verbose:gc +2CIUSERARG -Xverbosegclog:/opt/REDACTED/REDACTED/logs/REDACTED/verbosegc.%seq.log,10,7000 +2CIUSERARG -Xms3072m +2CIUSERARG -Xmx4096m +2CIUSERARG -Xcompressedrefs +2CIUSERARG -Xscmaxaot16M +2CIUSERARG -Xscmx120M +2CIUSERARG -Dws.ext.dirs=/opt/REDACTED/java/8.0/lib:/opt/REDACTED/REDACTED/classes:/opt/REDACTED/classes:/opt/REDACTED/lib:/opt/REDACTED/installedChannels:/opt/REDACTED/lib/ext:/opt/REDACTED/web/help:/opt/REDACTED/deploytool/itp/plugins/com.ibm.etools.ejbdeploy/runtime +2CIUSERARG -Dcom.ibm.itp.location=/opt/REDACTED/bin +2CIUSERARG -Djava.util.logging.configureByServer=true +2CIUSERARG -Duser.install.root=/opt/REDACTED/REDACTED +2CIUSERARG -Djava.ext.dirs=/opt/REDACTED/tivoli/tam:/opt/REDACTED/javaext:/opt/REDACTED/java/8.0/jre/lib/ext +2CIUSERARG -Dpython.cachedir=/opt/REDACTED/REDACTED/temp/cachedir +2CIUSERARG -Dwas.install.root=/opt/REDACTED +2CIUSERARG -Djava.util.logging.manager=com.ibm.ws.bootstrap.WsLogManager +2CIUSERARG -Dserver.root=/opt/REDACTED/REDACTED +2CIUSERARG -Dcom.ibm.security.jgss.debug=off +2CIUSERARG -Dcom.ibm.security.krb5.Krb5Debug=off +2CIUSERARG -DTEMAGCCollector.gclog.path=None +2CIUSERARG -Dam.home=/opt/REDACTED/itcamdc +2CIUSERARG -Dcom.ibm.tivoli.itcam.toolkit.ai.runtimebuilder.enable.rebuild=true +2CIUSERARG -DdisableWSAddressCaching=true +2CIUSERARG -Dlog4j2.formatMsgNoLookups=true +2CIUSERARG -Ddd.integration.jax-ws.enabled=true +2CIUSERARG -Ddd.trace.split-by-tags=servlet.context +2CIUSERARG -Ddd.jmxfetch.enabled=true +2CIUSERARG -Ddd.jmxfetch.start-delay=60 +2CIUSERARG -Ddd.trace.report-hostname=true +2CIUSERARG -Ddd.integration.jms.enabled=true +2CIUSERARG -Ddd.app.customjmxbuilder=false +2CIUSERARG -Ddd.tags=REDACTED:REDACTED, REDACTED:REDACTED,node:REDACTED,cluster:REDACTED +2CIUSERARG -Dcom.sun.management.jmxremote.port=REDACTED +2CIUSERARG -Djavax.management.builder.initial= +2CIUSERARG -Dcom.sun.management.jmxremote.ssl=false +2CIUSERARG -Dcom.sun.management.jmxremote=true +2CIUSERARG -Dcom.ibm.team.server.monitoring.mbean.server=WebSphere +2CIUSERARG -Dcom.sun.management.jmxremote.authenticate=false +2CIUSERARG -Dappdynamics.agent.nodeName=REDACTED +2CIUSERARG -Ddd.env=REDACTED +2CIUSERARG -Dagent.apm.enabled=true +2CIUSERARG -Ddd.version=REDACTED +2CIUSERARG -Ddd.service=REDACTED +2CIUSERARG -javaagent:/opt/redacted/lib/dd-java-agent.jar +2CIUSERARG -agentlib:hprof=cpu=samples,depth=100,interval=10,lineno=y,thread=y,file=out.hprof.txt +2CIUSERARG -Ddd.profiling.enabled=false +2CIUSERARG -Ddd.trace.classloaders.exclude=com.ibm.xml.xlxp2.jaxb.codegen.AbstractGeneratedStubFactory\$RootStubClassLoader +2CIUSERARG -XX:FlightRecorderOptions=stackdepth=256 +2CIUSERARG -agentlib:am_ibm_16=REDACTED +2CIUSERARG -verbose:gc +2CIUSERARG -Dsun.rmi.dgc.client.gcInterval=3600000 +2CIUSERARG -Dsun.rmi.dgc.server.gcInterval=3600000 +2CIUSERARG -Dsun.rmi.transport.connectionTimeout=300000 +2CIUSERARG -Dcom.ibm.jsse2.disableSSLv3=false +2CIUSERARG -Dsun.net.inetaddr.ttl=15 +2CIUSERARG -Dsun.net.inetaddr.negative.ttl=5 +2CIUSERARG -Djava.library.path=/opt/REDACTED/lib/native/aix/ppc_64/:/opt/REDACTED/java/8.0/jre/lib/ppc64/compressedrefs:/opt/REDACTED/java/8.0/jre/lib/ppc64:/opt/REDACTED/java/8.0/jre/lib/ppc64/j9vm:/opt/REDACTED/java/8.0/jre/lib/ppc64:/opt/REDACTED/java/8.0/jre/../lib/ppc64:/opt/REDACTED/java/8.0/jre//lib/icc:/opt/REDACTED/bin:/opt/REDACTED/nulldllsdir:/usr/lib64:/usr/lib:/lib:/opt/REDACTED/toolkit/lib/aix536:/opt/REDACTED/toolkit/lib/aix536/ttapi: +2CIUSERARG -Djava.endorsed.dirs=/opt/REDACTED/endorsed_apis:/opt/REDACTED/java/8.0/jre/lib/endorsed +2CIUSERARG -Djava.security.auth.login.config=/opt/REDACTED/REDACTED/properties/wsjaas.conf +2CIUSERARG -Djava.security.policy=/opt/REDACTED/REDACTED/properties/server.policy +2CIUSERARG -Dsun.java.command=com.ibm.wsspi.bootstrap.WSPreLauncher -nosplash -application com.ibm.ws.bootstrap.WSLauncher com.ibm.ws.runtime.WsServer /opt/REDACTED/REDACTED/config REDACTED REDACTED REDACTED +2CIUSERARG -Dsun.java.launcher=SUN_STANDARD +NULL +1CIIGNOREDARGS Ignored Args: +2CIIGNOREDARG -XX:FlightRecorderOptions=stackdepth=256 From 9e4050201fcb31d97d6baf84bd85c4b9d1f3f70c Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Wed, 1 Apr 2026 13:36:00 +0200 Subject: [PATCH 2/2] fix(crashtracking): use stringbuilder instead of concatenation --- .../crashtracking/parsers/RuntimeArgs.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/RuntimeArgs.java b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/RuntimeArgs.java index e11f2dad9e7..0e871a4c6a8 100644 --- a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/RuntimeArgs.java +++ b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/RuntimeArgs.java @@ -183,6 +183,7 @@ private static List joinArgumentTokens(List tokens) { } List joinedArgs = new ArrayList<>(); boolean canContinuePreviousArg = false; + StringBuilder argBuilder = new StringBuilder(); for (int i = 0; i < tokens.size(); i++) { String token = tokens.get(i); if (token.isEmpty()) { @@ -194,22 +195,26 @@ private static List joinArgumentTokens(List tokens) { && isContinuationToken(token) && acceptsContinuation(joinedArgs.get(joinedArgs.size() - 1))) { int last = joinedArgs.size() - 1; - joinedArgs.set(last, joinedArgs.get(last) + " " + token); + argBuilder.setLength(0); + argBuilder.append(joinedArgs.get(last)).append(' ').append(token); + joinedArgs.set(last, argBuilder.toString()); continue; } canContinuePreviousArg = false; continue; } - String arg = token; + argBuilder.setLength(0); + argBuilder.append(token); if (requiresSeparateValue(token) && i + 1 < tokens.size()) { - arg = token + " " + tokens.get(++i); + argBuilder.append(' ').append(tokens.get(++i)); } while (i + 1 < tokens.size() && isContinuationToken(tokens.get(i + 1)) - && acceptsContinuation(arg)) { - arg = arg + " " + tokens.get(++i); + && acceptsContinuation(argBuilder.toString())) { + argBuilder.append(' ').append(tokens.get(++i)); } + String arg = argBuilder.toString(); joinedArgs.add(arg); canContinuePreviousArg = acceptsContinuation(arg); }