Skip to content

Commit c71deb4

Browse files
committed
feat(crashtracking): Add crashtracking.experimental.register-mapping.enabled
1 parent 3ddb175 commit c71deb4

File tree

13 files changed

+186
-53
lines changed

13 files changed

+186
-53
lines changed

dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/ConfigManager.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public static class StoredConfig {
3535
final String reportUUID;
3636
final boolean agentless;
3737
final boolean sendToErrorTracking;
38+
final boolean registerMappingEnabled;
3839

3940
StoredConfig(
4041
String reportUUID,
@@ -45,7 +46,8 @@ public static class StoredConfig {
4546
String processTags,
4647
String runtimeId,
4748
boolean agentless,
48-
boolean sendToErrorTracking) {
49+
boolean sendToErrorTracking,
50+
boolean registerMappingEnabled) {
4951
this.service = service;
5052
this.env = env;
5153
this.version = version;
@@ -55,6 +57,11 @@ public static class StoredConfig {
5557
this.reportUUID = reportUUID;
5658
this.agentless = agentless;
5759
this.sendToErrorTracking = sendToErrorTracking;
60+
this.registerMappingEnabled = registerMappingEnabled;
61+
}
62+
63+
public CrashUploaderSettings toCrashUploaderSettings() {
64+
return new CrashUploaderSettings(registerMappingEnabled);
5865
}
5966

6067
public static class Builder {
@@ -67,6 +74,7 @@ public static class Builder {
6774
String reportUUID;
6875
boolean agentless;
6976
boolean sendToErrorTracking;
77+
boolean registerMappingEnabled;
7078

7179
public Builder(Config config) {
7280
// get sane defaults
@@ -77,6 +85,7 @@ public Builder(Config config) {
7785
this.reportUUID = RandomUtils.randomUUID().toString();
7886
this.agentless = config.isCrashTrackingAgentless();
7987
this.sendToErrorTracking = config.isCrashTrackingErrorsIntakeEnabled();
88+
this.registerMappingEnabled = config.isCrashTrackingExperimentalRegisterMappingEnabled();
8089
}
8190

8291
public Builder service(String service) {
@@ -119,6 +128,11 @@ public Builder agentless(boolean agentless) {
119128
return this;
120129
}
121130

131+
public Builder registerMappingEnabled(boolean registerMappingEnabled) {
132+
this.registerMappingEnabled = registerMappingEnabled;
133+
return this;
134+
}
135+
122136
// @VisibleForTesting
123137
Builder reportUUID(String reportUUID) {
124138
this.reportUUID = reportUUID;
@@ -135,7 +149,8 @@ public StoredConfig build() {
135149
processTags,
136150
runtimeId,
137151
agentless,
138-
sendToErrorTracking);
152+
sendToErrorTracking,
153+
registerMappingEnabled);
139154
}
140155
}
141156
}
@@ -194,6 +209,10 @@ static void writeConfigToFile(Config config, Path cfgPath, String... additionalE
194209
writeEntry(bw, "java_home", SystemProperties.get("java.home"));
195210
writeEntry(bw, "agentless", Boolean.toString(config.isCrashTrackingAgentless()));
196211
writeEntry(bw, "upload_to_et", Boolean.toString(config.isCrashTrackingErrorsIntakeEnabled()));
212+
writeEntry(
213+
bw,
214+
"register_mapping",
215+
Boolean.toString(config.isCrashTrackingExperimentalRegisterMappingEnabled()));
197216

198217
Runtime.getRuntime()
199218
.addShutdownHook(
@@ -257,6 +276,9 @@ public static StoredConfig readConfig(Config config, Path scriptPath) {
257276
case "upload_to_et":
258277
cfgBuilder.sendToErrorTracking(Boolean.parseBoolean(value));
259278
break;
279+
case "register_mapping":
280+
cfgBuilder.registerMappingEnabled(Boolean.parseBoolean(value));
281+
break;
260282
default:
261283
// ignore
262284
break;

dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/CrashLogParser.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,18 @@ public static CrashLog fromJ9Javacore(String uuid, String javacoreContent) {
3333
* </ul>
3434
*/
3535
public static CrashLog parse(String uuid, String content) {
36+
return parse(uuid, content, new CrashUploaderSettings(true));
37+
}
38+
39+
/**
40+
* Auto-detect crash log format and parse accordingly, using the provided settings to control
41+
* which sections are included in the result.
42+
*/
43+
public static CrashLog parse(String uuid, String content, CrashUploaderSettings settings) {
3644
if (isJ9Javacore(content)) {
3745
return fromJ9Javacore(uuid, content);
3846
}
39-
return fromHotspotCrashLog(uuid, content);
47+
return new HotspotCrashLogParser(settings).parse(uuid, content);
4048
}
4149

4250
/** Check if the content appears to be a J9 javacore file. */

dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/CrashUploader.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,8 @@ void remoteUpload(
257257
final String uuid = storedConfig.reportUUID;
258258
try {
259259
// Auto-detect crash log format (HotSpot hs_err or J9 javacore)
260-
CrashLog crashLog = CrashLogParser.parse(uuid, fileContent);
260+
CrashLog crashLog =
261+
CrashLogParser.parse(uuid, fileContent, storedConfig.toCrashUploaderSettings());
261262
if (sendToTelemetry) {
262263
uploadToTelemetry(crashLog);
263264
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package datadog.crashtracking;
2+
3+
/**
4+
* Crash-tracking-specific settings controlling which sections are included in parsed crash reports.
5+
*/
6+
public final class CrashUploaderSettings {
7+
8+
private final boolean registerMappingEnabled;
9+
10+
public CrashUploaderSettings(boolean registerMappingEnabled) {
11+
this.registerMappingEnabled = registerMappingEnabled;
12+
}
13+
14+
/** Whether the register-to-memory mapping section should be included in parsed crash reports. */
15+
public boolean isRegisterMappingEnabled() {
16+
return registerMappingEnabled;
17+
}
18+
}

dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/HotspotCrashLogParser.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
44

55
import datadog.common.version.VersionInfo;
6+
import datadog.crashtracking.CrashUploaderSettings;
67
import datadog.crashtracking.buildid.BuildIdCollector;
78
import datadog.crashtracking.buildid.BuildInfo;
89
import datadog.crashtracking.dto.CrashLog;
@@ -72,9 +73,15 @@ enum State {
7273
}
7374

7475
private State state = State.NEW;
76+
private final CrashUploaderSettings settings;
7577

7678
public HotspotCrashLogParser() {
79+
this(new CrashUploaderSettings(true));
80+
}
81+
82+
public HotspotCrashLogParser(CrashUploaderSettings settings) {
7783
this.buildIdCollector = new BuildIdCollector();
84+
this.settings = settings;
7885
}
7986

8087
private static final Pattern PLUS_SPLITTER = Pattern.compile("\\+");
@@ -598,12 +605,16 @@ public CrashLog parse(String uuid, String crashLog) {
598605
Metadata metadata = new Metadata("dd-trace-java", VersionInfo.VERSION, "java", null);
599606
Integer parsedPid = safelyParseInt(pid);
600607
ProcInfo procInfo = parsedPid != null ? new ProcInfo(parsedPid) : null;
601-
registerToMemoryMapping.replaceAll((k, v) -> RedactUtils.redactRegisterToMemoryMapping(v));
608+
Map<String, String> resolvedMapping = null;
609+
if (settings.isRegisterMappingEnabled() && !registerToMemoryMapping.isEmpty()) {
610+
registerToMemoryMapping.replaceAll((k, v) -> RedactUtils.redactRegisterToMemoryMapping(v));
611+
resolvedMapping = registerToMemoryMapping;
612+
}
602613
Experimental experimental =
603614
!registers.isEmpty()
604-
|| !registerToMemoryMapping.isEmpty()
615+
|| resolvedMapping != null
605616
|| (runtimeArgs != null && !runtimeArgs.isEmpty())
606-
? new Experimental(registers, registerToMemoryMapping, runtimeArgs)
617+
? new Experimental(registers, resolvedMapping, runtimeArgs)
607618
: null;
608619
DynamicLibs files =
609620
(dynamicLibraryLines != null && !dynamicLibraryLines.isEmpty())

dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/RedactUtils.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ public final class RedactUtils {
4444
private static final Pattern METHOD_IN_CLASS = Pattern.compile("( in ')([^']+)'");
4545

4646
// Object-reference field values in oop dumps: a 'com/company/Class'{0x...}
47-
private static final Pattern OBJ_FIELD_REF =
48-
Pattern.compile("(a ')([A-Za-z$_][A-Za-z0-9$_/]*)'");
47+
private static final Pattern OBJ_FIELD_REF = Pattern.compile("(a ')([A-Za-z$_][A-Za-z0-9$_/]*)'");
4948

5049
// Class name in nmethod compiled-method output (JDK 11+):
5150
// "Compiled method (c2) ... com.company.Foo::methodName (N bytes)" (PRODUCT — dots)
@@ -163,8 +162,7 @@ static String redactTypeDescriptors(String line) {
163162
* klass: 'redacted/Redacted'</code>
164163
*/
165164
static String redactKlassReference(String line) {
166-
return replaceAll(
167-
KLASS_REF, line, m -> m.group(1) + redactJvmClassName(m.group(2)) + "'");
165+
return replaceAll(KLASS_REF, line, m -> m.group(1) + redactJvmClassName(m.group(2)) + "'");
168166
}
169167

170168
/**
@@ -190,8 +188,7 @@ static String redactLibraryPath(String line) {
190188
* 'com/company/Class'</code> to <code>a 'redacted/Redacted'</code>.
191189
*/
192190
static String redactObjFieldRef(String line) {
193-
return replaceAll(
194-
OBJ_FIELD_REF, line, m -> m.group(1) + redactJvmClassName(m.group(2)) + "'");
191+
return replaceAll(OBJ_FIELD_REF, line, m -> m.group(1) + redactJvmClassName(m.group(2)) + "'");
195192
}
196193

197194
/**

dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/ConfigManagerTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public void testConfigWriteAndRead() throws IOException {
2424
.thenReturn(new WellKnownTags("1234", "", "env", "service", "version", ""));
2525
when(config.isCrashTrackingAgentless()).thenReturn(false);
2626
when(config.isCrashTrackingErrorsIntakeEnabled()).thenReturn(true);
27+
when(config.isCrashTrackingExperimentalRegisterMappingEnabled()).thenReturn(true);
2728
when(config.getMergedCrashTrackingTags()).thenReturn(Collections.singletonMap("key", "value"));
2829
File tmpFile = File.createTempFile("ConfigManagerTest", null);
2930
tmpFile.deleteOnExit();
@@ -41,6 +42,7 @@ public void testConfigWriteAndRead() throws IOException {
4142
deserialized.processTags);
4243
assertFalse(deserialized.agentless);
4344
assertTrue(deserialized.sendToErrorTracking);
45+
assertTrue(deserialized.registerMappingEnabled);
4446
}
4547

4648
@Test
@@ -56,5 +58,6 @@ public void testStoredConfigDefaults() {
5658
assertEquals("env", storedConfig.env);
5759
assertFalse(storedConfig.agentless);
5860
assertFalse(storedConfig.sendToErrorTracking);
61+
assertFalse(storedConfig.registerMappingEnabled);
5962
}
6063
}

dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/CrashUploaderTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ public void testErrorTrackingSerializesRegisterToMemoryMapping() throws Exceptio
401401
.processTags("a:b")
402402
.runtimeId("1234")
403403
.tags(ConfigManager.getMergedTagsForSerialization(Config.get()))
404+
.registerMappingEnabled(true)
404405
.build();
405406

406407
uploader = new CrashUploader(config, crashConfig);
@@ -418,6 +419,28 @@ public void testErrorTrackingSerializesRegisterToMemoryMapping() throws Exceptio
418419
assertThat(mapping.get("RDI").asText()).isEqualTo("0x0 is NULL");
419420
}
420421

422+
@Test
423+
public void testErrorTrackingOmitsRegisterToMemoryMappingByDefault() throws Exception {
424+
// registerMappingEnabled defaults to false — the mapping must not appear in the payload
425+
ConfigManager.StoredConfig crashConfig =
426+
new ConfigManager.StoredConfig.Builder(config)
427+
.reportUUID(SAMPLE_UUID)
428+
.processTags("a:b")
429+
.runtimeId("1234")
430+
.tags(ConfigManager.getMergedTagsForSerialization(Config.get()))
431+
.build();
432+
433+
uploader = new CrashUploader(config, crashConfig);
434+
server.enqueue(new MockResponse().setResponseCode(200));
435+
uploader.remoteUpload(readFileAsString("sample-crash.txt"), false, true);
436+
437+
final RecordedRequest recordedRequest = server.takeRequest(5, TimeUnit.SECONDS);
438+
final ObjectMapper mapper = new ObjectMapper();
439+
final JsonNode event = mapper.readTree(recordedRequest.getBody().readUtf8());
440+
441+
assertThat(event.at("/experimental/register_to_memory_mapping").isMissingNode()).isTrue();
442+
}
443+
421444
private void assertCommonHeader(JsonNode event) {
422445
assertEquals(TELEMETRY_API_VERSION, event.get("api_version").asText());
423446
assertEquals("logs", event.get("request_type").asText());

dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/HotspotCrashLogParserTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import static org.junit.jupiter.api.Assertions.assertNotNull;
88
import static org.junit.jupiter.api.Assertions.assertTrue;
99

10+
import datadog.crashtracking.CrashUploaderSettings;
1011
import datadog.crashtracking.dto.CrashLog;
1112
import java.io.BufferedReader;
1213
import java.io.IOException;
@@ -304,6 +305,31 @@ public void testParseCurrentThreadName(String line, String expected) {
304305
HotspotCrashLogParser.parseCurrentThreadName(line));
305306
}
306307

308+
@Test
309+
public void testRegisterToMemoryMappingExcludedWhenDisabled() throws Exception {
310+
CrashLog crashLog =
311+
new HotspotCrashLogParser(new CrashUploaderSettings(false))
312+
.parse(UUID.randomUUID().toString(), readFileAsString("sample-crash.txt"));
313+
314+
assertNotNull(crashLog.experimental, "registers and runtimeArgs should still be populated");
315+
assertThat(crashLog.experimental.registerToMemoryMapping)
316+
.as("registerToMemoryMapping should be absent when disabled")
317+
.isNull();
318+
}
319+
320+
@Test
321+
public void testRegisterToMemoryMappingIncludedWhenEnabled() throws Exception {
322+
CrashLog crashLog =
323+
new HotspotCrashLogParser(new CrashUploaderSettings(true))
324+
.parse(UUID.randomUUID().toString(), readFileAsString("sample-crash.txt"));
325+
326+
assertNotNull(crashLog.experimental);
327+
assertNotNull(crashLog.experimental.registerToMemoryMapping);
328+
assertFalse(
329+
crashLog.experimental.registerToMemoryMapping.isEmpty(),
330+
"registerToMemoryMapping should be populated when enabled");
331+
}
332+
307333
private String readFileAsString(String resource) throws IOException {
308334
try (InputStream stream = getClass().getClassLoader().getResourceAsStream(resource)) {
309335
return new BufferedReader(

0 commit comments

Comments
 (0)