Skip to content
Open
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 @@ -525,6 +525,9 @@ private RequestBody makeErrorTrackingRequestBody(@Nonnull CrashLog payload, bool
}
writer.name("type").value(payload.error.kind);
writer.name("message").value(payload.error.message);
if (payload.error.threadName != null) {
writer.name("thread_name").value(payload.error.threadName);
}
writer.name("source_type").value("Crashtracking");
if (payload.error.stack != null) {
writer.name("stack");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,22 @@ public final class ErrorData {
public final String kind;
public final String message;

@Json(name = "thread_name")
public final String threadName;

@Json(name = "source_type")
public final String sourceType = "Crashtracking";

public final StackTrace stack;

public ErrorData(String kind, String message, StackTrace stack) {
this(kind, message, null, stack);
}

public ErrorData(String kind, String message, String threadName, StackTrace stack) {
this.kind = kind;
this.message = message;
this.threadName = threadName;
this.stack = stack;
}

Expand All @@ -33,12 +41,13 @@ public boolean equals(Object o) {
return isCrash == errorData.isCrash
&& Objects.equals(kind, errorData.kind)
&& Objects.equals(message, errorData.message)
&& Objects.equals(threadName, errorData.threadName)
&& Objects.equals(sourceType, errorData.sourceType)
&& Objects.equals(stack, errorData.stack);
}

@Override
public int hashCode() {
return Objects.hash(isCrash, kind, message, sourceType, stack);
return Objects.hash(isCrash, kind, message, threadName, sourceType, stack);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ public final class StackFrame {
public final Integer line;
public final String function;

@Json(name = "type")
public final String frameType;

@Json(name = "build_id")
public final String buildId;

Expand All @@ -19,23 +22,35 @@ public final class StackFrame {
@Json(name = "file_type")
public final BuildInfo.FileType fileType;

@Json(name = "ip")
public final String ip;

@Json(name = "symbol_address")
public final String symbolAddress;

@Json(name = "relative_address")
public String relativeAddress;

public StackFrame(
String path,
Integer line,
String function,
String frameType,
String buildId,
BuildInfo.BuildIdType buildIdType,
BuildInfo.FileType fileType,
String ip,
String symbolAddress,
String relativeAddress) {
this.path = path;
this.line = line;
this.function = function;
this.frameType = frameType;
this.buildId = buildId;
this.buildIdType = buildIdType;
this.fileType = fileType;
this.ip = ip;
this.symbolAddress = symbolAddress;
this.relativeAddress = relativeAddress;
}

Expand All @@ -51,14 +66,27 @@ public boolean equals(Object o) {
return Objects.equals(path, that.path)
&& Objects.equals(line, that.line)
&& Objects.equals(function, that.function)
&& Objects.equals(frameType, that.frameType)
&& Objects.equals(buildId, that.buildId)
&& buildIdType == that.buildIdType
&& fileType == that.fileType
&& Objects.equals(ip, that.ip)
&& Objects.equals(symbolAddress, that.symbolAddress)
&& Objects.equals(relativeAddress, that.relativeAddress);
}

@Override
public int hashCode() {
return Objects.hash(path, line, function, buildId, buildIdType, fileType, relativeAddress);
return Objects.hash(
path,
line,
function,
frameType,
buildId,
buildIdType,
fileType,
ip,
symbolAddress,
relativeAddress);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,39 @@ public HotspotCrashLogParser() {
// 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 COMPILED_JAVA_ADDRESS_PARSER =
Pattern.compile("@\\s+(0x[0-9a-fA-F]+)\\s+\\[(0x[0-9a-fA-F]+)\\+(0x[0-9a-fA-F]+)\\]");

// HotSpot crash logs encode the execution kind in the first column of each frame line.
// Source references:
// JDK 8:
// https://github.com/openjdk/jdk8u/blob/73c9c6bcd062196cbebc4d9f22b13d2e20a14f98/hotspot/src/share/vm/runtime/frame.cpp#L710-L724
// JDK 11:
// https://github.com/openjdk/jdk11u/blob/970d6cf491a55fd6ab98ec3f449c13a58633078a/src/hotspot/share/runtime/frame.cpp#L647-L662
// JDK 25:
// https://github.com/openjdk/jdk25u/blob/2fe611a2a3386d097f636c15bd4d396a82dc695e/src/hotspot/share/runtime/frame.cpp#L652-L666
// Mainline:
// https://github.com/openjdk/jdk/blob/53c864a881d2183d3664a6a5a56480bd99fffe45/src/hotspot/share/runtime/frame.cpp#L647-L661
// Note: the marker set changes across JDK lines. In particular, "A" appears in some HotSpot
// versions but not all, so this mapping is best-effort rather than a stable cross-version enum.
private static String hotspotFrameType(char marker) {
switch (marker) {
case 'J':
return "compiled";
case 'A': // exists in JDK 11
return "aot_compiled";
case 'j':
return "interpreted";
case 'V':
return "vm";
case 'v':
return "stub";
case 'C':
return "native";
default:
return null;
}
}

private StackFrame parseLine(String line) {
if (line == null || line.isEmpty()) {
Expand All @@ -107,24 +140,52 @@ private StackFrame parseLine(String line) {
String functionName = null;
Integer functionLine = null;
String filename = null;
String ip = null;
String relAddress = null;
String symbolAddress = null;
char firstChar = line.charAt(0);
String frameType = hotspotFrameType(firstChar);
if (line.length() > 1 && !Character.isSpaceChar(line.charAt(1))) {
// We can find entries like this in between the frames
// Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
return null;
}
switch (firstChar) {
case 'J':
case 'A':
{
// spotless:off
// J 36572 c2 datadog.trace.util.AgentTaskScheduler$PeriodicTask.run()V (25 bytes) @ 0x00007f2fd0198488 [0x00007f2fd0198420+0x0000000000000068]
// J 3896 c2 java.nio.ByteBuffer.allocate(I)Ljava/nio/ByteBuffer; java.base@21.0.1 (20 bytes) @ 0x0000000112ad51e8 [0x0000000112ad4fc0+0x0000000000000228]
// J 302 java.util.zip.ZipFile.getEntry(J[BZ)J (0 bytes) @ 0x00007fa287303dce [0x00007fa287303d00+0xce]
// spotless:on
String[] parts = SPACE_SPLITTER.split(line);
if (parts.length > 3) {
int bytesToken = -1;
for (int i = 0; i < parts.length - 1; i++) {
if (parts[i].startsWith("(") && "bytes)".equals(parts[i + 1])) {
bytesToken = i;
break;
}
}
if (bytesToken > 1) {
String candidate = parts[bytesToken - 1];
// Newer JVMs insert a module token before "(NN bytes)".
if (candidate.contains("@")) {
candidate = parts[bytesToken - 2];
}
if (!candidate.startsWith("(")) {
functionName = candidate;
}
} else if (parts.length > 3 && !parts[3].startsWith("(")) {
functionName = parts[3];
}

Matcher matcher = COMPILED_JAVA_ADDRESS_PARSER.matcher(line);
if (matcher.find()) {
ip = matcher.group(1);
symbolAddress = matcher.group(2);
relAddress = matcher.group(3);
}
break;
}
case 'j':
Expand Down Expand Up @@ -200,9 +261,12 @@ private StackFrame parseLine(String line) {
filename,
functionLine,
stripCompilerAnnotations(functionName),
frameType,
null,
null,
null,
ip,
symbolAddress,
relAddress);
}
return null;
Expand Down Expand Up @@ -247,9 +311,30 @@ private static String normalizeFilename(String filename) {
return filename.substring(0, prefixLen) + filename.substring(end);
}

static String parseCurrentThreadName(String line) {
if (line == null || !line.startsWith("Current thread ")) {
return null;
}
final int separator = line.indexOf(':');
if (separator < 0) {
return null;
}

String threadDescriptor = line.substring(separator + 1).trim();
final int metadataStart = threadDescriptor.indexOf('[');
if (metadataStart >= 0) {
threadDescriptor = threadDescriptor.substring(0, metadataStart).trim();
}
if (threadDescriptor.isEmpty()) {
return null;
}
return threadDescriptor;
}

public CrashLog parse(String uuid, String crashLog) {
SigInfo sigInfo = null;
String pid = null;
String threadName = null;
List<StackFrame> frames = new ArrayList<>();
String datetime = null;
String datetimeRaw = null;
Expand Down Expand Up @@ -303,6 +388,9 @@ public CrashLog parse(String uuid, String crashLog) {
}
break;
case THREAD:
if (threadName == null) {
threadName = parseCurrentThreadName(line);
}
// Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
if (line.startsWith("Native frames: ")) {
state = State.STACKTRACE;
Expand Down Expand Up @@ -424,25 +512,32 @@ public CrashLog parse(String uuid, String crashLog) {
normalizeFilename(frame.path),
frame.line,
frame.function,
frame.frameType,
buildInfo.buildId,
buildInfo.buildIdType,
buildInfo.fileType,
frame.ip,
frame.symbolAddress,
frame.relativeAddress));
} else {
enrichedFrames.add(
new StackFrame(
normalizeFilename(frame.path),
frame.line,
frame.function,
frame.frameType,
null,
null,
null,
frame.ip,
frame.symbolAddress,
frame.relativeAddress));
}
}

ErrorData error =
new ErrorData(kind, message, new StackTrace(enrichedFrames.toArray(new StackFrame[0])));
new ErrorData(
kind, message, threadName, new StackTrace(enrichedFrames.toArray(new StackFrame[0])));
// We can not really extract the full metadata and os info from the crash log
// This code assumes the parser is run on the same machine as the crash happened
Metadata metadata = new Metadata("dd-trace-java", VersionInfo.VERSION, "java", null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public CrashLog parse(String uuid, String javacoreContent) {
String exceptionDetail = null;
String pid = null;
String datetime = null;
String currentThreadName = null;
List<StackFrame> frames = new ArrayList<>();
boolean incomplete = false;
boolean foundThreadSection = false;
Expand Down Expand Up @@ -183,6 +184,7 @@ public CrashLog parse(String uuid, String javacoreContent) {
if (inCurrentThread && line.startsWith("3XMTHREADINFO")) {
Matcher threadMatcher = THREAD_INFO_PATTERN.matcher(line);
if (threadMatcher.matches()) {
currentThreadName = threadMatcher.group(1);
collectingStack = true;
}
continue;
Expand Down Expand Up @@ -267,17 +269,24 @@ public CrashLog parse(String uuid, String javacoreContent) {
frame.path,
frame.line,
frame.function,
frame.frameType,
buildInfo.buildId,
buildInfo.buildIdType,
buildInfo.fileType,
frame.ip,
frame.symbolAddress,
frame.relativeAddress));
} else {
enrichedFrames.add(frame);
}
}

ErrorData error =
new ErrorData(kind, message, new StackTrace(enrichedFrames.toArray(new StackFrame[0])));
new ErrorData(
kind,
message,
currentThreadName,
new StackTrace(enrichedFrames.toArray(new StackFrame[0])));
Metadata metadata = new Metadata("dd-trace-java", VersionInfo.VERSION, "java", null);
Integer parsedPid = safelyParseInt(pid);
ProcInfo procInfo = parsedPid != null ? new ProcInfo(parsedPid) : null;
Expand Down Expand Up @@ -400,7 +409,7 @@ private StackFrame parseJavaStackFrame(String frameText) {
}
}

return new StackFrame(file, line, function, null, null, null, null);
return new StackFrame(file, line, function, null, null, null, null, null, null, null);
}

/**
Expand Down Expand Up @@ -462,7 +471,7 @@ private StackFrame parseNativeStackFrame(String frameText) {
}
}

return new StackFrame(file, null, function, null, null, null, relAddress);
return new StackFrame(file, null, function, "native", null, null, null, null, null, relAddress);
}

private String parseDateTime(String datePart, String timePart) {
Expand Down
Loading
Loading