Merge "Log cursor/adapter class name and such when detecting a crash"
This commit is contained in:
commit
0e1604b9d4
@ -37,6 +37,7 @@ import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWrapper;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
@ -1180,4 +1181,78 @@ public class Utility {
|
||||
}
|
||||
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;
|
||||
|
||||
import com.android.email.Email;
|
||||
import com.android.email.R;
|
||||
import com.android.email.Utility;
|
||||
import com.android.email.data.ThrottlingCursorLoader;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
@ -203,7 +205,7 @@ public class AccountSelectorAdapter extends CursorAdapter {
|
||||
countAccounts, countAccounts));
|
||||
rb.add(totalUnread);
|
||||
}
|
||||
return resultCursor;
|
||||
return Utility.CloseTraceCursorWrapper.get(resultCursor);
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,4 +227,18 @@ public class AccountSelectorAdapter extends CursorAdapter {
|
||||
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() {
|
||||
final Cursor mailboxesCursor = super.loadInBackground();
|
||||
if (mMode == MODE_MOVE_TO_TARGET) {
|
||||
return mailboxesCursor;
|
||||
return Utility.CloseTraceCursorWrapper.get(mailboxesCursor);
|
||||
}
|
||||
|
||||
// Add "Starred".
|
||||
@ -386,7 +386,7 @@ import android.widget.TextView;
|
||||
// starred.
|
||||
final int starredCount = Message.getFavoriteMessageCount(mContext);
|
||||
if (starredCount == 0) {
|
||||
return mailboxesCursor; // no starred message
|
||||
return Utility.CloseTraceCursorWrapper.get(mailboxesCursor); // no starred message
|
||||
}
|
||||
|
||||
final MatrixCursor starredCursor = new MatrixCursor(getProjection());
|
||||
@ -394,7 +394,8 @@ import android.widget.TextView;
|
||||
addSummaryMailboxRow(mContext, starredCursor,
|
||||
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();
|
||||
}
|
||||
|
||||
return combinedWithAccounts;
|
||||
return Utility.CloseTraceCursorWrapper.get(combinedWithAccounts);
|
||||
}
|
||||
}
|
||||
|
||||
@ -501,4 +502,18 @@ import android.widget.TextView;
|
||||
/* package */ static int getUnreadCountForTest(Cursor cursor) {
|
||||
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));
|
||||
|
||||
// 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 android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWrapper;
|
||||
import android.database.MatrixCursor;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
@ -34,6 +37,7 @@ import android.telephony.TelephonyManager;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.MoreAsserts;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
@ -435,6 +439,46 @@ public class UtilityUnitTests extends AndroidTestCase {
|
||||
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}.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user