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

Commit c1b14f8a authored by Fyodor Kupolov's avatar Fyodor Kupolov Committed by Android (Google) Code Review
Browse files

Merge "Provide remote stack trace information"

parents 4907b92b a81b8c09
Loading
Loading
Loading
Loading
+80 −0
Original line number Diff line number Diff line
@@ -27,6 +27,10 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

@RunWith(AndroidJUnit4.class)
@LargeTest
public class ParcelPerfTest {
@@ -167,4 +171,80 @@ public class ParcelPerfTest {
            Parcel.obtain().recycle();
        }
    }

    @Test
    public void timeWriteException() {
        timeWriteException(false);
    }

    @Test
    public void timeWriteExceptionWithStackTraceParceling() {
        timeWriteException(true);
    }

    @Test
    public void timeReadException() {
        timeReadException(false);
    }

    @Test
    public void timeReadExceptionWithStackTraceParceling() {
        timeReadException(true);
    }

    private void timeWriteException(boolean enableParceling) {
        if (enableParceling) {
            Parcel.setStackTraceParceling(true);
        }
        try {
            final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
            Parcel p = Parcel.obtain();
            SecurityException e = new SecurityException("TestMessage");
            while (state.keepRunning()) {
                p.setDataPosition(0);
                p.writeException(e);
            }
        } finally {
            if (enableParceling) {
                Parcel.setStackTraceParceling(false);
            }
        }
    }

    private void timeReadException(boolean enableParceling) {
        if (enableParceling) {
            Parcel.setStackTraceParceling(true);
        }
        try {
            final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
            Parcel p = Parcel.obtain();
            String msg = "TestMessage";
            p.writeException(new SecurityException(msg));
            p.setDataPosition(0);
            // First verify that remote cause is set (if parceling is enabled)
            try {
                p.readException();
            } catch (SecurityException e) {
                assertEquals(e.getMessage(), msg);
                if (enableParceling) {
                    assertTrue(e.getCause() instanceof RemoteException);
                } else {
                    assertNull(e.getCause());
                }
            }

            while (state.keepRunning()) {
                p.setDataPosition(0);
                try {
                    p.readException();
                } catch (SecurityException expected) {
                }
            }
        } finally {
            if (enableParceling) {
                Parcel.setStackTraceParceling(false);
            }
        }
    }

}
+72 −13
Original line number Diff line number Diff line
@@ -191,6 +191,7 @@ import java.util.Set;
 * {@link #readSparseArray(ClassLoader)}.
 */
public final class Parcel {

    private static final boolean DEBUG_RECYCLE = false;
    private static final boolean DEBUG_ARRAY_MAP = false;
    private static final String TAG = "Parcel";
@@ -209,6 +210,12 @@ public final class Parcel {

    private RuntimeException mStack;

    /**
     * Whether or not to parcel the stack trace of an exception. This has a performance
     * impact, so should only be included in specific processes and only on debug builds.
     */
    private static boolean sParcelExceptionStackTrace;

    private static final int POOL_SIZE = 6;
    private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
    private static final Parcel[] sHolderPool = new Parcel[POOL_SIZE];
@@ -325,6 +332,11 @@ public final class Parcel {
    private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName);
    private static native void nativeEnforceInterface(long nativePtr, String interfaceName);

    /** Last time exception with a stack trace was written */
    private static volatile long sLastWriteExceptionStackTrace;
    /** Used for throttling of writing stack trace, which is costly */
    private static final int WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS = 1000;

    @CriticalNative
    private static native long nativeGetBlobAshmemSize(long nativePtr);

@@ -1696,6 +1708,11 @@ public final class Parcel {
        }
    }

    /** @hide For debugging purposes */
    public static void setStackTraceParceling(boolean enabled) {
        sParcelExceptionStackTrace = enabled;
    }

    /**
     * Special function for writing an exception result at the header of
     * a parcel, to be used when returning an exception from a transaction.
@@ -1753,6 +1770,27 @@ public final class Parcel {
            throw new RuntimeException(e);
        }
        writeString(e.getMessage());
        final long timeNow = sParcelExceptionStackTrace ? SystemClock.elapsedRealtime() : 0;
        if (sParcelExceptionStackTrace && (timeNow - sLastWriteExceptionStackTrace
                > WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS)) {
            sLastWriteExceptionStackTrace = timeNow;
            final int sizePosition = dataPosition();
            writeInt(0); // Header size will be filled in later
            StackTraceElement[] stackTrace = e.getStackTrace();
            final int truncatedSize = Math.min(stackTrace.length, 5);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < truncatedSize; i++) {
                sb.append("\tat ").append(stackTrace[i]);
            }
            writeString(sb.toString());
            final int payloadPosition = dataPosition();
            setDataPosition(sizePosition);
            // Write stack trace header size. Used in native side to skip the header
            writeInt(payloadPosition - sizePosition);
            setDataPosition(payloadPosition);
        } else {
            writeInt(0);
        }
        switch (code) {
            case EX_SERVICE_SPECIFIC:
                writeInt(((ServiceSpecificException) e).errorCode);
@@ -1818,7 +1856,19 @@ public final class Parcel {
        int code = readExceptionCode();
        if (code != 0) {
            String msg = readString();
            readException(code, msg);
            String remoteStackTrace = null;
            final int remoteStackPayloadSize = readInt();
            if (remoteStackPayloadSize > 0) {
                remoteStackTrace = readString();
            }
            Exception e = createException(code, msg);
            // Attach remote stack trace if availalble
            if (remoteStackTrace != null) {
                RemoteException cause = new RemoteException(
                        "Remote stack trace:\n" + remoteStackTrace, null, false, false);
                e.initCause(cause);
            }
            SneakyThrow.sneakyThrow(e);
        }
    }

@@ -1863,32 +1913,41 @@ public final class Parcel {
     * @param msg The exception message.
     */
    public final void readException(int code, String msg) {
        SneakyThrow.sneakyThrow(createException(code, msg));
    }

    /**
     * Creates an exception with the given message.
     *
     * @param code Used to determine which exception class to throw.
     * @param msg The exception message.
     */
    private Exception createException(int code, String msg) {
        switch (code) {
            case EX_PARCELABLE:
                if (readInt() > 0) {
                    SneakyThrow.sneakyThrow(
                            (Exception) readParcelable(Parcelable.class.getClassLoader()));
                    return (Exception) readParcelable(Parcelable.class.getClassLoader());
                } else {
                    throw new RuntimeException(msg + " [missing Parcelable]");
                    return new RuntimeException(msg + " [missing Parcelable]");
                }
            case EX_SECURITY:
                throw new SecurityException(msg);
                return new SecurityException(msg);
            case EX_BAD_PARCELABLE:
                throw new BadParcelableException(msg);
                return new BadParcelableException(msg);
            case EX_ILLEGAL_ARGUMENT:
                throw new IllegalArgumentException(msg);
                return new IllegalArgumentException(msg);
            case EX_NULL_POINTER:
                throw new NullPointerException(msg);
                return new NullPointerException(msg);
            case EX_ILLEGAL_STATE:
                throw new IllegalStateException(msg);
                return new IllegalStateException(msg);
            case EX_NETWORK_MAIN_THREAD:
                throw new NetworkOnMainThreadException();
                return new NetworkOnMainThreadException();
            case EX_UNSUPPORTED_OPERATION:
                throw new UnsupportedOperationException(msg);
                return new UnsupportedOperationException(msg);
            case EX_SERVICE_SPECIFIC:
                throw new ServiceSpecificException(readInt(), msg);
                return new ServiceSpecificException(readInt(), msg);
        }
        throw new RuntimeException("Unknown exception code: " + code
        return new RuntimeException("Unknown exception code: " + code
                + " msg " + msg);
    }

+6 −0
Original line number Diff line number Diff line
@@ -30,6 +30,12 @@ public class RemoteException extends AndroidException {
        super(message);
    }

    /** @hide */
    public RemoteException(String message, Throwable cause, boolean enableSuppression,
            boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

    /** {@hide} */
    public RuntimeException rethrowAsRuntimeException() {
        throw new RuntimeException(this);
+6 −0
Original line number Diff line number Diff line
@@ -34,5 +34,11 @@ public class AndroidException extends Exception {
    public AndroidException(Exception cause) {
        super(cause);
    }

    /** @hide */
    protected AndroidException(String message, Throwable cause, boolean enableSuppression,
            boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
};
+4 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.os.FileUtils;
import android.os.IIncidentManager;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.PowerManager;
import android.os.Process;
import android.os.ServiceManager;
@@ -360,6 +361,9 @@ public final class SystemServer {
            // to avoid throwing BadParcelableException.
            BaseBundle.setShouldDefuse(true);

            // Within the system server, when parceling exceptions, include the stack trace
            Parcel.setStackTraceParceling(true);

            // Ensure binder calls into the system always run at foreground priority.
            BinderInternal.disableBackgroundScheduling(true);