• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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