diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java index ee7e0b1aedda41b65891cb4b3a5bc8577e71819d..e57311a77fa0d86af77c5cdba35562896f8a214e 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java @@ -243,15 +243,12 @@ public class RCommand { * logging the report will go to a file, so we print a message on * the console as well. */ - consoleHandler.println("internal error: " + e.getMessage() + " (see fastr_errors.log)"); RInternalError.reportError(e); + consoleHandler.println("internal error: " + e.getMessage() + " (see fastr_errors.log)"); } else { - /* - * This should never happen owing to earlier invariants of - * converting everything else to an RInternalError - */ - consoleHandler.println("unexpected internal error (" + e.getClass().getSimpleName() + "); " + e.getMessage()); + // Something else, e.g. NPE RInternalError.reportError(e); + consoleHandler.println("unexpected internal error (" + e.getClass().getSimpleName() + "); " + e.getMessage()); } } continue REPL; diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java index 2dd95afdfd4df88f31ad901f94fe40fb6a7ef702..7f13d4cc0f93bb2c8b72a004ee5e31b184fadcda 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java @@ -114,9 +114,12 @@ public class REmbedded { runRmainloop(vm); } + /** + * Upcalled from embedded mode to commit suicide. + */ @SuppressWarnings("unused") private static void R_Suicide(String msg) { - // TODO implement + Utils.exit(2); } } diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c b/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c index 3b1cbfe0ac19ce8b186f0c02724e04fc4b760128..785fdab402c221fc64eddde824297b5d61287b8a 100644 --- a/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c +++ b/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c @@ -438,6 +438,10 @@ JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1REmbed_nat (*ptr_R_CleanUp)(x, y, z); } +JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1REmbed_nativeSuicide(JNIEnv *jniEnv, jclass c, jstring string) { + const char *cbuf = (*jniEnv)->GetStringUTFChars(jniEnv, string, NULL); + (*ptr_R_Suicide)(cbuf); +} void uR_PolledEvents(void) { unimplemented("R_PolledEvents"); diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c b/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c index bdb2015fbbbfd2f1ae29a8409a35f4df3944aa57..d5522e24ac464009129d58a17c612d804feb70ab 100644 --- a/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c +++ b/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c @@ -75,10 +75,10 @@ static jmethodID SYMVALUE_MethodID; static jmethodID SET_SYMVALUE_MethodID; static jmethodID SET_STRING_ELT_MethodID; static jmethodID SET_VECTOR_ELT_MethodID; -static jmethodID RAW_MethodID; -static jmethodID INTEGER_MethodID; -static jmethodID REAL_MethodID; -static jmethodID LOGICAL_MethodID; +jmethodID RAW_MethodID; +jmethodID INTEGER_MethodID; +jmethodID REAL_MethodID; +jmethodID LOGICAL_MethodID; static jmethodID STRING_ELT_MethodID; static jmethodID VECTOR_ELT_MethodID; static jmethodID LENGTH_MethodID; @@ -528,24 +528,25 @@ int Rf_nrows(SEXP x) { SEXP Rf_protect(SEXP x) { + TRACE(TARGp, x); return x; } void Rf_unprotect(int x) { - // TODO perhaps we can use this + TRACE(TARGp, x); } void R_ProtectWithIndex(SEXP x, PROTECT_INDEX *y) { - + TRACE(TARGpd, x,y); } void R_Reprotect(SEXP x, PROTECT_INDEX y) { - + TRACE(TARGpd, x,y); } void Rf_unprotect_ptr(SEXP x) { - // TODO perhaps we can use this + TRACE(TARGp, x); } #define BUFSIZE 8192 @@ -1105,58 +1106,28 @@ int SETLEVELS(SEXP x, int v){ int *LOGICAL(SEXP x){ TRACE(TARGp, x); JNIEnv *thisenv = getEnv(); - jint *data = (jint *) findCopiedObject(thisenv, x); - if (data == NULL) { - jbyteArray byteArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, LOGICAL_MethodID, x); - int len = (*thisenv)->GetArrayLength(thisenv, byteArray); - jbyte* internalData = (*thisenv)->GetByteArrayElements(thisenv, byteArray, NULL); - data = malloc(len * sizeof(int)); - for (int i = 0; i < len; i++) { - char value = internalData[i]; - data[i] = value == 0 ? FALSE : value == 1 ? TRUE : NA_INTEGER; - } - (*thisenv)->ReleaseByteArrayElements(thisenv, byteArray, internalData, JNI_ABORT); - addCopiedObject(thisenv, x, LGLSXP, byteArray, data); - } + jint *data = (jint *) getNativeArray(thisenv, x, LGLSXP); return data; } int *INTEGER(SEXP x){ TRACE(TARGp, x); JNIEnv *thisenv = getEnv(); - jint *data = (jint *) findCopiedObject(thisenv, x); - if (data == NULL) { - jintArray intArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, INTEGER_MethodID, x); - int len = (*thisenv)->GetArrayLength(thisenv, intArray); - data = (*thisenv)->GetIntArrayElements(thisenv, intArray, NULL); - addCopiedObject(thisenv, x, INTSXP, intArray, data); - } + jint *data = (jint *) getNativeArray(thisenv, x, INTSXP); return data; } Rbyte *RAW(SEXP x){ JNIEnv *thisenv = getEnv(); - jbyte *data = (jbyte *) findCopiedObject(thisenv, x); - if (data == NULL) { - jbyteArray byteArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, RAW_MethodID, x); - int len = (*thisenv)->GetArrayLength(thisenv, byteArray); - data = (*thisenv)->GetByteArrayElements(thisenv, byteArray, NULL); - addCopiedObject(thisenv, x, RAWSXP, byteArray, data); - } - return (Rbyte*) data; + Rbyte *data = (Rbyte*) getNativeArray(thisenv, x, RAWSXP); + return data; } double *REAL(SEXP x){ JNIEnv *thisenv = getEnv(); - jdouble *data = (jdouble *) findCopiedObject(thisenv, x); - if (data == NULL) { - jdoubleArray doubleArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, REAL_MethodID, x); - int len = (*thisenv)->GetArrayLength(thisenv, doubleArray); - data = (*thisenv)->GetDoubleArrayElements(thisenv, doubleArray, NULL); - addCopiedObject(thisenv, x, REALSXP, doubleArray, data); - } + jdouble *data = (jdouble *) getNativeArray(thisenv, x, REALSXP); return data; } diff --git a/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.c b/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.c index 3d88384fb1884329d4ecc2119451e47704ea5633..43f13021a505226076c73c73e19162c98ce54186 100644 --- a/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.c +++ b/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.c @@ -30,8 +30,7 @@ * that needs to be saved for reuse in the many R functions such as Rf_allocVector. * Currently only single threaded access is permitted (via a semaphore in CallRFFIWithJNI) * so we are safe to use static variables. TODO Figure out where to store such state - * (portably) for MT use. JNI provides no help. N.B. The MT restriction also precludes - * recursive calls. + * (portably) for MT use. JNI provides no help. */ jclass CallRFFIHelperClass; jclass RDataFactoryClass; @@ -54,26 +53,37 @@ static int alwaysUseGlobal = 0; static SEXP *cachedGlobalRefs; static int cachedGlobalRefsLength; -typedef struct CopiedVectors_struct { +// Data structure for managing the required copying of +// Java arrays to return C arrays, e.g, int*. +// N.B. There are actually two levels to this as FastR +// wraps, e.g., int[] in an RIntVector. +typedef struct nativeArrayTable_struct { SEXPTYPE type; - SEXP obj; - void *jArray; - void *data; -} CopiedVector; + SEXP obj; // The jobject (SEXP) that data is derived from (e.g, RIntVector) + void *jArray; // the jarray corresponding to obj + void *data; // the (possibly) copied (or pinned) data from JNI GetXXXArrayElements +} NativeArrayElem; -#define COPIED_VECTORS_INITIAL_SIZE 64 +#define NATIVE_ARRAY_TABLE_INITIAL_SIZE 64 // A table of vectors that have been accessed and whose contents, e.g. the actual data // as a primitive array have been copied and handed out to the native code. -static CopiedVector *copiedVectors; -// hwm of copiedVectors -static int copiedVectorsIndex; -static int copiedVectorsLength; +static NativeArrayElem *nativeArrayTable; +// hwm of nativeArrayTable +static int nativeArrayTableHwm; +static int nativeArrayTableLength; +static void releaseNativeArray(JNIEnv *env, int index); static int isEmbedded = 0; void setEmbedded() { isEmbedded = 1; } +// native down call depth, indexes nativeArrayTableHwmStack +int callDepth; + +#define NATIVE_ARRAY_TABLE_HWM_STACK_SIZE 16 +int nativeArrayTableHwmStack[NATIVE_ARRAY_TABLE_HWM_STACK_SIZE] ; + void init_utils(JNIEnv *env) { curenv = env; if (TRACE_ENABLED && traceFile == NULL) { @@ -92,6 +102,8 @@ void init_utils(JNIEnv *env) { fprintf(stderr, "%s, %d", "failed to fdopen trace file on JNI side\n", errno); exit(1); } + // no buffering + setvbuf(traceFile, (char*) NULL, _IONBF, 0); } } RDataFactoryClass = checkFindClass(env, "com/oracle/truffle/r/runtime/data/RDataFactory"); @@ -103,9 +115,9 @@ void init_utils(JNIEnv *env) { validateMethodID = checkGetMethodID(env, CallRFFIHelperClass, "validate", "(Ljava/lang/Object;)Ljava/lang/Object;", 1); cachedGlobalRefs = calloc(CACHED_GLOBALREFS_INITIAL_SIZE, sizeof(SEXP)); cachedGlobalRefsLength = CACHED_GLOBALREFS_INITIAL_SIZE; - copiedVectors = calloc(COPIED_VECTORS_INITIAL_SIZE, sizeof(CopiedVector)); - copiedVectorsLength = COPIED_VECTORS_INITIAL_SIZE; - copiedVectorsIndex = 0; + nativeArrayTable = calloc(NATIVE_ARRAY_TABLE_INITIAL_SIZE, sizeof(NativeArrayElem)); + nativeArrayTableLength = NATIVE_ARRAY_TABLE_INITIAL_SIZE; + nativeArrayTableHwm = 0; } const char *stringToChars(JNIEnv *jniEnv, jstring string) { @@ -123,121 +135,187 @@ const char *stringToChars(JNIEnv *jniEnv, jstring string) { void callEnter(JNIEnv *env, jmp_buf *jmpbuf) { setEnv(env); callErrorJmpBuf = jmpbuf; -// printf("callEnter\n"); + if (callDepth >= NATIVE_ARRAY_TABLE_HWM_STACK_SIZE) { + fatalError("call stack overflow\n"); + } + nativeArrayTableHwmStack[callDepth] = nativeArrayTableHwm; + callDepth++; } jmp_buf *getErrorJmpBuf() { return callErrorJmpBuf; } -void releaseCopiedVector(JNIEnv *env, CopiedVector cv) { - if (cv.obj != NULL) { - switch (cv.type) { - case INTSXP: { - jintArray intArray = (jintArray) cv.jArray; - (*env)->ReleaseIntArrayElements(env, intArray, (jint *)cv.data, 0); - break; - } - - case LGLSXP: { - // for LOGICAL, we need to convert back to 1-byte elements - jintArray byteArray = (jbyteArray) cv.jArray; - int len = (*env)->GetArrayLength(env, byteArray); - jbyte* internalData = (*env)->GetByteArrayElements(env, byteArray, NULL); - int* data = (int*) cv.data; - for (int i = 0; i < len; i++) { - internalData[i] = data[i] == NA_INTEGER ? 255 : (jbyte) data[i]; - } - (*env)->ReleaseByteArrayElements(env, byteArray, internalData, 0); - break; - } - - case REALSXP: { - jdoubleArray doubleArray = (jdoubleArray) cv.jArray; - (*env)->ReleaseDoubleArrayElements(env, doubleArray, (jdouble *)cv.data, 0); - break; - - } - - case RAWSXP: { - jbyteArray byteArray = (jbyteArray) cv.jArray; - (*env)->ReleaseByteArrayElements(env, byteArray, (jbyte *)cv.data, 0); - break; - - } - default: - fatalError("copiedVector type"); - } - } -} - void callExit(JNIEnv *env) { -// fprintf(traceFile, "callExit\n"); - int i; - for (i = 0; i < copiedVectorsIndex; i++) { - releaseCopiedVector(env, copiedVectors[i]); + int oldHwm = nativeArrayTableHwmStack[callDepth - 1]; + for (int i = oldHwm; i < nativeArrayTableHwm; i++) { + releaseNativeArray(env, i); } - copiedVectorsIndex = 0; + nativeArrayTableHwm = oldHwm; + callDepth--; } -void invalidateCopiedObject(JNIEnv *env, SEXP oldObj) { +void invalidateNativeArray(JNIEnv *env, SEXP oldObj) { int i; - for (i = 0; i < copiedVectorsIndex; i++) { - CopiedVector cv = copiedVectors[i]; + for (i = 0; i < nativeArrayTableHwm; i++) { + NativeArrayElem cv = nativeArrayTable[i]; if ((*env)->IsSameObject(env, cv.obj, oldObj)) { -#if TRACE_COPIES - fprintf(traceFile, "invalidateCopiedObject(%p): found\n", oldObj); +#if TRACE_NATIVE_ARRAYS + fprintf(traceFile, "invalidateNativeArray(%p): found\n", oldObj); #endif - releaseCopiedVector(env, cv); - copiedVectors[i].obj = NULL; + releaseNativeArray(env, &cv); + nativeArrayTable[i].obj = NULL; } } -#if TRACE_COPIES - fprintf(traceFile, "invalidateCopiedObject(%p): not found\n", oldObj); +#if TRACE_NATIVE_ARRAYS + fprintf(traceFile, "invalidateNativeArray(%p): not found\n", oldObj); #endif } -void *findCopiedObject(JNIEnv *env, SEXP x) { +static void *findNativeArray(JNIEnv *env, SEXP x) { int i; - for (i = 0; i < copiedVectorsIndex; i++) { - CopiedVector cv = copiedVectors[i]; - if ((*env)->IsSameObject(env, cv.obj, x)) { - void *data = cv.data; -#if TRACE_COPIES - fprintf(traceFile, "findCopiedObject(%p): found %p\n", x, data); + for (i = 0; i < nativeArrayTableHwm; i++) { + NativeArrayElem cv = nativeArrayTable[i]; + if (cv.obj != NULL) { + if ((*env)->IsSameObject(env, cv.obj, x)) { + void *data = cv.data; +#if TRACE_NATIVE_ARRAYS + fprintf(traceFile, "findNativeArray(%p): found %p\n", x, data); #endif - return data; + return data; + } } } -#if TRACE_COPIES - fprintf(traceFile, "findCopiedObject(%p): not found\n", x); +#if TRACE_NATIVE_ARRAYS + fprintf(traceFile, "findNativeArray(%p): not found\n", x); #endif return NULL; } -void addCopiedObject(JNIEnv *env, SEXP x, SEXPTYPE type, void *jArray, void *data) { -#if TRACE_COPIES - fprintf(traceFile, "addCopiedObject(%p, %p)\n", x, data); +static void addNativeArray(JNIEnv *env, SEXP x, SEXPTYPE type, void *jArray, void *data) { +#if TRACE_NATIVE_ARRAYS + fprintf(traceFile, "addNativeArray(x=%p, t=%p, ix=%d)\n", x, data, nativeArrayTableHwm); #endif - if (copiedVectorsIndex >= copiedVectorsLength) { - int newLength = 2 * copiedVectorsLength; - CopiedVector *newCopiedVectors = calloc(newLength, sizeof(CopiedVector)); - if (newCopiedVectors == NULL) { + // check for overflow + if (nativeArrayTableHwm >= nativeArrayTableLength) { + int newLength = 2 * nativeArrayTableLength; + NativeArrayElem *newnativeArrayTable = calloc(newLength, sizeof(NativeArrayElem)); + if (newnativeArrayTable == NULL) { fatalError("FFI copied vectors table expansion failure"); } - memcpy(newCopiedVectors, copiedVectors, copiedVectorsLength * sizeof(CopiedVector)); - free(copiedVectors); - copiedVectors = newCopiedVectors; - copiedVectorsLength = newLength; + memcpy(newnativeArrayTable, nativeArrayTable, nativeArrayTableLength * sizeof(NativeArrayElem)); + free(nativeArrayTable); + nativeArrayTable = newnativeArrayTable; + nativeArrayTableLength = newLength; } - copiedVectors[copiedVectorsIndex].obj = x; - copiedVectors[copiedVectorsIndex].data = data; - copiedVectors[copiedVectorsIndex].type = type; - copiedVectors[copiedVectorsIndex].jArray = jArray; - copiedVectorsIndex++; -#if TRACE_COPIES - fprintf(traceFile, "copiedVectorsIndex: %d\n", copiedVectorsIndex); + nativeArrayTable[nativeArrayTableHwm].obj = x; + nativeArrayTable[nativeArrayTableHwm].data = data; + nativeArrayTable[nativeArrayTableHwm].type = type; + nativeArrayTable[nativeArrayTableHwm].jArray = jArray; + nativeArrayTableHwm++; +} + +void *getNativeArray(JNIEnv *thisenv, SEXP x, SEXPTYPE type) { + void *data = findNativeArray(thisenv, x); + jboolean isCopy; + if (data == NULL) { + jarray jArray; + switch (type) { + case INTSXP: { + jintArray intArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, INTEGER_MethodID, x); + int len = (*thisenv)->GetArrayLength(thisenv, intArray); + data = (*thisenv)->GetIntArrayElements(thisenv, intArray, &isCopy); + jArray = intArray; + break; + } + + case REALSXP: { + jdoubleArray doubleArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, REAL_MethodID, x); + int len = (*thisenv)->GetArrayLength(thisenv, doubleArray); + data = (*thisenv)->GetDoubleArrayElements(thisenv, doubleArray, &isCopy); + jArray = doubleArray; + break; + } + + case RAWSXP: { + jbyteArray byteArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, RAW_MethodID, x); + int len = (*thisenv)->GetArrayLength(thisenv, byteArray); + data = (*thisenv)->GetByteArrayElements(thisenv, byteArray, &isCopy); + jArray = byteArray; + break; + } + + case LGLSXP: { + // Special treatment becuase R FFI wants int* and FastR represents using byte[] + jbyteArray byteArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, LOGICAL_MethodID, x); + int len = (*thisenv)->GetArrayLength(thisenv, byteArray); + jbyte* internalData = (*thisenv)->GetByteArrayElements(thisenv, byteArray, &isCopy); + int* idata = malloc(len * sizeof(int)); + for (int i = 0; i < len; i++) { + char value = internalData[i]; + idata[i] = value == 0 ? FALSE : value == 1 ? TRUE : NA_INTEGER; + } + (*thisenv)->ReleaseByteArrayElements(thisenv, byteArray, internalData, JNI_ABORT); + jArray = byteArray; + data = idata; + break; + } + + default: + fatalError("getNativeArray: unexpected type"); + + } + addNativeArray(thisenv, x, type, jArray, data); + } + return data; +} + +static void releaseNativeArray(JNIEnv *env, int i) { + NativeArrayElem cv = nativeArrayTable[i]; +#if TRACE_NATIVE_ARRAYS + fprintf(traceFile, "releaseNativeArray(x=%p, ix=%d)\n", cv.obj, i); #endif + if (cv.obj != NULL) { + switch (cv.type) { + case INTSXP: { + jintArray intArray = (jintArray) cv.jArray; + (*env)->ReleaseIntArrayElements(env, intArray, (jint *)cv.data, 0); + break; + } + + case LGLSXP: { + // for LOGICAL, we need to convert back to 1-byte elements + jintArray byteArray = (jbyteArray) cv.jArray; + int len = (*env)->GetArrayLength(env, byteArray); + jbyte* internalData = (*env)->GetByteArrayElements(env, byteArray, NULL); + int* data = (int*) cv.data; + for (int i = 0; i < len; i++) { + internalData[i] = data[i] == NA_INTEGER ? 255 : (jbyte) data[i]; + } + (*env)->ReleaseByteArrayElements(env, byteArray, internalData, 0); + free(data); // was malloc'ed in addNativeArray + break; + } + + case REALSXP: { + jdoubleArray doubleArray = (jdoubleArray) cv.jArray; + (*env)->ReleaseDoubleArrayElements(env, doubleArray, (jdouble *)cv.data, 0); + break; + + } + + case RAWSXP: { + jbyteArray byteArray = (jbyteArray) cv.jArray; + (*env)->ReleaseByteArrayElements(env, byteArray, (jbyte *)cv.data, 0); + break; + + } + default: + fatalError("releaseNativeArray type"); + } + // free up the slot + cv.obj = NULL; + } } static SEXP checkCachedGlobalRef(JNIEnv *env, SEXP obj, int useGlobal) { diff --git a/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.h b/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.h index ee3ee6a5b7c9e200364beb863db28c61f9c19bee..62da16cdcf08af4579691d82e5b5d7b427c627f7 100644 --- a/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.h +++ b/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.h @@ -62,11 +62,12 @@ void allocExit(); jmp_buf *getErrorJmpBuf(); -// find an object for which we have cached the internal rep -void *findCopiedObject(JNIEnv *env, SEXP x); -// add a new object to the internal rep cache -void addCopiedObject(JNIEnv *env, SEXP x, SEXPTYPE type, void *jArray, void *data); -void invalidateCopiedObject(JNIEnv *env, SEXP oldObj); +// Given the x denotes an R vector type, return a pointer to +// the data as a C array +void *getNativeArray(JNIEnv *env, SEXP x, SEXPTYPE type); +// Rare case where an operation changes the internal +// data and thus the old C array should be invalidated +void invalidateNativeArray(JNIEnv *env, SEXP oldObj); void init_rmath(JNIEnv *env); void init_variables(JNIEnv *env, jobjectArray initialValues); @@ -86,10 +87,10 @@ extern jclass RRuntimeClass; extern FILE *traceFile; // tracing/debugging support, set to 1 and recompile to enable -#define TRACE_UPCALLS 1 // trace upcalls +#define TRACE_UPCALLS 0 // trace upcalls #define TRACE_REF_CACHE 0 // trace JNI reference cache -#define TRACE_COPIES 0 // trace copying of internal arrays -#define TRACE_ENABLED TRACE_UPCALLS || TRACE_REF_CACHE || TRACE_COPIES +#define TRACE_NATIVE_ARRAYS 0 // trace generation of internal arrays +#define TRACE_ENABLED TRACE_UPCALLS || TRACE_REF_CACHE || TRACE_NATIVE_ARRAYS #define TARGp "%s(%p)\n" #define TARGpp "%s(%p, %p)\n" @@ -111,4 +112,11 @@ extern FILE *traceFile; // convert a string into a char* const char *stringToChars(JNIEnv *jniEnv, jstring string); +extern jmethodID INTEGER_MethodID; +extern jmethodID LOGICAL_MethodID; +extern jmethodID REAL_MethodID; +extern jmethodID RAW_MethodID; + +extern int callDepth; + #endif /* RFFIUTILS_H */ diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Gc.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Gc.java index 6de029a68e68a984b81fcd88e9c16c1421cbc9a7..c2f7807c20c3beed3310ea433b4780382dab178e 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Gc.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Gc.java @@ -40,9 +40,7 @@ public abstract class Gc extends RBuiltinNode { @SuppressWarnings("unused") @Specialization protected RDoubleVector gc(RAbstractLogicalVector verbose, RAbstractLogicalVector reset) { - // manually triggering gc in Java is typically not a terribly good idea so we don't do it - // here at all - + System.gc(); // TODO: somehow produce the (semi?) correct values double[] data = new double[14]; Arrays.fill(data, RRuntime.DOUBLE_NA); diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/RFFIUtils.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/RFFIUtils.java index 07e8dfbf1bd9c0c7335de2aee6b139a4e18e67d8..227c57c707f1194a82ff8aacab27d17ae03d6a09 100644 --- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/RFFIUtils.java +++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/RFFIUtils.java @@ -25,9 +25,12 @@ package com.oracle.truffle.r.runtime.ffi; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; -import java.io.PrintStream; +import java.io.OutputStream; +import java.nio.file.Path; + import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.FastROptions; +import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RPairList; import com.oracle.truffle.r.runtime.data.RSymbol; @@ -58,24 +61,20 @@ public class RFFIUtils { * In embedded mode can't trust that cwd is writeable, so output placed in /tmp. Also, tag with * time in event of multiple concurrent instances (which happens with RStudio). */ - private static final String tracePathPrefix = "/tmp/fastr_trace_nativecalls.log-"; - private static FileOutputStream traceFileStream; - private static PrintStream traceStream; + private static final String TRACEFILE = "fastr_trace_nativecalls.log"; + private static OutputStream traceStream; + /** + * Records the call depth. TBD: make context specific + */ + private static int depth; - private static void initialize() { + public static void initialize() { if (!initialized) { traceEnabled = alwaysTrace || FastROptions.TraceNativeCalls.getBooleanValue(); if (traceEnabled) { if (RContext.isEmbedded()) { if (traceStream == null) { - String tracePath = tracePathPrefix + Long.toString(System.currentTimeMillis()); - try { - traceFileStream = new FileOutputStream(tracePath); - traceStream = new PrintStream(traceFileStream); - } catch (IOException ex) { - System.err.println(ex.getMessage()); - System.exit(1); - } + initTraceStream(); } } else { traceStream = System.out; @@ -85,14 +84,28 @@ public class RFFIUtils { } } + private static void initTraceStream() { + Path tracePath = Utils.getLogPath(TRACEFILE); + try { + traceStream = new FileOutputStream(tracePath.toString()); + } catch (IOException ex) { + System.err.println(ex.getMessage()); + System.exit(1); + } + } + /** - * Upcalled from native when tracing to get FD of the {@link #traceFileStream}. Allows the same - * fd to be used on both sides of the JNI boundary. + * Upcalled from native when tracing to get FD of the {@link #traceStream}. Allows the same fd + * to be used on both sides of the JNI boundary. */ @SuppressWarnings("unused") private static FileDescriptor getTraceFileDescriptor() { try { - return traceFileStream.getFD(); + if (traceStream == null) { + // Happens if native has tracing enabled and Java does not + initTraceStream(); + } + return ((FileOutputStream) traceStream).getFD(); } catch (IOException ex) { System.err.println(ex.getMessage()); System.exit(1); @@ -101,10 +114,10 @@ public class RFFIUtils { } private enum CallMode { - UP("Up"), - UP_RETURN("UpReturn"), - DOWN("Down"), - DOWN_RETURN("DownReturn"); + UP("U"), + UP_RETURN("UR"), + DOWN("D"), + DOWN_RETURN("DR"); private final String printName; @@ -114,34 +127,45 @@ public class RFFIUtils { } public static void traceUpCall(String name, Object... args) { - traceCall(CallMode.UP, name, args); + traceCall(CallMode.UP, name, depth, args); } - public static void traceUpCallReturn(String name, Object... args) { - traceCall(CallMode.UP_RETURN, name, args); + public static void traceUpCallReturn(String name, Object result) { + traceCall(CallMode.UP_RETURN, name, depth, result); } public static void traceDownCall(String name, Object... args) { - traceCall(CallMode.DOWN, name, args); + traceCall(CallMode.DOWN, name, ++depth, args); + } + + public static void traceDownCallReturn(String name, Object result) { + traceCall(CallMode.DOWN_RETURN, name, depth--, result); } public static boolean traceEnabled() { return traceEnabled; } - private static void traceCall(CallMode mode, String name, Object... args) { - initialize(); + private static void traceCall(CallMode mode, String name, int depthValue, Object... args) { + assert initialized; if (traceEnabled) { StringBuffer sb = new StringBuffer(); sb.append("CallRFFI["); sb.append(mode.printName); + sb.append(':'); + sb.append(depthValue); sb.append(']'); sb.append(name); sb.append('('); printArgs(sb, args); sb.append(')'); - traceStream.println(sb.toString()); - traceStream.flush(); + try { + traceStream.write(sb.toString().getBytes()); + traceStream.write('\n'); + traceStream.flush(); + } catch (IOException ex) { + // ignore + } } } diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNI_CallRFFI.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNI_CallRFFI.java index 0964f60ebd84d23a2fd8a7b28791755277029172..eb25283160677833f15438587ba2057b938d8b54 100644 --- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNI_CallRFFI.java +++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNI_CallRFFI.java @@ -23,8 +23,8 @@ package com.oracle.truffle.r.runtime.ffi.jnr; import static com.oracle.truffle.r.runtime.ffi.RFFIUtils.traceDownCall; - -import java.util.concurrent.Semaphore; +import static com.oracle.truffle.r.runtime.ffi.RFFIUtils.traceDownCallReturn; +import static com.oracle.truffle.r.runtime.ffi.RFFIUtils.traceEnabled; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.RInternalError; @@ -32,6 +32,7 @@ import com.oracle.truffle.r.runtime.ffi.CallRFFI; import com.oracle.truffle.r.runtime.ffi.DLL; import com.oracle.truffle.r.runtime.ffi.DLL.DLLException; import com.oracle.truffle.r.runtime.ffi.LibPaths; +import com.oracle.truffle.r.runtime.ffi.RFFIUtils; import com.oracle.truffle.r.runtime.ffi.RFFIVariables; /** @@ -66,37 +67,48 @@ public class JNI_CallRFFI implements CallRFFI { throw new RInternalError(ex, "error while loading " + librffiPath); } System.load(librffiPath); - traceDownCall("initialize"); - initialize(RFFIVariables.values()); - } + RFFIUtils.initialize(); + if (traceEnabled()) { + traceDownCall("initialize"); + } + try { + initialize(RFFIVariables.values()); + } finally { + if (traceEnabled()) { + traceDownCallReturn("initialize", null); + } - private static final Semaphore inCritical = new Semaphore(1, false); + } + } @Override - public Object invokeCall(long address, String name, Object[] args) { - traceDownCall(name, args); + public synchronized Object invokeCall(long address, String name, Object[] args) { + Object result = null; + if (traceEnabled()) { + traceDownCall(name, args); + } try { - inCritical.acquire(); switch (args.length) { // @formatter:off - case 0: return call0(address); - case 1: return call1(address, args[0]); - case 2: return call2(address, args[0], args[1]); - case 3: return call3(address, args[0], args[1], args[2]); - case 4: return call4(address, args[0], args[1], args[2], args[3]); - case 5: return call5(address, args[0], args[1], args[2], args[3], args[4]); - case 6: return call6(address, args[0], args[1], args[2], args[3], args[4], args[5]); - case 7: return call7(address, args[0], args[1], args[2], args[3], args[4], args[5], args[6]); - case 8: return call8(address, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); - case 9: return call9(address, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); + case 0: result = call0(address); break; + case 1: result = call1(address, args[0]); break; + case 2: result = call2(address, args[0], args[1]); break; + case 3: result = call3(address, args[0], args[1], args[2]); break; + case 4: result = call4(address, args[0], args[1], args[2], args[3]); break; + case 5: result = call5(address, args[0], args[1], args[2], args[3], args[4]); break; + case 6: result = call6(address, args[0], args[1], args[2], args[3], args[4], args[5]); break; + case 7: result = call7(address, args[0], args[1], args[2], args[3], args[4], args[5], args[6]); break; + case 8: result = call8(address, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); break; + case 9: result = call9(address, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); break; default: - return call(address, args); + result = call(address, args); break; // @formatter:on } - } catch (InterruptedException ex) { - throw RInternalError.shouldNotReachHere(); + return result; } finally { - inCritical.release(); + if (traceEnabled()) { + traceDownCallReturn(name, result); + } } } @@ -129,10 +141,11 @@ public class JNI_CallRFFI implements CallRFFI { private static native Object call9(long address, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9); @Override - public void invokeVoidCall(long address, String name, Object[] args) { - traceDownCall(name, args); + public synchronized void invokeVoidCall(long address, String name, Object[] args) { + if (traceEnabled()) { + traceDownCall(name, args); + } try { - inCritical.acquire(); switch (args.length) { case 0: callVoid0(address); @@ -143,9 +156,10 @@ public class JNI_CallRFFI implements CallRFFI { default: throw RInternalError.shouldNotReachHere(); } - } catch (InterruptedException ex) { } finally { - inCritical.release(); + if (traceEnabled()) { + traceDownCallReturn(name, null); + } } } @@ -154,27 +168,25 @@ public class JNI_CallRFFI implements CallRFFI { private static native void callVoid1(long address, Object arg1); @Override - public void setTempDir(String tempDir) { - traceDownCall("setTempDir", tempDir); - try { - inCritical.acquire(); - RFFIVariables.setTempDir(tempDir); - nativeSetTempDir(tempDir); - } catch (InterruptedException ex) { - } finally { - inCritical.release(); + public synchronized void setTempDir(String tempDir) { + if (traceEnabled()) { + traceDownCall("setTempDir", tempDir); + } + RFFIVariables.setTempDir(tempDir); + nativeSetTempDir(tempDir); + if (traceEnabled()) { + traceDownCallReturn("setTempDir", null); } } @Override - public void setInteractive(boolean interactive) { - traceDownCall("setInteractive", interactive); - try { - inCritical.acquire(); - nativeSetInteractive(interactive); - } catch (InterruptedException ex) { - } finally { - inCritical.release(); + public synchronized void setInteractive(boolean interactive) { + if (traceEnabled()) { + traceDownCall("setInteractive", interactive); + } + nativeSetInteractive(interactive); + if (traceEnabled()) { + traceDownCallReturn("setInteractive", null); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java index b4c1b37c54a42b09e97e252c2ff092e7a8fcf312..9437a68768d529f3211d64ddd7b0f976ff3e7f5b 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java @@ -88,10 +88,6 @@ public final class RCmdOptions { SLAVE(RCmdOptionType.BOOLEAN, true, "slave", false, "Make R run as quietly as possible"), INTERACTIVE(RCmdOptionType.BOOLEAN, true, "interactive", false, "Force an interactive session"), VERBOSE(RCmdOptionType.BOOLEAN, true, "verbose", false, "Print more information about progress"), - DEBUGGER(RCmdOptionType.STRING, true, "d", "debugger=NAME", null, "Run R through debugger NAME"), - DEBUGGER_ARGS(RCmdOptionType.STRING, false, "debugger-args=ARGS", null, "Pass ARGS as arguments to the debugger"), - GUI(RCmdOptionType.STRING, false, "g TYPE", "gui=TYPE", null, "Use TYPE as GUI; possible values are 'X11' (default)\nand 'Tk'."), - ARCH(RCmdOptionType.STRING, false, "arch=NAME", null, "Specify a sub-architecture"), ARGS(RCmdOptionType.BOOLEAN, true, "args", false, "Skip the rest of the command line"), FILE(RCmdOptionType.STRING, true, "f FILE", "file=FILE", null, "Take input from 'FILE'"), EXPR(RCmdOptionType.REPEATED_STRING, true, "e EXPR", null, null, "Execute 'EXPR' and exit"), diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java index 8208e1a3c4f43fc69596d5e91f8dfa118422e5ee..17fcab5f0bede00e64fd466d767abf374976494a 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java @@ -29,13 +29,14 @@ import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.charset.StandardCharsets; -import java.nio.file.FileSystems; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Date; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.r.runtime.context.RContext; /** * This class is intended to be used for internal errors that do not correspond to R errors. @@ -149,7 +150,8 @@ public final class RInternalError extends Error { System.err.println(verboseStackTrace); } if (FastROptions.PrintErrorStacktracesToFile.getBooleanValue()) { - try (BufferedWriter writer = Files.newBufferedWriter(FileSystems.getDefault().getPath(REnvVars.rHome(), "fastr_errors.log"), StandardCharsets.UTF_8, StandardOpenOption.APPEND, + Path logfile = Utils.getLogPath("fastr_errors.log"); + try (BufferedWriter writer = Files.newBufferedWriter(logfile, StandardCharsets.UTF_8, StandardOpenOption.APPEND, StandardOpenOption.CREATE)) { writer.append(new Date().toString()).append('\n'); writer.append(out.toString()).append('\n'); @@ -157,6 +159,9 @@ public final class RInternalError extends Error { } catch (IOException e) { e.printStackTrace(); } + if (RContext.isEmbedded()) { + Utils.rSuicide("FastR internal error"); + } } } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java index c4b71e1c6b205d6762a01225d0694ddaa127c116..b73330f0497ebdce00c34bfa10c358399daf30b0 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java @@ -153,21 +153,17 @@ public final class Utils { * polyglot context are. */ RPerfStats.report(); - if (RContext.getInstance() != null && RContext.getInstance().getStartParams() != null && RContext.getInstance().getStartParams().getDebugInitFile()) { - throw new DebugExitException(); - } else { - try { - /* - * This is not the proper way to dispose a PolyglotEngine, but it doesn't matter - * since we're going to System.exit anyway. - */ - RContext.getInstance().destroy(); - } catch (Throwable t) { - // ignore - } - System.exit(status); - return null; + try { + /* + * This is not the proper way to dispose a PolyglotEngine, but it doesn't matter since + * we're going to System.exit anyway. + */ + RContext.getInstance().destroy(); + } catch (Throwable t) { + // ignore } + System.exit(status); + return null; } public static RuntimeException fail(String msg) { @@ -250,6 +246,17 @@ public final class Utils { wdState().setCurrent(path); } + /** + * Returns a {@link Path} for a log file with base name {@code fileName}, taking into account + * whether the system is running in embedded mode. + */ + public static Path getLogPath(String fileName) { + String root = RContext.isEmbedded() ? "/tmp" : REnvVars.rHome(); + int pid = RFFIFactory.getRFFI().getBaseRFFI().getpid(); + String baseName = RContext.isEmbedded() ? fileName + "-" + Integer.toString(pid) : fileName; + return FileSystems.getDefault().getPath(root, baseName); + } + /** * Performs "~" expansion and also checks whether we need to take special case over relative * paths due to the curwd having moved from the initial setting. In the latter case, if the path diff --git a/com.oracle.truffle.r.test.native/embedded/src/main.c b/com.oracle.truffle.r.test.native/embedded/src/main.c index df885026aeca182dd0357a14c0a86df0efee84fa..5e89d7595d912d11fc3bab1babd3652497ac4112 100644 --- a/com.oracle.truffle.r.test.native/embedded/src/main.c +++ b/com.oracle.truffle.r.test.native/embedded/src/main.c @@ -70,6 +70,7 @@ void testR_CleanUp(SA_TYPE x, int y, int z) { void testR_Suicide(const char *msg) { printf("testR_Suicide: %s\n",msg); + (ptr_stdR_Suicide(msg)); } int testR_ReadConsole(const char *prompt, unsigned char *buf, int len, int h) { @@ -101,6 +102,7 @@ int main(int argc, char **argv) { R_SetParams(Rp); ptr_stdR_CleanUp = ptr_R_CleanUp; ptr_R_CleanUp = &testR_CleanUp; + ptr_stdR_Suicide = ptr_R_Suicide; ptr_R_Suicide = &testR_Suicide; ptr_R_ReadConsole = &testR_ReadConsole; ptr_R_WriteConsole = &testR_WriteConsole; diff --git a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R index 8df3af80f21eedb13da8598f4cb8a6c7d4a64881..d0f3fe92421d46382610fa11dec26a610ee811aa 100644 --- a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R +++ b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R @@ -58,3 +58,16 @@ rffi.rhome_dir <- function() { .Call("rHomeDir", PACKAGE = "testrffi") } +rffi.upcalled <- function(v) { + gc() + .Call("nestedCall2", PACKAGE = "testrffi", v) +} + +rffi.nested.call1 <- function() { + upcall <- quote(rffi.upcalled(v)) + v <- c(10L, 20L, 30L) + env <- new.env() + assign("v", v, env) + .Call("nestedCall1", PACKAGE = "testrffi", upcall, env) +} + diff --git a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c index aadc29d3f13cb4b668d007a7f31be44c5c464a69..6bf9acba57df7e2a5eaa0ee76cf4135ca6cc0d2d 100644 --- a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c +++ b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c @@ -150,7 +150,7 @@ SEXP tryEval(SEXP expr, SEXP env) { } SET_VECTOR_ELT(v, 0, r); SET_VECTOR_ELT(v, 1, ScalarLogical(error)); - UNPROTECT(v); + UNPROTECT(1); return v; } @@ -159,3 +159,42 @@ SEXP rHomeDir() { return ScalarString(mkChar(dir)); } +SEXP nestedCall1(SEXP upcall, SEXP env) { + SEXP vec; + PROTECT(vec = allocVector(INTSXP, 10)); + int *vecstar = INTEGER(vec); + for (int i = 0; i < 10; i++) { + vecstar[i] = i + 1; + } + SEXP upcallResult = tryEval(upcall, env); + int *vecstar2 = INTEGER(vec); + int ok = vecstar == vecstar2; + if (ok) { + for (int i = 0; i < 10; i++) { + if (vecstar[i] != i + 1) { + ok = 0; + break; + } + } + } + SEXP result; + PROTECT(result = allocVector(VECSXP, 2)); + SET_VECTOR_ELT(result, 0, upcallResult); + SET_VECTOR_ELT(result, 1, ScalarLogical(ok)); + UNPROTECT(2); + return result; +} + +SEXP nestedCall2(SEXP v) { + SEXP sumVec; + PROTECT(sumVec = allocVector(INTSXP, 1)); + int len = Rf_length(v); + int sum = 0; + for (int i = 0; i < len; i++) { + sum += INTEGER(v)[i]; + } + INTEGER(sumVec)[0] = sum; + UNPROTECT(1); + return sumVec; +} +