1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.database.sqlite; 18 19 import android.annotation.NonNull; 20 import android.annotation.TestApi; 21 import android.compat.annotation.UnsupportedAppUsage; 22 import android.os.Build; 23 import android.os.Process; 24 import android.os.SystemProperties; 25 import android.util.Log; 26 import android.util.Printer; 27 import java.util.ArrayList; 28 29 /** 30 * Provides debugging info about all SQLite databases running in the current process. 31 * 32 * {@hide} 33 */ 34 @TestApi 35 public final class SQLiteDebug { nativeGetPagerStats(PagerStats stats)36 private static native void nativeGetPagerStats(PagerStats stats); 37 38 /** 39 * Inner class to avoid getting the value frozen in zygote. 40 * 41 * {@hide} 42 */ 43 public static final class NoPreloadHolder { 44 /** 45 * Controls the printing of informational SQL log messages. 46 * 47 * Enable using "adb shell setprop log.tag.SQLiteLog VERBOSE". 48 */ 49 public static final boolean DEBUG_SQL_LOG = 50 Log.isLoggable("SQLiteLog", Log.VERBOSE); 51 52 /** 53 * Controls the printing of SQL statements as they are executed. 54 * 55 * Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE". 56 */ 57 public static final boolean DEBUG_SQL_STATEMENTS = 58 Log.isLoggable("SQLiteStatements", Log.VERBOSE); 59 60 /** 61 * Controls the printing of wall-clock time taken to execute SQL statements 62 * as they are executed. 63 * 64 * Enable using "adb shell setprop log.tag.SQLiteTime VERBOSE". 65 */ 66 public static final boolean DEBUG_SQL_TIME = 67 Log.isLoggable("SQLiteTime", Log.VERBOSE); 68 69 /** 70 * True to enable database performance testing instrumentation. 71 */ 72 public static final boolean DEBUG_LOG_SLOW_QUERIES = 73 Log.isLoggable("SQLiteSlowQueries", Log.VERBOSE); 74 75 private static final String SLOW_QUERY_THRESHOLD_PROP = "db.log.slow_query_threshold"; 76 77 private static final String SLOW_QUERY_THRESHOLD_UID_PROP = 78 SLOW_QUERY_THRESHOLD_PROP + "." + Process.myUid(); 79 80 /** 81 * Whether to add detailed information to slow query log. 82 */ 83 public static final boolean DEBUG_LOG_DETAILED = Build.IS_DEBUGGABLE 84 && SystemProperties.getBoolean("db.log.detailed", false); 85 86 /** 87 * Whether to accept double-quoted strings in SQL statements. Double-quoted strings are a 88 * syntax error but are accepted by sqlite in compatibility mode (the default). If the 89 * property is set to true, double-quoted strings will be treated by sqlite as a syntax 90 * error. 91 */ 92 public static final boolean NO_DOUBLE_QUOTED_STRS = 93 SystemProperties.getBoolean("debug.sqlite.no_double_quoted_strs", false); 94 } 95 SQLiteDebug()96 private SQLiteDebug() { 97 } 98 99 /** 100 * Determines whether a query should be logged. 101 * 102 * Reads the "db.log.slow_query_threshold" system property, which can be changed 103 * by the user at any time. If the value is zero, then all queries will 104 * be considered slow. If the value does not exist or is negative, then no queries will 105 * be considered slow. 106 * 107 * To enable it for a specific UID, "db.log.slow_query_threshold.UID" could also be used. 108 * 109 * This value can be changed dynamically while the system is running. 110 * For example, "adb shell setprop db.log.slow_query_threshold 200" will 111 * log all queries that take 200ms or longer to run. 112 * @hide 113 */ shouldLogSlowQuery(long elapsedTimeMillis)114 public static boolean shouldLogSlowQuery(long elapsedTimeMillis) { 115 final int slowQueryMillis = Math.min( 116 SystemProperties.getInt(NoPreloadHolder.SLOW_QUERY_THRESHOLD_PROP, 117 Integer.MAX_VALUE), 118 SystemProperties.getInt(NoPreloadHolder.SLOW_QUERY_THRESHOLD_UID_PROP, 119 Integer.MAX_VALUE)); 120 return elapsedTimeMillis >= slowQueryMillis; 121 } 122 123 /** 124 * Contains statistics about the active pagers in the current process. 125 * 126 * @see #nativeGetPagerStats(PagerStats) 127 */ 128 public static class PagerStats { 129 130 @UnsupportedAppUsage PagerStats()131 public PagerStats() { 132 } 133 134 /** the current amount of memory checked out by sqlite using sqlite3_malloc(). 135 * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 136 */ 137 @UnsupportedAppUsage 138 public int memoryUsed; 139 140 /** the number of bytes of page cache allocation which could not be sattisfied by the 141 * SQLITE_CONFIG_PAGECACHE buffer and where forced to overflow to sqlite3_malloc(). 142 * The returned value includes allocations that overflowed because they where too large 143 * (they were larger than the "sz" parameter to SQLITE_CONFIG_PAGECACHE) and allocations 144 * that overflowed because no space was left in the page cache. 145 * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 146 */ 147 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 148 public int pageCacheOverflow; 149 150 /** records the largest memory allocation request handed to sqlite3. 151 * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 152 */ 153 @UnsupportedAppUsage 154 public int largestMemAlloc; 155 156 /** a list of {@link DbStats} - one for each main database opened by the applications 157 * running on the android device 158 */ 159 @UnsupportedAppUsage 160 public ArrayList<DbStats> dbStats; 161 } 162 163 /** 164 * contains statistics about a database 165 */ 166 public static class DbStats { 167 /** name of the database */ 168 @UnsupportedAppUsage 169 public String dbName; 170 171 /** the page size for the database */ 172 @UnsupportedAppUsage 173 public long pageSize; 174 175 /** the database size */ 176 @UnsupportedAppUsage 177 public long dbSize; 178 179 /** 180 * Number of lookaside slots: http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */ 181 @UnsupportedAppUsage 182 public int lookaside; 183 184 /** @hide */ 185 final public int cacheHits; 186 /** @hide */ 187 final public int cacheMisses; 188 /** @hide */ 189 final public int cacheSize; 190 191 /** true if connection specific stats or whole connection pool if false */ 192 public final boolean arePoolStats; 193 DbStats(@onNull String dbName, long pageCount, long pageSize, int lookaside, int hits, int misses, int cachesize, boolean arePoolStats)194 public DbStats(@NonNull String dbName, long pageCount, long pageSize, int lookaside, 195 int hits, int misses, int cachesize, boolean arePoolStats) { 196 this.dbName = dbName; 197 this.pageSize = pageSize / 1024; 198 dbSize = (pageCount * pageSize) / 1024; 199 this.lookaside = lookaside; 200 this.cacheHits = hits; 201 this.cacheMisses = misses; 202 this.cacheSize = cachesize; 203 this.arePoolStats = arePoolStats; 204 } 205 } 206 207 /** 208 * return all pager and database stats for the current process. 209 * @return {@link PagerStats} 210 */ 211 @UnsupportedAppUsage getDatabaseInfo()212 public static PagerStats getDatabaseInfo() { 213 PagerStats stats = new PagerStats(); 214 nativeGetPagerStats(stats); 215 stats.dbStats = SQLiteDatabase.getDbStats(); 216 return stats; 217 } 218 219 /** 220 * Dumps detailed information about all databases used by the process. 221 * @param printer The printer for dumping database state. 222 * @param args Command-line arguments supplied to dumpsys dbinfo 223 */ dump(Printer printer, String[] args)224 public static void dump(Printer printer, String[] args) { 225 dump(printer, args, false); 226 } 227 228 /** @hide */ dump(Printer printer, String[] args, boolean isSystem)229 public static void dump(Printer printer, String[] args, boolean isSystem) { 230 boolean verbose = false; 231 for (String arg : args) { 232 if (arg.equals("-v")) { 233 verbose = true; 234 } 235 } 236 237 SQLiteDatabase.dumpAll(printer, verbose, isSystem); 238 } 239 } 240