summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorAndroid (Google) Code Review <android-gerrit@google.com>2009-09-06 16:41:35 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2009-09-06 16:41:35 -0700
commit88e625795943dea84b2e7c32f37e71303143b728 (patch)
treeeeec8775e624d38c948f8cd7ad76713d7cedfd79 /core
parenta4eb91da03bd785bc91bed0d25a9efaa9baba1c1 (diff)
parente540833fdff4d58e37c9ba859388e24e2945ed45 (diff)
downloadframeworks_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.java218
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java26
-rw-r--r--core/java/com/android/internal/os/SamplingProfilerIntegration.java144
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java30
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();