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.
This commit is contained in:
Android (Google) Code Review
2009-09-06 16:41:35 -07:00
5 changed files with 315 additions and 121 deletions

View File

@ -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");
}
}

View File

@ -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.
*/

View File

@ -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();
}
}

View File

@ -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();