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

Commit e540833f authored by Bob Lee's avatar Bob Lee
Browse files

Integrated the profiler into the framework. We run it all the time if the persist.sampling_profiler

system property is set. Saves snapshots to the SD card.
parent 9cc1817d
Loading
Loading
Loading
Loading
+120 −98
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ import android.view.WindowManagerImpl;

import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.SamplingProfilerIntegration;
import com.android.internal.util.ArrayUtils;

import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
@@ -87,6 +88,8 @@ import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Pattern;

import dalvik.system.SamplingProfiler;

final class IntentReceiverLeaked extends AndroidRuntimeException {
    public IntentReceiverLeaked(String msg) {
        super(msg);
@@ -1727,6 +1730,10 @@ public final class ActivityThread {
    }

    private final class H extends Handler {
        private H() {
            SamplingProfiler.getInstance().setEventThread(mLooper.getThread());
        }

        public static final int LAUNCH_ACTIVITY         = 100;
        public static final int PAUSE_ACTIVITY          = 101;
        public static final int PAUSE_ACTIVITY_FINISHING= 102;
@@ -1811,6 +1818,7 @@ public final class ActivityThread {
                } break;
                case PAUSE_ACTIVITY:
                    handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
                    maybeSnapshot();
                    break;
                case PAUSE_ACTIVITY_FINISHING:
                    handlePauseActivity((IBinder)msg.obj, true, msg.arg1 != 0, msg.arg2);
@@ -1853,6 +1861,7 @@ public final class ActivityThread {
                    break;
                case RECEIVER:
                    handleReceiver((ReceiverData)msg.obj);
                    maybeSnapshot();
                    break;
                case CREATE_SERVICE:
                    handleCreateService((CreateServiceData)msg.obj);
@@ -1868,6 +1877,7 @@ public final class ActivityThread {
                    break;
                case STOP_SERVICE:
                    handleStopService((IBinder)msg.obj);
                    maybeSnapshot();
                    break;
                case REQUEST_THUMBNAIL:
                    handleRequestThumbnail((IBinder)msg.obj);
@@ -1907,6 +1917,13 @@ public final class ActivityThread {
                    break;
            }
        }

        void maybeSnapshot() {
            if (mBoundApplication != null) {
                SamplingProfilerIntegration.writeSnapshot(
                        mBoundApplication.processName);
            }
        }
    }

    private final class Idler implements MessageQueue.IdleHandler {
@@ -4213,6 +4230,8 @@ public final class ActivityThread {
    }

    public static final void main(String[] args) {
        SamplingProfilerIntegration.start();

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();
@@ -4228,8 +4247,11 @@ public final class ActivityThread {

        thread.detach();
        String name;
        if (thread.mInitialApplication != null) name = thread.mInitialApplication.getPackageName();
        else name = "<unknown>";
        if (thread.mInitialApplication != null) {
            name = thread.mInitialApplication.getPackageName();
        } else {
            name = "<unknown>";
        }
        Log.i(TAG, "Main thread of " + name + " is now exiting");
    }
}
+144 −0
Original line number Diff line number Diff line
package com.android.internal.os;

import dalvik.system.SamplingProfiler;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import android.util.Log;
import android.os.*;
import android.net.Uri;

/**
 * Integrates the framework with Dalvik's sampling profiler.
 */
public class SamplingProfilerIntegration {

    private static final String TAG = "SamplingProfilerIntegration";

    private static final boolean enabled;
    private static final Executor snapshotWriter;
    static {
        enabled = "1".equals(SystemProperties.get("persist.sampling_profiler"));
        if (enabled) {
            snapshotWriter = Executors.newSingleThreadExecutor();
            Log.i(TAG, "Profiler is enabled.");
        } else {
            snapshotWriter = null;
            Log.i(TAG, "Profiler is disabled.");
        }
    }

    /**
     * Is profiling enabled?
     */
    public static boolean isEnabled() {
        return enabled;
    }

    /**
     * Starts the profiler if profiling is enabled.
     */
    public static void start() {
        if (!enabled) return;
        SamplingProfiler.getInstance().start(10);
    }

    /** Whether or not we've created the snapshots dir. */
    static boolean dirMade = false;

    /** Whether or not a snapshot is being persisted. */
    static volatile boolean pending;

    /**
     * Writes a snapshot to the SD card if profiling is enabled.
     */
    public static void writeSnapshot(final String name) {
        if (!enabled) return;

        if (!pending) {
            pending = true;
            snapshotWriter.execute(new Runnable() {
                public void run() {
                    String dir = "/sdcard/snapshots";
                    if (!dirMade) {
                        makeDirectory(dir);
                        dirMade = true;
                    }
                    try {
                        writeSnapshot(dir, name);
                    } finally {
                        pending = false;
                    }
                }
            });
        }
    }

    /**
     * Writes the zygote's snapshot to internal storage if profiling is enabled.
     */
    public static void writeZygoteSnapshot() {
        if (!enabled) return;

        String dir = "/data/zygote/snapshots";
        makeDirectory(dir);
        writeSnapshot(dir, "zygote");
    }

    private static void writeSnapshot(String dir, String name) {
        byte[] snapshot = SamplingProfiler.getInstance().snapshot();
        if (snapshot == null) {
            return;
        }

        /*
         * We use the current time as a unique ID. We can't use a counter
         * because processes restart. This could result in some overlap if
         * we capture two snapshots in rapid succession.
         */
        long start = System.currentTimeMillis();
        String path = dir + "/" + name.replace(':', '.') + "-"
                + System.currentTimeMillis() + ".snapshot";
        try {
            // Try to open the file a few times. The SD card may not be mounted.
            FileOutputStream out;
            int count = 0;
            while (true) {
                try {
                    out = new FileOutputStream(path);
                    break;
                } catch (FileNotFoundException e) {
                    if (++count > 3) {
                        Log.e(TAG, "Could not open " + path + ".");
                        return;
                    }
                    
                    // Sleep for a bit and then try again.
                    try {
                        Thread.sleep(2500);
                    } catch (InterruptedException e1) { /* ignore */ }
                }
            }

            try {
                out.write(snapshot);
            } finally {
                out.close();
            }
            long elapsed = System.currentTimeMillis() - start;
            Log.i(TAG, "Wrote snapshot for " + name
                    + " in " + elapsed + "ms.");
        } catch (IOException e) {
            Log.e(TAG, "Error writing snapshot.", e);
        }
    }

    private static void makeDirectory(String dir) {
        new File(dir).mkdirs();
    }
}
+20 −10
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.internal.os;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.net.LocalServerSocket;
import android.os.Debug;
@@ -31,6 +30,7 @@ import android.util.Log;

import dalvik.system.VMRuntime;
import dalvik.system.Zygote;
import dalvik.system.SamplingProfiler;

import java.io.BufferedReader;
import java.io.FileDescriptor;
@@ -573,6 +573,9 @@ public class ZygoteInit {

    public static void main(String argv[]) {
        try {
            // Start profiling the zygote initialization.
            SamplingProfilerIntegration.start();

            registerZygoteSocket();
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                SystemClock.uptimeMillis());
@@ -582,6 +585,13 @@ public class ZygoteInit {
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                SystemClock.uptimeMillis());

            if (SamplingProfilerIntegration.isEnabled()) {
                SamplingProfiler sp = SamplingProfiler.getInstance();
                sp.pause();
                SamplingProfilerIntegration.writeZygoteSnapshot();
                sp.shutDown();
            }

            // Do an initial gc to clean up after startup
            gc();

+18 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server;

import com.android.server.am.ActivityManagerService;
import com.android.server.status.StatusBarService;
import com.android.internal.os.SamplingProfilerIntegration;

import dalvik.system.VMRuntime;

@@ -41,6 +42,9 @@ import android.util.EventLog;
import android.util.Log;
import android.accounts.AccountManagerService;

import java.util.Timer;
import java.util.TimerTask;

class ServerThread extends Thread {
    private static final String TAG = "SystemServer";
    private final static boolean INCLUDE_DEMO = false;
@@ -452,6 +456,9 @@ public class SystemServer
    public static final int FACTORY_TEST_LOW_LEVEL = 1;
    public static final int FACTORY_TEST_HIGH_LEVEL = 2;

    static Timer timer;
    static final long SNAPSHOT_INTERVAL = 60 * 60 * 1000; // 1hr

    /**
     * This method is called from Zygote to initialize the system. This will cause the native
     * services (SurfaceFlinger, AudioFlinger, etc..) to be started. After that it will call back
@@ -460,6 +467,17 @@ public class SystemServer
    native public static void init1(String[] args);

    public static void main(String[] args) {
        if (SamplingProfilerIntegration.isEnabled()) {
            SamplingProfilerIntegration.start();
            timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    SamplingProfilerIntegration.writeSnapshot("system_server");
                }
            }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);
        }

        // The system server has to run all of the time, so it needs to be
        // as efficient as possible with its memory usage.
        VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
+13 −13

File changed.

Contains only whitespace changes.