/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.database.sqlite; import android.annotation.NonNull; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Process; import android.os.SystemProperties; import android.util.Log; import android.util.Printer; import java.util.ArrayList; /** * Provides debugging info about all SQLite databases running in the current process. * * {@hide} */ @TestApi public final class SQLiteDebug { private static native void nativeGetPagerStats(PagerStats stats); /** * Inner class to avoid getting the value frozen in zygote. * * {@hide} */ public static final class NoPreloadHolder { /** * Controls the printing of informational SQL log messages. * * Enable using "adb shell setprop log.tag.SQLiteLog VERBOSE". */ public static final boolean DEBUG_SQL_LOG = Log.isLoggable("SQLiteLog", Log.VERBOSE); /** * Controls the printing of SQL statements as they are executed. * * Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE". */ public static final boolean DEBUG_SQL_STATEMENTS = Log.isLoggable("SQLiteStatements", Log.VERBOSE); /** * Controls the printing of wall-clock time taken to execute SQL statements * as they are executed. * * Enable using "adb shell setprop log.tag.SQLiteTime VERBOSE". */ public static final boolean DEBUG_SQL_TIME = Log.isLoggable("SQLiteTime", Log.VERBOSE); /** * True to enable database performance testing instrumentation. */ public static final boolean DEBUG_LOG_SLOW_QUERIES = Log.isLoggable("SQLiteSlowQueries", Log.VERBOSE); private static final String SLOW_QUERY_THRESHOLD_PROP = "db.log.slow_query_threshold"; private static final String SLOW_QUERY_THRESHOLD_UID_PROP = SLOW_QUERY_THRESHOLD_PROP + "." + Process.myUid(); /** * Whether to add detailed information to slow query log. */ public static final boolean DEBUG_LOG_DETAILED = Build.IS_DEBUGGABLE && SystemProperties.getBoolean("db.log.detailed", false); } private SQLiteDebug() { } /** * Determines whether a query should be logged. * * Reads the "db.log.slow_query_threshold" system property, which can be changed * by the user at any time. If the value is zero, then all queries will * be considered slow. If the value does not exist or is negative, then no queries will * be considered slow. * * To enable it for a specific UID, "db.log.slow_query_threshold.UID" could also be used. * * This value can be changed dynamically while the system is running. * For example, "adb shell setprop db.log.slow_query_threshold 200" will * log all queries that take 200ms or longer to run. * @hide */ public static boolean shouldLogSlowQuery(long elapsedTimeMillis) { final int slowQueryMillis = Math.min( SystemProperties.getInt(NoPreloadHolder.SLOW_QUERY_THRESHOLD_PROP, Integer.MAX_VALUE), SystemProperties.getInt(NoPreloadHolder.SLOW_QUERY_THRESHOLD_UID_PROP, Integer.MAX_VALUE)); return elapsedTimeMillis >= slowQueryMillis; } /** * Contains statistics about the active pagers in the current process. * * @see #nativeGetPagerStats(PagerStats) */ public static class PagerStats { @UnsupportedAppUsage public PagerStats() { } /** the current amount of memory checked out by sqlite using sqlite3_malloc(). * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html */ @UnsupportedAppUsage public int memoryUsed; /** the number of bytes of page cache allocation which could not be sattisfied by the * SQLITE_CONFIG_PAGECACHE buffer and where forced to overflow to sqlite3_malloc(). * The returned value includes allocations that overflowed because they where too large * (they were larger than the "sz" parameter to SQLITE_CONFIG_PAGECACHE) and allocations * that overflowed because no space was left in the page cache. * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int pageCacheOverflow; /** records the largest memory allocation request handed to sqlite3. * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html */ @UnsupportedAppUsage public int largestMemAlloc; /** a list of {@link DbStats} - one for each main database opened by the applications * running on the android device */ @UnsupportedAppUsage public ArrayList dbStats; } /** * contains statistics about a database */ public static class DbStats { /** name of the database */ @UnsupportedAppUsage public String dbName; /** the page size for the database */ @UnsupportedAppUsage public long pageSize; /** the database size */ @UnsupportedAppUsage public long dbSize; /** * Number of lookaside slots: http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */ @UnsupportedAppUsage public int lookaside; /** @hide */ final public int cacheHits; /** @hide */ final public int cacheMisses; /** @hide */ final public int cacheSize; /** true if connection specific stats or whole connection pool if false */ public final boolean arePoolStats; public DbStats(@NonNull String dbName, long pageCount, long pageSize, int lookaside, int hits, int misses, int cachesize, boolean arePoolStats) { this.dbName = dbName; this.pageSize = pageSize / 1024; dbSize = (pageCount * pageSize) / 1024; this.lookaside = lookaside; this.cacheHits = hits; this.cacheMisses = misses; this.cacheSize = cachesize; this.arePoolStats = arePoolStats; } } /** * return all pager and database stats for the current process. * @return {@link PagerStats} */ @UnsupportedAppUsage public static PagerStats getDatabaseInfo() { PagerStats stats = new PagerStats(); nativeGetPagerStats(stats); stats.dbStats = SQLiteDatabase.getDbStats(); return stats; } /** * Dumps detailed information about all databases used by the process. * @param printer The printer for dumping database state. * @param args Command-line arguments supplied to dumpsys dbinfo */ public static void dump(Printer printer, String[] args) { dump(printer, args, false); } /** @hide */ public static void dump(Printer printer, String[] args, boolean isSystem) { boolean verbose = false; for (String arg : args) { if (arg.equals("-v")) { verbose = true; } } SQLiteDatabase.dumpAll(printer, verbose, isSystem); } }