Android 12 Watchdog Trace生成过程

Posted pecuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 12 Watchdog Trace生成过程相关的知识,希望对你有一定的参考价值。

文章托管在gitee上 Android Notes , 同步csdn

Trace生成流程

从第2篇可知,Watchdog的Trace生成过程如下:

  • 当等待时间 >Max/2 , 即评估状态为 WAITED_HALF,则会输出第一次Trace
  • 当等待时间 >Max, 即评估状态为 OVERDUE,则会输出第二次Trace,以及一些其他的信息(kernel log,binder相关信息,dropbox等)
  • 最终,会将两次生成的Trace合二为一,生成一个最终的Trace。

下面看判断条件

 else if (waitState == WAITED_HALF) 
    if (!waitedHalf) 
        Slog.i(TAG, "WAITED_HALF");
        waitedHalf = true;  // 设置标志,防止反复进入
        // We've waited half, but we'd need to do the stack trace dump w/o the lock.
        pids = new ArrayList<>(mInterestingJavaPids); // 需要dump的进程
        doWaitedHalfDump = true; // 设置标志,会生成trace
     else 
        continue;
    
 else   // OVERDUE, 会dump 第二次
    // something is overdue!
    blockedCheckers = getBlockedCheckersLocked();
    subject = describeCheckersLocked(blockedCheckers);
    allowRestart = mAllowRestart;
    pids = new ArrayList<>(mInterestingJavaPids);

输出第一次Trace

if (doWaitedHalfDump) 
    // We've waited half the deadlock-detection interval.  Pull a stack
    // trace and wait another half.
    ActivityManagerService.dumpStackTraces(pids, null, null,
            getInterestingNativePids(), null, subject);
    continue;

添加 dump Java进程

pids 表示需要dump的Java进程信息,通过mInterestingJavaPids所得,默认添加系统进程pid到mInterestingJavaPids,会输出system_server进程的trace。在一些特殊进程启动、退出时,会从mInterestingJavaPids添加、移除pid

/**
 * Notifies the watchdog when a Java process with @code pid is started.
 * This process may have its stack trace dumped during an ANR.
 */
public void processStarted(String processName, int pid)  // app 启动后回调
    if (isInterestingJavaProcess(processName)) 
        Slog.i(TAG, "Interesting Java process " + processName + " started. Pid " + pid);
        synchronized (mLock) 
            mInterestingJavaPids.add(pid);
        
    


// 兴趣进程判断
private static boolean isInterestingJavaProcess(String processName) 
    return processName.equals(StorageManagerService.sMediaStoreAuthorityProcessName)
            || processName.equals("com.android.phone");



/**
 * Notifies the watchdog when a Java process with @code pid dies.
 */
public void processDied(String processName, int pid)  // app 退出时回调
    if (isInterestingJavaProcess(processName)) 
        Slog.i(TAG, "Interesting Java process " + processName + " died. Pid " + pid);
        synchronized (mLock) 
            mInterestingJavaPids.remove(Integer.valueOf(pid));
        
    

添加 dump native进程

getInterestingNativePids() 参数获取要打印的native进程

static ArrayList<Integer> getInterestingNativePids() 
    HashSet<Integer> pids = new HashSet<>();
    addInterestingAidlPids(pids); // 添加 AIDL_INTERFACE_PREFIXES_OF_INTEREST 中的进程
    addInterestingHidlPids(pids); // 添加 HAL_INTERFACES_OF_INTEREST 中的进程

    // 添加NATIVE_STACKS_OF_INTEREST中的进程
    int[] nativePids = Process.getPidsForCommands(NATIVE_STACKS_OF_INTEREST);
    if (nativePids != null) 
        for (int i : nativePids) 
            pids.add(i);
        
    

    return new ArrayList<Integer>(pids);

addInterestingAidlPids

private static void addInterestingAidlPids(HashSet<Integer> pids) 
    ServiceDebugInfo[] infos = ServiceManager.getServiceDebugInfo();
    if (infos == null) return;

    for (ServiceDebugInfo info : infos)  // 添加 aidl 进程
        for (String prefix : AIDL_INTERFACE_PREFIXES_OF_INTEREST) 
            if (info.name.startsWith(prefix)) 
                pids.add(info.debugPid);
            
        
    

addInterestingHidlPids

private static void addInterestingHidlPids(HashSet<Integer> pids) 
    try 
        IServiceManager serviceManager = IServiceManager.getService();
        ArrayList<IServiceManager.InstanceDebugInfo> dump =
                serviceManager.debugDump();
        for (IServiceManager.InstanceDebugInfo info : dump) 
            if (info.pid == IServiceManager.PidConstant.NO_PID) 
                continue;
            
            // 添加 hidl 进程
            if (!HAL_INTERFACES_OF_INTEREST.contains(info.interfaceName)) 
                continue;
            

            pids.add(info.pid);
        
     catch (RemoteException e) 
        Log.w(TAG, e);
    

输出第二次Trace

long anrTime = SystemClock.uptimeMillis();
StringBuilder report = new StringBuilder();
report.append(MemoryPressureUtil.currentPsiState());
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(false);
StringWriter tracesFileException = new StringWriter();
final File stack = ActivityManagerService.dumpStackTraces(
        pids, processCpuTracker, new SparseArray<>(), getInterestingNativePids(),
        tracesFileException, subject);

subject 是卡顿原因的主题,通过 Watchdog.HandlerChecker#describeBlockedStateLocked 获取

String describeBlockedStateLocked() 
    if (mCurrentMonitor == null) 
        return "Blocked in handler on " + mName + " (" + getThread().getName() + ")";
     else 
        return "Blocked in monitor " + mCurrentMonitor.getClass().getName()
                + " on " + mName + " (" + getThread().getName() + ")";
    

dump kernel log

// Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel log
doSysRq('w');
doSysRq('l');

private void doSysRq(char c) 
    try  // 往 sysrq-trigger 写命令
        FileWriter sysrq_trigger = new FileWriter("/proc/sysrq-trigger");
        sysrq_trigger.write(c);
        sysrq_trigger.close();
     catch (IOException e) 
        Slog.w(TAG, "Failed to write to /proc/sysrq-trigger", e);
    


输出Watchdog到dropbox

// Try to add the error to the dropbox, but assuming that the ActivityManager
// itself may be deadlocked.  (which has happened, causing this statement to
// deadlock and the watchdog as a whole to be ineffective)
Thread dropboxThread = new Thread("watchdogWriteToDropbox") 
        public void run() 
            // If a watched thread hangs before init() is called, we don't have a
            // valid mActivity. So we can't log the error to dropbox.
            if (mActivity != null) 
                mActivity.addErrorToDropBox( // 实际通过DropBoxManager向DropBoxManagerService发起调用
                        "watchdog", null, "system_server", null, null, null,
                        null, report.toString(), stack, null, null, null,
                        errorId);
            
        
    ;
dropboxThread.start();
try 
    dropboxThread.join(2000);  // wait up to 2 seconds for it to return.
 catch (InterruptedException ignored) 

合并Trace

这部分将会合并两次的Trace。通过流将两个trace文件写入到新文件,生成一个新文件。这部分本地还没有最新代码,等更新了S最新代码,再上代码。

ActivityManagerService.dumpStackTraces 实现

从上面的分析来看,trace生成的关键实现是AMS#dumpStackTraces。这部分调用比较长,实际上是system_server通过socket连接tombstoned进程,让后者dump相关进程的信息返回相关数据,然后system_server将trace信息写入指定的文件。

/**
 * If a stack trace dump file is configured, dump process stack traces.
 * @param firstPids of dalvik VM processes to dump stack traces for first
 * @param lastPids of dalvik VM processes to dump stack traces for last
 * @param nativePids optional list of native pids to dump stack crawls
 * @param logExceptionCreatingFile optional writer to which we log errors creating the file
 * @param subject optional line related to the error
 */
public static File dumpStackTraces(ArrayList<Integer> firstPids,
        ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
        ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile,
        String subject) 
    return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePids,
            logExceptionCreatingFile, null, subject);

调用重载方法

/**
 * @param firstPidOffsets Optional, when it's set, it receives the start/end offset
 *                        of the very first pid to be dumped.
 */
