diff options
author | Android (Google) Code Review <android-gerrit@google.com> | 2009-09-06 16:41:35 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2009-09-06 16:41:35 -0700 |
commit | 88e625795943dea84b2e7c32f37e71303143b728 (patch) | |
tree | eeec8775e624d38c948f8cd7ad76713d7cedfd79 /core | |
parent | a4eb91da03bd785bc91bed0d25a9efaa9baba1c1 (diff) | |
parent | e540833fdff4d58e37c9ba859388e24e2945ed45 (diff) | |
download | frameworks_base-88e625795943dea84b2e7c32f37e71303143b728.zip frameworks_base-88e625795943dea84b2e7c32f37e71303143b728.tar.gz frameworks_base-88e625795943dea84b2e7c32f37e71303143b728.tar.bz2 |
Merge change 24060 into eclair
* changes:
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.
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/app/ActivityThread.java | 218 | ||||
-rw-r--r-- | core/java/com/android/internal/os/RuntimeInit.java | 26 | ||||
-rw-r--r-- | core/java/com/android/internal/os/SamplingProfilerIntegration.java | 144 | ||||
-rw-r--r-- | core/java/com/android/internal/os/ZygoteInit.java | 30 |
4 files changed, 297 insertions, 121 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8a26aba..2877aec 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -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); @@ -126,7 +129,7 @@ public final class ActivityThread { private static final int LOG_ON_PAUSE_CALLED = 30021; private static final int LOG_ON_RESUME_CALLED = 30022; - + public static final ActivityThread currentActivityThread() { return (ActivityThread)sThreadLocal.get(); } @@ -314,7 +317,7 @@ public final class ActivityThread { public ApplicationInfo getApplicationInfo() { return mApplicationInfo; } - + public boolean isSecurityViolation() { return mSecurityViolation; } @@ -322,7 +325,7 @@ public final class ActivityThread { /** * Gets the array of shared libraries that are listed as * used by the given package. - * + * * @param packageName the name of the package (note: not its * file name) * @return null-ok; the array of shared libraries, each one @@ -350,7 +353,7 @@ public final class ActivityThread { * result is a single string with the names of the libraries * separated by colons, or <code>null</code> if both lists * were <code>null</code> or empty. - * + * * @param list1 null-ok; the first list * @param list2 null-ok; the second list * @return null-ok; the combination @@ -378,7 +381,7 @@ public final class ActivityThread { if (dupCheck && ArrayUtils.contains(list1, s)) { continue; } - + if (first) { first = false; } else { @@ -390,7 +393,7 @@ public final class ActivityThread { return result.toString(); } - + public ClassLoader getClassLoader() { synchronized (this) { if (mClassLoader != null) { @@ -428,7 +431,7 @@ public final class ActivityThread { if ((mSharedLibraries != null) || (instrumentationLibs != null)) { - zip = + zip = combineLibs(mSharedLibraries, instrumentationLibs) + ':' + zip; } @@ -485,9 +488,9 @@ public final class ActivityThread { if (mApplication != null) { return mApplication; } - + Application app = null; - + String appClass = mApplicationInfo.className; if (forceDefaultAppClass || (appClass == null)) { appClass = "android.app.Application"; @@ -510,7 +513,7 @@ public final class ActivityThread { mActivityThread.mAllApplications.add(app); return mApplication = app; } - + public void removeContextRegistrations(Context context, String who, String what) { HashMap<BroadcastReceiver, ReceiverDispatcher> rmap = @@ -643,7 +646,7 @@ public final class ActivityThread { final static class InnerReceiver extends IIntentReceiver.Stub { final WeakReference<ReceiverDispatcher> mDispatcher; final ReceiverDispatcher mStrongRef; - + InnerReceiver(ReceiverDispatcher rd, boolean strong) { mDispatcher = new WeakReference<ReceiverDispatcher>(rd); mStrongRef = strong ? rd : null; @@ -661,7 +664,7 @@ public final class ActivityThread { } } } - + final IIntentReceiver.Stub mIIntentReceiver; final BroadcastReceiver mReceiver; final Context mContext; @@ -770,7 +773,7 @@ public final class ActivityThread { BroadcastReceiver getIntentReceiver() { return mReceiver; } - + IIntentReceiver getIIntentReceiver() { return mIIntentReceiver; } @@ -901,7 +904,7 @@ public final class ActivityThread { private static class InnerConnection extends IServiceConnection.Stub { final WeakReference<ServiceDispatcher> mDispatcher; - + InnerConnection(ServiceDispatcher sd) { mDispatcher = new WeakReference<ServiceDispatcher>(sd); } @@ -913,7 +916,7 @@ public final class ActivityThread { } } } - + private final HashMap<ComponentName, ConnectionInfo> mActiveConnections = new HashMap<ComponentName, ConnectionInfo>(); @@ -965,7 +968,7 @@ public final class ActivityThread { IServiceConnection getIServiceConnection() { return mIServiceConnection; } - + int getFlags() { return mFlags; } @@ -1191,7 +1194,7 @@ public final class ActivityThread { + " mode=" + backupMode + "}"; } } - + private static final class CreateServiceData { IBinder token; ServiceInfo info; @@ -1271,10 +1274,10 @@ public final class ActivityThread { private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s"; private static final String ONE_COUNT_COLUMN = "%17s %8d"; private static final String TWO_COUNT_COLUMNS = "%17s %8d %17s %8d"; - + // Formatting for checkin service - update version if row format changes private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1; - + public final void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges) { queueOrSendMessage( @@ -1343,7 +1346,7 @@ public final class ActivityThread { synchronized (mRelaunchingActivities) { mRelaunchingActivities.add(r); } - + queueOrSendMessage(H.RELAUNCH_ACTIVITY, r, configChanges); } @@ -1514,7 +1517,7 @@ public final class ActivityThread { throws RemoteException { receiver.performReceive(intent, resultCode, dataStr, extras, ordered); } - + public void scheduleLowMemory() { queueOrSendMessage(H.LOW_MEMORY, null); } @@ -1530,7 +1533,7 @@ public final class ActivityThread { } catch (RemoteException e) { } } - + public void profilerControl(boolean start, String path, ParcelFileDescriptor fd) { ProfilerControlData pcd = new ProfilerControlData(); pcd.path = path; @@ -1549,11 +1552,11 @@ public final class ActivityThread { Log.w(TAG, "Failed setting process group to " + group, e); } } - + public void getMemoryInfo(Debug.MemoryInfo outInfo) { Debug.getMemoryInfo(outInfo); } - + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { long nativeMax = Debug.getNativeHeapSize() / 1024; @@ -1589,7 +1592,7 @@ public final class ActivityThread { long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024; SQLiteDebug.PagerStats stats = new SQLiteDebug.PagerStats(); SQLiteDebug.getPagerStats(stats); - + // Check to see if we were called by checkin server. If so, print terse format. boolean doCheckinFormat = false; if (args != null) { @@ -1597,79 +1600,79 @@ public final class ActivityThread { if ("-c".equals(arg)) doCheckinFormat = true; } } - + // For checkin, we print one long comma-separated list of values if (doCheckinFormat) { // NOTE: if you change anything significant below, also consider changing // ACTIVITY_THREAD_CHECKIN_VERSION. - String processName = (mBoundApplication != null) + String processName = (mBoundApplication != null) ? mBoundApplication.processName : "unknown"; - + // Header pw.print(ACTIVITY_THREAD_CHECKIN_VERSION); pw.print(','); pw.print(Process.myPid()); pw.print(','); pw.print(processName); pw.print(','); - + // Heap info - max pw.print(nativeMax); pw.print(','); pw.print(dalvikMax); pw.print(','); pw.print("N/A,"); pw.print(nativeMax + dalvikMax); pw.print(','); - + // Heap info - allocated pw.print(nativeAllocated); pw.print(','); pw.print(dalvikAllocated); pw.print(','); pw.print("N/A,"); pw.print(nativeAllocated + dalvikAllocated); pw.print(','); - + // Heap info - free pw.print(nativeFree); pw.print(','); pw.print(dalvikFree); pw.print(','); pw.print("N/A,"); pw.print(nativeFree + dalvikFree); pw.print(','); - + // Heap info - proportional set size pw.print(memInfo.nativePss); pw.print(','); pw.print(memInfo.dalvikPss); pw.print(','); pw.print(memInfo.otherPss); pw.print(','); pw.print(memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); pw.print(','); - + // Heap info - shared - pw.print(nativeShared); pw.print(','); - pw.print(dalvikShared); pw.print(','); - pw.print(otherShared); pw.print(','); + pw.print(nativeShared); pw.print(','); + pw.print(dalvikShared); pw.print(','); + pw.print(otherShared); pw.print(','); pw.print(nativeShared + dalvikShared + otherShared); pw.print(','); - + // Heap info - private - pw.print(nativePrivate); pw.print(','); + pw.print(nativePrivate); pw.print(','); pw.print(dalvikPrivate); pw.print(','); pw.print(otherPrivate); pw.print(','); pw.print(nativePrivate + dalvikPrivate + otherPrivate); pw.print(','); - + // Object counts pw.print(viewInstanceCount); pw.print(','); pw.print(viewRootInstanceCount); pw.print(','); pw.print(appContextInstanceCount); pw.print(','); pw.print(activityInstanceCount); pw.print(','); - + pw.print(globalAssetCount); pw.print(','); pw.print(globalAssetManagerCount); pw.print(','); pw.print(binderLocalObjectCount); pw.print(','); pw.print(binderProxyObjectCount); pw.print(','); - + pw.print(binderDeathObjectCount); pw.print(','); pw.print(openSslSocketCount); pw.print(','); - + // SQL pw.print(sqliteAllocated); pw.print(','); - pw.print(stats.databaseBytes / 1024); pw.print(','); + pw.print(stats.databaseBytes / 1024); pw.print(','); pw.print(stats.numPagers); pw.print(','); pw.print((stats.totalBytes - stats.referencedBytes) / 1024); pw.print(','); pw.print(stats.referencedBytes / 1024); pw.print('\n'); - + return; } - + // otherwise, show human-readable format printRow(pw, HEAP_COLUMN, "", "native", "dalvik", "other", "total"); printRow(pw, HEAP_COLUMN, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax); @@ -1702,7 +1705,7 @@ public final class ActivityThread { printRow(pw, ONE_COUNT_COLUMN, "Death Recipients:", binderDeathObjectCount); printRow(pw, ONE_COUNT_COLUMN, "OpenSSL Sockets:", openSslSocketCount); - + // SQLite mem info pw.println(" "); pw.println(" SQL"); @@ -1711,7 +1714,7 @@ public final class ActivityThread { printRow(pw, TWO_COUNT_COLUMNS, "numPagers:", stats.numPagers, "inactivePageKB:", (stats.totalBytes - stats.referencedBytes) / 1024); printRow(pw, ONE_COUNT_COLUMN, "activePageKB:", stats.referencedBytes / 1024); - + // Asset details. String assetAlloc = AssetManager.getAssetAllocations(); if (assetAlloc != null) { @@ -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 { @@ -1947,13 +1964,13 @@ public final class ActivityThread { final private String mResDir; final private float mScale; final private int mHash; - + ResourcesKey(String resDir, float scale) { mResDir = resDir; mScale = scale; mHash = mResDir.hashCode() << 2 + (int) (mScale * 2); } - + @Override public int hashCode() { return mHash; @@ -2004,7 +2021,7 @@ public final class ActivityThread { final ArrayList<ActivityRecord> mRelaunchingActivities = new ArrayList<ActivityRecord>(); Configuration mPendingConfiguration = null; - + // These can be accessed by multiple threads; mPackages is the lock. // XXX For now we keep around information about all packages we have // seen, not removing entries from this map. @@ -2139,7 +2156,7 @@ public final class ActivityThread { return false; } } - + ActivityThread() { } @@ -2172,11 +2189,11 @@ public final class ActivityThread { public Application getApplication() { return mInitialApplication; } - + public String getProcessName() { return mBoundApplication.processName; } - + public ApplicationContext getSystemContext() { synchronized (this) { if (mSystemContext == null) { @@ -2231,7 +2248,7 @@ public final class ActivityThread { } return aInfo; } - + public final Activity startActivityNow(Activity parent, String id, Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state, Object lastNonConfigurationInstance) { @@ -2314,7 +2331,7 @@ public final class ActivityThread { r.packageInfo = getPackageInfo(aInfo.applicationInfo, Context.CONTEXT_INCLUDE_CODE); } - + ComponentName component = r.intent.getComponent(); if (component == null) { component = r.intent.resolveActivity( @@ -2346,7 +2363,7 @@ public final class ActivityThread { try { Application app = r.packageInfo.makeApplication(false); - + if (localLOGV) Log.v(TAG, "Performing launch of " + r); if (localLOGV) Log.v( TAG, r + ": app=" + app @@ -2365,7 +2382,7 @@ public final class ActivityThread { r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstance, r.lastNonConfigurationChildInstances, config); - + if (customIntent != null) { activity.mIntent = customIntent; } @@ -2503,7 +2520,7 @@ public final class ActivityThread { } } } - + private final void handleNewIntent(NewIntentData data) { performNewIntents(data.token, data.intents); } @@ -2541,7 +2558,7 @@ public final class ActivityThread { try { Application app = packageInfo.makeApplication(false); - + if (localLOGV) Log.v( TAG, "Performing receive of " + data.intent + ": app=" + app @@ -2598,7 +2615,7 @@ public final class ActivityThread { + " already exists"); return; } - + BackupAgent agent = null; String classname = data.appInfo.backupAgentName; if (classname == null) { @@ -2652,7 +2669,7 @@ public final class ActivityThread { // Tear down a BackupAgent private final void handleDestroyBackupAgent(CreateBackupAgentData data) { if (DEBUG_BACKUP) Log.v(TAG, "handleDestroyBackupAgent: " + data); - + PackageInfo packageInfo = getPackageInfoNoCheck(data.appInfo); String packageName = packageInfo.mPackageName; BackupAgent agent = mBackupAgents.get(packageName); @@ -2857,9 +2874,9 @@ public final class ActivityThread { } r.activity.performResume(); - EventLog.writeEvent(LOG_ON_RESUME_CALLED, + EventLog.writeEvent(LOG_ON_RESUME_CALLED, r.activity.getComponentName().getClassName()); - + r.paused = false; r.stopped = false; if (r.activity.mStartedActivity) { @@ -2895,7 +2912,7 @@ public final class ActivityThread { final int forwardBit = isForward ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0; - + // If the window hasn't yet been added to the window manager, // and this guy didn't finish itself or start another activity, // then go ahead and add the window. @@ -3014,7 +3031,7 @@ public final class ActivityThread { if (userLeaving) { performUserLeavingActivity(r); } - + r.activity.mConfigChangeFlags |= configChanges; Bundle state = performPauseActivity(token, finished, true); @@ -3191,7 +3208,7 @@ public final class ActivityThread { + " win=" + r.window); updateVisibility(r, show); - + // Tell activity manager we have been stopped. try { ActivityManagerNative.getDefault().activityStopped( @@ -3307,7 +3324,7 @@ public final class ActivityThread { try { r.activity.mCalled = false; mInstrumentation.callActivityOnPause(r.activity); - EventLog.writeEvent(LOG_ON_PAUSE_CALLED, + EventLog.writeEvent(LOG_ON_PAUSE_CALLED, r.activity.getComponentName().getClassName()); if (!r.activity.mCalled) { throw new SuperNotCalledException( @@ -3364,7 +3381,7 @@ public final class ActivityThread { + ": " + e.toString(), e); } } - + } try { r.activity.mCalled = false; @@ -3446,7 +3463,7 @@ public final class ActivityThread { unscheduleGcIdler(); Configuration changedConfig = null; - + // First: make sure we have the most recent configuration and most // recent version of the activity, or skip it if some previous call // had taken a more recent version. @@ -3463,38 +3480,38 @@ public final class ActivityThread { N--; } } - + if (tmp == null) { return; } - + if (mPendingConfiguration != null) { changedConfig = mPendingConfiguration; mPendingConfiguration = null; } } - + // If there was a pending configuration change, execute it first. if (changedConfig != null) { handleConfigurationChanged(changedConfig); } - + ActivityRecord r = mActivities.get(tmp.token); if (localLOGV) Log.v(TAG, "Handling relaunch of " + r); if (r == null) { return; } - + r.activity.mConfigChangeFlags |= configChanges; Intent currentIntent = r.activity.mIntent; - + Bundle savedState = null; if (!r.paused) { savedState = performPauseActivity(r.token, false, true); } - + handleDestroyActivity(r.token, false, configChanges, true); - + r.activity = null; r.window = null; r.hideForNow = false; @@ -3518,7 +3535,7 @@ public final class ActivityThread { if (savedState != null) { r.state = savedState; } - + handleLaunchActivity(r, currentIntent); } @@ -3548,7 +3565,7 @@ public final class ActivityThread { boolean allActivities, Configuration newConfig) { ArrayList<ComponentCallbacks> callbacks = new ArrayList<ComponentCallbacks>(); - + if (mActivities.size() > 0) { Iterator<ActivityRecord> it = mActivities.values().iterator(); while (it.hasNext()) { @@ -3589,10 +3606,10 @@ public final class ActivityThread { for (int i=0; i<N; i++) { callbacks.add(mAllApplications.get(i)); } - + return callbacks; } - + private final void performConfigurationChanged( ComponentCallbacks cb, Configuration config) { // Only for Activity objects, check that they actually call up to their @@ -3602,18 +3619,18 @@ public final class ActivityThread { if (activity != null) { activity.mCalled = false; } - + boolean shouldChangeConfig = false; if ((activity == null) || (activity.mCurrentConfig == null)) { shouldChangeConfig = true; } else { - + // If the new config is the same as the config this Activity // is already running with then don't bother calling // onConfigurationChanged int diff = activity.mCurrentConfig.diff(config); if (diff != 0) { - + // If this activity doesn't handle any of the config changes // then don't bother calling onConfigurationChanged as we're // going to destroy it. @@ -3622,10 +3639,10 @@ public final class ActivityThread { } } } - + if (shouldChangeConfig) { cb.onConfigurationChanged(config); - + if (activity != null) { if (!activity.mCalled) { throw new SuperNotCalledException( @@ -3639,17 +3656,17 @@ public final class ActivityThread { } final void handleConfigurationChanged(Configuration config) { - + synchronized (mRelaunchingActivities) { if (mPendingConfiguration != null) { config = mPendingConfiguration; mPendingConfiguration = null; } } - + ArrayList<ComponentCallbacks> callbacks = new ArrayList<ComponentCallbacks>(); - + synchronized(mPackages) { if (mConfiguration == null) { mConfiguration = new Configuration(); @@ -3684,10 +3701,10 @@ public final class ActivityThread { } } } - + callbacks = collectComponentCallbacksLocked(false, config); } - + final int N = callbacks.size(); for (int i=0; i<N; i++) { performConfigurationChanged(callbacks.get(i), config); @@ -3699,7 +3716,7 @@ public final class ActivityThread { if (r == null || r.activity == null) { return; } - + performConfigurationChanged(r.activity, mConfiguration); } @@ -3722,7 +3739,7 @@ public final class ActivityThread { Debug.stopMethodTracing(); } } - + final void handleLowMemory() { ArrayList<ComponentCallbacks> callbacks = new ArrayList<ComponentCallbacks>(); @@ -3730,7 +3747,7 @@ public final class ActivityThread { synchronized(mPackages) { callbacks = collectComponentCallbacksLocked(true, null); } - + final int N = callbacks.size(); for (int i=0; i<N; i++) { callbacks.get(i).onLowMemory(); @@ -3741,7 +3758,7 @@ public final class ActivityThread { int sqliteReleased = SQLiteDatabase.releaseMemory(); EventLog.writeEvent(SQLITE_MEM_RELEASED_EVENT_LOG_TAG, sqliteReleased); } - + // Ask graphics to free up as much as possible (font/image caches) Canvas.freeCaches(); @@ -3788,7 +3805,7 @@ public final class ActivityThread { == 0) { Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT); } - + if (data.debugMode != IApplicationThread.DEBUG_OFF) { // XXX should have option to change the port. Debug.changeDebugPort(8100); @@ -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"); } } diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 8486272..4e6f9ca 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -83,7 +83,7 @@ public class RuntimeInit { Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); int hasQwerty = getQwertyKeyboard(); - + if (Config.LOGV) Log.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty); if (hasQwerty == 1) { System.setProperty("qwerty", "1"); @@ -133,13 +133,13 @@ public class RuntimeInit { * @param className Fully-qualified class name * @param argv Argument vector for main() */ - private static void invokeStaticMain(String className, String[] argv) + private static void invokeStaticMain(String className, String[] argv) throws ZygoteInit.MethodAndArgsCaller { - + // We want to be fairly aggressive about heap utilization, to avoid // holding on to a lot of memory that isn't needed. VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); - + Class<?> cl; try { @@ -178,7 +178,7 @@ public class RuntimeInit { public static final void main(String[] argv) { commonInit(); - + /* * Now that we're running in interpreted code, call back into native code * to run the system. @@ -187,7 +187,7 @@ public class RuntimeInit { if (Config.LOGV) Log.d(TAG, "Leaving RuntimeInit!"); } - + public static final native void finishInit(); /** @@ -236,7 +236,7 @@ public class RuntimeInit { } // Remaining arguments are passed to the start class's static main - + String startClass = argv[curArg++]; String[] startArgs = new String[argv.length - curArg]; @@ -245,28 +245,28 @@ public class RuntimeInit { } public static final native void zygoteInitNative(); - + /** * Returns 1 if the computer is on. If the computer isn't on, the value returned by this method is undefined. */ public static final native int isComputerOn(); /** - * Turns the computer on if the computer is off. If the computer is on, the behavior of this method is undefined. + * Turns the computer on if the computer is off. If the computer is on, the behavior of this method is undefined. */ public static final native void turnComputerOn(); /** - * + * * @return 1 if the device has a qwerty keyboard */ public static native int getQwertyKeyboard(); - + /** * Report a fatal error in the current process. If this is a user-process, * a dialog may be displayed informing the user of the error. This * function does not return; it forces the current process to exit. - * + * * @param tag to use when logging the error * @param t exception that was generated by the error */ @@ -405,7 +405,7 @@ public class RuntimeInit { /** * Replay an encoded CrashData record back into a useable CrashData record. This can be * helpful for providing debugging output after a process error. - * + * * @param crashDataBytes The byte array containing the encoded crash record * @return new CrashData record, or null if could not create one. */ diff --git a/core/java/com/android/internal/os/SamplingProfilerIntegration.java b/core/java/com/android/internal/os/SamplingProfilerIntegration.java new file mode 100644 index 0000000..44bcd16 --- /dev/null +++ b/core/java/com/android/internal/os/SamplingProfilerIntegration.java @@ -0,0 +1,144 @@ +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(); + } +} diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index a2d3cd8..404c513 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -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; @@ -73,7 +73,7 @@ public class ZygoteInit { * never gets destroyed. */ private static Resources mResources; - + /** * The number of times that the main Zygote loop * should run before calling gc() again. @@ -192,7 +192,7 @@ public class ZygoteInit { * RuntimeException on failure. */ private static ZygoteConnection acceptCommandPeer() { - try { + try { return new ZygoteConnection(sServerSocket.accept()); } catch (IOException ex) { throw new RuntimeException( @@ -251,7 +251,7 @@ public class ZygoteInit { */ private static void preloadClasses() { final VMRuntime runtime = VMRuntime.getRuntime(); - + InputStream is = ZygoteInit.class.getClassLoader().getResourceAsStream( PRELOADED_CLASSES); if (is == null) { @@ -259,7 +259,7 @@ public class ZygoteInit { } else { Log.i(TAG, "Preloading classes..."); long startTime = SystemClock.uptimeMillis(); - + // Drop root perms while running static initializers. setEffectiveGroup(UNPRIVILEGED_GID); setEffectiveUser(UNPRIVILEGED_UID); @@ -275,7 +275,7 @@ public class ZygoteInit { Debug.startAllocCounting(); try { - BufferedReader br + BufferedReader br = new BufferedReader(new InputStreamReader(is), 256); int count = 0; @@ -394,7 +394,7 @@ public class ZygoteInit { */ private static void preloadResources() { final VMRuntime runtime = VMRuntime.getRuntime(); - + Debug.startAllocCounting(); try { runtime.gcSoftReferences(); @@ -527,7 +527,7 @@ public class ZygoteInit { /** * Prepare the arguments and fork for the system server process. */ - private static boolean startSystemServer() + private static boolean startSystemServer() throws MethodAndArgsCaller, RuntimeException { /* Hardcoded command line to start the system server */ String args[] = { @@ -561,8 +561,8 @@ public class ZygoteInit { parsedArgs.gids, debugFlags, null); } catch (IllegalArgumentException ex) { throw new RuntimeException(ex); - } - + } + /* For child process */ if (pid == 0) { handleSystemServerProcess(parsedArgs); @@ -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(); |