1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.sts.common.util; 18 19 import com.android.server.os.TombstoneProtos.*; 20 import com.android.tradefed.log.LogUtil.CLog; 21 22 import java.util.ArrayList; 23 import java.util.Arrays; 24 import java.util.Iterator; 25 import java.util.List; 26 import java.util.function.Consumer; 27 import java.util.regex.Matcher; 28 import java.util.regex.Pattern; 29 30 /** Parses tombstones and from a tombstone file or logcat. */ 31 public class TombstoneParser { 32 33 private static final String TOMBSTONE_HEADER = 34 "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***"; 35 private static final Pattern TOMBSTONE_HEADER_PATTERN = 36 Pattern.compile(TOMBSTONE_HEADER.replace("*", "\\*")); 37 private static final Pattern NATIVE_CRASH_TIME_PATTERN = 38 Pattern.compile("Native Crash TIME: (?<time>\\d+)"); 39 private static final Pattern FINGERPRINT_PATTERN = 40 Pattern.compile("Build fingerprint: '(?<fingerprint>.*)'"); 41 private static final Pattern REVISION_PATTERN = 42 Pattern.compile("Revision: '(?<revision>.*)'\\s*"); 43 private static final Pattern ABI_PATTERN = Pattern.compile("ABI: '(?<abi>.*)'"); 44 private static final Pattern TIMESTAMP_PATTERN = Pattern.compile("Timestamp: (?<timestamp>.*)"); 45 private static final Pattern UPTIME_PATTERN = 46 Pattern.compile("Process uptime: (?<uptime>\\d+)s"); 47 private static final Pattern GET_MAIN_THREAD_FAILURE_PATTERN = 48 Pattern.compile("failed to find entry for main thread in tombstone"); 49 private static final Pattern THREAD_SEPARATOR_PATTERN = 50 Pattern.compile("--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---"); 51 // " fd %d: %s (%s)" 52 private static final Pattern OPEN_FILE_ROW_PATTERN = 53 Pattern.compile( 54 " *fd (?<fd>\\d+?): (?<path>.*?) \\((?:(?<unowned>unowned)|(?:owned by" 55 + " (?<owner>\\S+) 0x(?<tag>\\p{XDigit}{1,16})))\\)"); 56 private static final Pattern SIGNAL_MISSING_PATTERN = 57 Pattern.compile("signal information missing"); 58 // "signal %d (%s), code %d (%s%s), fault addr %s" 59 // "signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7e772b8cfbe0" 60 private static final Pattern SIGNAL_PATTERN = 61 Pattern.compile( 62 "signal (?<number>\\d+?) \\((?<name>.+?)\\), code (?<code>(?:-)?\\d+?)" 63 + " \\((?<codename>\\S+?)(?: from pid (?<senderpid>\\d+?), uid" 64 + " (?<senderuid>\\d+?))?\\), fault addr" 65 + " (?:0x)?(?<faultaddress>\\p{XDigit}{1,16}|--------)" 66 + "( (?<register>\\(.+\\)))?"); 67 private static final Pattern CAUSE_PATTERN = Pattern.compile("Cause: (?<cause>.*?)"); 68 // Be greedy because some abort messages are multiple lines long 69 private static final Pattern ABORT_PATTERN = 70 Pattern.compile( 71 "^Abort message: '(?<message>.*)'$", Pattern.MULTILINE | Pattern.DOTALL); 72 private static final Pattern DEALLOC_PATTERN = 73 Pattern.compile("deallocated by thread (?<tid>\\d+):"); 74 private static final Pattern ALLOC_PATTERN = 75 Pattern.compile("allocated by thread (?<tid>\\d+):"); 76 private static final Pattern NO_MEMORY_MAPS_PATTERN = Pattern.compile("No memory maps found"); 77 // "memory map (%d %s):" + ("\n--->Fault address falls at %s before any mapped regions" | " 78 // (fault address prefixed with --->)") 79 private static final Pattern MEMORY_MAP_HEADER_PATTERN = 80 Pattern.compile( 81 "memory map \\((?<count>\\d+?) entr(?:y|ies)\\):(?: \\(fault address prefixed" 82 + " with --->\\))?"); 83 private static final Pattern MEMORY_MAP_FAULT_ADDRESS_BETWEEN_PATTERN = 84 Pattern.compile( 85 "--->Fault address falls at" 86 + " (?<faultaddresshigh>\\p{XDigit}{4,8})'(?<faultaddresslow>\\p{XDigit}{4,8})" 87 + " between mapped regions"); 88 // " " + ("--->")? + "%s-%s" + %s%s%s" + " %8" PRIx64 " %8" PRIx64" + (" %s")? + (" 89 // (BuildId: %s)")? + (" (load bias 0x%" PRIx64 ")")? 90 private static final Pattern MEMORY_MAP_LINE_PATTERN = 91 Pattern.compile( 92 " *(?:--->)?(?<beginaddresshigh>\\p{XDigit}{4,8})'(?<beginaddresslow>\\p{XDigit}{4,8})-(?<endaddresshigh>\\p{XDigit}{4,8})'(?<endaddresslow>\\p{XDigit}{4,8})" 93 + " (?<read>\\S)(?<write>\\S)(?<execute>\\S) " 94 + " {0,7}(?<offset>\\p{XDigit}{1,8}) {0,7}(?<length>\\p{XDigit}{1,8})(?:" 95 + " (?<mappingname>.+?))?(?: \\(BuildId: (?<buildid>\\d+)\\))?(?: \\(load" 96 + " bias 0x(?<loadbias>\\p{XDigit}+)\\))?"); 97 private static final Pattern MEMORY_MAP_FAULT_ADDRESS_AFTER_PATTERN = 98 Pattern.compile( 99 "--->Fault address falls at" 100 + " (?<faultaddresshigh>\\p{XDigit}{4,8})'(?<faultaddresslow>\\p{XDigit}{4,8})" 101 + " after any mapped regions"); 102 private static final Pattern CMD_LINE_PATTERN = Pattern.compile("Cmdline: (?<cmd>.*)"); 103 private static final Pattern THREAD_HEADER_1_PATTERN = 104 Pattern.compile( 105 "pid: (?<pid>\\d+), tid: (?<tid>\\d+), name: (?<threadname>.+?) >>>" 106 + " (?<processname>.+?) <<<"); 107 private static final Pattern THREAD_HEADER_2_PATTERN = Pattern.compile("uid: (?<uid>\\d+)\\s*"); 108 private static final Pattern TAGGED_ADDR_CTRL_PATTERN = 109 Pattern.compile( 110 "tagged_addr_ctrl: (?<taggedaddrctrl>\\p{XDigit}{16})(?<description>.+)?"); 111 // TODO: finish description 112 private static final Pattern PAC_ENABLED_KEYS_PATTERN = 113 Pattern.compile( 114 "pac_enabled_keys: (?<pacenabledkeys>\\p{XDigit}{16})(?<description>.+)?"); 115 private static final Pattern REGISTER_ROW_PATTERN = Pattern.compile(" .*"); 116 private static final Pattern BACKTRACE_HEADER_PATTERN = Pattern.compile("backtrace:"); 117 private static final Pattern BACKTRACE_NOTE_PATTERN = Pattern.compile(" *NOTE: (?<note>.*)"); 118 // " #05 pc 000000000004faf6 /apex/com.android.runtime/lib64/bionic/libc.so 119 // (__libc_init+86) (BuildId: 284d864ffe434d73dc722b84a1d3d9ca)" 120 private static final Pattern BACKTRACE_PATTERN = 121 Pattern.compile( 122 " *#(?<index>\\d{2}) pc (?<programcounter>\\p{XDigit}{8,16}) " 123 + " (?<filename>.+?)(?:" 124 + " \\((?<functionname>.*?)\\+(?<functionoffset>\\d+)\\))?(?: \\(BuildId:" 125 + " (?<buildid>.*?)\\))?"); 126 private static final Pattern MEMORY_NEAR_PATTERN = 127 Pattern.compile("memory near (?<registername>\\S+?)(?: \\((?<mappingname>\\S+?)\\))?:"); 128 // " 0000006f0bb2a8a0 6f735c65646f635c 6d61675c65637275 \code\source\gam" 129 private static final Pattern MEMORY_DUMP_ROW_PATTERN = 130 Pattern.compile( 131 " *(?<address>\\p{XDigit}{8,16}) (?<memory1>(?:\\p{XDigit}{8,16}|-{8,16}))(?:" 132 + " (?<memory2>(?:\\p{XDigit}{8,16}|-{8,16})))?(?:" 133 + " (?<memory3>(?:\\p{XDigit}{8,16}|-{8,16})))?(?:" 134 + " (?<memory4>(?:\\p{XDigit}{8,16}|-{8,16})))? (?<ascii>.{1,16})"); 135 private static final Pattern MEMORY_TAG_PATTERN = 136 Pattern.compile( 137 "Memory tags around the fault address" 138 + " \\(0x(?<faultaddress>\\p{XDigit}{8,16})\\), one tag per" 139 + " (?<taggranulesize>) bytes:"); 140 private static final Pattern MEMORY_TAG_ROW_PATTERN = 141 Pattern.compile(" (?:=>| )0x(?<address>\\p{XDigit}{8,16}):(?<tags>.*)"); 142 private static final Pattern LOG_PATTERN = 143 Pattern.compile("---------(?<istail> tail end of) log (?<buffername>.*)"); 144 private static final Pattern LOG_LINE_PATTERN = 145 Pattern.compile( 146 "(?<timestamp>.+?) {1,5}(?<pid>\\d+) {1,5}(?<tid>\\d+) (?<priority>\\S)" 147 + " (?<tag>\\S*) {0,8}: (?<message>.*)"); 148 149 private static final Pattern CURRENT_BACKTRACE_BLOB_PATTERN = 150 Pattern.compile( 151 String.format( 152 "^%s\n(?:%s\n)*(?:%s\n)*", 153 BACKTRACE_HEADER_PATTERN.pattern(), 154 BACKTRACE_NOTE_PATTERN.pattern(), 155 BACKTRACE_PATTERN.pattern()), 156 Pattern.MULTILINE); 157 private static final Pattern ALLOC_BACKTRACE_BLOB_PATTERN = 158 Pattern.compile( 159 String.format( 160 "^%s\n(?:%s\n)*", ALLOC_PATTERN.pattern(), BACKTRACE_PATTERN.pattern()), 161 Pattern.MULTILINE); 162 private static final Pattern DEALLOC_BACKTRACE_BLOB_PATTERN = 163 Pattern.compile( 164 String.format( 165 "^%s\n(?:%s\n)*", 166 DEALLOC_PATTERN.pattern(), BACKTRACE_PATTERN.pattern()), 167 Pattern.MULTILINE); 168 169 /** Parse a logcat snippet and build a list of tombstones */ parseLogcat(String logcat)170 public static final List<Tombstone> parseLogcat(String logcat) { 171 String[] potentialTombstones = splitPattern(TOMBSTONE_HEADER_PATTERN).split(logcat); 172 173 List<Tombstone> tombstones = new ArrayList<>(); 174 for (String potentialTombstone : potentialTombstones) { 175 Tombstone.Builder tombstoneBuilder = Tombstone.newBuilder(); 176 List<String> lines = lines(potentialTombstone); 177 if (lines.isEmpty()) { 178 continue; 179 } 180 if (!lines.get(0).contains(TOMBSTONE_HEADER)) { 181 continue; 182 } 183 if (NATIVE_CRASH_TIME_PATTERN.matcher(lines.get(1)).find()) { 184 CLog.d("ignoring crash time"); 185 continue; 186 } 187 188 String tombstoneBlob = 189 lines.stream() 190 .filter(line -> line.contains("DEBUG :")) 191 .map( 192 line -> { 193 // logcat removes trailing space after ":" in the case of 194 // empty lines 195 String[] split = line.split("DEBUG :(?: )?", 2); 196 return split.length == 2 ? split[1] : "<empty>"; 197 }) 198 .collect( 199 StringBuilder::new, 200 (sb, line) -> { 201 sb.append(line); 202 sb.append('\n'); 203 }, 204 StringBuilder::append) 205 .toString(); 206 207 if (!parseTombstone(tombstoneBlob, tombstoneBuilder)) { 208 CLog.w("parsing tombstone failed: \n" + tombstoneBlob); 209 } 210 Tombstone tombstone = tombstoneBuilder.build(); 211 tombstones.add(tombstone); 212 } 213 return tombstones; 214 } 215 parseTombstone(String tombstoneBlob, Tombstone.Builder tombstoneBuilder)216 public static boolean parseTombstone(String tombstoneBlob, Tombstone.Builder tombstoneBuilder) { 217 // get build or bail 218 // get revision or bail 219 // get ABI or bail 220 // get timestamp or bail 221 // get uptime or bail 222 // try "failed to find entry for main thread..." and add to notes(?) (if present, return) 223 // 224 // get main thread: 225 // 226 // get logs: 227 // try ----- tail end of log %x 228 // else log %x 229 // loop 230 // try jfeiowajfklewjaoi 231 // 232 // loop 233 // try --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- 234 // get thread 235 // try "" 236 // ifp open files 237 // ifp loop 238 // " fd ..." 239 // get logs 240 241 String[] threadBlobs = THREAD_SEPARATOR_PATTERN.split(tombstoneBlob); 242 String headerAndMainThreadBlob = threadBlobs[0]; 243 List<String> headerAndMainThreadLines = lines(headerAndMainThreadBlob); 244 245 // get fingerprint 246 if (!matchLine( 247 headerAndMainThreadLines.iterator(), 248 FINGERPRINT_PATTERN, 249 m -> { 250 String fingerprint = m.group("fingerprint"); 251 CLog.i(fingerprint); 252 tombstoneBuilder.setBuildFingerprint(fingerprint); 253 })) { 254 CLog.w("fingerprint failed"); 255 return false; 256 } 257 258 // get revision 259 if (!matchLine( 260 headerAndMainThreadLines.iterator(), 261 REVISION_PATTERN, 262 m -> { 263 tombstoneBuilder.setRevision(m.group("revision")); 264 })) { 265 CLog.w("revision failed"); 266 return false; 267 } 268 269 // get ABI 270 if (!matchLine( 271 headerAndMainThreadLines.iterator(), 272 ABI_PATTERN, 273 m -> { 274 final String abi = m.group("abi"); 275 Architecture arch = null; 276 switch (abi) { 277 case "arm": 278 arch = Architecture.ARM32; 279 break; 280 case "arm64": 281 arch = Architecture.ARM64; 282 break; 283 case "riscv64": 284 arch = Architecture.RISCV64; 285 break; 286 case "x86": 287 arch = Architecture.X86; 288 break; 289 case "x86_64": 290 arch = Architecture.X86_64; 291 break; 292 default: 293 CLog.i("unknown arch"); 294 return; 295 } 296 CLog.d("set arch to: " + arch); 297 tombstoneBuilder.setArch(arch); 298 })) { 299 CLog.w("abi failed"); 300 return false; 301 } 302 303 // get timestamp 304 matchLine( 305 headerAndMainThreadLines.iterator(), 306 TIMESTAMP_PATTERN, 307 m -> { 308 tombstoneBuilder.setTimestamp(m.group("timestamp")); 309 }); 310 311 // get process uptime 312 matchLine( 313 headerAndMainThreadLines.iterator(), 314 UPTIME_PATTERN, 315 m -> { 316 String uptime = m.group("uptime"); 317 tombstoneBuilder.setProcessUptime(Integer.valueOf(uptime)); 318 }); 319 320 // try main thread get failure note 321 if (matchLine(headerAndMainThreadLines.iterator(), GET_MAIN_THREAD_FAILURE_PATTERN, null)) { 322 // tombstoned couldn't get the main thread info, so that's all we get 323 return true; 324 } 325 326 // get main thread 327 if (!parseMainThread(headerAndMainThreadBlob, tombstoneBuilder)) { 328 CLog.w("main thread failed"); 329 return false; 330 } 331 332 // get logs 333 if (!parseLogs(headerAndMainThreadBlob, tombstoneBuilder)) { 334 CLog.w("logs failed"); 335 return false; 336 } 337 338 // get threads 339 for (int i = 1; i < threadBlobs.length; i++) { 340 String threadBlob = threadBlobs[i]; 341 com.android.server.os.TombstoneProtos.Thread.Builder threadBuilder = 342 com.android.server.os.TombstoneProtos.Thread.newBuilder(); 343 if (!parseThread(threadBlob, tombstoneBuilder, threadBuilder)) { 344 CLog.w("thread failed"); 345 return false; 346 } 347 tombstoneBuilder.putThreads(threadBuilder.getId(), threadBuilder.build()); 348 } 349 350 // get end of blob 351 String tailBlob = threadBlobs[threadBlobs.length - 1]; 352 List<String> tailLines = lines(tailBlob); 353 354 // get open files 355 matchLines( 356 tailLines.iterator(), 357 OPEN_FILE_ROW_PATTERN, 358 m -> { 359 FD.Builder fdBuilder = 360 FD.newBuilder() 361 .setFd(Integer.valueOf(m.group("fd"))) 362 .setPath(m.group("path")); 363 String owner = m.group("owner"); 364 String tag = m.group("tag"); 365 if (owner != null && tag != null) { 366 fdBuilder.setOwner(owner).setTag(parsePointer(tag)); 367 } else { 368 fdBuilder.setOwner("unowned"); 369 } 370 tombstoneBuilder.addOpenFds(fdBuilder.build()); 371 }); 372 373 if (!parseLogs(tailBlob, tombstoneBuilder)) { 374 CLog.w("logs failed"); 375 return false; 376 } 377 378 return true; 379 } 380 parseMainThread( String headerAndMainThreadBlob, Tombstone.Builder tombstoneBuilder)381 private static boolean parseMainThread( 382 String headerAndMainThreadBlob, Tombstone.Builder tombstoneBuilder) { 383 com.android.server.os.TombstoneProtos.Thread.Builder mainThreadBuilder = 384 com.android.server.os.TombstoneProtos.Thread.newBuilder(); 385 386 List<String> headerAndMainThreadLines = lines(headerAndMainThreadBlob); 387 String[] causeBlobs = splitPattern(CAUSE_PATTERN).split(headerAndMainThreadBlob); 388 String headerBlob = causeBlobs[0]; 389 List<String> headerLines = lines(headerBlob); 390 String tailBlob = causeBlobs[causeBlobs.length - 1]; 391 List<String> tailLines = lines(tailBlob); 392 393 try { 394 if (!parseThreadHeader( 395 headerLines, tombstoneBuilder, mainThreadBuilder, /* isMainThread */ true)) { 396 CLog.w("main thread get header failed"); 397 return false; 398 } 399 400 // get signal or no signal 401 boolean matchedSignal = false; 402 matchedSignal |= matchLine(headerLines.iterator(), SIGNAL_MISSING_PATTERN, null); 403 matchedSignal |= 404 matchLine( 405 headerLines.iterator(), 406 SIGNAL_PATTERN, 407 m -> { 408 Signal.Builder signalBuilder = 409 Signal.newBuilder() 410 .setNumber(Integer.valueOf(m.group("number"))) 411 .setName(m.group("name")) 412 .setCode(Integer.valueOf(m.group("code"))) 413 .setCodeName(m.group("codename")); 414 415 String faultAddress = m.group("faultaddress"); 416 if (!faultAddress.equals("--------")) { 417 signalBuilder 418 .setHasFaultAddress(true) 419 .setFaultAddress(parsePointer(faultAddress)); 420 } else { 421 signalBuilder.setHasFaultAddress(false); 422 } 423 String senderUid = m.group("senderuid"); 424 String senderPid = m.group("senderpid"); 425 if (senderUid != null && senderPid != null) { 426 signalBuilder 427 .setSenderUid(Integer.valueOf(senderUid)) 428 .setSenderPid(Integer.valueOf(senderPid)); 429 } 430 // TODO: add fault-adjacent metadata, which we don't get until the 431 // tag dump :( 432 tombstoneBuilder.setSignalInfo(signalBuilder.build()); 433 }); 434 if (!matchedSignal) { 435 // must match one or the other 436 CLog.w("couldn't match signal messages"); 437 return false; 438 } 439 440 // single cause pattern goes here, so the "header blob" won't contain stuff after 441 442 // get abort if present 443 { 444 Matcher m = ABORT_PATTERN.matcher(headerAndMainThreadBlob); 445 if (m.find()) { 446 tombstoneBuilder.setAbortMessage(m.group("message")); 447 } 448 } 449 450 if (!parseThreadRegisters(headerAndMainThreadLines, mainThreadBuilder)) { 451 CLog.w("main thread get thread registers failed"); 452 return false; 453 } 454 455 if (!parseThreadBacktrace(headerAndMainThreadBlob, mainThreadBuilder)) { 456 CLog.w("main thread get thread backtrace failed"); 457 return false; 458 } 459 460 // get causes 461 for (int i = 1; i < causeBlobs.length; i++) { 462 String causeBlob = causeBlobs[i]; 463 List<String> causeLines = lines(causeBlob); 464 Cause.Builder causeBuilder = Cause.newBuilder(); 465 if (!matchLine( 466 causeLines.iterator(), 467 CAUSE_PATTERN, 468 m -> { 469 // must delay adding cause because the memory error is printed later 470 CLog.d("cause message matched"); 471 causeBuilder.setHumanReadable(m.group("cause")); 472 })) { 473 // not a cause 474 continue; 475 } 476 477 boolean hasMemoryError = false; 478 MemoryError.Builder memoryErrorBuilder = MemoryError.newBuilder(); 479 HeapObject.Builder heapObjectBuilder = HeapObject.newBuilder(); 480 481 { 482 Matcher deallocBlobMatcher = DEALLOC_BACKTRACE_BLOB_PATTERN.matcher(causeBlob); 483 if (deallocBlobMatcher.find()) { 484 List<String> deallocLines = lines(deallocBlobMatcher.group(0)); 485 hasMemoryError = true; 486 CLog.d("dealloc matched"); 487 List<BacktraceFrame> backtraceFrames = new ArrayList<>(); 488 if (!parseBacktrace(deallocLines, backtraceFrames)) { 489 return false; 490 } 491 heapObjectBuilder.addAllDeallocationBacktrace(backtraceFrames); 492 } 493 } 494 495 { 496 Matcher allocBlobMatcher = ALLOC_BACKTRACE_BLOB_PATTERN.matcher(causeBlob); 497 if (allocBlobMatcher.find()) { 498 List<String> allocLines = lines(allocBlobMatcher.group(0)); 499 hasMemoryError = true; 500 CLog.d("alloc matched"); 501 List<BacktraceFrame> backtraceFrames = new ArrayList<>(); 502 if (!parseBacktrace(allocLines, backtraceFrames)) { 503 return false; 504 } 505 heapObjectBuilder.addAllAllocationBacktrace(backtraceFrames); 506 } 507 } 508 509 if (hasMemoryError) { 510 memoryErrorBuilder.setHeap(heapObjectBuilder.build()); 511 causeBuilder.setMemoryError(memoryErrorBuilder.build()); 512 } else { 513 CLog.d("no memory errors"); 514 } 515 tombstoneBuilder.addCauses(causeBuilder.build()); 516 } 517 518 if (!parseTagDump(tailLines, tombstoneBuilder)) { 519 CLog.w("tag dump failed"); 520 return false; 521 } 522 523 if (!parseThreadMemoryDump(tailBlob, mainThreadBuilder)) { 524 CLog.w("memory dump failed"); 525 return false; 526 } 527 } finally { 528 tombstoneBuilder.putThreads(mainThreadBuilder.getId(), mainThreadBuilder.build()); 529 } 530 531 // check if no memory maps 532 if (matchLine(tailLines.iterator(), NO_MEMORY_MAPS_PATTERN, null)) { 533 // no more lines follow 534 return true; 535 } 536 537 String[] memoryMapBlobs = splitPattern(MEMORY_MAP_HEADER_PATTERN).split(tailBlob); 538 539 for (int i = 1; i < memoryMapBlobs.length; i++) { 540 MemoryMapping.Builder memoryMappingBuilder = MemoryMapping.newBuilder(); 541 542 String memoryMapBlob = memoryMapBlobs[i]; 543 List<String> memoryMapLines = lines(memoryMapBlob); 544 545 matchLine( 546 memoryMapLines.iterator(), 547 MEMORY_MAP_FAULT_ADDRESS_BETWEEN_PATTERN, 548 m -> { 549 CLog.d("got fault address between"); 550 // TODO: put fault address in memoryMappingBuilder 551 }); 552 553 matchLine( 554 memoryMapLines.iterator(), 555 MEMORY_MAP_HEADER_PATTERN, 556 m -> { 557 int count = Integer.valueOf(m.group("count")); 558 CLog.d("got memory map entries count: " + count); 559 matchLines( 560 memoryMapLines.iterator(), 561 MEMORY_MAP_LINE_PATTERN, 562 mLine -> { 563 // TODO: parse 564 tombstoneBuilder.addMemoryMappings( 565 memoryMappingBuilder.build()); 566 }); 567 }); 568 569 matchLine( 570 memoryMapLines.iterator(), 571 MEMORY_MAP_FAULT_ADDRESS_AFTER_PATTERN, 572 m -> { 573 CLog.d("got fault address after"); 574 // TODO: put fault address in memoryMappingBuilder 575 }); 576 } 577 578 return true; 579 } 580 parseThread( String blob, Tombstone.Builder tombstoneBuilder, com.android.server.os.TombstoneProtos.Thread.Builder threadBuilder)581 private static boolean parseThread( 582 String blob, 583 Tombstone.Builder tombstoneBuilder, 584 com.android.server.os.TombstoneProtos.Thread.Builder threadBuilder) { 585 List<String> lines = lines(blob); 586 if (!parseThreadHeader(lines, tombstoneBuilder, threadBuilder, /* isMainThread */ false)) { 587 return false; 588 } 589 if (!parseThreadRegisters(lines, threadBuilder)) { 590 return false; 591 } 592 if (!parseThreadBacktrace(blob, threadBuilder)) { 593 return false; 594 } 595 if (!parseThreadMemoryDump(blob, threadBuilder)) { 596 return false; 597 } 598 return true; 599 } 600 parseThreadHeader( List<String> lines, Tombstone.Builder tombstoneBuilder, com.android.server.os.TombstoneProtos.Thread.Builder threadBuilder, boolean isMainThread)601 private static boolean parseThreadHeader( 602 List<String> lines, 603 Tombstone.Builder tombstoneBuilder, 604 com.android.server.os.TombstoneProtos.Thread.Builder threadBuilder, 605 boolean isMainThread) { 606 matchLine( 607 lines.iterator(), 608 CMD_LINE_PATTERN, 609 m -> { 610 if (isMainThread) { 611 tombstoneBuilder.addAllCommandLine( 612 Arrays.asList(m.group("cmd").split(" "))); 613 } 614 }); 615 matchLine( 616 lines.iterator(), 617 THREAD_HEADER_1_PATTERN, 618 m -> { 619 int tid = Integer.valueOf(m.group("tid")); 620 if (isMainThread) { 621 tombstoneBuilder.setPid(Integer.valueOf(m.group("pid"))).setTid(tid); 622 if (tombstoneBuilder.getCommandLineList().isEmpty()) { 623 tombstoneBuilder.addCommandLine(m.group("processname")); 624 } 625 } 626 threadBuilder.setId(tid).setName(m.group("threadname")); 627 }); 628 matchLine( 629 lines.iterator(), 630 THREAD_HEADER_2_PATTERN, 631 m -> { 632 if (isMainThread) { 633 tombstoneBuilder.setUid(Integer.valueOf(m.group("uid"))); 634 } 635 }); 636 637 matchLine( 638 lines.iterator(), 639 TAGGED_ADDR_CTRL_PATTERN, 640 m -> { 641 // TODO: add to tombstone 642 }); 643 644 matchLine( 645 lines.iterator(), 646 PAC_ENABLED_KEYS_PATTERN, 647 m -> { 648 // TODO: add to tombstone 649 }); 650 651 return true; 652 } 653 parseThreadRegisters( List<String> lines, com.android.server.os.TombstoneProtos.Thread.Builder threadBuilder)654 private static boolean parseThreadRegisters( 655 List<String> lines, 656 com.android.server.os.TombstoneProtos.Thread.Builder threadBuilder) { 657 return matchLines( 658 lines.iterator(), 659 REGISTER_ROW_PATTERN, 660 m -> { 661 // TODO: parse line and add to tombstone 662 }) 663 > 0; 664 } 665 parseThreadBacktrace( String blob, com.android.server.os.TombstoneProtos.Thread.Builder threadBuilder)666 private static boolean parseThreadBacktrace( 667 String blob, com.android.server.os.TombstoneProtos.Thread.Builder threadBuilder) { 668 Matcher threadBacktraceMatcher = CURRENT_BACKTRACE_BLOB_PATTERN.matcher(blob); 669 if (!threadBacktraceMatcher.find()) { 670 CLog.i("didn't match backtrace blob"); 671 return true; // no backtrace 672 } 673 String threadBacktraceBlob = threadBacktraceMatcher.group(0); // entire match 674 List<String> lines = lines(threadBacktraceBlob); 675 676 matchLines( 677 lines.iterator(), 678 BACKTRACE_NOTE_PATTERN, 679 m -> { 680 threadBuilder.addBacktraceNote(m.group("note")); 681 }); 682 683 List<BacktraceFrame> backtraceFrames = new ArrayList<>(); 684 if (!parseBacktrace(lines, backtraceFrames)) { 685 return false; 686 } 687 threadBuilder.addAllCurrentBacktrace(backtraceFrames); 688 return true; 689 } 690 parseBacktrace( List<String> lines, List<BacktraceFrame> backtraceFrames)691 private static boolean parseBacktrace( 692 List<String> lines, List<BacktraceFrame> backtraceFrames) { 693 matchLines( 694 lines.iterator(), 695 BACKTRACE_PATTERN, 696 m -> { 697 BacktraceFrame.Builder backtraceFrameBuilder = 698 BacktraceFrame.newBuilder() 699 .setRelPc(parsePointer(m.group("programcounter"))) 700 .setFileName(m.group("filename")); 701 String functionName = m.group("functionname"); 702 String functionOffset = m.group("functionoffset"); 703 if (functionName != null && functionOffset != null) { 704 backtraceFrameBuilder 705 .setFunctionName(functionName) 706 .setFunctionOffset(Long.valueOf(functionOffset)); 707 } 708 String buildId = m.group("buildid"); 709 if (buildId != null) { 710 backtraceFrameBuilder.setBuildId(buildId); 711 } 712 backtraceFrames.add(backtraceFrameBuilder.build()); 713 }); 714 715 return true; 716 } 717 parseThreadMemoryDump( String blob, com.android.server.os.TombstoneProtos.Thread.Builder threadBuilder)718 private static boolean parseThreadMemoryDump( 719 String blob, com.android.server.os.TombstoneProtos.Thread.Builder threadBuilder) { 720 721 String[] memoryDumpBlobs = splitPattern(MEMORY_NEAR_PATTERN).split(blob); 722 String headerBlob = memoryDumpBlobs[0]; 723 List<String> headerLines = lines(headerBlob); 724 725 for (int i = 1; i < memoryDumpBlobs.length; i++) { 726 String memoryDumpBlob = memoryDumpBlobs[i]; 727 List<String> lines = lines(memoryDumpBlob); 728 729 matchLine( 730 lines.iterator(), 731 MEMORY_NEAR_PATTERN, 732 m -> { 733 // TODO: add memory dump to tombstone 734 }); 735 736 matchLines( 737 lines.iterator(), 738 MEMORY_DUMP_ROW_PATTERN, 739 m -> { 740 // TODO: add memory dump to tombstone 741 }); 742 } 743 744 return true; 745 } 746 parseTagDump(List<String> lines, Tombstone.Builder tombstoneBuilder)747 private static boolean parseTagDump(List<String> lines, Tombstone.Builder tombstoneBuilder) { 748 749 matchLine( 750 lines.iterator(), 751 MEMORY_TAG_PATTERN, 752 m -> { 753 // TODO: add memory tag to tombstone builder 754 }); 755 756 matchLines( 757 lines.iterator(), 758 MEMORY_TAG_ROW_PATTERN, 759 m -> { 760 // TODO: add memory tag to tombstone builder 761 }); 762 763 return true; 764 } 765 parseLogs(String blob, Tombstone.Builder tombstoneBuilder)766 private static boolean parseLogs(String blob, Tombstone.Builder tombstoneBuilder) { 767 768 String[] logBlobs = LOG_PATTERN.split(blob); 769 for (int i = 1; i < logBlobs.length; i++) { 770 List<String> lines = lines(logBlobs[i]); 771 772 matchLine( 773 lines.iterator(), 774 LOG_PATTERN, 775 m -> { 776 CLog.i("found logs"); 777 // TODO: determine if we're the tail end of the logs 778 // TODO: add logs to tombstone builder 779 }); 780 781 matchLines( 782 lines.iterator(), 783 LOG_LINE_PATTERN, 784 m -> { 785 // TODO: add logs to tombstone builder 786 }); 787 } 788 789 return true; 790 } 791 matchLine( Iterator<String> lines, Pattern pattern, Consumer<Matcher> onMatch)792 private static boolean matchLine( 793 Iterator<String> lines, Pattern pattern, Consumer<Matcher> onMatch) { 794 return matchLines(lines, pattern, 1, onMatch) == 1; 795 } 796 matchLines( Iterator<String> lines, Pattern pattern, Consumer<Matcher> onMatch)797 private static int matchLines( 798 Iterator<String> lines, Pattern pattern, Consumer<Matcher> onMatch) { 799 return matchLines(lines, pattern, -1, onMatch); 800 } 801 matchLines( Iterator<String> lines, Pattern pattern, int times, Consumer<Matcher> onMatch)802 private static int matchLines( 803 Iterator<String> lines, Pattern pattern, int times, Consumer<Matcher> onMatch) { 804 int matches = 0; 805 while (lines.hasNext() && (times < 0 || matches < times)) { 806 String line = lines.next(); 807 Matcher m = pattern.matcher(line); 808 if (m.matches()) { 809 matches++; 810 if (onMatch != null) { 811 onMatch.accept(m); 812 } 813 } 814 } 815 return matches; 816 } 817 lines(String s)818 private static List<String> lines(String s) { 819 return Arrays.asList(s.split("\\R")); 820 } 821 splitPattern(Pattern pattern)822 private static Pattern splitPattern(Pattern pattern) { 823 return Pattern.compile(String.format("((?=^.*%s))", pattern.pattern()), Pattern.MULTILINE); 824 } 825 parsePointer(String pointerString)826 public static long parsePointer(String pointerString) { 827 return Long.parseUnsignedLong(pointerString, 16); 828 } 829 } 830