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