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 #define LOG_TAG "OSMemory" 18 19 #include "JNIHelp.h" 20 #include "JniConstants.h" 21 #include "UniquePtr.h" 22 23 #include <errno.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <sys/mman.h> 27 28 /* 29 * Cached dalvik.system.VMRuntime pieces. 30 */ 31 static struct { 32 jmethodID method_trackExternalAllocation; 33 jmethodID method_trackExternalFree; 34 35 jobject runtimeInstance; 36 } gIDCache; 37 38 template <typename T> static T cast(jint address) { 39 return reinterpret_cast<T>(static_cast<uintptr_t>(address)); 40 } 41 42 static jint OSMemory_malloc(JNIEnv* env, jclass, jint size) { 43 jboolean allowed = env->CallBooleanMethod(gIDCache.runtimeInstance, 44 gIDCache.method_trackExternalAllocation, static_cast<jlong>(size)); 45 if (!allowed) { 46 LOGW("External allocation of %d bytes was rejected\n", size); 47 jniThrowException(env, "java/lang/OutOfMemoryError", NULL); 48 return 0; 49 } 50 51 LOGV("OSMemory alloc %d\n", size); 52 void* block = malloc(size + sizeof(jlong)); 53 if (block == NULL) { 54 jniThrowException(env, "java/lang/OutOfMemoryError", NULL); 55 return 0; 56 } 57 58 /* 59 * Tuck a copy of the size at the head of the buffer. We need this 60 * so OSMemory_free() knows how much memory is being freed. 61 */ 62 jlong* result = reinterpret_cast<jlong*>(block); 63 *result++ = size; 64 return static_cast<jint>(reinterpret_cast<uintptr_t>(result)); 65 } 66 67 static void OSMemory_free(JNIEnv* env, jclass, jint address) { 68 jlong* p = reinterpret_cast<jlong*>(static_cast<uintptr_t>(address)); 69 jlong size = *--p; 70 LOGV("OSMemory free %ld\n", size); 71 env->CallVoidMethod(gIDCache.runtimeInstance, gIDCache.method_trackExternalFree, size); 72 free(reinterpret_cast<void*>(p)); 73 } 74 75 static void OSMemory_memset(JNIEnv*, jclass, jint dstAddress, jbyte value, jlong length) { 76 memset(cast<void*>(dstAddress), value, length); 77 } 78 79 static void OSMemory_memmove(JNIEnv*, jclass, jint dstAddress, jint srcAddress, jlong length) { 80 memmove(cast<void*>(dstAddress), cast<const void*>(srcAddress), length); 81 } 82 83 static jbyte OSMemory_getByte(JNIEnv*, jclass, jint srcAddress) { 84 return *cast<const jbyte*>(srcAddress); 85 } 86 87 static void OSMemory_getByteArray(JNIEnv* env, jclass, jint srcAddress, 88 jbyteArray dst, jint offset, jint length) { 89 env->SetByteArrayRegion(dst, offset, length, cast<const jbyte*>(srcAddress)); 90 } 91 92 static void OSMemory_setByte(JNIEnv*, jclass, jint dstAddress, jbyte value) { 93 *cast<jbyte*>(dstAddress) = value; 94 } 95 96 static void OSMemory_setByteArray(JNIEnv* env, jclass, 97 jint dstAddress, jbyteArray src, jint offset, jint length) { 98 env->GetByteArrayRegion(src, offset, length, cast<jbyte*>(dstAddress)); 99 } 100 101 static void swapShorts(jshort* shorts, int count) { 102 jbyte* src = reinterpret_cast<jbyte*>(shorts); 103 jbyte* dst = src; 104 for (int i = 0; i < count; ++i) { 105 jbyte b0 = *src++; 106 jbyte b1 = *src++; 107 *dst++ = b1; 108 *dst++ = b0; 109 } 110 } 111 112 static void swapInts(jint* ints, int count) { 113 jbyte* src = reinterpret_cast<jbyte*>(ints); 114 jbyte* dst = src; 115 for (int i = 0; i < count; ++i) { 116 jbyte b0 = *src++; 117 jbyte b1 = *src++; 118 jbyte b2 = *src++; 119 jbyte b3 = *src++; 120 *dst++ = b3; 121 *dst++ = b2; 122 *dst++ = b1; 123 *dst++ = b0; 124 } 125 } 126 127 static void OSMemory_setFloatArray(JNIEnv* env, jclass, jint dstAddress, 128 jfloatArray src, jint offset, jint length, jboolean swap) { 129 jfloat* dst = cast<jfloat*>(dstAddress); 130 env->GetFloatArrayRegion(src, offset, length, dst); 131 if (swap) { 132 swapInts(reinterpret_cast<jint*>(dst), length); 133 } 134 } 135 136 static void OSMemory_setIntArray(JNIEnv* env, jclass, 137 jint dstAddress, jintArray src, jint offset, jint length, jboolean swap) { 138 jint* dst = cast<jint*>(dstAddress); 139 env->GetIntArrayRegion(src, offset, length, dst); 140 if (swap) { 141 swapInts(dst, length); 142 } 143 } 144 145 static void OSMemory_setShortArray(JNIEnv* env, jclass, 146 jint dstAddress, jshortArray src, jint offset, jint length, jboolean swap) { 147 jshort* dst = cast<jshort*>(dstAddress); 148 env->GetShortArrayRegion(src, offset, length, dst); 149 if (swap) { 150 swapShorts(dst, length); 151 } 152 } 153 154 static jshort OSMemory_getShort(JNIEnv*, jclass, jint srcAddress) { 155 if ((srcAddress & 0x1) == 0) { 156 return *cast<const jshort*>(srcAddress); 157 } else { 158 // Handle unaligned memory access one byte at a time 159 jshort result; 160 const jbyte* src = cast<const jbyte*>(srcAddress); 161 jbyte* dst = reinterpret_cast<jbyte*>(&result); 162 dst[0] = src[0]; 163 dst[1] = src[1]; 164 return result; 165 } 166 } 167 168 static void OSMemory_setShort(JNIEnv*, jclass, jint dstAddress, jshort value) { 169 if ((dstAddress & 0x1) == 0) { 170 *cast<jshort*>(dstAddress) = value; 171 } else { 172 // Handle unaligned memory access one byte at a time 173 const jbyte* src = reinterpret_cast<const jbyte*>(&value); 174 jbyte* dst = cast<jbyte*>(dstAddress); 175 dst[0] = src[0]; 176 dst[1] = src[1]; 177 } 178 } 179 180 static jint OSMemory_getInt(JNIEnv*, jclass, jint srcAddress) { 181 if ((srcAddress & 0x3) == 0) { 182 return *cast<const jint*>(srcAddress); 183 } else { 184 // Handle unaligned memory access one byte at a time 185 jint result; 186 const jbyte* src = cast<const jbyte*>(srcAddress); 187 jbyte* dst = reinterpret_cast<jbyte*>(&result); 188 dst[0] = src[0]; 189 dst[1] = src[1]; 190 dst[2] = src[2]; 191 dst[3] = src[3]; 192 return result; 193 } 194 } 195 196 static void OSMemory_setInt(JNIEnv*, jclass, jint dstAddress, jint value) { 197 if ((dstAddress & 0x3) == 0) { 198 *cast<jint*>(dstAddress) = value; 199 } else { 200 // Handle unaligned memory access one byte at a time 201 const jbyte* src = reinterpret_cast<const jbyte*>(&value); 202 jbyte* dst = cast<jbyte*>(dstAddress); 203 dst[0] = src[0]; 204 dst[1] = src[1]; 205 dst[2] = src[2]; 206 dst[3] = src[3]; 207 } 208 } 209 210 template <typename T> static T get(jint srcAddress) { 211 if ((srcAddress & (sizeof(T) - 1)) == 0) { 212 return *cast<const T*>(srcAddress); 213 } else { 214 // Cast to void* so GCC can't assume correct alignment and optimize this out. 215 const void* src = cast<const void*>(srcAddress); 216 T result; 217 memcpy(&result, src, sizeof(T)); 218 return result; 219 } 220 } 221 222 template <typename T> static void set(jint dstAddress, T value) { 223 if ((dstAddress & (sizeof(T) - 1)) == 0) { 224 *cast<T*>(dstAddress) = value; 225 } else { 226 // Cast to void* so GCC can't assume correct alignment and optimize this out. 227 void* dst = cast<void*>(dstAddress); 228 memcpy(dst, &value, sizeof(T)); 229 } 230 } 231 232 static jlong OSMemory_getLong(JNIEnv*, jclass, jint srcAddress) { 233 return get<jlong>(srcAddress); 234 } 235 236 static void OSMemory_setLong(JNIEnv*, jclass, jint dstAddress, jlong value) { 237 set<jlong>(dstAddress, value); 238 } 239 240 static jfloat OSMemory_getFloat(JNIEnv*, jclass, jint srcAddress) { 241 return get<jfloat>(srcAddress); 242 } 243 244 static void OSMemory_setFloat(JNIEnv*, jclass, jint dstAddress, jfloat value) { 245 set<jfloat>(dstAddress, value); 246 } 247 248 static jdouble OSMemory_getDouble(JNIEnv*, jclass, jint srcAddress) { 249 return get<jdouble>(srcAddress); 250 } 251 252 static void OSMemory_setDouble(JNIEnv*, jclass, jint dstAddress, jdouble value) { 253 set<jdouble>(dstAddress, value); 254 } 255 256 static jint OSMemory_getAddress(JNIEnv*, jclass, jint srcAddress) { 257 return *cast<const jint*>(srcAddress); 258 } 259 260 static void OSMemory_setAddress(JNIEnv*, jclass, jint dstAddress, jint value) { 261 *cast<jint*>(dstAddress) = value; 262 } 263 264 static jint OSMemory_mmapImpl(JNIEnv* env, jclass, jint fd, jlong offset, jlong size, jint mapMode) { 265 int prot, flags; 266 switch (mapMode) { 267 case 0: // MapMode.PRIVATE 268 prot = PROT_READ|PROT_WRITE; 269 flags = MAP_PRIVATE; 270 break; 271 case 1: // MapMode.READ_ONLY 272 prot = PROT_READ; 273 flags = MAP_SHARED; 274 break; 275 case 2: // MapMode.READ_WRITE 276 prot = PROT_READ|PROT_WRITE; 277 flags = MAP_SHARED; 278 break; 279 default: 280 jniThrowIOException(env, EINVAL); 281 LOGE("bad mapMode %i", mapMode); 282 return -1; 283 } 284 285 void* mapAddress = mmap(0, size, prot, flags, fd, offset); 286 if (mapAddress == MAP_FAILED) { 287 jniThrowIOException(env, errno); 288 } 289 return reinterpret_cast<uintptr_t>(mapAddress); 290 } 291 292 static void OSMemory_unmap(JNIEnv*, jclass, jint address, jlong size) { 293 munmap(cast<void*>(address), size); 294 } 295 296 static void OSMemory_load(JNIEnv*, jclass, jint address, jlong size) { 297 if (mlock(cast<void*>(address), size) != -1) { 298 munlock(cast<void*>(address), size); 299 } 300 } 301 302 static jboolean OSMemory_isLoaded(JNIEnv*, jclass, jint address, jlong size) { 303 if (size == 0) { 304 return JNI_TRUE; 305 } 306 307 static int page_size = getpagesize(); 308 309 int align_offset = address % page_size;// addr should align with the boundary of a page. 310 address -= align_offset; 311 size += align_offset; 312 int page_count = (size + page_size - 1) / page_size; 313 314 UniquePtr<unsigned char[]> vec(new unsigned char[page_count]); 315 int rc = mincore(cast<void*>(address), size, vec.get()); 316 if (rc == -1) { 317 return JNI_FALSE; 318 } 319 320 for (int i = 0; i < page_count; ++i) { 321 if (vec[i] != 1) { 322 return JNI_FALSE; 323 } 324 } 325 return JNI_TRUE; 326 } 327 328 static void OSMemory_flush(JNIEnv*, jclass, jint address, jlong size) { 329 msync(cast<void*>(address), size, MS_SYNC); 330 } 331 332 static JNINativeMethod gMethods[] = { 333 NATIVE_METHOD(OSMemory, flush, "(IJ)V"), 334 NATIVE_METHOD(OSMemory, free, "(I)V"), 335 NATIVE_METHOD(OSMemory, getAddress, "(I)I"), 336 NATIVE_METHOD(OSMemory, getByte, "(I)B"), 337 NATIVE_METHOD(OSMemory, getByteArray, "(I[BII)V"), 338 NATIVE_METHOD(OSMemory, getDouble, "(I)D"), 339 NATIVE_METHOD(OSMemory, getFloat, "(I)F"), 340 NATIVE_METHOD(OSMemory, getInt, "(I)I"), 341 NATIVE_METHOD(OSMemory, getLong, "(I)J"), 342 NATIVE_METHOD(OSMemory, getShort, "(I)S"), 343 NATIVE_METHOD(OSMemory, isLoaded, "(IJ)Z"), 344 NATIVE_METHOD(OSMemory, load, "(IJ)V"), 345 NATIVE_METHOD(OSMemory, malloc, "(I)I"), 346 NATIVE_METHOD(OSMemory, memmove, "(IIJ)V"), 347 NATIVE_METHOD(OSMemory, memset, "(IBJ)V"), 348 NATIVE_METHOD(OSMemory, mmapImpl, "(IJJI)I"), 349 NATIVE_METHOD(OSMemory, setAddress, "(II)V"), 350 NATIVE_METHOD(OSMemory, setByte, "(IB)V"), 351 NATIVE_METHOD(OSMemory, setByteArray, "(I[BII)V"), 352 NATIVE_METHOD(OSMemory, setDouble, "(ID)V"), 353 NATIVE_METHOD(OSMemory, setFloat, "(IF)V"), 354 NATIVE_METHOD(OSMemory, setFloatArray, "(I[FIIZ)V"), 355 NATIVE_METHOD(OSMemory, setInt, "(II)V"), 356 NATIVE_METHOD(OSMemory, setIntArray, "(I[IIIZ)V"), 357 NATIVE_METHOD(OSMemory, setLong, "(IJ)V"), 358 NATIVE_METHOD(OSMemory, setShort, "(IS)V"), 359 NATIVE_METHOD(OSMemory, setShortArray, "(I[SIIZ)V"), 360 NATIVE_METHOD(OSMemory, unmap, "(IJ)V"), 361 }; 362 int register_org_apache_harmony_luni_platform_OSMemory(JNIEnv* env) { 363 /* 364 * We need to call VMRuntime.trackExternal{Allocation,Free}. Cache 365 * method IDs and a reference to the singleton. 366 */ 367 gIDCache.method_trackExternalAllocation = env->GetMethodID(JniConstants::vmRuntimeClass, 368 "trackExternalAllocation", "(J)Z"); 369 gIDCache.method_trackExternalFree = env->GetMethodID(JniConstants::vmRuntimeClass, 370 "trackExternalFree", "(J)V"); 371 372 jmethodID method_getRuntime = env->GetStaticMethodID(JniConstants::vmRuntimeClass, 373 "getRuntime", "()Ldalvik/system/VMRuntime;"); 374 375 if (gIDCache.method_trackExternalAllocation == NULL || 376 gIDCache.method_trackExternalFree == NULL || 377 method_getRuntime == NULL) 378 { 379 LOGE("Unable to find VMRuntime methods\n"); 380 return -1; 381 } 382 383 jobject instance = env->CallStaticObjectMethod(JniConstants::vmRuntimeClass, method_getRuntime); 384 if (instance == NULL) { 385 LOGE("Unable to obtain VMRuntime instance\n"); 386 return -1; 387 } 388 gIDCache.runtimeInstance = env->NewGlobalRef(instance); 389 390 return jniRegisterNativeMethods(env, "org/apache/harmony/luni/platform/OSMemory", 391 gMethods, NELEM(gMethods)); 392 } 393