Home | History | Annotate | Download | only in native
      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