• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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