1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Add some JNI-internal metrics, accessible via passing -v when running Tester1.java. Document an OpenJDK bug which leads to incorrect -Xlint:jni warnings.

FossilOrigin-Name: a5d68a6b64abe3c2dfc3a32157f70fd8a4ad89feef2510b3bbb2d86b325d51ae
This commit is contained in:
stephan
2023-07-31 12:10:32 +00:00
parent 8f714e21a0
commit e3e9bdcd0b
4 changed files with 83 additions and 19 deletions

View File

@ -145,6 +145,7 @@ $(sqlite3-jni.dll): $(dir.bld.c) $(sqlite3-jni.c) $(SQLite3Jni.java) $(MAKEFILE)
$(sqlite3-jni.c) -shared -o $@ $(sqlite3-jni.c) -shared -o $@
all: $(sqlite3-jni.dll) all: $(sqlite3-jni.dll)
test.flags ?= -v
test: $(SQLite3Jni.class) $(sqlite3-jni.dll) test: $(SQLite3Jni.class) $(sqlite3-jni.dll)
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \ $(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
$(java.flags) -cp $(classpath) \ $(java.flags) -cp $(classpath) \

View File

@ -13,6 +13,17 @@
** org.sqlite.jni.SQLiteJni (from which sqlite3-jni.h is generated). ** org.sqlite.jni.SQLiteJni (from which sqlite3-jni.h is generated).
*/ */
/**
If you found this comment by searching the code for
CallStaticObjectMethod then you're the victim of an OpenJDK bug:
https://bugs.openjdk.org/browse/JDK-8130659
It's known to happen with OpenJDK v8 but not with v19.
This code does not use JNI's CallStaticObjectMethod().
*/
/* /*
** Define any SQLITE_... config defaults we want if they aren't ** Define any SQLITE_... config defaults we want if they aren't
** overridden by the builder. Please keep these alphabetized. ** overridden by the builder. Please keep these alphabetized.
@ -158,6 +169,10 @@
(*env)->ExceptionDescribe(env) (*env)->ExceptionDescribe(env)
#define IFTHREW_REPORT IFTHREW EXCEPTION_REPORT #define IFTHREW_REPORT IFTHREW EXCEPTION_REPORT
#define IFTHREW_CLEAR IFTHREW EXCEPTION_CLEAR #define IFTHREW_CLEAR IFTHREW EXCEPTION_CLEAR
#define EXCEPTION_CANNOT_HAPPEN IFTHREW{\
EXCEPTION_REPORT; \
(*env)->FatalError(env,"This \"cannot happen\"."); \
}
/** Helpers for extracting pointers from jobjects, noting that the /** Helpers for extracting pointers from jobjects, noting that the
@ -391,6 +406,20 @@ static struct {
PerDbStateJni * aUsed; PerDbStateJni * aUsed;
PerDbStateJni * aFree; PerDbStateJni * aFree;
} perDb; } perDb;
struct {
unsigned nphCacheHits;
unsigned nphCacheMisses;
unsigned envCacheHits;
unsigned envCacheMisses;
unsigned nDestroy;
struct {
unsigned nFunc;
unsigned nStep;
unsigned nFinal;
unsigned nValue;
unsigned nInverse;
} udf;
} metrics;
} S3Global; } S3Global;
/** /**
@ -455,6 +484,7 @@ static void s3jni_call_xDestroy(JNIEnv *env, jobject jObj, jclass klazz){
method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V"); method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V");
//MARKER(("jObj=%p, klazz=%p, method=%p\n", jObj, klazz, method)); //MARKER(("jObj=%p, klazz=%p, method=%p\n", jObj, klazz, method));
if(method){ if(method){
++S3Global.metrics.nDestroy;
(*env)->CallVoidMethod(env, jObj, method); (*env)->CallVoidMethod(env, jObj, method);
IFTHREW{ IFTHREW{
EXCEPTION_WARN_CALLBACK_THREW; EXCEPTION_WARN_CALLBACK_THREW;
@ -472,7 +502,6 @@ static void s3jni_call_xDestroy(JNIEnv *env, jobject jObj, jclass klazz){
insofar as possible. Calls (*env)->FatalError() if the cache insofar as possible. Calls (*env)->FatalError() if the cache
fills up. That's hypothetically possible but "shouldn't happen." fills up. That's hypothetically possible but "shouldn't happen."
If it does, we can dynamically allocate these instead. If it does, we can dynamically allocate these instead.
*/ */
FIXME_THREADING FIXME_THREADING
static struct JNIEnvCacheLine * S3Global_env_cache(JNIEnv * env){ static struct JNIEnvCacheLine * S3Global_env_cache(JNIEnv * env){
@ -480,9 +509,13 @@ static struct JNIEnvCacheLine * S3Global_env_cache(JNIEnv * env){
int i = 0; int i = 0;
for( ; i < JNIEnvCache_SIZE; ++i ){ for( ; i < JNIEnvCache_SIZE; ++i ){
row = &S3Global.envCache.lines[i]; row = &S3Global.envCache.lines[i];
if(row->env == env) return row; if(row->env == env){
++S3Global.metrics.envCacheHits;
return row;
}
else if(!row->env) break; else if(!row->env) break;
} }
++S3Global.metrics.envCacheMisses;
if(i == JNIEnvCache_SIZE){ if(i == JNIEnvCache_SIZE){
(*env)->FatalError(env, "Maintenance required: JNIEnvCache is full."); (*env)->FatalError(env, "Maintenance required: JNIEnvCache is full.");
return NULL; return NULL;
@ -510,8 +543,8 @@ static struct JNIEnvCacheLine * S3Global_env_cache(JNIEnv * env){
zClassName must be a static string so we can use its address as a zClassName must be a static string so we can use its address as a
cache key. cache key.
This simple cache catches the overwhelming majority of searches This simple cache catches >99% of searches in the current
(>95%) in the current (2023-07-24) tests. (2023-07-31) tests.
*/ */
FIXME_THREADING FIXME_THREADING
static struct NphCacheLine * S3Global_nph_cache(JNIEnv *env, const char *zClassName){ static struct NphCacheLine * S3Global_nph_cache(JNIEnv *env, const char *zClassName){
@ -538,11 +571,11 @@ static struct NphCacheLine * S3Global_nph_cache(JNIEnv *env, const char *zClassN
for( i = 0; i < NphCache_SIZE; ++i ){ for( i = 0; i < NphCache_SIZE; ++i ){
cacheLine = &envRow->nph[i]; cacheLine = &envRow->nph[i];
if(zClassName == cacheLine->zClassName){ if(zClassName == cacheLine->zClassName){
++S3Global.metrics.nphCacheHits;
#define DUMP_NPH_CACHES 0 #define DUMP_NPH_CACHES 0
#if DUMP_NPH_CACHES #if DUMP_NPH_CACHES
static unsigned int n = 0;
MARKER(("Cache hit #%u %s klazz@%p nativePointer field@%p, ctor@%p\n", MARKER(("Cache hit #%u %s klazz@%p nativePointer field@%p, ctor@%p\n",
++n, zClassName, cacheLine->klazz, cacheLine->fidValue, S3Global.metrics.nphCacheHits, zClassName, cacheLine->klazz, cacheLine->fidValue,
cacheLine->midCtor)); cacheLine->midCtor));
#endif #endif
assert(cacheLine->klazz); assert(cacheLine->klazz);
@ -554,10 +587,12 @@ static struct NphCacheLine * S3Global_nph_cache(JNIEnv *env, const char *zClassN
if(freeSlot){ if(freeSlot){
freeSlot->zClassName = zClassName; freeSlot->zClassName = zClassName;
freeSlot->klazz = REF_G((*env)->FindClass(env, zClassName)); freeSlot->klazz = REF_G((*env)->FindClass(env, zClassName));
EXCEPTION_CANNOT_HAPPEN;
++S3Global.metrics.nphCacheMisses;
#if DUMP_NPH_CACHES #if DUMP_NPH_CACHES
static unsigned int cacheMisses = 0; static unsigned int cacheMisses = 0;
MARKER(("Cache miss #%u %s klazz@%p nativePointer field@%p, ctor@%p\n", MARKER(("Cache miss #%u %s klazz@%p nativePointer field@%p, ctor@%p\n",
++cacheMisses, zClassName, freeSlot->klazz, S3Global.metrics.nphCacheMisses, zClassName, freeSlot->klazz,
freeSlot->fidValue, freeSlot->midCtor)); freeSlot->fidValue, freeSlot->midCtor));
#endif #endif
#undef DUMP_NPH_CACHES #undef DUMP_NPH_CACHES
@ -756,7 +791,7 @@ static void PerDbStateJni_dump(PerDbStateJni *s){
If called with a NULL jDb and non-NULL pDb then allocIfNeeded MUST If called with a NULL jDb and non-NULL pDb then allocIfNeeded MUST
be false and it will look for a matching db object. That usage is be false and it will look for a matching db object. That usage is
required for functions, like sqlite3_context_db_handle(), which required for functions, like sqlite3_context_db_handle(), which
return a (sqlite3*). return a (sqlite3*) but do not take one as an argument.
*/ */
FIXME_THREADING FIXME_THREADING
static PerDbStateJni * PerDbStateJni_for_db(JNIEnv *env, jobject jDb, sqlite3 *pDb, int allocIfNeeded){ static PerDbStateJni * PerDbStateJni_for_db(JNIEnv *env, jobject jDb, sqlite3 *pDb, int allocIfNeeded){
@ -1161,8 +1196,7 @@ static int udf_report_exception(sqlite3_context * cx, UDFState *s,
const char *zFuncType){ const char *zFuncType){
int rc; int rc;
char * z = char * z =
sqlite3_mprintf("UDF %s.%s() threw. FIXME: extract " sqlite3_mprintf("UDF %s.%s() threw. It should not do that.",
"Java-side exception message.",
s->zFuncName, zFuncType); s->zFuncName, zFuncType);
if(z){ if(z){
sqlite3_result_error(cx, z, -1); sqlite3_result_error(cx, z, -1);
@ -1174,6 +1208,10 @@ static int udf_report_exception(sqlite3_context * cx, UDFState *s,
return rc; return rc;
} }
/**
Sets up the state for calling a Java-side xFunc/xStep/xInverse()
UDF, calls it, and returns 0 on success.
*/
static int udf_xFSI(sqlite3_context* pCx, int argc, static int udf_xFSI(sqlite3_context* pCx, int argc,
sqlite3_value** argv, sqlite3_value** argv,
UDFState * s, UDFState * s,
@ -1199,6 +1237,10 @@ static int udf_xFSI(sqlite3_context* pCx, int argc,
return rc; return rc;
} }
/**
Sets up the state for calling a Java-side xFinal/xValue() UDF,
calls it, and returns 0 on success.
*/
static int udf_xFV(sqlite3_context* cx, UDFState * s, static int udf_xFV(sqlite3_context* cx, UDFState * s,
jmethodID xMethodID, jmethodID xMethodID,
const char *zFuncType){ const char *zFuncType){
@ -1227,24 +1269,29 @@ static int udf_xFV(sqlite3_context* cx, UDFState * s,
static void udf_xFunc(sqlite3_context* cx, int argc, static void udf_xFunc(sqlite3_context* cx, int argc,
sqlite3_value** argv){ sqlite3_value** argv){
UDFState * const s = (UDFState*)sqlite3_user_data(cx); UDFState * const s = (UDFState*)sqlite3_user_data(cx);
++S3Global.metrics.udf.nFunc;
udf_xFSI(cx, argc, argv, s, s->jmidxFunc, "xFunc"); udf_xFSI(cx, argc, argv, s, s->jmidxFunc, "xFunc");
} }
static void udf_xStep(sqlite3_context* cx, int argc, static void udf_xStep(sqlite3_context* cx, int argc,
sqlite3_value** argv){ sqlite3_value** argv){
UDFState * const s = (UDFState*)sqlite3_user_data(cx); UDFState * const s = (UDFState*)sqlite3_user_data(cx);
++S3Global.metrics.udf.nStep;
udf_xFSI(cx, argc, argv, s, s->jmidxStep, "xStep"); udf_xFSI(cx, argc, argv, s, s->jmidxStep, "xStep");
} }
static void udf_xFinal(sqlite3_context* cx){ static void udf_xFinal(sqlite3_context* cx){
UDFState * const s = (UDFState*)sqlite3_user_data(cx); UDFState * const s = (UDFState*)sqlite3_user_data(cx);
++S3Global.metrics.udf.nFinal;
udf_xFV(cx, s, s->jmidxFinal, "xFinal"); udf_xFV(cx, s, s->jmidxFinal, "xFinal");
} }
static void udf_xValue(sqlite3_context* cx){ static void udf_xValue(sqlite3_context* cx){
UDFState * const s = (UDFState*)sqlite3_user_data(cx); UDFState * const s = (UDFState*)sqlite3_user_data(cx);
++S3Global.metrics.udf.nValue;
udf_xFV(cx, s, s->jmidxValue, "xValue"); udf_xFV(cx, s, s->jmidxValue, "xValue");
} }
static void udf_xInverse(sqlite3_context* cx, int argc, static void udf_xInverse(sqlite3_context* cx, int argc,
sqlite3_value** argv){ sqlite3_value** argv){
UDFState * const s = (UDFState*)sqlite3_user_data(cx); UDFState * const s = (UDFState*)sqlite3_user_data(cx);
++S3Global.metrics.udf.nInverse;
udf_xFSI(cx, argc, argv, s, s->jmidxInverse, "xInverse"); udf_xFSI(cx, argc, argv, s, s->jmidxInverse, "xInverse");
} }
@ -2374,12 +2421,26 @@ JDECL(jbyteArray,1value_1text16be)(JENV_JSELF, jobject jpSVal){
JDECL(void,1do_1something_1for_1developer)(JENV_JSELF){ JDECL(void,1do_1something_1for_1developer)(JENV_JSELF){
MARKER(("\nVarious bits of internal info:\n")); MARKER(("\nVarious bits of internal info:\n"));
#define SO(T) printf("sizeof(" #T ") = %u\n", (unsigned)sizeof(T)) puts("sizeofs:");
#define SO(T) printf("\tsizeof(" #T ") = %u\n", (unsigned)sizeof(T))
SO(void*); SO(void*);
SO(JniHookState); SO(JniHookState);
SO(PerDbStateJni); SO(PerDbStateJni);
SO(S3Global); SO(S3Global);
SO(JNIEnvCache); SO(JNIEnvCache);
printf("Cache info:\n");
printf("\tNativePointerHolder cache: %u misses, %u hits\n",
S3Global.metrics.nphCacheMisses,
S3Global.metrics.nphCacheHits);
printf("\tJNIEnv cache %u misses, %u hits\n",
S3Global.metrics.envCacheMisses,
S3Global.metrics.envCacheHits);
puts("UDF calls:");
#define UDF(T) printf("\t%-8s = %u\n", #T, S3Global.metrics.udf.n##T)
UDF(Func); UDF(Step); UDF(Final); UDF(Value); UDF(Inverse);
#undef UDF
printf("xDestroy calls across all callback types: %u\n",
S3Global.metrics.nDestroy);
#undef SO #undef SO
} }
@ -2443,8 +2504,10 @@ Java_org_sqlite_jni_SQLite3Jni_init(JNIEnv *env, jclass self, jobject sJni){
for( pLimit = &aLimits[0]; pLimit->zName; ++pLimit ){ for( pLimit = &aLimits[0]; pLimit->zName; ++pLimit ){
fieldId = (*env)->GetStaticFieldID(env, klazz, pLimit->zName, "I"); fieldId = (*env)->GetStaticFieldID(env, klazz, pLimit->zName, "I");
EXCEPTION_CANNOT_HAPPEN;
//MARKER(("Setting %s (field=%p) = %d\n", pLimit->zName, fieldId, pLimit->value)); //MARKER(("Setting %s (field=%p) = %d\n", pLimit->zName, fieldId, pLimit->value));
assert(fieldId); assert(fieldId);
(*env)->SetStaticIntField(env, klazz, fieldId, (jint)pLimit->value); (*env)->SetStaticIntField(env, klazz, fieldId, (jint)pLimit->value);
EXCEPTION_CANNOT_HAPPEN;
} }
} }

View File

@ -1,5 +1,5 @@
C Correct\sa\smis-cast\sin\sthe\sJNI\swrapper\swhich\sjust\shappened\sto\saccidentally\swork.\sUpdate\sJNI\sbinding\sof\ssqlite3_context_db_handle()\sto\sreturn\sthe\sbound-at-open()\sdb\sinstance\sinstead\sof\sa\snew/temp\sproxy\sobject. C Add\ssome\sJNI-internal\smetrics,\saccessible\svia\spassing\s-v\swhen\srunning\sTester1.java.\sDocument\san\sOpenJDK\sbug\swhich\sleads\sto\sincorrect\s-Xlint:jni\swarnings.
D 2023-07-31T10:55:30.156 D 2023-07-31T12:10:32.812
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -230,9 +230,9 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c
F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9 F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9
F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
F ext/jni/GNUmakefile 72a1549aa5ef6fd21b7af58baccd512147d0912ec85963da46c3aa011b6f3450 F ext/jni/GNUmakefile 3d1f106e7a08bb54279c12979b31492b3dea702a732eab445dbc765120995182
F ext/jni/README.md c0e6e80935e7761acead89b69c87765b23a6bcb2858c321c3d05681fd338292a F ext/jni/README.md c0e6e80935e7761acead89b69c87765b23a6bcb2858c321c3d05681fd338292a
F ext/jni/src/c/sqlite3-jni.c b552ef9c4ec60d4ea29815d5b2cf7e64b20e100a7eb2741c5382e17b0e49999f F ext/jni/src/c/sqlite3-jni.c 7be564e523b576b130d930ed7ad3b389cf22372689101766c3a032166f4438a5
F ext/jni/src/c/sqlite3-jni.h 74aaf87e77f99857aa3afc013517c934cbc2c16618c83d8f5d6294351bc8e7b1 F ext/jni/src/c/sqlite3-jni.h 74aaf87e77f99857aa3afc013517c934cbc2c16618c83d8f5d6294351bc8e7b1
F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c
F ext/jni/src/org/sqlite/jni/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1 F ext/jni/src/org/sqlite/jni/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1
@ -2071,8 +2071,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 8b322d92e247be606f83977767dc361ee4f7bc819122a630bdaa1110177db9b8 P 9faca5d9ed4a749421e08bd1da8b7672c0fd31366124fdb613c46e19dece0fc1
R 42363264995216c484a93a93750ae3b7 R 2f7ebe9a2d19552b91a45c55eec1bac7
U stephan U stephan
Z e56a10315f9f662ddf790f3b196580e4 Z 7f375306dd5a29931e39a5081c815b0c
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
9faca5d9ed4a749421e08bd1da8b7672c0fd31366124fdb613c46e19dece0fc1 a5d68a6b64abe3c2dfc3a32157f70fd8a4ad89feef2510b3bbb2d86b325d51ae