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
30 extern "C" mspace sqlite3_get_mspace();
31
32 // From sqlite.c, hacked in for Android
33 extern "C" void sqlite3_get_pager_stats(sqlite3_int64 * totalBytesOut,
34 sqlite3_int64 * referencedBytesOut,
35 sqlite3_int64 * dbBytesOut,
36 int * numPagersOut);
37
38 namespace android {
39
40 static jfieldID gTotalBytesField;
41 static jfieldID gReferencedBytesField;
42 static jfieldID gDbBytesField;
43 static jfieldID gNumPagersField;
44
45
46 #define USE_MSPACE 0
47
getPagerStats(JNIEnv * env,jobject clazz,jobject statsObj)48 static 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
getHeapSize(JNIEnv * env,jobject clazz)64 static 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
getHeapAllocatedSize(JNIEnv * env,jobject clazz)82 static 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
getHeapFreeSize(JNIEnv * env,jobject clazz)92 static 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
read_mapinfo(FILE * fp,int * sharedPages,int * privatePages)102 static 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
117 again:
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
load_maps(int pid,int * sharedPages,int * privatePages)161 static 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
getHeapDirtyPages(JNIEnv * env,jobject clazz,jintArray pages)176 static 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
196 static 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
register_android_database_SQLiteDebug(JNIEnv * env)206 int 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