Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 570227e8 authored by Narayan Kamath's avatar Narayan Kamath Committed by Android (Google) Code Review
Browse files

Merge "ActivityManagerService: Add support for new stack dumping scheme."

parents 4b763aa8 f013daa3
Loading
Loading
Loading
Loading
+28 −13
Original line number Diff line number Diff line
@@ -16,13 +16,20 @@

package android.os;

import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.TypedProperties;

import android.app.AppGlobals;
import android.content.Context;
import android.util.Log;

import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.TypedProperties;

import dalvik.bytecode.OpcodeInfo;
import dalvik.system.VMDebug;

import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
@@ -31,21 +38,16 @@ import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;

import dalvik.bytecode.OpcodeInfo;
import dalvik.system.VMDebug;


/**
@@ -2219,13 +2221,26 @@ public final class Debug
    }

    /**
     * Append the stack traces of a given native process to a specified file.
     * Append the Java stack traces of a given native process to a specified file.
     *
     * @param pid pid to dump.
     * @param file path of file to append dump to.
     * @param timeoutSecs time to wait in seconds, or 0 to wait forever.
     * @hide
     */
    public static native boolean dumpJavaBacktraceToFileTimeout(int pid, String file,
                                                                int timeoutSecs);

    /**
     * Append the native stack traces of a given process to a specified file.
     *
     * @param pid pid to dump.
     * @param file path of file to append dump to.
     * @param timeoutSecs time to wait in seconds, or 0 to wait forever.
     * @hide
     */
    public static native void dumpNativeBacktraceToFileTimeout(int pid, String file, int timeoutSecs);
    public static native boolean dumpNativeBacktraceToFileTimeout(int pid, String file,
                                                                  int timeoutSecs);

    /**
     * Get description of unreachable native memory.
+27 −19
Original line number Diff line number Diff line
@@ -33,12 +33,14 @@
#include <string>

#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <debuggerd/client.h>
#include <log/log.h>
#include <utils/misc.h>
#include <utils/String8.h>

#include "JNIHelp.h"
#include "ScopedUtfChars.h"
#include "jni.h"
#include <memtrack/memtrack.h>
#include <memunreachable/memunreachable.h>
@@ -1008,30 +1010,34 @@ static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz,
    ALOGD("Native heap dump complete.\n");
}

static void android_os_Debug_dumpNativeBacktraceToFileTimeout(JNIEnv* env, jobject clazz,
    jint pid, jstring fileName, jint timeoutSecs)
{
    if (fileName == NULL) {
        jniThrowNullPointerException(env, "file == null");
        return;
    }
    const jchar* str = env->GetStringCritical(fileName, 0);
    String8 fileName8;
    if (str) {
        fileName8 = String8(reinterpret_cast<const char16_t*>(str),
                            env->GetStringLength(fileName));
        env->ReleaseStringCritical(fileName, str);
static bool dumpTraces(JNIEnv* env, jint pid, jstring fileName, jint timeoutSecs,
                       DebuggerdDumpType dumpType) {
    const ScopedUtfChars fileNameChars(env, fileName);
    if (fileNameChars.c_str() == nullptr) {
        return false;
    }

    int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC | O_APPEND, 0666);
    android::base::unique_fd fd(open(fileNameChars.c_str(),
                                     O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC | O_APPEND,
                                     0666));
    if (fd < 0) {
        fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno));
        return;
        fprintf(stderr, "Can't open %s: %s\n", fileNameChars.c_str(), strerror(errno));
        return false;
    }

    dump_backtrace_to_file_timeout(pid, kDebuggerdNativeBacktrace, timeoutSecs, fd);
    return (dump_backtrace_to_file_timeout(pid, dumpType, timeoutSecs, fd) == 0);
}

    close(fd);
static jboolean android_os_Debug_dumpJavaBacktraceToFileTimeout(JNIEnv* env, jobject clazz,
        jint pid, jstring fileName, jint timeoutSecs) {
    const bool ret =  dumpTraces(env, pid, fileName, timeoutSecs, kDebuggerdJavaBacktrace);
    return ret ? JNI_TRUE : JNI_FALSE;
}

static jboolean android_os_Debug_dumpNativeBacktraceToFileTimeout(JNIEnv* env, jobject clazz,
        jint pid, jstring fileName, jint timeoutSecs) {
    const bool ret = dumpTraces(env, pid, fileName, timeoutSecs, kDebuggerdNativeBacktrace);
    return ret ? JNI_TRUE : JNI_FALSE;
}

static jstring android_os_Debug_getUnreachableMemory(JNIEnv* env, jobject clazz,
@@ -1074,7 +1080,9 @@ static const JNINativeMethod gMethods[] = {
            (void*)android_os_Debug_getProxyObjectCount },
    { "getBinderDeathObjectCount", "()I",
            (void*)android_os_Debug_getDeathObjectCount },
    { "dumpNativeBacktraceToFileTimeout", "(ILjava/lang/String;I)V",
    { "dumpJavaBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
            (void*)android_os_Debug_dumpJavaBacktraceToFileTimeout },
    { "dumpNativeBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
            (void*)android_os_Debug_dumpNativeBacktraceToFileTimeout },
    { "getUnreachableMemory", "(IZ)Ljava/lang/String;",
            (void*)android_os_Debug_getUnreachableMemory },
+116 −32
Original line number Diff line number Diff line
@@ -599,6 +599,8 @@ public class ActivityManagerService extends IActivityManager.Stub
    // the notification will not be legible to the user.
    private static final int MAX_BUGREPORT_TITLE_SIZE = 50;
    private static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds;
    /** All system services */
    SystemServiceManager mSystemServiceManager;
    AssistUtils mAssistUtils;