/* package */ static File dumpStackTraces(ArrayList<Integer> firstPids,
        ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
        ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile,
        long[] firstPidOffsets, String subject) 
    ArrayList<Integer> extraPids = null;

    Slog.i(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids);

    // Measure CPU usage as soon as we're called in order to get a realistic sampling
    // of the top users at the time of the request.
    ...

    final File tracesDir = new File(ANR_TRACE_DIR); // ANR_TRACE_DIR = "/data/anr";
    // Each set of ANR traces is written to a separate file and dumpstate will process
    // all such files and add them to a captured bug report if they're recent enough.
    maybePruneOldTraces(tracesDir); // 删除过旧的trace文件

    // NOTE: We should consider creating the file in native code atomically once we've
    // gotten rid of the old scheme of dumping and lot of the code that deals with paths
    // can be removed.
    File tracesFile;
    try  // 创建trace文件 anr_yyyy-MM-dd-HH-mm-ss-SSS
        tracesFile = createAnrDumpFile(tracesDir);
     catch (IOException e) 
        Slog.w(TAG, "Exception creating ANR dump file:", e);
        if (logExceptionCreatingFile != null) 
            logExceptionCreatingFile.append("----- Exception creating ANR dump file -----\\n");
            e.printStackTrace(new PrintWriter(logExceptionCreatingFile));
        
        return null;
    
    // 标题,Trace文件第一行,Subject: Blocked in monitor *** on xxx thread
    if (subject != null) 
        try (FileOutputStream fos = new FileOutputStream(tracesFile, true)) 
            String header = "Subject: " + subject + "\\n";
            fos.write(header.getBytes(StandardCharsets.UTF_8));
         catch (IOException e) 
            Slog.w(TAG, "Exception writing subject to ANR dump file:", e);
        
    
    // 开始执行dump
    Pair<Long, Long> offsets = dumpStackTraces(
            tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids);
    if (firstPidOffsets != null) 
        if (offsets == null) 
            firstPidOffsets[0] = firstPidOffsets[1] = -1;
         else 
            firstPidOffsets[0] = offsets.first; // Start offset to the ANR trace file
            firstPidOffsets[1] = offsets.second; // End offset to the ANR trace file
        
    
    return tracesFile;

dumpStackTraces

  • 执行 dump Java 进程trace, 调用 dumpJavaTracesTombstoned
  • 执行 dump native 进程trace, 调用Debug.dumpNativeBacktraceToFileTimeout
  • 执行 extra 进程trace, 调用dumpJavaTracesTombstoned
/**
 * @return The start/end offset of the trace of the very first PID
 */
public static Pair<Long, Long> dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
        ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) 

    Slog.i(TAG, "Dumping to " + tracesFile);

    // We don't need any sort of inotify based monitoring when we're dumping traces via
    // tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full
    // control of all writes to the file in question.

    // We must complete all stack dumps within 20 seconds.
    long remainingTime = 20 * 1000;  // dump 总时间 20s

    // As applications are usually interested with the ANR stack traces, but we can't share with
    // them the stack traces other than their own stacks. So after the very first PID is
    // dumped, remember the current file size.
    long firstPidStart = -1;
    long firstPidEnd = -1;

    // First collect all of the stacks of the most important pids.
    if (firstPids != null) 
        int num = firstPids.size();
        for (int i = 0; i < num; i++) 
            final int pid = firstPids.get(i);
            // We don't copy ANR traces from the system_server intentionally.
            final boolean firstPid = i == 0 && MY_PID != pid;
            File tf = null;
            if (firstPid) 
                tf = new File(tracesFile);
                firstPidStart = tf.exists() ? tf.length() : 0;
            

            Slog.i(TAG, "Collecting stacks for pid " + pid);
            // dump Java 进程信息
            final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile,
                                                            remainingTime);

            remainingTime -= timeTaken;
            if (remainingTime <= 0) 
                Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + pid
                        + "); deadline exceeded.");
                return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
            

            if (firstPid) 
                firstPidEnd = tf.length();
            
            if (DEBUG_ANR) 
                Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
            
        
    

    // Next collect the stacks of the native pids
    if (nativePids != null) 
        for (int pid : nativePids) 
            Slog.i(TAG, "Collecting stacks for native pid " + pid);
            final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);

            final long start = SystemClock.elapsedRealtime();
            // dump native 进程
            Debug.dumpNativeBacktraceToFileTimeout(
                    pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
            final long timeTaken = SystemClock.elapsedRealtime() - start;

            remainingTime -= timeTaken;
            if (remainingTime <= 0) 
                以上是关于Android 12 Watchdog Trace生成过程的主要内容,如果未能解决你的问题,请参考以下文章

Android 12 Java trace 生成过程分析

Android 12 Java trace 生成过程分析

Android 12 Java trace 生成过程分析

ANR系列——ANR监听方案之WatchDog

Android Runtime | Trace文件的生成机制

Android Runtime | Trace文件的生成机制