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

Commit 9a8c5cef authored by Romain Guy's avatar Romain Guy
Browse files

Ouput looper traces as traceview traces

Change-Id: I96c8e85fd7497d970febbf6f5aefc4ab903add8e
parent 7eabe55d
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1104,10 +1104,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
            data.enforceInterface(IActivityManager.descriptor);
            String process = data.readString();
            boolean start = data.readInt() != 0;
            int profileType = data.readInt();
            String path = data.readString();
            ParcelFileDescriptor fd = data.readInt() != 0
                    ? data.readFileDescriptor() : null;
            int profileType = data.readInt();
            boolean res = profileControl(process, start, path, fd, profileType);
            reply.writeNoException();
            reply.writeInt(res ? 1 : 0);
@@ -2896,6 +2896,7 @@ class ActivityManagerProxy implements IActivityManager
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeString(process);
        data.writeInt(start ? 1 : 0);
        data.writeInt(profileType);
        data.writeString(path);
        if (fd != null) {
            data.writeInt(1);
@@ -2903,7 +2904,6 @@ class ActivityManagerProxy implements IActivityManager
        } else {
            data.writeInt(0);
        }
        data.writeInt(profileType);
        mRemote.transact(PROFILE_CONTROL_TRANSACTION, data, reply, 0);
        reply.readException();
        boolean res = reply.readInt() != 0;
+2 −2
Original line number Diff line number Diff line
@@ -376,10 +376,10 @@ public abstract class ApplicationThreadNative extends Binder
        {
            data.enforceInterface(IApplicationThread.descriptor);
            boolean start = data.readInt() != 0;
            int profileType = data.readInt();
            String path = data.readString();
            ParcelFileDescriptor fd = data.readInt() != 0
                    ? data.readFileDescriptor() : null;
            int profileType = data.readInt();
            profilerControl(start, path, fd, profileType);
            return true;
        }