@@ -5411,29 +5413,14 @@ public class ActivityManagerService extends IActivityManager.Stub
     * @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
     * @return file containing stack traces, or null if no dump file is configured
     */
    public static File dumpStackTraces(boolean clearTraces, ArrayList<Integer> firstPids,
            ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
            ArrayList<Integer> nativePids) {
        String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
        if (tracesPath == null || tracesPath.length() == 0) {
            return null;
        }
        File tracesFile = new File(tracesPath);
        try {
            if (clearTraces && tracesFile.exists()) tracesFile.delete();
            tracesFile.createNewFile();
            FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1); // -rw-rw-rw-
        } catch (IOException e) {
            Slog.w(TAG, "Unable to prepare ANR traces file: " + tracesPath, e);
            return null;
        }
        ArrayList<Integer> extraPids = null;
        // Lastly, measure CPU usage.
        // 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.
        if (processCpuTracker != null) {
            processCpuTracker.init();
            try {
@@ -5459,14 +5446,70 @@ public class ActivityManagerService extends IActivityManager.Stub
            }
        }
        dumpStackTraces(tracesPath, firstPids, nativePids, extraPids);
        boolean useTombstonedForJavaTraces = false;
        File tracesFile;
        final String tracesDir = SystemProperties.get("dalvik.vm.stack-trace-dir", "");
        if (tracesDir.isEmpty()) {
            // When dalvik.vm.stack-trace-dir is not set, we are using the "old" trace
            // dumping scheme. All traces are written to a global trace file (usually
            // "/data/anr/traces.txt") so the code below must take care to unlink and recreate
            // the file if requested.
            //
            // This mode of operation will be removed in the near future.
            String globalTracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
            if (globalTracesPath.isEmpty()) {
                Slog.w(TAG, "dumpStackTraces: no trace path configured");
                return null;
            }
            tracesFile = new File(globalTracesPath);
            try {
                if (clearTraces && tracesFile.exists()) {
                    tracesFile.delete();
                }
                tracesFile.createNewFile();
                FileUtils.setPermissions(globalTracesPath, 0666, -1, -1); // -rw-rw-rw-
            } catch (IOException e) {
                Slog.w(TAG, "Unable to prepare ANR traces file: " + tracesFile, e);
                return null;
            }
        } else {
            // When dalvik.vm.stack-trace-dir is set, we use the "new" trace dumping scheme.
            // 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.
            //
            // 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.
            try {
                tracesFile = File.createTempFile("anr_", "", new File(tracesDir));
                FileUtils.setPermissions(tracesFile.getAbsolutePath(), 0600, -1, -1); // -rw-------
            } catch (IOException ioe) {
                Slog.w(TAG, "Unable to create ANR traces file: ", ioe);
                return null;
            }
            useTombstonedForJavaTraces = true;
        }
        dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids,
                useTombstonedForJavaTraces);
        return tracesFile;
    }
    /**
     * Legacy code, do not use. Existing users will be deleted.
     *
     * @deprecated
     */
    @Deprecated
    public static class DumpStackFileObserver extends FileObserver {
        // Keep in sync with frameworks/native/cmds/dumpstate/utils.cpp
        private static final int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
        static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds;
        private final String mTracesPath;
        private boolean mClosed;
@@ -5520,16 +5563,45 @@ public class ActivityManagerService extends IActivityManager.Stub
        }
    }
    private static void dumpStackTraces(String tracesPath, ArrayList<Integer> firstPids,
            ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) {
    /**
     * Dump java traces for process {@code pid} to the specified file. If java trace dumping
     * fails, a native backtrace is attempted. Note that the timeout {@code timeoutMs} only applies
     * to the java section of the trace, a further {@code NATIVE_DUMP_TIMEOUT_MS} might be spent
     * attempting to obtain native traces in the case of a failure. Returns the total time spent
     * capturing traces.
     */
    private static long dumpJavaTracesTombstoned(int pid, String fileName, long timeoutMs) {
        final long timeStart = SystemClock.elapsedRealtime();
        if (!Debug.dumpJavaBacktraceToFileTimeout(pid, fileName, (int) (timeoutMs / 1000))) {
            Debug.dumpNativeBacktraceToFileTimeout(pid, fileName,
                    (NATIVE_DUMP_TIMEOUT_MS / 1000));
        }
        return SystemClock.elapsedRealtime() - timeStart;
    }
    private static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
            ArrayList<Integer> nativePids, ArrayList<Integer> extraPids,
            boolean useTombstonedForJavaTraces) {
        // 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.
        final DumpStackFileObserver observer;
        if (useTombstonedForJavaTraces) {
            observer = null;
        } else {
            // Use a FileObserver to detect when traces finish writing.
            // The order of traces is considered important to maintain for legibility.
        DumpStackFileObserver observer = new DumpStackFileObserver(tracesPath);
            observer = new DumpStackFileObserver(tracesFile);
        }
        // We must complete all stack dumps within 20 seconds.
        long remainingTime = 20 * 1000;
        try {
            if (observer != null) {
                observer.startWatching();
            }
            // First collect all of the stacks of the most important pids.
            if (firstPids != null) {
@@ -5537,7 +5609,12 @@ public class ActivityManagerService extends IActivityManager.Stub
                for (int i = 0; i < num; i++) {
                    if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid "
                            + firstPids.get(i));
                    final long timeTaken = observer.dumpWithTimeout(firstPids.get(i), remainingTime);
                    final long timeTaken;
                    if (useTombstonedForJavaTraces) {
                        timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile, remainingTime);
                    } else {
                        timeTaken = observer.dumpWithTimeout(firstPids.get(i), remainingTime);
                    }
                    remainingTime -= timeTaken;
                    if (remainingTime <= 0) {
@@ -5556,12 +5633,11 @@ public class ActivityManagerService extends IActivityManager.Stub
            if (nativePids != null) {
                for (int pid : nativePids) {
                    if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid);
                    final long nativeDumpTimeoutMs = Math.min(
                            DumpStackFileObserver.NATIVE_DUMP_TIMEOUT_MS, remainingTime);
                    final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);
                    final long start = SystemClock.elapsedRealtime();
                    Debug.dumpNativeBacktraceToFileTimeout(
                            pid, tracesPath, (int) (nativeDumpTimeoutMs / 1000));
                            pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
                    final long timeTaken = SystemClock.elapsedRealtime() - start;
                    remainingTime -= timeTaken;
@@ -5582,7 +5658,13 @@ public class ActivityManagerService extends IActivityManager.Stub
                for (int pid : extraPids) {
                    if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + pid);
                    final long timeTaken = observer.dumpWithTimeout(pid, remainingTime);
                    final long timeTaken;
                    if (useTombstonedForJavaTraces) {
                        timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
                    } else {
                        timeTaken = observer.dumpWithTimeout(pid, remainingTime);
                    }
                    remainingTime -= timeTaken;
                    if (remainingTime <= 0) {
                        Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid +
@@ -5596,9 +5678,11 @@ public class ActivityManagerService extends IActivityManager.Stub
                }
            }
        } finally {
            if (observer != null) {
                observer.stopWatching();
            }
        }
    }
    final void logAppTooSlow(ProcessRecord app, long startTime, String msg) {
        if (true || IS_USER_BUILD) {
@@ -5643,7 +5727,7 @@ public class ActivityManagerService extends IActivityManager.Stub
            if (app != null) {
                ArrayList<Integer> firstPids = new ArrayList<Integer>();
                firstPids.add(app.pid);
                dumpStackTraces(tracesPath, firstPids, null, null);
                dumpStackTraces(tracesPath, firstPids, null, null, true /* useTombstoned */);
            }
            File lastTracesFile = null;
+6 −4
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
@@ -908,7 +909,8 @@ class AppErrors {

        // For background ANRs, don't pass the ProcessCpuTracker to
        // avoid spending 1/2 second collecting stats to rank lastPids.
        File tracesFile = mService.dumpStackTraces(true, firstPids,
        File tracesFile = ActivityManagerService.dumpStackTraces(
                true, firstPids,
                (isSilentANR) ? null : processCpuTracker,
                (isSilentANR) ? null : lastPids,
                nativePids);