From d2cc2b20bc60b39bf251ed853009b552868dfc79 Mon Sep 17 00:00:00 2001 From: Adnan Begovic Date: Tue, 10 Feb 2015 14:00:16 -0800 Subject: Trebuchet: Catch SQLiteReadOnlyException. Change-Id: I5e3cbd85a2fba84263b1e6df25f00f98b1ef55c0 --- src/com/android/launcher3/WidgetPreviewLoader.java | 87 ++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 3db0b51ad..a030ab8b6 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -9,9 +9,11 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.database.Cursor; +import android.database.sqlite.SQLiteCantOpenDatabaseException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDiskIOException; import android.database.sqlite.SQLiteOpenHelper; +import android.database.sqlite.SQLiteReadOnlyDatabaseException; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; @@ -29,11 +31,14 @@ import android.util.Log; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.IOException; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; abstract class SoftReferenceThreadLocal { private ThreadLocal> mThreadLocal; @@ -367,6 +372,10 @@ public class WidgetPreviewLoader { try { db.delete(CacheDb.TABLE_NAME, null, null); } catch (SQLiteDiskIOException e) { + } catch (SQLiteCantOpenDatabaseException e) { + } catch (SQLiteReadOnlyDatabaseException e) { + dumpOpenFiles(); + throw e; } } @@ -672,4 +681,82 @@ public class WidgetPreviewLoader { } } + private static final int MAX_OPEN_FILES = 1024; + private static final int SAMPLE_RATE = 23; + /** + * Dumps all files that are open in this process without allocating a file descriptor. + */ + private static void dumpOpenFiles() { + try { + Log.i(TAG, "DUMP OF OPEN FILES (sample rate: 1 every " + SAMPLE_RATE + "):"); + final String TYPE_APK = "apk"; + final String TYPE_JAR = "jar"; + final String TYPE_PIPE = "pipe"; + final String TYPE_SOCKET = "socket"; + final String TYPE_DB = "db"; + final String TYPE_ANON_INODE = "anon_inode"; + final String TYPE_DEV = "dev"; + final String TYPE_NON_FS = "non-fs"; + final String TYPE_OTHER = "other"; + List types = Arrays.asList(TYPE_APK, TYPE_JAR, TYPE_PIPE, TYPE_SOCKET, TYPE_DB, + TYPE_ANON_INODE, TYPE_DEV, TYPE_NON_FS, TYPE_OTHER); + int[] count = new int[types.size()]; + int[] duplicates = new int[types.size()]; + HashSet files = new HashSet(); + int total = 0; + for (int i = 0; i < MAX_OPEN_FILES; i++) { + // This is a gigantic hack but unfortunately the only way to resolve an fd + // to a file name. Note that we have to loop over all possible fds because + // reading the directory would require allocating a new fd. The kernel is + // currently implemented such that no fd is larger then the current rlimit, + // which is why it's safe to loop over them in such a way. + String fd = "/proc/self/fd/" + i; + try { + // getCanonicalPath() uses readlink behind the scene which doesn't require + // a file descriptor. + String resolved = new File(fd).getCanonicalPath(); + int type = types.indexOf(TYPE_OTHER); + if (resolved.startsWith("/dev/")) { + type = types.indexOf(TYPE_DEV); + } else if (resolved.endsWith(".apk")) { + type = types.indexOf(TYPE_APK); + } else if (resolved.endsWith(".jar")) { + type = types.indexOf(TYPE_JAR); + } else if (resolved.contains("/fd/pipe:")) { + type = types.indexOf(TYPE_PIPE); + } else if (resolved.contains("/fd/socket:")) { + type = types.indexOf(TYPE_SOCKET); + } else if (resolved.contains("/fd/anon_inode:")) { + type = types.indexOf(TYPE_ANON_INODE); + } else if (resolved.endsWith(".db") || resolved.contains("/databases/")) { + type = types.indexOf(TYPE_DB); + } else if (resolved.startsWith("/proc/") && resolved.contains("/fd/")) { + // Those are the files that don't point anywhere on the file system. + // getCanonicalPath() wrongly interprets these as relative symlinks and + // resolves them within /proc//fd/. + type = types.indexOf(TYPE_NON_FS); + } + count[type]++; + total++; + if (files.contains(resolved)) { + duplicates[type]++; + } + files.add(resolved); + if (total % SAMPLE_RATE == 0) { + Log.i(TAG, " fd " + i + ": " + resolved + + " (" + types.get(type) + ")"); + } + } catch (IOException e) { + // Ignoring exceptions for non-existing file descriptors. + } + } + for (int i = 0; i < types.size(); i++) { + Log.i(TAG, String.format("Open %10s files: %4d total, %4d duplicates", + types.get(i), count[i], duplicates[i])); + } + } catch (Throwable t) { + // Catch everything. This is called from an exception handler that we shouldn't upset. + Log.e(TAG, "Unable to log open files.", t); + } + } } -- cgit v1.2.3