@@ -98,6 +98,39 @@ public HotspotCrashLogParser() {
9898 // find(), which would otherwise match the lowercase "sp"/"pc" tokens embedded in those lines.
9999 private static final Pattern REGISTER_LINE_START =
100100 Pattern .compile ("^\\ s*[A-Za-z][A-Za-z0-9]*\\ s*=\\ s*0x" );
101+ private static final Pattern COMPILED_JAVA_ADDRESS_PARSER =
102+ Pattern .compile ("@\\ s+(0x[0-9a-fA-F]+)\\ s+\\ [(0x[0-9a-fA-F]+)\\ +(0x[0-9a-fA-F]+)\\ ]" );
103+
104+ // HotSpot crash logs encode the execution kind in the first column of each frame line.
105+ // Source references:
106+ // JDK 8:
107+ // https://github.com/openjdk/jdk8u/blob/73c9c6bcd062196cbebc4d9f22b13d2e20a14f98/hotspot/src/share/vm/runtime/frame.cpp#L710-L724
108+ // JDK 11:
109+ // https://github.com/openjdk/jdk11u/blob/970d6cf491a55fd6ab98ec3f449c13a58633078a/src/hotspot/share/runtime/frame.cpp#L647-L662
110+ // JDK 25:
111+ // https://github.com/openjdk/jdk25u/blob/2fe611a2a3386d097f636c15bd4d396a82dc695e/src/hotspot/share/runtime/frame.cpp#L652-L666
112+ // Mainline:
113+ // https://github.com/openjdk/jdk/blob/53c864a881d2183d3664a6a5a56480bd99fffe45/src/hotspot/share/runtime/frame.cpp#L647-L661
114+ // Note: the marker set changes across JDK lines. In particular, "A" appears in some HotSpot
115+ // versions but not all, so this mapping is best-effort rather than a stable cross-version enum.
116+ private static String hotspotFrameType (char marker ) {
117+ switch (marker ) {
118+ case 'J' :
119+ return "compiled" ;
120+ case 'A' : // exists in JDK 11
121+ return "aot_compiled" ;
122+ case 'j' :
123+ return "interpreted" ;
124+ case 'V' :
125+ return "vm" ;
126+ case 'v' :
127+ return "stub" ;
128+ case 'C' :
129+ return "native" ;
130+ default :
131+ return null ;
132+ }
133+ }
101134
102135 private StackFrame parseLine (String line ) {
103136 if (line == null || line .isEmpty ()) {
@@ -107,24 +140,52 @@ private StackFrame parseLine(String line) {
107140 String functionName = null ;
108141 Integer functionLine = null ;
109142 String filename = null ;
143+ String ip = null ;
110144 String relAddress = null ;
145+ String symbolAddress = null ;
111146 char firstChar = line .charAt (0 );
147+ String frameType = hotspotFrameType (firstChar );
112148 if (line .length () > 1 && !Character .isSpaceChar (line .charAt (1 ))) {
113149 // We can find entries like this in between the frames
114150 // Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
115151 return null ;
116152 }
117153 switch (firstChar ) {
118154 case 'J' :
155+ case 'A' :
119156 {
120157 // spotless:off
121158 // J 36572 c2 datadog.trace.util.AgentTaskScheduler$PeriodicTask.run()V (25 bytes) @ 0x00007f2fd0198488 [0x00007f2fd0198420+0x0000000000000068]
122159 // J 3896 c2 java.nio.ByteBuffer.allocate(I)Ljava/nio/ByteBuffer; java.base@21.0.1 (20 bytes) @ 0x0000000112ad51e8 [0x0000000112ad4fc0+0x0000000000000228]
160+ // J 302 java.util.zip.ZipFile.getEntry(J[BZ)J (0 bytes) @ 0x00007fa287303dce [0x00007fa287303d00+0xce]
123161 // spotless:on
124162 String [] parts = SPACE_SPLITTER .split (line );
125- if (parts .length > 3 ) {
163+ int bytesToken = -1 ;
164+ for (int i = 0 ; i < parts .length - 1 ; i ++) {
165+ if (parts [i ].startsWith ("(" ) && "bytes)" .equals (parts [i + 1 ])) {
166+ bytesToken = i ;
167+ break ;
168+ }
169+ }
170+ if (bytesToken > 1 ) {
171+ String candidate = parts [bytesToken - 1 ];
172+ // Newer JVMs insert a module token before "(NN bytes)".
173+ if (candidate .contains ("@" )) {
174+ candidate = parts [bytesToken - 2 ];
175+ }
176+ if (!candidate .startsWith ("(" )) {
177+ functionName = candidate ;
178+ }
179+ } else if (parts .length > 3 && !parts [3 ].startsWith ("(" )) {
126180 functionName = parts [3 ];
127181 }
182+
183+ Matcher matcher = COMPILED_JAVA_ADDRESS_PARSER .matcher (line );
184+ if (matcher .find ()) {
185+ ip = matcher .group (1 );
186+ symbolAddress = matcher .group (2 );
187+ relAddress = matcher .group (3 );
188+ }
128189 break ;
129190 }
130191 case 'j' :
@@ -200,9 +261,12 @@ private StackFrame parseLine(String line) {
200261 filename ,
201262 functionLine ,
202263 stripCompilerAnnotations (functionName ),
264+ frameType ,
203265 null ,
204266 null ,
205267 null ,
268+ ip ,
269+ symbolAddress ,
206270 relAddress );
207271 }
208272 return null ;
@@ -247,9 +311,30 @@ private static String normalizeFilename(String filename) {
247311 return filename .substring (0 , prefixLen ) + filename .substring (end );
248312 }
249313
314+ static String parseCurrentThreadName (String line ) {
315+ if (line == null || !line .startsWith ("Current thread " )) {
316+ return null ;
317+ }
318+ final int separator = line .indexOf (':' );
319+ if (separator < 0 ) {
320+ return null ;
321+ }
322+
323+ String threadDescriptor = line .substring (separator + 1 ).trim ();
324+ final int metadataStart = threadDescriptor .indexOf ('[' );
325+ if (metadataStart >= 0 ) {
326+ threadDescriptor = threadDescriptor .substring (0 , metadataStart ).trim ();
327+ }
328+ if (threadDescriptor .isEmpty ()) {
329+ return null ;
330+ }
331+ return threadDescriptor ;
332+ }
333+
250334 public CrashLog parse (String uuid , String crashLog ) {
251335 SigInfo sigInfo = null ;
252336 String pid = null ;
337+ String threadName = null ;
253338 List <StackFrame > frames = new ArrayList <>();
254339 String datetime = null ;
255340 String datetimeRaw = null ;
@@ -303,6 +388,9 @@ public CrashLog parse(String uuid, String crashLog) {
303388 }
304389 break ;
305390 case THREAD :
391+ if (threadName == null ) {
392+ threadName = parseCurrentThreadName (line );
393+ }
306394 // Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
307395 if (line .startsWith ("Native frames: " )) {
308396 state = State .STACKTRACE ;
@@ -424,25 +512,32 @@ public CrashLog parse(String uuid, String crashLog) {
424512 normalizeFilename (frame .path ),
425513 frame .line ,
426514 frame .function ,
515+ frame .frameType ,
427516 buildInfo .buildId ,
428517 buildInfo .buildIdType ,
429518 buildInfo .fileType ,
519+ frame .ip ,
520+ frame .symbolAddress ,
430521 frame .relativeAddress ));
431522 } else {
432523 enrichedFrames .add (
433524 new StackFrame (
434525 normalizeFilename (frame .path ),
435526 frame .line ,
436527 frame .function ,
528+ frame .frameType ,
437529 null ,
438530 null ,
439531 null ,
532+ frame .ip ,
533+ frame .symbolAddress ,
440534 frame .relativeAddress ));
441535 }
442536 }
443537
444538 ErrorData error =
445- new ErrorData (kind , message , new StackTrace (enrichedFrames .toArray (new StackFrame [0 ])));
539+ new ErrorData (
540+ kind , message , threadName , new StackTrace (enrichedFrames .toArray (new StackFrame [0 ])));
446541 // We can not really extract the full metadata and os info from the crash log
447542 // This code assumes the parser is run on the same machine as the crash happened
448543 Metadata metadata = new Metadata ("dd-trace-java" , VersionInfo .VERSION , "java" , null );
0 commit comments