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