Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public static CrashLog parse(String uuid, String content) {
if (isJ9Javacore(content)) {
return fromJ9Javacore(uuid, content);
}
return fromHotspotCrashLog(uuid, content);
return new HotspotCrashLogParser().parse(uuid, content);
}

/** Check if the content appears to be a J9 javacore file. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@ private RequestBody makeErrorTrackingRequestBody(@Nonnull CrashLog payload, bool
// experimental
if (payload.experimental != null
&& (payload.experimental.ucontext != null
|| payload.experimental.registerToMemoryMapping != null
|| payload.experimental.runtimeArgs != null)) {
writer.name("experimental");
writer.beginObject();
Expand All @@ -590,6 +591,16 @@ private RequestBody makeErrorTrackingRequestBody(@Nonnull CrashLog payload, bool
}
writer.endObject();
}
if (uploaderSettings.isExtendedInfoEnabled()
&& payload.experimental.registerToMemoryMapping != null) {
writer.name("register_to_memory_mapping");
writer.beginObject();
for (Map.Entry<String, String> entry :
payload.experimental.registerToMemoryMapping.entrySet()) {
writer.name(entry.getKey()).value(entry.getValue());
}
writer.endObject();
}
if (uploaderSettings.isExtendedInfoEnabled()
&& payload.experimental.runtimeArgs != null) {
writer.name("runtime_args");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,40 @@
public final class Experimental {
public final Map<String, String> ucontext;

@Json(name = "register_to_memory_mapping")
public final Map<String, String> registerToMemoryMapping;

@Json(name = "runtime_args")
public final List<String> runtimeArgs;

public Experimental(Map<String, String> ucontext) {
this(ucontext, null);
this(ucontext, null, null);
}

public Experimental(Map<String, String> ucontext, List<String> runtimeArgs) {
this(ucontext, null, runtimeArgs);
}

public Experimental(
Map<String, String> ucontext,
Map<String, String> registerToMemoryMapping,
List<String> runtimeArgs) {
this.ucontext = ucontext;
this.registerToMemoryMapping = registerToMemoryMapping;
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) && Objects.equals(runtimeArgs, that.runtimeArgs);
return Objects.equals(ucontext, that.ucontext)
&& Objects.equals(registerToMemoryMapping, that.registerToMemoryMapping)
&& Objects.equals(runtimeArgs, that.runtimeArgs);
}

@Override
public int hashCode() {
return Objects.hash(ucontext, runtimeArgs);
return Objects.hash(ucontext, registerToMemoryMapping, runtimeArgs);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ enum State {
SUMMARY,
THREAD,
STACKTRACE,
REGISTER_TO_MEMORY_MAPPING,
REGISTERS,
PROCESS,
VM_ARGUMENTS,
Expand Down Expand Up @@ -95,12 +96,15 @@ public HotspotCrashLogParser() {
// per line.
private static final Pattern REGISTER_ENTRY_PARSER =
Pattern.compile("([A-Za-z][A-Za-z0-9]*)\\s*=\\s*(0x[0-9a-fA-F]+)");
private static final Pattern REGISTER_TO_MEMORY_MAPPING_PARSER =
Pattern.compile("^\\s*([A-Za-z][A-Za-z0-9]*)\\s*=");
// Used for the REGISTERS-state exit condition only: the register name must start the line
// (after optional whitespace). This prevents lines like "Top of Stack: (sp=0x...)" and
// "Instructions: (pc=0x...)" from being mistaken for register entries by REGISTER_ENTRY_PARSER's
// find(), which would otherwise match the lowercase "sp"/"pc" tokens embedded in those lines.
private static final Pattern REGISTER_LINE_START =
Pattern.compile("^\\s*[A-Za-z][A-Za-z0-9]*\\s*=\\s*0x");
private static final Pattern SUBSECTION_TITLE = Pattern.compile("^\\s*[A-Za-z][\\w ]*:.+$");
private static final Pattern COMPILED_JAVA_ADDRESS_PARSER =
Pattern.compile("@\\s+(0x[0-9a-fA-F]+)\\s+\\[(0x[0-9a-fA-F]+)\\+(0x[0-9a-fA-F]+)\\]");

Expand Down Expand Up @@ -350,10 +354,14 @@ public CrashLog parse(String uuid, String crashLog) {
String datetimeRaw = null;
boolean incomplete = false;
String oomMessage = null;
Map<String, String> registers = null;
Map<String, String> registers = new LinkedHashMap<>();
Comment thread
jbachorik marked this conversation as resolved.
Map<String, String> registerToMemoryMapping = new LinkedHashMap<>();
String currentRegisterToMemoryMapping = "";
List<String> runtimeArgs = null;
List<String> dynamicLibraryLines = null;
String dynamicLibraryKey = null;
boolean previousLineBlank = false;
State nextThreadSectionState = null;

String[] lines = NEWLINE_SPLITTER.split(crashLog);
outer:
Expand Down Expand Up @@ -410,7 +418,10 @@ public CrashLog parse(String uuid, String crashLog) {
}
break;
case STACKTRACE:
if (line.startsWith("siginfo:")) {
nextThreadSectionState = nextThreadSectionState(line, previousLineBlank);
if (nextThreadSectionState != null) {
state = nextThreadSectionState;
} else if (line.startsWith("siginfo:")) {
// spotless:off
// siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x70
// siginfo: si_signo: 11 (SIGSEGV), si_code: 0 (SI_USER), si_pid: 554848, si_uid: 1000
Expand All @@ -426,11 +437,6 @@ public CrashLog parse(String uuid, String crashLog) {
Integer siUid = safelyParseInt(siginfoMatcher.group(7));
sigInfo = new SigInfo(number, name, siCode, sigAction, address, siPid, siUid);
}
} else if (line.startsWith("Registers:")) {
registers = new LinkedHashMap<>();
state = State.REGISTERS;
} else if (line.contains("P R O C E S S")) {
state = State.PROCESS;
} else {
// Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
final StackFrame frame = parseLine(line);
Expand All @@ -439,8 +445,31 @@ public CrashLog parse(String uuid, String crashLog) {
}
}
break;
case REGISTER_TO_MEMORY_MAPPING:
nextThreadSectionState = nextThreadSectionState(line, previousLineBlank);
if (nextThreadSectionState != null) {
currentRegisterToMemoryMapping = "";
state = nextThreadSectionState;
} else if (line.startsWith("siginfo:")) {
// siginfo marks the end of the register-to-memory mapping section;
// stop appending continuations to the last register's value
currentRegisterToMemoryMapping = "";
} else if (!line.isEmpty()) {
Comment thread
bric3 marked this conversation as resolved.
final Matcher m = REGISTER_TO_MEMORY_MAPPING_PARSER.matcher(line);
if (m.lookingAt()) {
currentRegisterToMemoryMapping = m.group(1);
registerToMemoryMapping.put(currentRegisterToMemoryMapping, line.substring(m.end()));
} else if (!currentRegisterToMemoryMapping.isEmpty()) {
registerToMemoryMapping.computeIfPresent(
currentRegisterToMemoryMapping, (key, value) -> value + "\n" + line);
}
}
break;
case REGISTERS:
if (!line.isEmpty() && !REGISTER_LINE_START.matcher(line).find()) {
nextThreadSectionState = nextThreadSectionState(line, previousLineBlank);
if (nextThreadSectionState != null) {
state = nextThreadSectionState;
} else if (!line.isEmpty() && !REGISTER_LINE_START.matcher(line).find()) {
// non-empty line that does not start with a register entry signals end of section
state = State.STACKTRACE;
} else {
Expand Down Expand Up @@ -508,6 +537,7 @@ public CrashLog parse(String uuid, String crashLog) {
// unexpected parser state; bail out
break outer;
}
previousLineBlank = line.isEmpty();
}

// PROCESS and SYSTEM sections are late enough that all critical data is captured
Expand Down Expand Up @@ -571,10 +601,16 @@ public CrashLog parse(String uuid, String crashLog) {
Metadata metadata = new Metadata("dd-trace-java", VersionInfo.VERSION, "java", null);
Integer parsedPid = safelyParseInt(pid);
ProcInfo procInfo = parsedPid != null ? new ProcInfo(parsedPid) : null;
Map<String, String> resolvedMapping = null;
if (!registerToMemoryMapping.isEmpty()) {
registerToMemoryMapping.replaceAll((k, v) -> RedactUtils.redactRegisterToMemoryMapping(v));
resolvedMapping = registerToMemoryMapping;
}
Experimental experimental =
(registers != null && !registers.isEmpty())
!registers.isEmpty()
|| resolvedMapping != null
|| (runtimeArgs != null && !runtimeArgs.isEmpty())
? new Experimental(registers, runtimeArgs)
? new Experimental(registers, resolvedMapping, runtimeArgs)
: null;
DynamicLibs files =
(dynamicLibraryLines != null && !dynamicLibraryLines.isEmpty())
Expand Down Expand Up @@ -608,6 +644,27 @@ static String dateTimeToISO(String datetime) {
}
}

private static State nextThreadSectionState(String line, boolean previousLineBlank) {
if (line.startsWith("Register to memory mapping:")) {
return State.REGISTER_TO_MEMORY_MAPPING;
}
if (line.startsWith("Registers:")) {
return State.REGISTERS;
}
if (line.startsWith("siginfo:")) {
// siginfo is handled directly in the STACKTRACE case; returning null here does not change the
// parser state
return null;
Comment thread
bric3 marked this conversation as resolved.
}
if (line.contains("P R O C E S S")) {
return State.PROCESS;
}
if (previousLineBlank && SUBSECTION_TITLE.matcher(line).matches()) {
return State.STACKTRACE;
}
return null;
}

/**
* Detects whether the Dynamic libraries section comes from Linux {@code /proc/self/maps} (address
* range format {@code addr-addr perms ...}) or from the BSD/macOS dyld callback (format {@code
Expand Down
Loading