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