@@ -62,6 +62,7 @@ enum State {
6262 SUMMARY ,
6363 THREAD ,
6464 STACKTRACE ,
65+ REGISTER_TO_MEMORY_MAPPING ,
6566 REGISTERS ,
6667 PROCESS ,
6768 VM_ARGUMENTS ,
@@ -95,12 +96,16 @@ public HotspotCrashLogParser() {
9596 // per line.
9697 private static final Pattern REGISTER_ENTRY_PARSER =
9798 Pattern .compile ("([A-Za-z][A-Za-z0-9]*)\\ s*=\\ s*(0x[0-9a-fA-F]+)" );
99+ private static final Pattern REGISTER_TO_MEMORY_MAPPING_PARSER =
100+ Pattern .compile ("^\\ s*([A-Za-z][A-Za-z0-9]*)\\ s*=" );
98101 // Used for the REGISTERS-state exit condition only: the register name must start the line
99102 // (after optional whitespace). This prevents lines like "Top of Stack: (sp=0x...)" and
100103 // "Instructions: (pc=0x...)" from being mistaken for register entries by REGISTER_ENTRY_PARSER's
101104 // find(), which would otherwise match the lowercase "sp"/"pc" tokens embedded in those lines.
102105 private static final Pattern REGISTER_LINE_START =
103106 Pattern .compile ("^\\ s*[A-Za-z][A-Za-z0-9]*\\ s*=\\ s*0x" );
107+ private static final Pattern SUBSECTION_TITLE =
108+ Pattern .compile ("^\\ s*[A-Za-z][\\ w ]*:.+$" );
104109 private static final Pattern COMPILED_JAVA_ADDRESS_PARSER =
105110 Pattern .compile ("@\\ s+(0x[0-9a-fA-F]+)\\ s+\\ [(0x[0-9a-fA-F]+)\\ +(0x[0-9a-fA-F]+)\\ ]" );
106111
@@ -350,10 +355,14 @@ public CrashLog parse(String uuid, String crashLog) {
350355 String datetimeRaw = null ;
351356 boolean incomplete = false ;
352357 String oomMessage = null ;
353- Map <String , String > registers = null ;
358+ Map <String , String > registers = new LinkedHashMap <>();
359+ Map <String , String > registerToMemoryMapping = new LinkedHashMap <>();
360+ String currentRegisterToMemoryMapping = "" ;
354361 List <String > runtimeArgs = null ;
355362 List <String > dynamicLibraryLines = null ;
356363 String dynamicLibraryKey = null ;
364+ boolean previousLineBlank = false ;
365+ State nextThreadSectionState = null ;
357366
358367 String [] lines = NEWLINE_SPLITTER .split (crashLog );
359368 outer :
@@ -410,7 +419,10 @@ public CrashLog parse(String uuid, String crashLog) {
410419 }
411420 break ;
412421 case STACKTRACE :
413- if (line .startsWith ("siginfo:" )) {
422+ nextThreadSectionState = nextThreadSectionState (line , previousLineBlank );
423+ if (nextThreadSectionState != null ) {
424+ state = nextThreadSectionState ;
425+ } else if (line .startsWith ("siginfo:" )) {
414426 // spotless:off
415427 // siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x70
416428 // siginfo: si_signo: 11 (SIGSEGV), si_code: 0 (SI_USER), si_pid: 554848, si_uid: 1000
@@ -426,11 +438,6 @@ public CrashLog parse(String uuid, String crashLog) {
426438 Integer siUid = safelyParseInt (siginfoMatcher .group (7 ));
427439 sigInfo = new SigInfo (number , name , siCode , sigAction , address , siPid , siUid );
428440 }
429- } else if (line .startsWith ("Registers:" )) {
430- registers = new LinkedHashMap <>();
431- state = State .REGISTERS ;
432- } else if (line .contains ("P R O C E S S" )) {
433- state = State .PROCESS ;
434441 } else {
435442 // Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
436443 final StackFrame frame = parseLine (line );
@@ -439,8 +446,28 @@ public CrashLog parse(String uuid, String crashLog) {
439446 }
440447 }
441448 break ;
449+ case REGISTER_TO_MEMORY_MAPPING :
450+ nextThreadSectionState = nextThreadSectionState (line , previousLineBlank );
451+ if (nextThreadSectionState != null ) {
452+ currentRegisterToMemoryMapping = "" ;
453+ state = nextThreadSectionState ;
454+ } else if (!line .isEmpty ()) {
455+ final Matcher m = REGISTER_TO_MEMORY_MAPPING_PARSER .matcher (line );
456+ if (m .lookingAt ()) {
457+ currentRegisterToMemoryMapping = m .group (1 );
458+ registerToMemoryMapping .put (
459+ currentRegisterToMemoryMapping , line .substring (line .indexOf ('=' ) + 1 ));
460+ } else if (!currentRegisterToMemoryMapping .isEmpty ()) {
461+ registerToMemoryMapping .computeIfPresent (
462+ currentRegisterToMemoryMapping , (key , value ) -> value + "\n " + line );
463+ }
464+ }
465+ break ;
442466 case REGISTERS :
443- if (!line .isEmpty () && !REGISTER_LINE_START .matcher (line ).find ()) {
467+ nextThreadSectionState = nextThreadSectionState (line , previousLineBlank );
468+ if (nextThreadSectionState != null ) {
469+ state = nextThreadSectionState ;
470+ } else if (!line .isEmpty () && !REGISTER_LINE_START .matcher (line ).find ()) {
444471 // non-empty line that does not start with a register entry signals end of section
445472 state = State .STACKTRACE ;
446473 } else {
@@ -508,6 +535,7 @@ public CrashLog parse(String uuid, String crashLog) {
508535 // unexpected parser state; bail out
509536 break outer ;
510537 }
538+ previousLineBlank = line .isEmpty ();
511539 }
512540
513541 // PROCESS and SYSTEM sections are late enough that all critical data is captured
@@ -572,9 +600,10 @@ public CrashLog parse(String uuid, String crashLog) {
572600 Integer parsedPid = safelyParseInt (pid );
573601 ProcInfo procInfo = parsedPid != null ? new ProcInfo (parsedPid ) : null ;
574602 Experimental experimental =
575- (registers != null && !registers .isEmpty ())
603+ !registers .isEmpty ()
604+ || !registerToMemoryMapping .isEmpty ()
576605 || (runtimeArgs != null && !runtimeArgs .isEmpty ())
577- ? new Experimental (registers , runtimeArgs )
606+ ? new Experimental (registers , registerToMemoryMapping , runtimeArgs )
578607 : null ;
579608 DynamicLibs files =
580609 (dynamicLibraryLines != null && !dynamicLibraryLines .isEmpty ())
@@ -608,6 +637,27 @@ static String dateTimeToISO(String datetime) {
608637 }
609638 }
610639
640+ private static State nextThreadSectionState (String line , boolean previousLineBlank ) {
641+ if (line .startsWith ("Register to memory mapping:" )) {
642+ return State .REGISTER_TO_MEMORY_MAPPING ;
643+ }
644+ if (line .startsWith ("Registers:" )) {
645+ return State .REGISTERS ;
646+ }
647+ if (line .startsWith ("siginfo:" )) {
648+ return null ;
649+ }
650+ if (line .contains ("P R O C E S S" )) {
651+ return State .PROCESS ;
652+ }
653+ if (previousLineBlank
654+ && !line .contains ("=" )
655+ && SUBSECTION_TITLE .matcher (line ).matches ()) {
656+ return State .STACKTRACE ;
657+ }
658+ return null ;
659+ }
660+
611661 /**
612662 * Detects whether the Dynamic libraries section comes from Linux {@code /proc/self/maps} (address
613663 * range format {@code addr-addr perms ...}) or from the BSD/macOS dyld callback (format {@code
0 commit comments