android_database_SQLiteDebug.cpp revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
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#include <JNIHelp.h>
18#include <jni.h>
19#include <utils/misc.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24#include <cutils/mspace.h>
25#include <utils/Log.h>
26
27#include <sqlite3.h>
28
29// From mem_mspace.c in libsqlite
30extern "C" mspace sqlite3_get_mspace();
31
32// From sqlite.c, hacked in for Android
33extern "C" void sqlite3_get_pager_stats(sqlite3_int64 * totalBytesOut,
34                                       sqlite3_int64 * referencedBytesOut,
35                                       sqlite3_int64 * dbBytesOut,
36                                       int * numPagersOut);
37
38namespace android {
39
40static jfieldID gTotalBytesField;
41static jfieldID gReferencedBytesField;
42static jfieldID gDbBytesField;
43static jfieldID gNumPagersField;
44
45
46#define USE_MSPACE 0
47
48static void getPagerStats(JNIEnv *env, jobject clazz, jobject statsObj)
49{
50    sqlite3_int64 totalBytes;
51    sqlite3_int64 referencedBytes;
52    sqlite3_int64 dbBytes;
53    int numPagers;
54
55    sqlite3_get_pager_stats(&totalBytes, &referencedBytes, &dbBytes,
56            &numPagers);
57
58    env->SetLongField(statsObj, gTotalBytesField, totalBytes);
59    env->SetLongField(statsObj, gReferencedBytesField, referencedBytes);
60    env->SetLongField(statsObj, gDbBytesField, dbBytes);
61    env->SetIntField(statsObj, gNumPagersField, numPagers);
62}
63
64static jlong getHeapSize(JNIEnv *env, jobject clazz)
65{
66#if !NO_MALLINFO
67    struct mallinfo info = mspace_mallinfo(sqlite3_get_mspace());
68    struct mallinfo info = dlmallinfo();
69    return (jlong) info.usmblks;
70#elif USE_MSPACE
71    mspace space = sqlite3_get_mspace();
72    if (space != 0) {
73        return mspace_footprint(space);
74    } else {
75        return 0;
76    }
77#else
78    return 0;
79#endif
80}
81
82static jlong getHeapAllocatedSize(JNIEnv *env, jobject clazz)
83{
84#if !NO_MALLINFO
85    struct mallinfo info = mspace_mallinfo(sqlite3_get_mspace());
86    return (jlong) info.uordblks;
87#else
88    return sqlite3_memory_used();
89#endif
90}
91
92static jlong getHeapFreeSize(JNIEnv *env, jobject clazz)
93{
94#if !NO_MALLINFO
95    struct mallinfo info = mspace_mallinfo(sqlite3_get_mspace());
96    return (jlong) info.fordblks;
97#else
98    return getHeapSize(env, clazz) - sqlite3_memory_used();
99#endif
100}
101
102static int read_mapinfo(FILE *fp,
103        int *sharedPages, int *privatePages)
104{
105    char line[1024];
106    int len;
107    int skip;
108
109    unsigned start = 0, size = 0, resident = 0;
110    unsigned shared_clean = 0, shared_dirty = 0;
111    unsigned private_clean = 0, private_dirty = 0;
112    unsigned referenced = 0;
113
114    int isAnon = 0;
115    int isHeap = 0;
116
117again:
118    skip = 0;
119
120    if(fgets(line, 1024, fp) == 0) return 0;
121
122    len = strlen(line);
123    if (len < 1) return 0;
124    line[--len] = 0;
125
126    /* ignore guard pages */
127    if (line[18] == '-') skip = 1;
128
129    start = strtoul(line, 0, 16);
130
131    if (len > 50 && !strncmp(line + 49, "/tmp/sqlite-heap", strlen("/tmp/sqlite-heap"))) {
132        isHeap = 1;
133    }
134
135    if (fgets(line, 1024, fp) == 0) return 0;
136    if (sscanf(line, "Size: %d kB", &size) != 1) return 0;
137    if (fgets(line, 1024, fp) == 0) return 0;
138    if (sscanf(line, "Rss: %d kB", &resident) != 1) return 0;
139    if (fgets(line, 1024, fp) == 0) return 0;
140    if (sscanf(line, "Shared_Clean: %d kB", &shared_clean) != 1) return 0;
141    if (fgets(line, 1024, fp) == 0) return 0;
142    if (sscanf(line, "Shared_Dirty: %d kB", &shared_dirty) != 1) return 0;
143    if (fgets(line, 1024, fp) == 0) return 0;
144    if (sscanf(line, "Private_Clean: %d kB", &private_clean) != 1) return 0;
145    if (fgets(line, 1024, fp) == 0) return 0;
146    if (sscanf(line, "Private_Dirty: %d kB", &private_dirty) != 1) return 0;
147    if (fgets(line, 1024, fp) == 0) return 0;
148    if (sscanf(line, "Referenced: %d kB", &referenced) != 1) return 0;
149
150    if (skip) {
151        goto again;
152    }
153
154    if (isHeap) {
155        *sharedPages += shared_dirty;
156        *privatePages += private_dirty;
157    }
158    return 1;
159}
160
161static void load_maps(int pid, int *sharedPages, int *privatePages)
162{
163    char tmp[128];
164    FILE *fp;
165
166    sprintf(tmp, "/proc/%d/smaps", pid);
167    fp = fopen(tmp, "r");
168    if (fp == 0) return;
169
170    while (read_mapinfo(fp, sharedPages, privatePages) != 0) {
171        // Do nothing
172    }
173    fclose(fp);
174}
175
176static void getHeapDirtyPages(JNIEnv *env, jobject clazz, jintArray pages)
177{
178    int _pages[2];
179
180    _pages[0] = 0;
181    _pages[1] = 0;
182
183    load_maps(getpid(), &_pages[0], &_pages[1]);
184
185    // Convert from kbytes to 4K pages
186    _pages[0] /= 4;
187    _pages[1] /= 4;
188
189    env->SetIntArrayRegion(pages, 0, 2, _pages);
190}
191
192/*
193 * JNI registration.
194 */
195
196static JNINativeMethod gMethods[] =
197{
198    { "getPagerStats", "(Landroid/database/sqlite/SQLiteDebug$PagerStats;)V",
199            (void*) getPagerStats },
200    { "getHeapSize", "()J", (void*) getHeapSize },
201    { "getHeapAllocatedSize", "()J", (void*) getHeapAllocatedSize },
202    { "getHeapFreeSize", "()J", (void*) getHeapFreeSize },
203    { "getHeapDirtyPages", "([I)V", (void*) getHeapDirtyPages },
204};
205
206int register_android_database_SQLiteDebug(JNIEnv *env)
207{
208    jclass clazz;
209
210    clazz = env->FindClass("android/database/sqlite/SQLiteDebug$PagerStats");
211    if (clazz == NULL) {
212        LOGE("Can't find android/database/sqlite/SQLiteDebug$PagerStats");
213        return -1;
214    }
215
216    gTotalBytesField = env->GetFieldID(clazz, "totalBytes", "J");
217    if (gTotalBytesField == NULL) {
218        LOGE("Can't find totalBytes");
219        return -1;
220    }
221
222    gReferencedBytesField = env->GetFieldID(clazz, "referencedBytes", "J");
223    if (gReferencedBytesField == NULL) {
224        LOGE("Can't find referencedBytes");
225        return -1;
226    }
227
228    gDbBytesField = env->GetFieldID(clazz, "databaseBytes", "J");
229    if (gDbBytesField == NULL) {
230        LOGE("Can't find databaseBytes");
231        return -1;
232    }
233
234    gNumPagersField = env->GetFieldID(clazz, "numPagers", "I");
235    if (gNumPagersField == NULL) {
236        LOGE("Can't find numPagers");
237        return -1;
238    }
239
240    return jniRegisterNativeMethods(env, "android/database/sqlite/SQLiteDebug",
241            gMethods, NELEM(gMethods));
242}
243
244} // namespace android
245