Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(293)

Unified Diff: content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java

Issue 19969003: Android: distinguish between crashes and out-of-memory kills in content (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Amend assert in onRenderProcessSwap(). Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
index eb1a5cd60877879e1e57507f86be295d153344f8..682ce08a30ef968965be50309c74ddb57f5f2c9d 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
@@ -34,10 +34,42 @@ import org.chromium.content.common.TraceEvent;
* the service when it is in active use (between calls to attachAsActive() and detachAsActive()).
*/
public class ChildProcessConnection {
+ /**
+ * Used to notify the consumer about disconnection of the service. This callback is provided
+ * earlier than ConnectionCallbacks below, as a child process might die before the connection is
+ * fully set up.
+ */
interface DeathCallback {
void onChildProcessDied(int pid);
}
+ /**
+ * Used to notify the consumer about the connection being established and about out-of-memory
+ * bindings being bound for the connection. "Out-of-memory" bindings are bindings that raise the
+ * priority of the service process so that it does not get killed by the OS out-of-memory killer
+ * during normal operation (yet it still may get killed under drastic memory pressure).
+ */
+ interface ConnectionCallbacks {
+ /**
+ * Called when the connection to the service is established. It will be called before any
+ * calls to onOomBindingsAdded(), onOomBindingRemoved().
+ * @param pid Pid of the child process.
+ * @param oomBindingCount Number of the out-of-memory bindings bound before the connection
+ * was established.
+ */
+ void onConnected(int pid, int oomBindingCount);
+
+ /**
+ * Called when a new out-of-memory binding is bound.
+ */
+ void onOomBindingAdded(int pid);
+
+ /**
+ * Called when an out-of-memory binding is unbound.
+ */
+ void onOomBindingRemoved(int pid);
+ }
+
// Names of items placed in the bind intent or connection bundle.
public static final String EXTRA_COMMAND_LINE =
"com.google.android.apps.chrome.extra.command_line";
@@ -68,6 +100,9 @@ public class ChildProcessConnection {
private IChildProcessService mService = null;
// Set to true when the service connect is finished, even if it fails.
private boolean mServiceConnectComplete = false;
+ // Set to true when the service disconnects, as opposed to being properly closed. This happens
+ // when the process crashes or gets killed by the system out-of-memory killer.
+ private boolean mServiceDisconnected = false;
private int mPID = 0; // Process ID of the corresponding child process.
// Initial binding protects the newly spawned process from being killed before it is put to use,
// it is maintained between calls to start() and removeInitialBinding().
@@ -89,17 +124,12 @@ public class ChildProcessConnection {
final String[] mCommandLine;
final FileDescriptorInfo[] mFilesToBeMapped;
final IChildProcessCallback mCallback;
- final Runnable mOnConnectionCallback;
- ConnectionParams(
- String[] commandLine,
- FileDescriptorInfo[] filesToBeMapped,
- IChildProcessCallback callback,
- Runnable onConnectionCallback) {
+ ConnectionParams(String[] commandLine, FileDescriptorInfo[] filesToBeMapped,
+ IChildProcessCallback callback) {
mCommandLine = commandLine;
mFilesToBeMapped = filesToBeMapped;
mCallback = callback;
- mOnConnectionCallback = onConnectionCallback;
}
}
@@ -108,13 +138,19 @@ public class ChildProcessConnection {
// the connection is being set up.
private ConnectionParams mConnectionParams;
+ // Callbacks used to notify the consumer about connection events. This is also provided in
+ // setupConnection(), but remains valid after setup.
+ private ChildProcessConnection.ConnectionCallbacks mConnectionCallbacks;
+
private class ChildServiceConnection implements ServiceConnection {
private boolean mBound = false;
private final int mBindFlags;
+ private final boolean mProtectsFromOom;
- public ChildServiceConnection(int bindFlags) {
+ public ChildServiceConnection(int bindFlags, boolean protectsFromOom) {
mBindFlags = bindFlags;
+ mProtectsFromOom = protectsFromOom;
}
boolean bind(String[] commandLine) {
@@ -124,6 +160,9 @@ public class ChildProcessConnection {
intent.putExtra(EXTRA_COMMAND_LINE, commandLine);
}
mBound = mContext.bindService(intent, this, mBindFlags);
+ if (mBound && mProtectsFromOom && mConnectionCallbacks != null) {
+ mConnectionCallbacks.onOomBindingAdded(getPid());
+ }
}
return mBound;
}
@@ -132,6 +171,12 @@ public class ChildProcessConnection {
if (mBound) {
mContext.unbindService(this);
mBound = false;
+ // When the process crashes, we stop reporting bindings being unbound (so that their
+ // numbers can be inspected to determine if the process crash could be caused by the
+ // out-of-memory killing), hence the mServiceDisconnected check below.
+ if (mProtectsFromOom && mConnectionCallbacks != null && !mServiceDisconnected) {
+ mConnectionCallbacks.onOomBindingRemoved(getPid());
+ }
}
}
@@ -150,6 +195,8 @@ public class ChildProcessConnection {
TraceEvent.begin();
mServiceConnectComplete = true;
mService = IChildProcessService.Stub.asInterface(service);
+ // Make sure that the connection parameters have already been provided. If not,
+ // doConnectionSetup() will be called from setupConnection().
if (mConnectionParams != null) {
doConnectionSetup();
}
@@ -163,19 +210,20 @@ public class ChildProcessConnection {
public void onServiceDisconnected(ComponentName className) {
// Ensure that the disconnection logic runs only once (instead of once per each
// ChildServiceConnection).
- if (mService == null) {
+ if (mServiceDisconnected) {
return;
}
- int pid = mPID; // Stash pid & connection callback since stop() will clear them.
- Runnable onConnectionCallback =
- mConnectionParams != null ? mConnectionParams.mOnConnectionCallback : null;
+ mServiceDisconnected = true;
+ int pid = mPID; // Stash the pid for DeathCallback since stop() will clear it.
+ boolean disconnectedWhileBeingSetUp = mConnectionParams != null;
Log.w(TAG, "onServiceDisconnected (crash or killed by oom): pid=" + pid);
stop(); // We don't want to auto-restart on crash. Let the browser do that.
if (pid != 0) {
mDeathCallback.onChildProcessDied(pid);
}
- if (onConnectionCallback != null) {
- onConnectionCallback.run();
+ // TODO(ppi): does anyone know why we need to do that?
+ if (disconnectedWhileBeingSetUp && mConnectionCallbacks != null) {
+ mConnectionCallbacks.onConnected(0, 0);
}
}
}
@@ -188,11 +236,11 @@ public class ChildProcessConnection {
mInSandbox = inSandbox;
mDeathCallback = deathCallback;
mServiceClass = serviceClass;
- mInitialBinding = new ChildServiceConnection(Context.BIND_AUTO_CREATE);
+ mInitialBinding = new ChildServiceConnection(Context.BIND_AUTO_CREATE, true);
mStrongBinding = new ChildServiceConnection(
- Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, true);
mWaivedBinding = new ChildServiceConnection(
- Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY);
+ Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY, false);
}
int getServiceNumber() {
@@ -244,18 +292,21 @@ public class ChildProcessConnection {
* @param commandLine (Optional) will be ignored if the command line was already sent in bind()
* @param fileToBeMapped a list of file descriptors that should be registered
* @param callback Used for status updates regarding this process connection.
- * @param onConnectionCallback will be run when the connection is setup and ready to use.
+ * @param connectionCallbacks will notify the consumer about the connection being established
+ * and the status of the out-of-memory bindings being bound for the connection.
*/
void setupConnection(
String[] commandLine,
FileDescriptorInfo[] filesToBeMapped,
- IChildProcessCallback callback,
- Runnable onConnectionCallback) {
+ IChildProcessCallback processCallback,
+ ConnectionCallbacks connectionCallbacks) {
synchronized(mUiThreadLock) {
TraceEvent.begin();
assert mConnectionParams == null;
- mConnectionParams = new ConnectionParams(commandLine, filesToBeMapped, callback,
- onConnectionCallback);
+ mConnectionCallbacks = connectionCallbacks;
+ mConnectionParams = new ConnectionParams(commandLine, filesToBeMapped, processCallback);
+ // Make sure that the service is already connected. If not, doConnectionSetup() will be
+ // called from onServiceConnected().
if (mServiceConnectComplete) {
doConnectionSetup();
}
@@ -291,18 +342,16 @@ public class ChildProcessConnection {
}
/**
- * Called when the connection parameters have been set, and a connection has been established
- * (as signaled by onServiceConnected), or if the connection failed (as signaled by
- * onBindFailed), in which case mService will be false.
+ * Called after the connection parameters have been set (in setupConnection()) *and* a
+ * connection has been established (as signaled by onServiceConnected()) or failed (as signaled
+ * by onBindFailed(), in this case mService will be null). These two events can happen in any
+ * order.
*/
private void doConnectionSetup() {
TraceEvent.begin();
assert mServiceConnectComplete && mConnectionParams != null;
- // Capture the callback before it is potentially nulled in unbind().
- Runnable onConnectionCallback = mConnectionParams.mOnConnectionCallback;
- if (onConnectionCallback == null) {
- stop();
- } else if (mService != null) {
+
+ if (mService != null) {
Bundle bundle = new Bundle();
bundle.putStringArray(EXTRA_COMMAND_LINE, mConnectionParams.mCommandLine);
@@ -343,7 +392,7 @@ public class ChildProcessConnection {
} catch (android.os.RemoteException re) {
Log.e(TAG, "Failed to setup connection.", re);
}
- // We proactivley close the FDs rather than wait for GC & finalizer.
+ // We proactively close the FDs rather than wait for GC & finalizer.
try {
for (ParcelFileDescriptor parcelFile : parcelFiles) {
if (parcelFile != null) parcelFile.close();
@@ -353,8 +402,12 @@ public class ChildProcessConnection {
}
}
mConnectionParams = null;
- if (onConnectionCallback != null) {
- onConnectionCallback.run();
+
+ if (mConnectionCallbacks != null) {
+ // Number of out-of-memory bindings bound before the connection was set up.
+ int oomBindingCount =
+ (mInitialBinding.isBound() ? 1 : 0) + (mStrongBinding.isBound() ? 1 : 0);
+ mConnectionCallbacks.onConnected(getPid(), oomBindingCount);
}
TraceEvent.end();
}
@@ -431,7 +484,7 @@ public class ChildProcessConnection {
/**
* @return The connection PID, or 0 if not yet connected.
*/
- public int getPid() {
+ int getPid() {
synchronized(mUiThreadLock) {
return mPID;
}

Powered by Google App Engine
This is Rietveld 408576698