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
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