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

Commit d5854bc0 authored by Siim Sammul's avatar Siim Sammul Committed by Automerger Merge Worker
Browse files

Merge "Change the way tombstones are added to dropbox." into main am: 6d26e8ef

parents 4569f000 6d26e8ef
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -201,6 +201,7 @@ java_library {
        "apex_aidl_interface-java",
        "packagemanager_aidl-java",
        "framework-protos",
        "libtombstone_proto_java",
        "updatable-driver-protos",
        "ota_metadata_proto_java",
        "android.hidl.base-V1.0-java",
+0 −1
Original line number Diff line number Diff line
@@ -34,7 +34,6 @@ gensrcs {
        ":ipconnectivity-proto-src",
        ":libstats_atom_enum_protos",
        ":libstats_atom_message_protos",
        ":libtombstone_proto-src",
        "core/proto/**/*.proto",
        "libs/incident/**/*.proto",
    ],

STABILITY_OWNERS

0 → 100644
+2 −0
Original line number Diff line number Diff line
gaillard@google.com
+57 −26
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@ import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.am.DropboxRateLimiter;
import com.android.server.os.TombstoneProtos;
import com.android.server.os.TombstoneProtos.Tombstone;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -60,11 +62,14 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Performs a number of miscellaneous, non-system-critical actions
@@ -327,12 +332,12 @@ public class BootReceiver extends BroadcastReceiver {
     *
     * @param ctx Context
     * @param tombstone path to the tombstone
     * @param proto whether the tombstone is stored as proto
     * @param tombstoneProto the parsed proto tombstone
     * @param processName the name of the process corresponding to the tombstone
     * @param tmpFileLock the lock for reading/writing tmp files
     */
    public static void addTombstoneToDropBox(
                Context ctx, File tombstone, boolean proto, String processName,
                Context ctx, File tombstone, Tombstone tombstoneProto, String processName,
                ReentrantLock tmpFileLock) {
        final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
        if (db == null) {
@@ -342,32 +347,34 @@ public class BootReceiver extends BroadcastReceiver {

        // Check if we should rate limit and abort early if needed.
        DropboxRateLimiter.RateLimitResult rateLimitResult =
                sDropboxRateLimiter.shouldRateLimit(
                        proto ? TAG_TOMBSTONE_PROTO_WITH_HEADERS : TAG_TOMBSTONE, processName);
                sDropboxRateLimiter.shouldRateLimit(TAG_TOMBSTONE_PROTO_WITH_HEADERS, processName);
        if (rateLimitResult.shouldRateLimit()) return;

        HashMap<String, Long> timestamps = readTimestamps();
        try {
            if (proto) {
            // Remove the memory data from the proto.
            Tombstone tombstoneProtoWithoutMemory = removeMemoryFromTombstone(tombstoneProto);

            final byte[] tombstoneBytes = tombstoneProtoWithoutMemory.toByteArray();

            // Use JNI to call the c++ proto to text converter and add the headers to the tombstone.
            String tombstoneWithoutMemory = new StringBuilder(getBootHeadersToLogAndUpdate())
                    .append(rateLimitResult.createHeader())
                    .append(getTombstoneText(tombstoneBytes))
                    .toString();

            // Add the tombstone without memory data to dropbox.
            db.addText(TAG_TOMBSTONE, tombstoneWithoutMemory);

            // Add the tombstone proto to dropbox.
            if (recordFileTimestamp(tombstone, timestamps)) {
                    // We need to attach the count indicating the number of dropped dropbox entries
                    // due to rate limiting. Do this by enclosing the proto tombsstone in a
                    // container proto that has the dropped entry count and the proto tombstone as
                    // bytes (to avoid the complexity of reading and writing nested protos).
                tmpFileLock.lock();
                try {
                        addAugmentedProtoToDropbox(tombstone, db, rateLimitResult);
                    addAugmentedProtoToDropbox(tombstone, tombstoneBytes, db, rateLimitResult);
                } finally {
                    tmpFileLock.unlock();
                }
            }
            } else {
                // Add the header indicating how many events have been dropped due to rate limiting.
                final String headers = getBootHeadersToLogAndUpdate()
                        + rateLimitResult.createHeader();
                addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
                                 TAG_TOMBSTONE);
            }
        } catch (IOException e) {
            Slog.e(TAG, "Can't log tombstone", e);
        }
@@ -375,11 +382,8 @@ public class BootReceiver extends BroadcastReceiver {
    }

    private static void addAugmentedProtoToDropbox(
                File tombstone, DropBoxManager db,
                File tombstone, byte[] tombstoneBytes, DropBoxManager db,
                DropboxRateLimiter.RateLimitResult rateLimitResult) throws IOException {
        // Read the proto tombstone file as bytes.
        final byte[] tombstoneBytes = Files.readAllBytes(tombstone.toPath());

        final File tombstoneProtoWithHeaders = File.createTempFile(
                tombstone.getName(), ".tmp", TOMBSTONE_TMP_DIR);
        Files.setPosixFilePermissions(
@@ -412,6 +416,8 @@ public class BootReceiver extends BroadcastReceiver {
        }
    }

    private static native String getTombstoneText(byte[] tombstoneBytes);

    private static void addLastkToDropBox(
            DropBoxManager db, HashMap<String, Long> timestamps,
            String headers, String footers, String filename, int maxSize,
@@ -429,6 +435,31 @@ public class BootReceiver extends BroadcastReceiver {
        addFileWithFootersToDropBox(db, timestamps, headers, footers, filename, maxSize, tag);
    }

    /** Removes memory information from the Tombstone proto. */
    @VisibleForTesting
    public static Tombstone removeMemoryFromTombstone(Tombstone tombstoneProto) {
        Tombstone.Builder tombstoneBuilder = tombstoneProto.toBuilder()
                .clearMemoryMappings()
                .clearThreads()
                .putAllThreads(tombstoneProto.getThreadsMap().entrySet()
                        .stream()
                        .map(BootReceiver::clearMemoryDump)
                        .collect(Collectors.toMap(e->e.getKey(), e->e.getValue())));

        if (tombstoneProto.hasSignalInfo()) {
            tombstoneBuilder.setSignalInfo(
                    tombstoneProto.getSignalInfo().toBuilder().clearFaultAdjacentMetadata());
        }

        return tombstoneBuilder.build();
    }

    private static AbstractMap.SimpleEntry<Integer, TombstoneProtos.Thread> clearMemoryDump(
            Map.Entry<Integer, TombstoneProtos.Thread> e) {
        return new AbstractMap.SimpleEntry<Integer, TombstoneProtos.Thread>(
            e.getKey(), e.getValue().toBuilder().clearMemoryDump().build());
    }

    private static void addFileToDropBox(
            DropBoxManager db, HashMap<String, Long> timestamps,
            String headers, String filename, int maxSize, String tag) throws IOException {
+66 −69
Original line number Diff line number Diff line
@@ -41,14 +41,13 @@ import android.system.Os;
import android.system.StructStat;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoParseException;

import com.android.internal.annotations.GuardedBy;
import com.android.server.BootReceiver;
import com.android.server.ServiceThread;
import com.android.server.os.TombstoneProtos.Cause;
import com.android.server.os.TombstoneProtos.Tombstone;
import com.android.server.os.protobuf.CodedInputStream;

import libcore.io.IoUtils;

@@ -128,18 +127,21 @@ public final class NativeTombstoneManager {
            return;
        }

        String processName = "UNKNOWN";
        final boolean isProtoFile = filename.endsWith(".pb");
        File protoPath = isProtoFile ? path : new File(path.getAbsolutePath() + ".pb");
        if (!isProtoFile) {
            return;
        }

        Optional<TombstoneFile> parsedTombstone = handleProtoTombstone(protoPath, isProtoFile);
        Optional<ParsedTombstone> parsedTombstone = handleProtoTombstone(path, true);
        if (parsedTombstone.isPresent()) {
            processName = parsedTombstone.get().getProcessName();
            BootReceiver.addTombstoneToDropBox(
                    mContext, path, parsedTombstone.get().getTombstone(),
                    parsedTombstone.get().getProcessName(), mTmpFileLock);
        }
        BootReceiver.addTombstoneToDropBox(mContext, path, isProtoFile, processName, mTmpFileLock);
    }

    private Optional<TombstoneFile> handleProtoTombstone(File path, boolean addToList) {
    private Optional<ParsedTombstone> handleProtoTombstone(
            File path, boolean addToList) {
        final String filename = path.getName();
        if (!filename.endsWith(".pb")) {
            Slog.w(TAG, "unexpected tombstone name: " + path);
@@ -169,7 +171,7 @@ public final class NativeTombstoneManager {
            return Optional.empty();
        }

        final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd);
        final Optional<ParsedTombstone> parsedTombstone = TombstoneFile.parse(pfd);
        if (!parsedTombstone.isPresent()) {
            IoUtils.closeQuietly(pfd);
            return Optional.empty();
@@ -182,7 +184,7 @@ public final class NativeTombstoneManager {
                    previous.dispose();
                }

                mTombstones.put(number, parsedTombstone.get());
                mTombstones.put(number, parsedTombstone.get().getTombstoneFile());
            }
        }

@@ -330,6 +332,27 @@ public final class NativeTombstoneManager {
        }
    }

    static class ParsedTombstone {
        TombstoneFile mTombstoneFile;
        Tombstone mTombstone;
        ParsedTombstone(TombstoneFile tombstoneFile, Tombstone tombstone) {
            mTombstoneFile = tombstoneFile;
            mTombstone = tombstone;
        }

        public String getProcessName() {
            return mTombstoneFile.getProcessName();
        }

        public TombstoneFile getTombstoneFile() {
            return mTombstoneFile;
        }

        public Tombstone getTombstone() {
            return mTombstone;
        }
    }

    static class TombstoneFile {
        final ParcelFileDescriptor mPfd;

@@ -412,67 +435,21 @@ public final class NativeTombstoneManager {
            }
        }

        static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
            final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
            final ProtoInputStream stream = new ProtoInputStream(is);

            int pid = 0;
            int uid = 0;
            String processName = null;
            String crashReason = "";
            String selinuxLabel = "";

            try {
                while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
                    switch (stream.getFieldNumber()) {
                        case (int) Tombstone.PID:
                            pid = stream.readInt(Tombstone.PID);
                            break;

                        case (int) Tombstone.UID:
                            uid = stream.readInt(Tombstone.UID);
                            break;

                        case (int) Tombstone.COMMAND_LINE:
                            if (processName == null) {
                                processName = stream.readString(Tombstone.COMMAND_LINE);
                            }
                            break;
        static Optional<ParsedTombstone> parse(ParcelFileDescriptor pfd) {
            Tombstone tombstoneProto;
            try (FileInputStream is = new FileInputStream(pfd.getFileDescriptor())) {
                final byte[] tombstoneBytes = is.readAllBytes();

                        case (int) Tombstone.CAUSES:
                            if (!crashReason.equals("")) {
                                // Causes appear in decreasing order of likelihood. For now we only
                                // want the most likely crash reason here, so ignore all others.
                                break;
                            }
                            long token = stream.start(Tombstone.CAUSES);
                        cause:
                            while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
                                switch (stream.getFieldNumber()) {
                                    case (int) Cause.HUMAN_READABLE:
                                        crashReason = stream.readString(Cause.HUMAN_READABLE);
                                        break cause;

                                    default:
                                        break;
                                }
                            }
                            stream.end(token);
                            break;

                        case (int) Tombstone.SELINUX_LABEL:
                            selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
                            break;

                        default:
                            break;
                    }
                }
            } catch (IOException | ProtoParseException ex) {
                tombstoneProto = Tombstone.parseFrom(
                        CodedInputStream.newInstance(tombstoneBytes));
            } catch (IOException ex) {
                Slog.e(TAG, "Failed to parse tombstone", ex);
                return Optional.empty();
            }

            int pid = tombstoneProto.getPid();
            int uid = tombstoneProto.getUid();

            if (!UserHandle.isApp(uid)) {
                Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring");
                return Optional.empty();
@@ -489,6 +466,7 @@ public final class NativeTombstoneManager {
            final int userId = UserHandle.getUserId(uid);
            final int appId = UserHandle.getAppId(uid);

            String selinuxLabel = tombstoneProto.getSelinuxLabel();
            if (!selinuxLabel.startsWith("u:r:untrusted_app")) {
                Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring");
                return Optional.empty();
@@ -500,11 +478,30 @@ public final class NativeTombstoneManager {
            result.mAppId = appId;
            result.mPid = pid;
            result.mUid = uid;
            result.mProcessName = processName == null ? "" : processName;
            result.mProcessName = getCmdLineProcessName(tombstoneProto);
            result.mTimestampMs = timestampMs;
            result.mCrashReason = crashReason;
            result.mCrashReason = getCrashReason(tombstoneProto);

            return Optional.of(result);
            return Optional.of(new ParsedTombstone(result, tombstoneProto));
        }

        private static String getCmdLineProcessName(Tombstone tombstoneProto) {
            for (String cmdline : tombstoneProto.getCommandLineList()) {
                if (cmdline != null) {
                    return cmdline;
                }
            }
            return "";
        }

        private static String getCrashReason(Tombstone tombstoneProto) {
            for (Cause cause : tombstoneProto.getCausesList()) {
                if (cause.getHumanReadable() != null
                        && !cause.getHumanReadable().equals("")) {
                    return cause.getHumanReadable();
                }
            }
            return "";
        }

        public IParcelFileDescriptorRetriever getPfdRetriever() {
Loading