*/
#include "Dalvik.h"
#include "JniInternal.h"
+#include "Misc.h"
#include "ScopedPthreadMutexLock.h"
#include "UniquePtr.h"
*/
+static void ReportJniError() {
+ dvmDumpThread(dvmThreadSelf(), false);
+ dvmAbort();
+}
+
#ifdef WITH_JNI_STACK_CHECK
# define COMPUTE_STACK_SUM(_self) computeStackSum(_self);
# define CHECK_STACK_SUM(_self) checkStackSum(_self);
ALOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad", stackCrc);
} else {
ALOGW("JNI: bad stack CRC (%08x vs %08x)", crc, stackCrc);
- dvmAbort();
+ ReportJniError();
}
}
self->stackCrc = (u4) -1; /* make logic errors more noticeable */
#define kGlobalRefsTableInitialSize 512
#define kGlobalRefsTableMaxSize 51200 /* arbitrary, must be < 64K */
-#define kGrefWaterInterval 100
-#define kTrackGrefUsage true
#define kWeakGlobalRefsTableInitialSize 16
dvmInitMutex(&gDvm.jniGlobalRefLock);
dvmInitMutex(&gDvm.jniWeakGlobalRefLock);
- gDvm.jniGlobalRefLoMark = 0;
- gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
if (!dvmInitReferenceTable(&gDvm.jniPinRefTable, kPinTableInitialSize, kPinTableMaxSize)) {
return false;
dvmClearReferenceTable(&gDvm.jniPinRefTable);
}
+bool dvmIsBadJniVersion(int version) {
+ // We don't support JNI_VERSION_1_1. These are the only other valid versions.
+ return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
+}
+
/*
* Find the JNIEnv associated with the current thread.
*
Object* result = self->jniLocalRefTable.get(jobj);
if (UNLIKELY(result == NULL)) {
ALOGE("JNI ERROR (app bug): use of deleted local reference (%p)", jobj);
- dvmAbort();
+ ReportJniError();
}
return result;
}
Object* result = pRefTable->get(jobj);
if (UNLIKELY(result == NULL)) {
ALOGE("JNI ERROR (app bug): use of deleted global reference (%p)", jobj);
- dvmAbort();
+ ReportJniError();
}
return result;
}
result = NULL;
} else if (UNLIKELY(result == NULL)) {
ALOGE("JNI ERROR (app bug): use of deleted weak global reference (%p)", jobj);
- dvmAbort();
+ ReportJniError();
}
return result;
}
return reinterpret_cast<Object*>(jobj);
}
ALOGW("Invalid indirect reference %p in decodeIndirectRef", jobj);
- dvmAbort();
+ ReportJniError();
return kInvalidIndirectRefObject;
}
}
static void AddLocalReferenceFailure(IndirectRefTable* pRefTable) {
pRefTable->dump("JNI local");
ALOGE("Failed adding to JNI local ref table (has %zd entries)", pRefTable->capacity());
- dvmDumpThread(dvmThreadSelf(), false);
- dvmAbort(); // spec says call FatalError; this is equivalent
+ ReportJniError(); // spec says call FatalError; this is equivalent
}
/*
if (UNLIKELY(jobj == NULL)) {
AddLocalReferenceFailure(pRefTable);
}
+
if (UNLIKELY(gDvmJni.workAroundAppJniBugs)) {
// Hand out direct pointers to support broken old apps.
return reinterpret_cast<jobject>(obj);
gDvm.jniGlobalRefTable.dump("JNI global");
ALOGE("Failed adding to JNI global ref table (%zd entries)",
gDvm.jniGlobalRefTable.capacity());
- dvmAbort();
+ ReportJniError();
}
LOGVV("GREF add %p (%s.%s)", obj,
dvmGetCurrentJNIMethod()->clazz->descriptor,
dvmGetCurrentJNIMethod()->name);
- /* GREF usage tracking; should probably be disabled for production env */
- if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
- int count = gDvm.jniGlobalRefTable.capacity();
- // TODO: adjust for "holes"
- if (count > gDvm.jniGlobalRefHiMark) {
- ALOGD("GREF has increased to %d", count);
- gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
- gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
-
- /* watch for "excessive" use; not generally appropriate */
- if (count >= gDvm.jniGrefLimit) {
- if (gDvmJni.warnOnly) {
- ALOGW("Excessive JNI global references (%d)", count);
- } else {
- gDvm.jniGlobalRefTable.dump("JNI global");
- ALOGE("Excessive JNI global references (%d)", count);
- dvmAbort();
- }
- }
- }
- }
return jobj;
}
if (jobj == NULL) {
gDvm.jniWeakGlobalRefTable.dump("JNI weak global");
ALOGE("Failed adding to JNI weak global ref table (%zd entries)", table->capacity());
- dvmAbort();
+ ReportJniError();
}
return jobj;
}
ALOGW("JNI: DeleteGlobalRef(%p) failed to find entry", jobj);
return;
}
-
- if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
- int count = gDvm.jniGlobalRefTable.capacity();
- // TODO: not quite right, need to subtract holes
- if (count < gDvm.jniGlobalRefLoMark) {
- ALOGD("GREF has decreased to %d", count);
- gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
- gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
- }
- }
}
/*
dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
ALOGE("Failed adding to JNI pinned array ref table (%d entries)",
(int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
- dvmDumpThread(dvmThreadSelf(), false);
- dvmAbort();
+ ReportJniError();
}
/*
- * If we're watching global ref usage, also keep an eye on these.
- *
* The total number of pinned primitive arrays should be pretty small.
* A single array should not be pinned more than once or twice; any
* more than that is a strong indicator that a Release function is
* not being called.
*/
- if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
- int count = 0;
- Object** ppObj = gDvm.jniPinRefTable.table;
- while (ppObj < gDvm.jniPinRefTable.nextEntry) {
- if (*ppObj++ == (Object*) arrayObj)
- count++;
+ int count = 0;
+ Object** ppObj = gDvm.jniPinRefTable.table;
+ while (ppObj < gDvm.jniPinRefTable.nextEntry) {
+ if (*ppObj++ == (Object*) arrayObj) {
+ count++;
}
+ }
- if (count > kPinComplainThreshold) {
- ALOGW("JNI: pin count on array %p (%s) is now %d",
- arrayObj, arrayObj->clazz->descriptor, count);
- /* keep going */
- }
+ if (count > kPinComplainThreshold) {
+ ALOGW("JNI: pin count on array %p (%s) is now %d",
+ arrayObj, arrayObj->clazz->descriptor, count);
+ /* keep going */
}
}
dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
}
+void dvmDumpJniStats(DebugOutputTarget* target) {
+ dvmPrintDebugMessage(target, "JNI: CheckJNI is %s", gDvmJni.useCheckJni ? "on" : "off");
+ if (gDvmJni.forceCopy) {
+ dvmPrintDebugMessage(target, " (with forcecopy)");
+ }
+ dvmPrintDebugMessage(target, "; workarounds are %s", gDvmJni.workAroundAppJniBugs ? "on" : "off");
+
+ dvmLockMutex(&gDvm.jniPinRefLock);
+ dvmPrintDebugMessage(target, "; pins=%d", dvmReferenceTableEntries(&gDvm.jniPinRefTable));
+ dvmUnlockMutex(&gDvm.jniPinRefLock);
+
+ dvmLockMutex(&gDvm.jniGlobalRefLock);
+ dvmPrintDebugMessage(target, "; globals=%d", gDvm.jniGlobalRefTable.capacity());
+ dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+
+ dvmLockMutex(&gDvm.jniWeakGlobalRefLock);
+ size_t weaks = gDvm.jniWeakGlobalRefTable.capacity();
+ if (weaks > 0) {
+ dvmPrintDebugMessage(target, " (plus %d weak)", weaks);
+ }
+ dvmUnlockMutex(&gDvm.jniWeakGlobalRefLock);
+
+ dvmPrintDebugMessage(target, "\n\n");
+}
+
/*
* Verify that a reference passed in from native code is one that the
* code is allowed to have.
dumpMethods(clazz->directMethods, clazz->directMethodCount, methodName);
}
+static void throwNoSuchMethodError(ClassObject* c, const char* name, const char* sig, const char* kind) {
+ std::string msg(StringPrintf("no %s method \"%s.%s%s\"", kind, c->descriptor, name, sig));
+ dvmThrowNoSuchMethodError(msg.c_str());
+}
+
/*
* Register a method that uses JNI calling conventions.
*/
}
if (method == NULL) {
dumpCandidateMethods(clazz, methodName, signature);
+ throwNoSuchMethodError(clazz, methodName, signature, "static or non-static");
return false;
}
if (!dvmIsNativeMethod(method)) {
ALOGW("Unable to register: not native: %s.%s:%s", clazz->descriptor, methodName, signature);
+ throwNoSuchMethodError(clazz, methodName, signature, "native");
return false;
}
if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
ALOGE("Unable to initialize monitor tracking table");
- dvmAbort();
+ ReportJniError();
}
}
if (!dvmAddToReferenceTable(refTable, obj)) {
/* ran out of memory? could throw exception instead */
ALOGE("Unable to add entry to monitor tracking table");
- dvmAbort();
+ ReportJniError();
} else {
LOGVV("--- added monitor %p", obj);
}
static void FatalError(JNIEnv* env, const char* msg) {
//dvmChangeStatus(NULL, THREAD_RUNNING);
ALOGE("JNI posting fatal error: %s", msg);
- dvmAbort();
+ ReportJniError();
}
/*
if (obj != NULL && !dvmCanPutArrayElement(obj->clazz, arrayObj->clazz)) {
ALOGV("Can't put a '%s'(%p) into array type='%s'(%p)",
obj->clazz->descriptor, obj,
- arrayObj->obj.clazz->descriptor, arrayObj);
+ arrayObj->clazz->descriptor, arrayObj);
dvmThrowArrayStoreExceptionIncompatibleElement(obj->clazz, arrayObj->clazz);
return;
}
/*
* Allocate and return a new java.nio.ByteBuffer for this block of memory.
- *
- * "address" may not be NULL, and "capacity" must be > 0. (These are only
- * verified when CheckJNI is enabled.)
*/
static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
ScopedJniThreadState ts(env);
- /* create an instance of java.nio.ReadWriteDirectByteBuffer */
- ClassObject* bufferClazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
+ if (capacity < 0) {
+ ALOGE("JNI ERROR (app bug): negative buffer capacity: %lld", capacity);
+ ReportJniError();
+ }
+ if (address == NULL && capacity != 0) {
+ ALOGE("JNI ERROR (app bug): non-zero capacity for NULL pointer: %lld", capacity);
+ ReportJniError();
+ }
+
+ /* create an instance of java.nio.DirectByteBuffer */
+ ClassObject* bufferClazz = gDvm.classJavaNioDirectByteBuffer;
if (!dvmIsClassInitialized(bufferClazz) && !dvmInitClass(bufferClazz)) {
return NULL;
}
/* call the constructor */
jobject result = addLocalReference(ts.self(), newObj);
JValue unused;
- dvmCallMethod(ts.self(), gDvm.methJavaNioReadWriteDirectByteBuffer_init,
- newObj, &unused, (jint) address, (jint) capacity);
+ dvmCallMethod(ts.self(), gDvm.methJavaNioDirectByteBuffer_init,
+ newObj, &unused, (jlong) address, (jint) capacity);
if (dvmGetException(ts.self()) != NULL) {
deleteLocalReference(ts.self(), result);
return NULL;
// All Buffer objects have an effectiveDirectAddress field.
Object* bufObj = dvmDecodeIndirectRef(ts.self(), jbuf);
- return (void*) dvmGetFieldInt(bufObj, gDvm.offJavaNioBuffer_effectiveDirectAddress);
+ return (void*) dvmGetFieldLong(bufObj, gDvm.offJavaNioBuffer_effectiveDirectAddress);
}
/*
argsCopy.name = NULL;
argsCopy.group = (jobject) dvmGetMainThreadGroup();
} else {
- assert(args->version >= JNI_VERSION_1_2);
+ if (dvmIsBadJniVersion(args->version)) {
+ ALOGE("Bad JNI version passed to %s: %d",
+ (isDaemon ? "AttachCurrentThreadAsDaemon" : "AttachCurrentThread"),
+ args->version);
+ return JNI_EVERSION;
+ }
argsCopy.version = args->version;
argsCopy.name = args->name;
static jint GetEnv(JavaVM* vm, void** env, jint version) {
Thread* self = dvmThreadSelf();
- if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6) {
+ // GetEnv also accepts JNI_VERSION_1_1, but always returns a JNIEnv*
+ // corresponding to the most current supported JNI version.
+ if (dvmIsBadJniVersion(version) && version != JNI_VERSION_1_1) {
+ ALOGE("Bad JNI version passed to GetEnv: %d", version);
return JNI_EVERSION;
}
*/
jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
- if (args->version < JNI_VERSION_1_2) {
+ if (dvmIsBadJniVersion(args->version)) {
+ ALOGE("Bad JNI version passed to CreateJavaVM: %d", args->version);
return JNI_EVERSION;
}
} else {
dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjniopts option '%s'\n",
jniOpt);
+ free(pVM);
+ free(jniOpts);
return JNI_ERR;
}
jniOpt += strlen(jniOpt) + 1;
if (gDvmJni.jniVm != NULL) {
dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\n");
+ free(pVM);
return JNI_ERR;
}
gDvmJni.jniVm = (JavaVM*) pVM;