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.os.Build;
21 import android.os.Process;
22 import android.os.SystemProperties;
23 import android.util.Log;
24 import android.util.Printer;
25 
26 import java.util.ArrayList;
27 
28 /**
29  * Provides debugging info about all SQLite databases running in the current process.
30  *
31  * {@hide}
32  */
33 @TestApi
34 public final class SQLiteDebug {
nativeGetPagerStats(PagerStats stats)35     private static native void nativeGetPagerStats(PagerStats stats);
36 
37     /**
38      * Inner class to avoid getting the value frozen in zygote.
39      *
40      * {@hide}
41      */
42     public static final class NoPreloadHolder {
43         /**
44          * Controls the printing of informational SQL log messages.
45          *
46          * Enable using "adb shell setprop log.tag.SQLiteLog VERBOSE".
47          */
48         public static final boolean DEBUG_SQL_LOG =
49                 Log.isLoggable("SQLiteLog", Log.VERBOSE);
50 
51         /**
52          * Controls the printing of SQL statements as they are executed.
53          *
54          * Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE".
55          */
56         public static final boolean DEBUG_SQL_STATEMENTS =
57                 Log.isLoggable("SQLiteStatements", Log.VERBOSE);
58 
59         /**
60          * Controls the printing of wall-clock time taken to execute SQL statements
61          * as they are executed.
62          *
63          * Enable using "adb shell setprop log.tag.SQLiteTime VERBOSE".
64          */
65         public static final boolean DEBUG_SQL_TIME =
66                 Log.isLoggable("SQLiteTime", Log.VERBOSE);
67 
68 
69         /**
70          * True to enable database performance testing instrumentation.
71          */
72         public static final boolean DEBUG_LOG_SLOW_QUERIES = Build.IS_DEBUGGABLE;
73 
74         private static final String SLOW_QUERY_THRESHOLD_PROP = "db.log.slow_query_threshold";
75 
76         private static final String SLOW_QUERY_THRESHOLD_UID_PROP =
77                 SLOW_QUERY_THRESHOLD_PROP + "." + Process.myUid();
78 
79         /**
80          * Whether to add detailed information to slow query log.
81          */
82         public static final boolean DEBUG_LOG_DETAILED = Build.IS_DEBUGGABLE
83                 && SystemProperties.getBoolean("db.log.detailed", false);
84     }
85 
SQLiteDebug()86     private SQLiteDebug() {
87     }
88 
89     /**
90      * Determines whether a query should be logged.
91      *
92      * Reads the "db.log.slow_query_threshold" system property, which can be changed
93      * by the user at any time.  If the value is zero, then all queries will
94      * be considered slow.  If the value does not exist or is negative, then no queries will
95      * be considered slow.
96      *
97      * To enable it for a specific UID, "db.log.slow_query_threshold.UID" could also be used.
98      *
99      * This value can be changed dynamically while the system is running.
100      * For example, "adb shell setprop db.log.slow_query_threshold 200" will
101      * log all queries that take 200ms or longer to run.
102      * @hide
103      */
shouldLogSlowQuery(long elapsedTimeMillis)104     public static boolean shouldLogSlowQuery(long elapsedTimeMillis) {
105         final int slowQueryMillis = Math.min(
106                 SystemProperties.getInt(NoPreloadHolder.SLOW_QUERY_THRESHOLD_PROP,
107                         Integer.MAX_VALUE),
108                 SystemProperties.getInt(NoPreloadHolder.SLOW_QUERY_THRESHOLD_UID_PROP,
109                         Integer.MAX_VALUE));
110         return elapsedTimeMillis >= slowQueryMillis;
111     }
112 
113     /**
114      * Contains statistics about the active pagers in the current process.
115      *
116      * @see #nativeGetPagerStats(PagerStats)
117      */
118     public static class PagerStats {
119         /** the current amount of memory checked out by sqlite using sqlite3_malloc().
120          * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
121          */
122         public int memoryUsed;
123 
124         /** the number of bytes of page cache allocation which could not be sattisfied by the
125          * SQLITE_CONFIG_PAGECACHE buffer and where forced to overflow to sqlite3_malloc().
126          * The returned value includes allocations that overflowed because they where too large
127          * (they were larger than the "sz" parameter to SQLITE_CONFIG_PAGECACHE) and allocations
128          * that overflowed because no space was left in the page cache.
129          * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
130          */
131         public int pageCacheOverflow;
132 
133         /** records the largest memory allocation request handed to sqlite3.
134          * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
135          */
136         public int largestMemAlloc;
137 
138         /** a list of {@link DbStats} - one for each main database opened by the applications
139          * running on the android device
140          */
141         public ArrayList<DbStats> dbStats;
142     }
143 
144     /**
145      * contains statistics about a database
146      */
147     public static class DbStats {
148         /** name of the database */
149         public String dbName;
150 
151         /** the page size for the database */
152         public long pageSize;
153 
154         /** the database size */
155         public long dbSize;
156 
157         /**
158          * Number of lookaside slots: http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */
159         public int lookaside;
160 
161         /** statement cache stats: hits/misses/cachesize */
162         public String cache;
163 
DbStats(String dbName, long pageCount, long pageSize, int lookaside, int hits, int misses, int cachesize)164         public DbStats(String dbName, long pageCount, long pageSize, int lookaside,
165             int hits, int misses, int cachesize) {
166             this.dbName = dbName;
167             this.pageSize = pageSize / 1024;
168             dbSize = (pageCount * pageSize) / 1024;
169             this.lookaside = lookaside;
170             this.cache = hits + "/" + misses + "/" + cachesize;
171         }
172     }
173 
174     /**
175      * return all pager and database stats for the current process.
176      * @return {@link PagerStats}
177      */
getDatabaseInfo()178     public static PagerStats getDatabaseInfo() {
179         PagerStats stats = new PagerStats();
180         nativeGetPagerStats(stats);
181         stats.dbStats = SQLiteDatabase.getDbStats();
182         return stats;
183     }
184 
185     /**
186      * Dumps detailed information about all databases used by the process.
187      * @param printer The printer for dumping database state.
188      * @param args Command-line arguments supplied to dumpsys dbinfo
189      */
dump(Printer printer, String[] args)190     public static void dump(Printer printer, String[] args) {
191         dump(printer, args, false);
192     }
193 
194     /** @hide */
dump(Printer printer, String[] args, boolean isSystem)195     public static void dump(Printer printer, String[] args, boolean isSystem) {
196         boolean verbose = false;
197         for (String arg : args) {
198             if (arg.equals("-v")) {
199                 verbose = true;
200             }
201         }
202 
203         SQLiteDatabase.dumpAll(printer, verbose, isSystem);
204     }
205 }
206