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.TestApi; 20 import android.compat.annotation.UnsupportedAppUsage; 21 import android.os.Build; 22 import android.os.Process; 23 import android.os.SystemProperties; 24 import android.util.Log; 25 import android.util.Printer; 26 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 /** 71 * True to enable database performance testing instrumentation. 72 */ 73 public static final boolean DEBUG_LOG_SLOW_QUERIES = Build.IS_DEBUGGABLE; 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 SQLiteDebug()87 private SQLiteDebug() { 88 } 89 90 /** 91 * Determines whether a query should be logged. 92 * 93 * Reads the "db.log.slow_query_threshold" system property, which can be changed 94 * by the user at any time. If the value is zero, then all queries will 95 * be considered slow. If the value does not exist or is negative, then no queries will 96 * be considered slow. 97 * 98 * To enable it for a specific UID, "db.log.slow_query_threshold.UID" could also be used. 99 * 100 * This value can be changed dynamically while the system is running. 101 * For example, "adb shell setprop db.log.slow_query_threshold 200" will 102 * log all queries that take 200ms or longer to run. 103 * @hide 104 */ shouldLogSlowQuery(long elapsedTimeMillis)105 public static boolean shouldLogSlowQuery(long elapsedTimeMillis) { 106 final int slowQueryMillis = Math.min( 107 SystemProperties.getInt(NoPreloadHolder.SLOW_QUERY_THRESHOLD_PROP, 108 Integer.MAX_VALUE), 109 SystemProperties.getInt(NoPreloadHolder.SLOW_QUERY_THRESHOLD_UID_PROP, 110 Integer.MAX_VALUE)); 111 return elapsedTimeMillis >= slowQueryMillis; 112 } 113 114 /** 115 * Contains statistics about the active pagers in the current process. 116 * 117 * @see #nativeGetPagerStats(PagerStats) 118 */ 119 public static class PagerStats { 120 121 @UnsupportedAppUsage PagerStats()122 public PagerStats() { 123 } 124 125 /** the current amount of memory checked out by sqlite using sqlite3_malloc(). 126 * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 127 */ 128 @UnsupportedAppUsage 129 public int memoryUsed; 130 131 /** the number of bytes of page cache allocation which could not be sattisfied by the 132 * SQLITE_CONFIG_PAGECACHE buffer and where forced to overflow to sqlite3_malloc(). 133 * The returned value includes allocations that overflowed because they where too large 134 * (they were larger than the "sz" parameter to SQLITE_CONFIG_PAGECACHE) and allocations 135 * that overflowed because no space was left in the page cache. 136 * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 137 */ 138 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 139 public int pageCacheOverflow; 140 141 /** records the largest memory allocation request handed to sqlite3. 142 * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 143 */ 144 @UnsupportedAppUsage 145 public int largestMemAlloc; 146 147 /** a list of {@link DbStats} - one for each main database opened by the applications 148 * running on the android device 149 */ 150 @UnsupportedAppUsage 151 public ArrayList<DbStats> dbStats; 152 } 153 154 /** 155 * contains statistics about a database 156 */ 157 public static class DbStats { 158 /** name of the database */ 159 @UnsupportedAppUsage 160 public String dbName; 161 162 /** the page size for the database */ 163 @UnsupportedAppUsage 164 public long pageSize; 165 166 /** the database size */ 167 @UnsupportedAppUsage 168 public long dbSize; 169 170 /** 171 * Number of lookaside slots: http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */ 172 @UnsupportedAppUsage 173 public int lookaside; 174 175 /** statement cache stats: hits/misses/cachesize */ 176 public String cache; 177 DbStats(String dbName, long pageCount, long pageSize, int lookaside, int hits, int misses, int cachesize)178 public DbStats(String dbName, long pageCount, long pageSize, int lookaside, 179 int hits, int misses, int cachesize) { 180 this.dbName = dbName; 181 this.pageSize = pageSize / 1024; 182 dbSize = (pageCount * pageSize) / 1024; 183 this.lookaside = lookaside; 184 this.cache = hits + "/" + misses + "/" + cachesize; 185 } 186 } 187 188 /** 189 * return all pager and database stats for the current process. 190 * @return {@link PagerStats} 191 */ 192 @UnsupportedAppUsage getDatabaseInfo()193 public static PagerStats getDatabaseInfo() { 194 PagerStats stats = new PagerStats(); 195 nativeGetPagerStats(stats); 196 stats.dbStats = SQLiteDatabase.getDbStats(); 197 return stats; 198 } 199 200 /** 201 * Dumps detailed information about all databases used by the process. 202 * @param printer The printer for dumping database state. 203 * @param args Command-line arguments supplied to dumpsys dbinfo 204 */ dump(Printer printer, String[] args)205 public static void dump(Printer printer, String[] args) { 206 dump(printer, args, false); 207 } 208 209 /** @hide */ dump(Printer printer, String[] args, boolean isSystem)210 public static void dump(Printer printer, String[] args, boolean isSystem) { 211 boolean verbose = false; 212 for (String arg : args) { 213 if (arg.equals("-v")) { 214 verbose = true; 215 } 216 } 217 218 SQLiteDatabase.dumpAll(printer, verbose, isSystem); 219 } 220 } 221