@@ -941,6 +941,7 @@ class ApplicationThreadProxy implements IApplicationThread {
        Parcel data = Parcel.obtain();
        data.writeInterfaceToken(IApplicationThread.descriptor);
        data.writeInt(start ? 1 : 0);
        data.writeInt(profileType);
        data.writeString(path);
        if (fd != null) {
            data.writeInt(1);
@@ -948,7 +949,6 @@ class ApplicationThreadProxy implements IApplicationThread {
        } else {
            data.writeInt(0);
        }
        data.writeInt(profileType);
        mRemote.transact(PROFILER_CONTROL_TRANSACTION, data, null,
                IBinder.FLAG_ONEWAY);
        data.recycle();
+128 −35
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.os.Debug;
import android.os.Environment;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.DisplayMetrics;
@@ -50,6 +51,9 @@ import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
@@ -453,24 +457,33 @@ public class ViewDebug {
    }

    private static class LooperProfiler implements Looper.Profiler, Printer {
        private static final int LOOPER_PROFILER_VERSION = 1;

        private static final String LOG_TAG = "LooperProfiler";

        private static final int TRACE_VERSION_NUMBER = 3;
        private static final int ACTION_EXIT_METHOD = 0x1;
        private static final int HEADER_SIZE = 32;
        private static final String HEADER_MAGIC = "SLOW";
        private static final short HEADER_RECORD_SIZE = (short) 14;

        private final long mTraceWallStart;
        private final long mTraceThreadStart;
        
        private final ArrayList<Entry> mTraces = new ArrayList<Entry>(512);

        private final HashMap<String, Short> mTraceNames = new HashMap<String, Short>(32);
        private short mTraceId = 0;
        private final HashMap<String, Integer> mTraceNames = new HashMap<String, Integer>(32);
        private int mTraceId = 0;

        private final String mPath;
        private final FileDescriptor mFileDescriptor;
        private ParcelFileDescriptor mFileDescriptor;

        LooperProfiler(String path, FileDescriptor fileDescriptor) {
            mPath = path;
            mFileDescriptor = fileDescriptor;
            try {
                mFileDescriptor = ParcelFileDescriptor.dup(fileDescriptor);
            } catch (IOException e) {
                Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
                throw new RuntimeException(e);
            }
            mTraceWallStart = SystemClock.currentTimeMicro();
            mTraceThreadStart = SystemClock.currentThreadTimeMicro();            
        }
@@ -493,11 +506,11 @@ public class ViewDebug {
            mTraces.add(entry);
        }

        private short getTraceId(Message message) {
        private int getTraceId(Message message) {
            String name = message.getTarget().getMessageName(message);
            Short traceId = mTraceNames.get(name);
            Integer traceId = mTraceNames.get(name);
            if (traceId == null) {
                traceId = mTraceId++;
                traceId = mTraceId++ << 4;
                mTraceNames.put(name, traceId);
            }
            return traceId;
@@ -514,51 +527,131 @@ public class ViewDebug {
        }

        private void saveTraces() {
            FileOutputStream fos = new FileOutputStream(mFileDescriptor);
            FileOutputStream fos = new FileOutputStream(mFileDescriptor.getFileDescriptor());
            DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));

            try {
                out.writeInt(LOOPER_PROFILER_VERSION);
                out.writeLong(mTraceWallStart);
                out.writeLong(mTraceThreadStart);

                out.writeInt(mTraceNames.size());
                for (Map.Entry<String, Short> entry : mTraceNames.entrySet()) {
                    saveTraceName(entry.getKey(), entry.getValue(), out);
                }
                writeHeader(out, mTraceWallStart, mTraceNames, mTraces);
                out.flush();

                out.writeInt(mTraces.size());
                for (Entry entry : mTraces) {
                    saveTrace(entry, out);
                }
                writeTraces(fos, out.size(), mTraceWallStart, mTraceThreadStart, mTraces);

                Log.d(LOG_TAG, "Looper traces ready: " + mPath);
            } catch (IOException e) {
                Log.e(LOG_TAG, "Could not write trace file: ", e);
                Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
            } finally {
                try {
                    out.close();
                } catch (IOException e) {
                    // Ignore
                    Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
                }
                try {
                    mFileDescriptor.close();
                } catch (IOException e) {
                    Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
                }
            }
        }
        
        private static void writeTraces(FileOutputStream out, long offset, long wallStart,
                long threadStart, ArrayList<Entry> entries) throws IOException {
    
            FileChannel channel = out.getChannel();
    
            // Header
            ByteBuffer buffer = ByteBuffer.allocateDirect(HEADER_SIZE);
            buffer.put(HEADER_MAGIC.getBytes());
            buffer = buffer.order(ByteOrder.LITTLE_ENDIAN);
            buffer.putShort((short) TRACE_VERSION_NUMBER);    // version
            buffer.putShort((short) HEADER_SIZE);             // offset to data
            buffer.putLong(wallStart);                        // start time in usec
            buffer.putShort(HEADER_RECORD_SIZE);              // size of a record in bytes
            // padding to 32 bytes
            for (int i = 0; i < HEADER_SIZE - 18; i++) {
                buffer.put((byte) 0);
            }
    
            buffer.flip();
            channel.position(offset).write(buffer);
            
            buffer = ByteBuffer.allocateDirect(14).order(ByteOrder.LITTLE_ENDIAN);
            for (Entry entry : entries) {
                buffer.putShort((short) 1);   // we simulate only one thread
                buffer.putInt(entry.traceId); // entering method
                buffer.putInt((int) (entry.threadStart - threadStart));
                buffer.putInt((int) (entry.wallStart - wallStart));
    
                buffer.flip();
                channel.write(buffer);
                buffer.clear();
    
                buffer.putShort((short) 1);
                buffer.putInt(entry.traceId | ACTION_EXIT_METHOD); // exiting method
                buffer.putInt((int) (entry.threadStart + entry.threadTime - threadStart));
                buffer.putInt((int) (entry.wallStart + entry.wallTime - wallStart));
    
                buffer.flip();
                channel.write(buffer);
                buffer.clear();
            }
    
            channel.close();
        }
    
        private static void writeHeader(DataOutputStream out, long start,
                HashMap<String, Integer> names, ArrayList<Entry> entries) throws IOException {
    
            Entry last = entries.get(entries.size() - 1);
            long wallTotal = (last.wallStart + last.wallTime) - start;
    
            startSection("version", out);
            addValue(null, Integer.toString(TRACE_VERSION_NUMBER), out);
            addValue("data-file-overflow", "false", out);
            addValue("clock", "dual", out);
            addValue("elapsed-time-usec", Long.toString(wallTotal), out);
            addValue("num-method-calls", Integer.toString(entries.size()), out);
            addValue("clock-call-overhead-nsec", "1", out);
            addValue("vm", "dalvik", out);
    
            startSection("threads", out);
            addThreadId(1, "main", out);
    
            startSection("methods", out);
            addMethods(names, out);
    
            startSection("end", out);
        }
    
        private static void addMethods(HashMap<String, Integer> names, DataOutputStream out)
                throws IOException {
    
            for (Map.Entry<String, Integer> name : names.entrySet()) {
                out.writeBytes(String.format("0x%08x\tEventQueue\t%s\t()V\tLooper\t-2\n",
                        name.getValue(), name.getKey()));
            }
        }
    
        private void saveTraceName(String name, short id, DataOutputStream out) throws IOException {
            out.writeShort(id);
            out.writeUTF(name);
        private static void addThreadId(int id, String name, DataOutputStream out)
                throws IOException {

            out.writeBytes(Integer.toString(id) + '\t' + name + '\n');
        }
    
        private static void addValue(String name, String value, DataOutputStream out)
                throws IOException {
    
            if (name != null) {
                out.writeBytes(name + "=");
            }
            out.writeBytes(value + '\n');
        }
    
        private void saveTrace(Entry entry, DataOutputStream out) throws IOException {
            out.writeShort(entry.traceId);
            out.writeLong(entry.wallStart);
            out.writeLong(entry.wallTime);
            out.writeLong(entry.threadStart);
            out.writeLong(entry.threadTime);
        private static void startSection(String name, DataOutputStream out) throws IOException {
            out.writeBytes("*" + name + '\n');
        }

        static class Entry {
            short traceId;
            int traceId;
            long wallStart;
            long wallTime;
            long threadStart;