Log cursor/adapter class name and such when detecting a crash
Also log where the cursor was closed, if it is. To investigate bug 3308465 and bug 3305706 Change-Id: I2b0fd9ea14757b6cf7597cd7162686d050d43fe9
This commit is contained in:
parent
2959a7e073
commit
adbb6f8bc4
|
@ -37,6 +37,7 @@ import android.content.pm.ActivityInfo;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.database.CursorWrapper;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
@ -1180,4 +1181,78 @@ public class Utility {
|
||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stringify a cursor for logging purpose.
|
||||||
|
*/
|
||||||
|
public static String dumpCursor(Cursor c) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("[");
|
||||||
|
while (c != null) {
|
||||||
|
sb.append(c.getClass()); // Class name may not be available if toString() is overridden
|
||||||
|
sb.append("/");
|
||||||
|
sb.append(c.toString());
|
||||||
|
if (c.isClosed()) {
|
||||||
|
sb.append(" (closed)");
|
||||||
|
}
|
||||||
|
if (c instanceof CursorWrapper) {
|
||||||
|
c = ((CursorWrapper) c).getWrappedCursor();
|
||||||
|
sb.append(", ");
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append("]");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cursor wrapper that remembers where it was closed.
|
||||||
|
*
|
||||||
|
* Use {@link #get} to create a wrapped cursor.
|
||||||
|
* USe {@link #getTraceIfAvailable} to get the stack trace.
|
||||||
|
* Use {@link #log} to log if/where it was closed.
|
||||||
|
*/
|
||||||
|
public static class CloseTraceCursorWrapper extends CursorWrapper {
|
||||||
|
private static final boolean TRACE_ENABLED = true; // STOPSHIP make it false
|
||||||
|
|
||||||
|
private Exception mTrace;
|
||||||
|
|
||||||
|
private CloseTraceCursorWrapper(Cursor cursor) {
|
||||||
|
super(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
mTrace = new Exception("STACK TRACE");
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Exception getTraceIfAvailable(Cursor c) {
|
||||||
|
if (c instanceof CloseTraceCursorWrapper) {
|
||||||
|
return ((CloseTraceCursorWrapper) c).mTrace;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void log(Cursor c) {
|
||||||
|
if (c == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c.isClosed()) {
|
||||||
|
Log.w(Email.LOG_TAG, "Cursor was closed here: Cursor=" + c, getTraceIfAvailable(c));
|
||||||
|
} else {
|
||||||
|
Log.w(Email.LOG_TAG, "Cursor not closed. Cursor=" + c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Cursor get(Cursor original) {
|
||||||
|
return TRACE_ENABLED ? new CloseTraceCursorWrapper(original) : original;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ static CloseTraceCursorWrapper alwaysCreateForTest(Cursor original) {
|
||||||
|
return new CloseTraceCursorWrapper(original);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
|
|
||||||
package com.android.email.activity;
|
package com.android.email.activity;
|
||||||
|
|
||||||
|
import com.android.email.Email;
|
||||||
import com.android.email.R;
|
import com.android.email.R;
|
||||||
|
import com.android.email.Utility;
|
||||||
import com.android.email.data.ThrottlingCursorLoader;
|
import com.android.email.data.ThrottlingCursorLoader;
|
||||||
import com.android.email.provider.EmailContent;
|
import com.android.email.provider.EmailContent;
|
||||||
import com.android.email.provider.EmailContent.Account;
|
import com.android.email.provider.EmailContent.Account;
|
||||||
|
@ -203,7 +205,7 @@ public class AccountSelectorAdapter extends CursorAdapter {
|
||||||
countAccounts, countAccounts));
|
countAccounts, countAccounts));
|
||||||
rb.add(totalUnread);
|
rb.add(totalUnread);
|
||||||
}
|
}
|
||||||
return resultCursor;
|
return Utility.CloseTraceCursorWrapper.get(resultCursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,4 +227,18 @@ public class AccountSelectorAdapter extends CursorAdapter {
|
||||||
super.close();
|
super.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// STOPSHIP delete this
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
try {
|
||||||
|
return super.getItemId(position);
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
final Cursor c = getCursor();
|
||||||
|
android.util.Log.w(Email.LOG_TAG, "Crash in getItemId, this=" + this
|
||||||
|
+ " cursor=" + Utility.dumpCursor(c), re);
|
||||||
|
Utility.CloseTraceCursorWrapper.log(c);
|
||||||
|
throw re;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -378,7 +378,7 @@ import android.widget.TextView;
|
||||||
public Cursor loadInBackground() {
|
public Cursor loadInBackground() {
|
||||||
final Cursor mailboxesCursor = super.loadInBackground();
|
final Cursor mailboxesCursor = super.loadInBackground();
|
||||||
if (mMode == MODE_MOVE_TO_TARGET) {
|
if (mMode == MODE_MOVE_TO_TARGET) {
|
||||||
return mailboxesCursor;
|
return Utility.CloseTraceCursorWrapper.get(mailboxesCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add "Starred".
|
// Add "Starred".
|
||||||
|
@ -386,7 +386,7 @@ import android.widget.TextView;
|
||||||
// starred.
|
// starred.
|
||||||
final int starredCount = Message.getFavoriteMessageCount(mContext);
|
final int starredCount = Message.getFavoriteMessageCount(mContext);
|
||||||
if (starredCount == 0) {
|
if (starredCount == 0) {
|
||||||
return mailboxesCursor; // no starred message
|
return Utility.CloseTraceCursorWrapper.get(mailboxesCursor); // no starred message
|
||||||
}
|
}
|
||||||
|
|
||||||
final MatrixCursor starredCursor = new MatrixCursor(getProjection());
|
final MatrixCursor starredCursor = new MatrixCursor(getProjection());
|
||||||
|
@ -394,7 +394,8 @@ import android.widget.TextView;
|
||||||
addSummaryMailboxRow(mContext, starredCursor,
|
addSummaryMailboxRow(mContext, starredCursor,
|
||||||
Mailbox.QUERY_ALL_FAVORITES, Mailbox.TYPE_MAIL, starredCount, true);
|
Mailbox.QUERY_ALL_FAVORITES, Mailbox.TYPE_MAIL, starredCount, true);
|
||||||
|
|
||||||
return new MergeCursor(new Cursor[] {starredCursor, mailboxesCursor});
|
return Utility.CloseTraceCursorWrapper.get(
|
||||||
|
new MergeCursor(new Cursor[] {starredCursor, mailboxesCursor}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,7 +441,7 @@ import android.widget.TextView;
|
||||||
accounts.close();
|
accounts.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return combinedWithAccounts;
|
return Utility.CloseTraceCursorWrapper.get(combinedWithAccounts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,4 +502,18 @@ import android.widget.TextView;
|
||||||
/* package */ static int getUnreadCountForTest(Cursor cursor) {
|
/* package */ static int getUnreadCountForTest(Cursor cursor) {
|
||||||
return cursor.getInt(COLUMN_UNREAD_COUNT);
|
return cursor.getInt(COLUMN_UNREAD_COUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// STOPSHIP delete this
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
try {
|
||||||
|
return super.getItemId(position);
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
final Cursor c = getCursor();
|
||||||
|
android.util.Log.w(Email.LOG_TAG, "Crash in getItemId, this=" + this
|
||||||
|
+ " cursor=" + Utility.dumpCursor(c), re);
|
||||||
|
Utility.CloseTraceCursorWrapper.log(c);
|
||||||
|
throw re;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -227,7 +227,21 @@ import java.util.Set;
|
||||||
setSelection(Utility.buildMailboxIdSelection(mContext, mMailboxId));
|
setSelection(Utility.buildMailboxIdSelection(mContext, mMailboxId));
|
||||||
|
|
||||||
// Then do a query.
|
// Then do a query.
|
||||||
return super.loadInBackground();
|
return Utility.CloseTraceCursorWrapper.get(super.loadInBackground());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// STOPSHIP delete this
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
try {
|
||||||
|
return super.getItemId(position);
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
final Cursor c = getCursor();
|
||||||
|
android.util.Log.w(Email.LOG_TAG, "Crash in getItemId, this=" + this
|
||||||
|
+ " cursor=" + Utility.dumpCursor(c), re);
|
||||||
|
Utility.CloseTraceCursorWrapper.log(c);
|
||||||
|
throw re;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,9 @@ import com.android.email.provider.EmailContent.Mailbox;
|
||||||
import com.android.email.provider.ProviderTestUtils;
|
import com.android.email.provider.ProviderTestUtils;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.CursorWrapper;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -34,6 +37,7 @@ import android.telephony.TelephonyManager;
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
import android.test.MoreAsserts;
|
import android.test.MoreAsserts;
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
@ -435,6 +439,46 @@ public class UtilityUnitTests extends AndroidTestCase {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testDumpCursor() {
|
||||||
|
// Just make sure the method won't crash and returns non-empty string.
|
||||||
|
final Cursor c1 = new MatrixCursor(new String[] {"col"});
|
||||||
|
final Cursor c2 = new CursorWrapper(c1);
|
||||||
|
|
||||||
|
// Note it's a subclass of CursorWrapper.
|
||||||
|
final Cursor c3 = new CursorWrapper(c2) {
|
||||||
|
};
|
||||||
|
|
||||||
|
assertFalse(TextUtils.isEmpty(Utility.dumpCursor(c1)));
|
||||||
|
assertFalse(TextUtils.isEmpty(Utility.dumpCursor(c2)));
|
||||||
|
assertFalse(TextUtils.isEmpty(Utility.dumpCursor(c3)));
|
||||||
|
assertFalse(TextUtils.isEmpty(Utility.dumpCursor(null)));
|
||||||
|
|
||||||
|
// Test again with closed cursor.
|
||||||
|
c1.close();
|
||||||
|
assertFalse(TextUtils.isEmpty(Utility.dumpCursor(c1)));
|
||||||
|
assertFalse(TextUtils.isEmpty(Utility.dumpCursor(c2)));
|
||||||
|
assertFalse(TextUtils.isEmpty(Utility.dumpCursor(c3)));
|
||||||
|
assertFalse(TextUtils.isEmpty(Utility.dumpCursor(null)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCloseTraceCursorWrapper() {
|
||||||
|
final Cursor org = new MatrixCursor(new String[] {"col"});
|
||||||
|
final Utility.CloseTraceCursorWrapper c =
|
||||||
|
Utility.CloseTraceCursorWrapper.alwaysCreateForTest(org);
|
||||||
|
|
||||||
|
// Not closed -- no stack trace
|
||||||
|
assertNull(Utility.CloseTraceCursorWrapper.getTraceIfAvailable(c));
|
||||||
|
Utility.CloseTraceCursorWrapper.log(c); // shouldn't crash
|
||||||
|
|
||||||
|
// Close, now stack trace should be available
|
||||||
|
c.close();
|
||||||
|
assertNotNull(Utility.CloseTraceCursorWrapper.getTraceIfAvailable(c));
|
||||||
|
Utility.CloseTraceCursorWrapper.log(c);
|
||||||
|
|
||||||
|
// shouldn't crash
|
||||||
|
Utility.CloseTraceCursorWrapper.log(null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ListView} used by {@link #testListStateSaver}.
|
* A {@link ListView} used by {@link #testListStateSaver}.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue