1 /* 2 * Copyright (C) 2023 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.server.am; 18 19 import static android.text.format.DateUtils.DAY_IN_MILLIS; 20 21 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR; 22 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; 23 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; 24 25 import android.annotation.NonNull; 26 import android.os.Build; 27 import android.os.Debug; 28 import android.os.FileUtils; 29 import android.os.SystemClock; 30 import android.os.SystemProperties; 31 import android.util.Slog; 32 import android.util.SparseBooleanArray; 33 34 import com.android.internal.annotations.GuardedBy; 35 import com.android.internal.os.ProcessCpuTracker; 36 import com.android.internal.os.anr.AnrLatencyTracker; 37 38 import java.io.File; 39 import java.io.FileOutputStream; 40 import java.io.IOException; 41 import java.io.PrintWriter; 42 import java.io.StringWriter; 43 import java.nio.charset.StandardCharsets; 44 import java.nio.file.Files; 45 import java.text.SimpleDateFormat; 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.Comparator; 49 import java.util.Date; 50 import java.util.LinkedHashMap; 51 import java.util.Map; 52 import java.util.concurrent.CompletableFuture; 53 import java.util.concurrent.ExecutionException; 54 import java.util.concurrent.Executor; 55 import java.util.concurrent.Future; 56 import java.util.concurrent.TimeUnit; 57 import java.util.concurrent.TimeoutException; 58 import java.util.concurrent.atomic.AtomicLong; 59 import java.util.function.Supplier; 60 61 62 /** 63 * A helper for dumping stack traces. 64 */ 65 public class StackTracesDumpHelper { 66 static final String TAG = TAG_WITH_CLASS_NAME ? "StackTracesDumpHelper" : TAG_AM; 67 68 @GuardedBy("StackTracesDumpHelper.class") 69 private static final SimpleDateFormat ANR_FILE_DATE_FORMAT = 70 new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS"); 71 72 static final String ANR_FILE_PREFIX = "anr_"; 73 static final String ANR_TEMP_FILE_PREFIX = "temp_anr_"; 74 75 public static final String ANR_TRACE_DIR = "/data/anr"; 76 private static final int NATIVE_DUMP_TIMEOUT_MS = 77 2000 * Build.HW_TIMEOUT_MULTIPLIER; // 2 seconds; 78 private static final int JAVA_DUMP_MINIMUM_SIZE = 100; // 100 bytes. 79 // The time limit for a single process's dump 80 private static final int TEMP_DUMP_TIME_LIMIT = 81 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; // 10 seconds 82 83 84 /** 85 * If a stack trace dump file is configured, dump process stack traces. 86 * @param firstPids of dalvik VM processes to dump stack traces for first 87 * @param lastPids of dalvik VM processes to dump stack traces for last 88 * @param nativePidsFuture optional future for a list of native pids to dump stack crawls 89 * @param logExceptionCreatingFile optional writer to which we log errors creating the file 90 * @param auxiliaryTaskExecutor executor to execute auxiliary tasks on 91 * @param latencyTracker the latency tracker instance of the current ANR. 92 */ dumpStackTraces(ArrayList<Integer> firstPids, ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids, Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile, @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker)93 public static File dumpStackTraces(ArrayList<Integer> firstPids, 94 ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids, 95 Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile, 96 @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) { 97 return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture, 98 logExceptionCreatingFile, null, null, null, null, auxiliaryTaskExecutor, null, 99 latencyTracker); 100 } 101 102 /** 103 * @param subject the subject of the dumped traces 104 * @param criticalEventSection the critical event log, passed as a string 105 * @param extraHeaders Optional, Extra headers added by the dumping method 106 */ dumpStackTraces(ArrayList<Integer> firstPids, ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids, Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile, String subject, String criticalEventSection, LinkedHashMap<String, String> extraHeaders, @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker)107 public static File dumpStackTraces(ArrayList<Integer> firstPids, 108 ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids, 109 Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile, 110 String subject, String criticalEventSection, 111 LinkedHashMap<String, String> extraHeaders, @NonNull Executor auxiliaryTaskExecutor, 112 AnrLatencyTracker latencyTracker) { 113 return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture, 114 logExceptionCreatingFile, null, subject, criticalEventSection, 115 extraHeaders, auxiliaryTaskExecutor, null, latencyTracker); 116 } 117 118 /** 119 * @param firstPidEndOffset Optional, when it's set, it receives the start/end offset 120 * of the very first pid to be dumped. 121 */ dumpStackTraces(ArrayList<Integer> firstPids, ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids, Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile, AtomicLong firstPidEndOffset, String subject, String criticalEventSection, LinkedHashMap<String, String> extraHeaders, @NonNull Executor auxiliaryTaskExecutor, Future<File> firstPidFilePromise, AnrLatencyTracker latencyTracker)122 /* package */ static File dumpStackTraces(ArrayList<Integer> firstPids, 123 ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids, 124 Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile, 125 AtomicLong firstPidEndOffset, String subject, String criticalEventSection, 126 LinkedHashMap<String, String> extraHeaders, @NonNull Executor auxiliaryTaskExecutor, 127 Future<File> firstPidFilePromise, AnrLatencyTracker latencyTracker) { 128 try { 129 130 if (latencyTracker != null) { 131 latencyTracker.dumpStackTracesStarted(); 132 } 133 134 Slog.i(TAG, "dumpStackTraces pids=" + lastPids); 135 136 // Measure CPU usage as soon as we're called in order to get a realistic sampling 137 // of the top users at the time of the request. 138 Supplier<ArrayList<Integer>> extraPidsSupplier = processCpuTracker != null 139 ? () -> getExtraPids(processCpuTracker, lastPids, latencyTracker) : null; 140 Future<ArrayList<Integer>> extraPidsFuture = null; 141 if (extraPidsSupplier != null) { 142 extraPidsFuture = 143 CompletableFuture.supplyAsync(extraPidsSupplier, auxiliaryTaskExecutor); 144 } 145 146 final File tracesDir = new File(ANR_TRACE_DIR); 147 148 // NOTE: We should consider creating the file in native code atomically once we've 149 // gotten rid of the old scheme of dumping and lot of the code that deals with paths 150 // can be removed. 151 File tracesFile; 152 try { 153 tracesFile = createAnrDumpFile(tracesDir); 154 } catch (IOException e) { 155 Slog.w(TAG, "Exception creating ANR dump file:", e); 156 if (logExceptionCreatingFile != null) { 157 logExceptionCreatingFile.append( 158 "----- Exception creating ANR dump file -----\n"); 159 e.printStackTrace(new PrintWriter(logExceptionCreatingFile)); 160 } 161 if (latencyTracker != null) { 162 latencyTracker.anrSkippedDumpStackTraces(); 163 } 164 return null; 165 } 166 boolean extraHeadersExist = extraHeaders != null && !extraHeaders.isEmpty(); 167 168 if (subject != null || criticalEventSection != null || extraHeadersExist) { 169 appendtoANRFile(tracesFile.getAbsolutePath(), 170 (subject != null ? "Subject: " + subject + "\n" : "") 171 + (extraHeadersExist ? stringifyHeaders(extraHeaders) + "\n\n" : "") 172 + (criticalEventSection != null ? criticalEventSection : "")); 173 } 174 175 long firstPidEndPos = dumpStackTraces( 176 tracesFile.getAbsolutePath(), firstPids, nativePidsFuture, 177 extraPidsFuture, firstPidFilePromise, latencyTracker); 178 if (firstPidEndOffset != null) { 179 firstPidEndOffset.set(firstPidEndPos); 180 } 181 // Each set of ANR traces is written to a separate file and dumpstate will process 182 // all such files and add them to a captured bug report if they're recent enough. 183 maybePruneOldTraces(tracesDir); 184 185 return tracesFile; 186 } finally { 187 if (latencyTracker != null) { 188 latencyTracker.dumpStackTracesEnded(); 189 } 190 } 191 } 192 193 /** 194 * @return The end offset of the trace of the very first PID 195 */ dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids, Future<ArrayList<Integer>> nativePidsFuture, Future<ArrayList<Integer>> extraPidsFuture, Future<File> firstPidFilePromise, AnrLatencyTracker latencyTracker)196 public static long dumpStackTraces(String tracesFile, 197 ArrayList<Integer> firstPids, Future<ArrayList<Integer>> nativePidsFuture, 198 Future<ArrayList<Integer>> extraPidsFuture, Future<File> firstPidFilePromise, 199 AnrLatencyTracker latencyTracker) { 200 201 Slog.i(TAG, "Dumping to " + tracesFile); 202 203 // We don't need any sort of inotify based monitoring when we're dumping traces via 204 // tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full 205 // control of all writes to the file in question. 206 207 // We must complete all stack dumps within 20 seconds. 208 long remainingTime = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; 209 210 // As applications are usually interested with the ANR stack traces, but we can't share 211 // with them the stack traces other than their own stacks. So after the very first PID is 212 // dumped, remember the current file size. 213 long firstPidEnd = -1; 214 215 // Was the first pid copied from the temporary file that was created in the predump phase? 216 boolean firstPidTempDumpCopied = false; 217 218 // First copy the first pid's dump from the temporary file it was dumped into earlier, 219 // The first pid should always exist in firstPids but we check the size just in case. 220 if (firstPidFilePromise != null && firstPids != null && firstPids.size() > 0) { 221 final int primaryPid = firstPids.get(0); 222 final long start = SystemClock.elapsedRealtime(); 223 firstPidTempDumpCopied = copyFirstPidTempDump(tracesFile, firstPidFilePromise, 224 remainingTime, latencyTracker); 225 final long timeTaken = SystemClock.elapsedRealtime() - start; 226 remainingTime -= timeTaken; 227 if (remainingTime <= 0) { 228 Slog.e(TAG, "Aborting stack trace dump (currently copying primary pid" + primaryPid 229 + "); deadline exceeded."); 230 return firstPidEnd; 231 } 232 // We don't copy ANR traces from the system_server intentionally. 233 if (firstPidTempDumpCopied && primaryPid != ActivityManagerService.MY_PID) { 234 firstPidEnd = new File(tracesFile).length(); 235 } 236 // Append the Durations/latency comma separated array after the first PID. 237 if (firstPidTempDumpCopied && latencyTracker != null) { 238 appendtoANRFile(tracesFile, 239 latencyTracker.dumpAsCommaSeparatedArrayWithHeader()); 240 } 241 } 242 // Next collect all of the stacks of the most important pids. 243 if (firstPids != null) { 244 if (latencyTracker != null) { 245 latencyTracker.dumpingFirstPidsStarted(); 246 } 247 248 int num = firstPids.size(); 249 for (int i = firstPidTempDumpCopied ? 1 : 0; i < num; i++) { 250 final int pid = firstPids.get(i); 251 // We don't copy ANR traces from the system_server intentionally. 252 final boolean firstPid = i == 0 && ActivityManagerService.MY_PID != pid; 253 Slog.i(TAG, "Collecting stacks for pid " + pid); 254 final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime, 255 latencyTracker); 256 remainingTime -= timeTaken; 257 if (remainingTime <= 0) { 258 Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + pid 259 + "); deadline exceeded."); 260 return firstPidEnd; 261 } 262 263 if (firstPid) { 264 firstPidEnd = new File(tracesFile).length(); 265 // Full latency dump 266 if (latencyTracker != null) { 267 appendtoANRFile(tracesFile, 268 latencyTracker.dumpAsCommaSeparatedArrayWithHeader()); 269 } 270 } 271 if (DEBUG_ANR) { 272 Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms"); 273 } 274 } 275 if (latencyTracker != null) { 276 latencyTracker.dumpingFirstPidsEnded(); 277 } 278 } 279 280 // Next collect the stacks of the native pids 281 ArrayList<Integer> nativePids = collectPids(nativePidsFuture, "native pids"); 282 283 Slog.i(TAG, "dumpStackTraces nativepids=" + nativePids); 284 285 if (nativePids != null) { 286 if (latencyTracker != null) { 287 latencyTracker.dumpingNativePidsStarted(); 288 } 289 for (int pid : nativePids) { 290 Slog.i(TAG, "Collecting stacks for native pid " + pid); 291 final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime); 292 293 if (latencyTracker != null) { 294 latencyTracker.dumpingPidStarted(pid); 295 } 296 final long start = SystemClock.elapsedRealtime(); 297 Debug.dumpNativeBacktraceToFileTimeout( 298 pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000)); 299 final long timeTaken = SystemClock.elapsedRealtime() - start; 300 if (latencyTracker != null) { 301 latencyTracker.dumpingPidEnded(); 302 } 303 remainingTime -= timeTaken; 304 if (remainingTime <= 0) { 305 Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid 306 + "); deadline exceeded."); 307 return firstPidEnd; 308 } 309 310 if (DEBUG_ANR) { 311 Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms"); 312 } 313 } 314 if (latencyTracker != null) { 315 latencyTracker.dumpingNativePidsEnded(); 316 } 317 } 318 319 // Lastly, dump stacks for all extra PIDs from the CPU tracker. 320 ArrayList<Integer> extraPids = collectPids(extraPidsFuture, "extra pids"); 321 322 if (extraPidsFuture != null) { 323 try { 324 extraPids = extraPidsFuture.get(); 325 } catch (ExecutionException e) { 326 Slog.w(TAG, "Failed to collect extra pids", e.getCause()); 327 } catch (InterruptedException e) { 328 Slog.w(TAG, "Interrupted while collecting extra pids", e); 329 } 330 } 331 Slog.i(TAG, "dumpStackTraces extraPids=" + extraPids); 332 333 if (extraPids != null) { 334 if (latencyTracker != null) { 335 latencyTracker.dumpingExtraPidsStarted(); 336 } 337 for (int pid : extraPids) { 338 Slog.i(TAG, "Collecting stacks for extra pid " + pid); 339 final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime, 340 latencyTracker); 341 remainingTime -= timeTaken; 342 if (remainingTime <= 0) { 343 Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid 344 + "); deadline exceeded."); 345 return firstPidEnd; 346 } 347 348 if (DEBUG_ANR) { 349 Slog.d(TAG, "Done with extra pid " + pid + " in " + timeTaken + "ms"); 350 } 351 } 352 if (latencyTracker != null) { 353 latencyTracker.dumpingExtraPidsEnded(); 354 } 355 } 356 // Append the dumping footer with the current uptime 357 appendtoANRFile(tracesFile, "----- dumping ended at " + SystemClock.uptimeMillis() + "\n"); 358 Slog.i(TAG, "Done dumping"); 359 360 return firstPidEnd; 361 } 362 363 /** 364 * Dumps the supplied pid to a temporary file. 365 * @param pid the PID to be dumped 366 * @param latencyTracker the latency tracker instance of the current ANR. 367 */ dumpStackTracesTempFile(int pid, AnrLatencyTracker latencyTracker)368 public static File dumpStackTracesTempFile(int pid, AnrLatencyTracker latencyTracker) { 369 try { 370 if (latencyTracker != null) { 371 latencyTracker.dumpStackTracesTempFileStarted(); 372 } 373 374 File tmpTracesFile; 375 try { 376 tmpTracesFile = File.createTempFile(ANR_TEMP_FILE_PREFIX, ".txt", 377 new File(ANR_TRACE_DIR)); 378 Slog.d(TAG, "created ANR temporary file:" + tmpTracesFile.getAbsolutePath()); 379 } catch (IOException e) { 380 Slog.w(TAG, "Exception creating temporary ANR dump file:", e); 381 if (latencyTracker != null) { 382 latencyTracker.dumpStackTracesTempFileCreationFailed(); 383 } 384 return null; 385 } 386 387 Slog.i(TAG, "Collecting stacks for pid " + pid + " into temporary file " 388 + tmpTracesFile.getName()); 389 if (latencyTracker != null) { 390 latencyTracker.dumpingPidStarted(pid); 391 } 392 final long timeTaken = dumpJavaTracesTombstoned(pid, tmpTracesFile.getAbsolutePath(), 393 TEMP_DUMP_TIME_LIMIT); 394 if (latencyTracker != null) { 395 latencyTracker.dumpingPidEnded(); 396 } 397 if (TEMP_DUMP_TIME_LIMIT <= timeTaken) { 398 Slog.e(TAG, "Aborted stack trace dump (current primary pid=" + pid 399 + "); deadline exceeded."); 400 if (latencyTracker != null) { 401 latencyTracker.dumpStackTracesTempFileTimedOut(); 402 } 403 } 404 if (DEBUG_ANR) { 405 Slog.d(TAG, "Done with primary pid " + pid + " in " + timeTaken + "ms" 406 + " dumped into temporary file " + tmpTracesFile.getName()); 407 } 408 return tmpTracesFile; 409 } finally { 410 if (latencyTracker != null) { 411 latencyTracker.dumpStackTracesTempFileEnded(); 412 } 413 } 414 } 415 copyFirstPidTempDump(String tracesFile, Future<File> firstPidFilePromise, long timeLimitMs, AnrLatencyTracker latencyTracker)416 private static boolean copyFirstPidTempDump(String tracesFile, Future<File> firstPidFilePromise, 417 long timeLimitMs, AnrLatencyTracker latencyTracker) { 418 419 boolean copySucceeded = false; 420 try (FileOutputStream fos = new FileOutputStream(tracesFile, true)) { 421 if (latencyTracker != null) { 422 latencyTracker.copyingFirstPidStarted(); 423 } 424 final File tempfile = firstPidFilePromise.get(timeLimitMs, TimeUnit.MILLISECONDS); 425 if (tempfile != null) { 426 Files.copy(tempfile.toPath(), fos); 427 // Delete the temporary first pid dump file 428 tempfile.delete(); 429 copySucceeded = true; 430 return copySucceeded; 431 } 432 return false; 433 } catch (ExecutionException e) { 434 Slog.w(TAG, "Failed to collect the first pid's predump to the main ANR file", 435 e.getCause()); 436 return false; 437 } catch (InterruptedException e) { 438 Slog.w(TAG, "Interrupted while collecting the first pid's predump" 439 + " to the main ANR file", e); 440 return false; 441 } catch (IOException e) { 442 Slog.w(TAG, "Failed to read the first pid's predump file", e); 443 return false; 444 } catch (TimeoutException e) { 445 Slog.w(TAG, "Copying the first pid timed out", e); 446 return false; 447 } finally { 448 if (latencyTracker != null) { 449 latencyTracker.copyingFirstPidEnded(copySucceeded); 450 } 451 } 452 } 453 createAnrDumpFile(File tracesDir)454 private static synchronized File createAnrDumpFile(File tracesDir) throws IOException { 455 final String formattedDate = ANR_FILE_DATE_FORMAT.format(new Date()); 456 final File anrFile = new File(tracesDir, ANR_FILE_PREFIX + formattedDate); 457 458 if (anrFile.createNewFile()) { 459 FileUtils.setPermissions(anrFile.getAbsolutePath(), 0600, -1, -1); // -rw------- 460 return anrFile; 461 } else { 462 throw new IOException("Unable to create ANR dump file: createNewFile failed"); 463 } 464 } 465 getExtraPids(ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids, AnrLatencyTracker latencyTracker)466 private static ArrayList<Integer> getExtraPids(ProcessCpuTracker processCpuTracker, 467 SparseBooleanArray lastPids, AnrLatencyTracker latencyTracker) { 468 if (latencyTracker != null) { 469 latencyTracker.processCpuTrackerMethodsCalled(); 470 } 471 ArrayList<Integer> extraPids = new ArrayList<>(); 472 synchronized (processCpuTracker) { 473 processCpuTracker.init(); 474 } 475 try { 476 Thread.sleep(200); 477 } catch (InterruptedException ignored) { 478 } 479 480 synchronized (processCpuTracker) { 481 processCpuTracker.update(); 482 // We'll take the stack crawls of just the top apps using CPU. 483 final int workingStatsNumber = processCpuTracker.countWorkingStats(); 484 for (int i = 0; i < workingStatsNumber && extraPids.size() < 2; i++) { 485 ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i); 486 if (lastPids.indexOfKey(stats.pid) >= 0) { 487 if (DEBUG_ANR) { 488 Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid); 489 } 490 491 extraPids.add(stats.pid); 492 } else { 493 Slog.i(TAG, 494 "Skipping next CPU consuming process, not a java proc: " 495 + stats.pid); 496 } 497 } 498 } 499 if (latencyTracker != null) { 500 latencyTracker.processCpuTrackerMethodsReturned(); 501 } 502 return extraPids; 503 } 504 505 /** 506 * Prune all trace files that are more than a day old. 507 * 508 * NOTE: It might make sense to move this functionality to tombstoned eventually, along with a 509 * shift away from anr_XX and tombstone_XX to a more descriptive name. We do it here for now 510 * since it's the system_server that creates trace files for most ANRs. 511 */ maybePruneOldTraces(File tracesDir)512 private static void maybePruneOldTraces(File tracesDir) { 513 final File[] files = tracesDir.listFiles(); 514 if (files == null) return; 515 516 final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64); 517 final long now = System.currentTimeMillis(); 518 try { 519 Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed()); 520 for (int i = 0; i < files.length; ++i) { 521 if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) { 522 if (!files[i].delete()) { 523 Slog.w(TAG, "Unable to prune stale trace file: " + files[i]); 524 } 525 } 526 } 527 } catch (IllegalArgumentException e) { 528 // The modification times changed while we were sorting. Bail... 529 // https://issuetracker.google.com/169836837 530 Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e); 531 } 532 } 533 dumpJavaTracesTombstoned(int pid, String fileName, long timeoutMs, AnrLatencyTracker latencyTracker)534 private static long dumpJavaTracesTombstoned(int pid, String fileName, long timeoutMs, 535 AnrLatencyTracker latencyTracker) { 536 try { 537 if (latencyTracker != null) { 538 latencyTracker.dumpingPidStarted(pid); 539 } 540 return dumpJavaTracesTombstoned(pid, fileName, timeoutMs); 541 } finally { 542 if (latencyTracker != null) { 543 latencyTracker.dumpingPidEnded(); 544 } 545 } 546 } 547 548 /** 549 * Dump java traces for process {@code pid} to the specified file. If java trace dumping 550 * fails, a native backtrace is attempted. Note that the timeout {@code timeoutMs} only applies 551 * to the java section of the trace, a further {@code NATIVE_DUMP_TIMEOUT_MS} might be spent 552 * attempting to obtain native traces in the case of a failure. Returns the total time spent 553 * capturing traces. 554 */ dumpJavaTracesTombstoned(int pid, String fileName, long timeoutMs)555 private static long dumpJavaTracesTombstoned(int pid, String fileName, long timeoutMs) { 556 final long timeStart = SystemClock.elapsedRealtime(); 557 int headerSize = writeUptimeStartHeaderForPid(pid, fileName); 558 boolean javaSuccess = Debug.dumpJavaBacktraceToFileTimeout(pid, fileName, 559 (int) (timeoutMs / 1000)); 560 if (javaSuccess) { 561 // Check that something is in the file, actually. Try-catch should not be necessary, 562 // but better safe than sorry. 563 try { 564 long size = new File(fileName).length(); 565 if ((size - headerSize) < JAVA_DUMP_MINIMUM_SIZE) { 566 Slog.w(TAG, "Successfully created Java ANR file is empty!"); 567 javaSuccess = false; 568 } 569 } catch (Exception e) { 570 Slog.w(TAG, "Unable to get ANR file size", e); 571 javaSuccess = false; 572 } 573 } 574 if (!javaSuccess) { 575 Slog.w(TAG, "Dumping Java threads failed, initiating native stack dump."); 576 if (!Debug.dumpNativeBacktraceToFileTimeout(pid, fileName, 577 (NATIVE_DUMP_TIMEOUT_MS / 1000))) { 578 Slog.w(TAG, "Native stack dump failed!"); 579 } 580 } 581 582 return SystemClock.elapsedRealtime() - timeStart; 583 } 584 appendtoANRFile(String fileName, String text)585 private static int appendtoANRFile(String fileName, String text) { 586 try (FileOutputStream fos = new FileOutputStream(fileName, true)) { 587 byte[] header = text.getBytes(StandardCharsets.UTF_8); 588 fos.write(header); 589 return header.length; 590 } catch (IOException e) { 591 Slog.w(TAG, "Exception writing to ANR dump file:", e); 592 return 0; 593 } 594 } 595 596 /* 597 * Writes a header containing the process id and the current system uptime. 598 */ writeUptimeStartHeaderForPid(int pid, String fileName)599 private static int writeUptimeStartHeaderForPid(int pid, String fileName) { 600 return appendtoANRFile(fileName, "----- dumping pid: " + pid + " at " 601 + SystemClock.uptimeMillis() + "\n"); 602 } 603 collectPids(Future<ArrayList<Integer>> pidsFuture, String logName)604 private static ArrayList<Integer> collectPids(Future<ArrayList<Integer>> pidsFuture, 605 String logName) { 606 607 ArrayList<Integer> pids = null; 608 609 if (pidsFuture == null) { 610 return pids; 611 } 612 try { 613 pids = pidsFuture.get(); 614 } catch (ExecutionException e) { 615 Slog.w(TAG, "Failed to collect " + logName, e.getCause()); 616 } catch (InterruptedException e) { 617 Slog.w(TAG, "Interrupted while collecting " + logName , e); 618 } 619 return pids; 620 } 621 stringifyHeaders(@onNull LinkedHashMap<String, String> headers)622 private static String stringifyHeaders(@NonNull LinkedHashMap<String, String> headers) { 623 StringBuilder headersString = new StringBuilder(); 624 for (Map.Entry<String, String> entry : headers.entrySet()) { 625 headersString.append(entry.getKey()) 626 .append(": ") 627 .append(entry.getValue()) 628 .append("\n"); 629 } 630 return headersString.toString(); 631 } 632 633 } 634