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