Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/jextractnatives/jextractnatives.c
5986 views
1
/*******************************************************************************
2
* Copyright (c) 1991, 2021 IBM Corp. and others
3
*
4
* This program and the accompanying materials are made available under
5
* the terms of the Eclipse Public License 2.0 which accompanies this
6
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7
* or the Apache License, Version 2.0 which accompanies this distribution and
8
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9
*
10
* This Source Code may also be made available under the following
11
* Secondary Licenses when the conditions for such availability set
12
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13
* General Public License, version 2 with the GNU Classpath
14
* Exception [1] and GNU General Public License, version 2 with the
15
* OpenJDK Assembly Exception [2].
16
*
17
* [1] https://www.gnu.org/software/classpath/license.html
18
* [2] http://openjdk.java.net/legal/assembly-exception.html
19
*
20
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
21
*******************************************************************************/
22
23
#include "jni.h"
24
25
#include "j9dbgext.h"
26
#include "j9protos.h"
27
#include "j9port.h"
28
#include "jextractnatives_internal.h"
29
#include "j9version.h"
30
31
#include <stdarg.h>
32
#include <stdlib.h>
33
34
#define CACHE_SIZE 1024
35
36
#define DEBUG_CACHE_STATISTICS 0
37
38
typedef struct dbgCacheElement {
39
UDATA address;
40
U_8 data[4096];
41
} dbgCacheElement;
42
43
static JNIEnv *globalEnv;
44
static jobject globalDumpObj;
45
static jmethodID globalGetMemMid;
46
static jmethodID globalFindPatternMid;
47
static dbgCacheElement cache[CACHE_SIZE];
48
49
static jint cacheIDs(JNIEnv* env, jobject dumpObj);
50
static jboolean validateDump(JNIEnv *env, jboolean disableBuildIdCheck);
51
static jint callFindPattern(U_8* pattern, jint patternLength, jint patternAlignment, jlong startSearchFrom, jlong* resultP);
52
static void callGetMemoryBytes(UDATA address, void *structure, UDATA size, UDATA *bytesRead);
53
static void readCachedMemory(UDATA address, void *structure, UDATA size, UDATA *bytesRead);
54
static void flushCache(void);
55
56
void
57
dbgReadMemory(UDATA address, void *structure, UDATA size, UDATA *bytesRead)
58
{
59
if (address == 0) {
60
memset(structure, 0, size);
61
*bytesRead = 0;
62
return;
63
}
64
65
readCachedMemory(address, structure, size, bytesRead);
66
if (*bytesRead != size) {
67
callGetMemoryBytes(address, structure, size, bytesRead);
68
}
69
}
70
71
UDATA
72
dbgGetExpression(const char *args)
73
{
74
#ifdef WIN64
75
return (UDATA)_strtoui64(args, NULL, 16);
76
#else
77
return (UDATA)strtoul(args, NULL, 16);
78
#endif
79
}
80
81
/*
82
* See dbgFindPatternInRange
83
*/
84
void*
85
dbgFindPattern(U_8 *pattern, UDATA patternLength, UDATA patternAlignment, U_8 *startSearchFrom, UDATA *bytesSearched)
86
{
87
jlong result = 0;
88
89
*bytesSearched = 0;
90
91
if (callFindPattern(pattern, (jint) patternLength, (jint) patternAlignment, (jlong) (UDATA) startSearchFrom, &result)) {
92
return NULL;
93
}
94
95
*bytesSearched = (UDATA)-1;
96
if (result == (jlong)-1) {
97
return NULL;
98
} else {
99
return (void*)(UDATA)result;
100
}
101
}
102
103
/*
104
* Find the J9RAS structure and validate that it is correct.
105
* This prevents jextract from one build or platform being used with a
106
* dump produced by a different build or platform.
107
*
108
* Return JNI_TRUE if the dump matches jextract.
109
* Return JNI_FALSE and set a Java exception if the dump does not match.
110
*/
111
static jboolean
112
validateDump(JNIEnv *env, jboolean disableBuildIdCheck)
113
{
114
jlong startFrom = 0;
115
jlong eyecatcher = 0;
116
char errBuf[256];
117
118
PORT_ACCESS_FROM_VMC((J9VMThread*)env);
119
120
jclass errorClazz = (*env)->FindClass(env, "java/lang/Error");
121
if (errorClazz == NULL) {
122
return JNI_FALSE;
123
}
124
125
for(;;) {
126
J9RAS *ras = NULL;
127
const char *rasString = "J9VMRAS";
128
129
if (callFindPattern((U_8*)rasString, sizeof(rasString), 8, startFrom, &eyecatcher)) {
130
(*env)->ThrowNew(env, errorClazz, "An error occurred while searching for the J9VMRAS eyecatcher");
131
return JNI_FALSE;
132
}
133
134
if (eyecatcher == (jlong)-1) {
135
j9str_printf(PORTLIB,
136
errBuf, sizeof(errBuf),
137
"JVM anchor block (J9VMRAS) not found in dump. Dump may be truncated, corrupted or contains a partially initialized JVM.");
138
(*env)->ThrowNew(env, errorClazz, errBuf);
139
return JNI_FALSE;
140
}
141
142
#if !defined(J9VM_ENV_DATA64)
143
if ((U_64)eyecatcher > (U_64)0xFFFFFFFF) {
144
j9str_printf(PORTLIB,
145
errBuf, sizeof(errBuf),
146
"J9RAS is out of range for a 32-bit pointer (0x%16.16llx). This version of jextract is incompatible with this dump.",
147
eyecatcher);
148
(*env)->ThrowNew(env, errorClazz, errBuf);
149
return JNI_FALSE;
150
}
151
#endif
152
/* Allocate this, since on 64-bit platforms we want to know now that we can't allocate
153
* the scratch space. This allows us to exit early with a simple error message.
154
*/
155
ras = dbgMallocAndRead(sizeof(J9RAS), (void *)(UDATA)eyecatcher);
156
if (ras != NULL) {
157
if (ras->bitpattern1 == 0xaa55aa55 && ras->bitpattern2 == 0xaa55aa55) {
158
if (ras->version != J9RASVersion) {
159
j9str_printf(PORTLIB,
160
errBuf, sizeof(errBuf),
161
"J9RAS.version is incorrect (found %u, expecting %u). This version of jextract is incompatible with this dump.",
162
ras->version,
163
J9RASVersion);
164
(*env)->ThrowNew(env, errorClazz, errBuf);
165
return JNI_FALSE;
166
}
167
if (ras->length != sizeof(J9RAS)) {
168
j9str_printf(PORTLIB,
169
errBuf, sizeof(errBuf),
170
"J9RAS.length is incorrect (found %u, expecting %u). This version of jextract is incompatible with this dump.",
171
ras->length,
172
sizeof(J9RAS));
173
(*env)->ThrowNew(env, errorClazz, errBuf);
174
return JNI_FALSE;
175
}
176
if (ras->buildID != J9UniqueBuildID) {
177
if (disableBuildIdCheck) {
178
j9tty_printf(PORTLIB,
179
"Ignoring incorrect J9RAS.buildID (found %llx, expecting %llx)."
180
" This version of jextract may be incompatible with this dump.\n",
181
ras->buildID,
182
(U_64)J9UniqueBuildID);
183
} else {
184
j9str_printf(PORTLIB,
185
errBuf, sizeof(errBuf),
186
"J9RAS.buildID is incorrect (found %llx, expecting %llx)."
187
" This version of jextract is incompatible with this dump"
188
" (use '-r' option to relax this check).",
189
ras->buildID,
190
(U_64)J9UniqueBuildID);
191
(*env)->ThrowNew(env, errorClazz, errBuf);
192
return JNI_FALSE;
193
}
194
}
195
196
/* cache the value here so that dbgSniffForJavaVM doesn't need to duplicate this work */
197
dbgSetVM((J9JavaVM*)ras->vm);
198
return JNI_TRUE;
199
}
200
dbgFree(ras);
201
} else {
202
/* On 64-bit platforms, the code that tries to allocate the scratch space J9DBGEXT_SCRATCH_SIZE
203
* scratch space will have issued it's own informative error already.
204
*/
205
j9str_printf(PORTLIB,
206
errBuf, sizeof(errBuf),
207
"Cannot allocate %zu bytes of memory for initial RAS eyecatcher, cannot continue processing this dump.",
208
sizeof(J9RAS));
209
(*env)->ThrowNew(env, errorClazz, errBuf);
210
return JNI_FALSE;
211
}
212
213
/* this isn't it -- look for the next occurrence */
214
startFrom = eyecatcher + 8;
215
}
216
}
217
218
/*
219
* See dbgFindPatternInRange
220
*/
221
static jint
222
callFindPattern(U_8* pattern, jint patternLength, jint patternAlignment, jlong startSearchFrom, jlong* resultP)
223
{
224
jbyteArray patternArray = NULL;
225
jlong result = 0;
226
227
if (!globalDumpObj || !globalFindPatternMid) {
228
return -1;
229
}
230
231
patternArray = (*globalEnv)->NewByteArray(globalEnv, (jsize)patternLength);
232
if (patternArray == NULL) {
233
(*globalEnv)->ExceptionDescribe(globalEnv);
234
return -1;
235
}
236
237
(*globalEnv)->SetByteArrayRegion(globalEnv, patternArray, 0, (jsize)patternLength, (jbyte*)pattern);
238
if ((*globalEnv)->ExceptionCheck(globalEnv)) {
239
(*globalEnv)->DeleteLocalRef(globalEnv, patternArray);
240
(*globalEnv)->ExceptionDescribe(globalEnv);
241
return -1;
242
}
243
244
result = (*globalEnv)->CallLongMethod(globalEnv,
245
globalDumpObj,
246
globalFindPatternMid,
247
patternArray,
248
(jint)patternAlignment,
249
(jlong)startSearchFrom);
250
251
(*globalEnv)->DeleteLocalRef(globalEnv, patternArray);
252
253
if ((*globalEnv)->ExceptionCheck(globalEnv)) {
254
(*globalEnv)->ExceptionDescribe(globalEnv);
255
return -1;
256
}
257
258
*resultP = result;
259
return 0;
260
}
261
262
static jint
263
cacheIDs(JNIEnv* env, jobject dumpObj)
264
{
265
jclass cls = NULL;
266
267
globalEnv = env;
268
globalDumpObj = dumpObj;
269
270
if (!dumpObj) {
271
return -1;
272
}
273
274
cls = (*env)->GetObjectClass(env, dumpObj);
275
if (!cls) {
276
return -1;
277
}
278
279
globalGetMemMid = (*env)->GetMethodID(env, cls,"getMemoryBytes","(JI)[B");
280
if (!globalGetMemMid) {
281
return -1;
282
}
283
284
globalFindPatternMid = (*env)->GetMethodID(env, cls,"findPattern","([BIJ)J");
285
if (!globalFindPatternMid) {
286
return -1;
287
}
288
289
return 0;
290
}
291
292
void JNICALL
293
Java_com_ibm_jvm_j9_dump_extract_Main_doCommand(JNIEnv *env, jobject obj, jobject dumpObj, jstring commandObject)
294
{
295
const char *command = (*env)->GetStringUTFChars(env, commandObject, 0);
296
PORT_ACCESS_FROM_VMC((J9VMThread*)env);
297
298
if (command == NULL) {
299
return;
300
}
301
302
if (cacheIDs(env, dumpObj)) {
303
return;
304
}
305
306
/* hook the debug extension's malloc and free up to ours, so that it can benefit from -memorycheck */
307
OMRPORT_FROM_J9PORT(dbgGetPortLibrary())->mem_allocate_memory = OMRPORT_FROM_J9PORT(PORTLIB)->mem_allocate_memory;
308
OMRPORT_FROM_J9PORT(dbgGetPortLibrary())->mem_free_memory = OMRPORT_FROM_J9PORT(PORTLIB)->mem_free_memory;
309
OMRPORT_FROM_J9PORT(dbgGetPortLibrary())->port_control = OMRPORT_FROM_J9PORT(PORTLIB)->port_control;
310
311
run_command(command);
312
313
(*env)->ReleaseStringUTFChars(env, commandObject, command);
314
}
315
316
/**
317
* Gets the environment pointer from the J9RAS structure.
318
*/
319
jlong JNICALL
320
Java_com_ibm_jvm_j9_dump_extract_Main_getEnvironmentPointer(JNIEnv * env, jobject obj, jobject dumpObj, jboolean disableBuildIdCheck)
321
{
322
J9JavaVM* vmPtr = NULL;
323
J9JavaVM* localVMPtr = NULL;
324
J9RAS* localRAS = NULL;
325
jlong toReturn = 0;
326
327
if (cacheIDs(env, dumpObj)) {
328
goto end;
329
}
330
331
if (!validateDump(env, disableBuildIdCheck)) {
332
goto end;
333
}
334
335
vmPtr = dbgSniffForJavaVM();
336
if (!vmPtr) {
337
goto end;
338
}
339
340
localVMPtr = dbgMallocAndRead(sizeof(J9JavaVM), (void *)(UDATA)vmPtr);
341
if (!localVMPtr) {
342
goto end;
343
}
344
345
localRAS = dbgMallocAndRead(sizeof(J9RAS), (void *)(UDATA)localVMPtr->j9ras);
346
if (!localRAS) {
347
goto end;
348
}
349
350
#if defined(J9VM_ENV_DATA64)
351
toReturn = (jlong)(IDATA)localRAS->environment;
352
#else
353
toReturn = (jlong)(IDATA)localRAS->environment & J9CONST64(0xFFFFFFFF);
354
#endif
355
356
end:
357
flushCache();
358
dbgFreeAll();
359
360
return toReturn;
361
}
362
363
I_32
364
dbg_j9port_create_library(J9PortLibrary *portLib, J9PortLibraryVersion *version, UDATA size)
365
{
366
PORT_ACCESS_FROM_ENV(globalEnv);
367
368
return PORTLIB->port_create_library(portLib, version, size);
369
}
370
371
/*
372
* Need this to get it to link. This is how the debug extension code write messages
373
* to the platform debugger - or rather to stdout here when we are not running under the
374
* platform debugger.
375
*/
376
void
377
dbgWriteString(const char* message)
378
{
379
PORT_ACCESS_FROM_VMC((J9VMThread*)globalEnv);
380
381
j9tty_printf(PORTLIB, "%s", message);
382
}
383
384
static void
385
callGetMemoryBytes(UDATA address, void *structure, UDATA size, UDATA *bytesRead)
386
{
387
jbyteArray data = NULL;
388
jlong ja = address;
389
jint js = (jsize)size;
390
391
*bytesRead = 0;
392
memset(structure, 0, size);
393
394
/* ensure that size can be represented as a jsize */
395
if ((js < 0) || ((UDATA)js != size)) {
396
return;
397
}
398
399
if (!globalDumpObj || !globalGetMemMid) {
400
return;
401
}
402
403
/* we need to allocate another 1-3 local refs so make sure we can get them to satisfy -Xcheck:jni */
404
(*globalEnv)->EnsureLocalCapacity(globalEnv, 3);
405
if ((*globalEnv)->ExceptionCheck(globalEnv)) {
406
/* if we fail to allocate local ref storage, just fail since this definitely won't work */
407
(*globalEnv)->ExceptionClear(globalEnv);
408
return;
409
}
410
data = (jbyteArray)((*globalEnv)->CallObjectMethod(globalEnv, globalDumpObj, globalGetMemMid, ja, js));
411
if ((*globalEnv)->ExceptionCheck(globalEnv)) {
412
/* CMVC 110117: ExceptionDescribe causes an uncaught event so manually call printStackTrace() */
413
jthrowable exception = (*globalEnv)->ExceptionOccurred(globalEnv);
414
jclass exceptionClass = NULL;
415
jmethodID printStackTraceID = NULL;
416
417
(*globalEnv)->ExceptionClear(globalEnv);
418
/* note that the error cases where we are missing an exception, a class or printStackTrace, we have no good solution */
419
exceptionClass = (*globalEnv)->GetObjectClass(globalEnv, exception);
420
printStackTraceID = (*globalEnv)->GetMethodID(globalEnv, exceptionClass, "printStackTrace", "()V");
421
(*globalEnv)->CallVoidMethod(globalEnv, exception, printStackTraceID);
422
/* if we throw while trying to print a stack trace, all bets are off in terms of how to handle that exception so just clear it */
423
(*globalEnv)->ExceptionClear(globalEnv);
424
(*globalEnv)->DeleteLocalRef(globalEnv, exception);
425
(*globalEnv)->DeleteLocalRef(globalEnv, exceptionClass);
426
return;
427
}
428
429
if (data) {
430
jsize jbytesRead = (*globalEnv)->GetArrayLength(globalEnv, data);
431
if (jbytesRead > js) {
432
/* throw an exception here? */
433
} else {
434
(*globalEnv)->GetByteArrayRegion(globalEnv, data, 0, jbytesRead, structure);
435
}
436
(*globalEnv)->DeleteLocalRef(globalEnv, data);
437
*bytesRead = (UDATA)jbytesRead;
438
}
439
}
440
441
/**
442
* Flush all elements from the small object memory cache.
443
*/
444
static void
445
flushCache(void)
446
{
447
int i = 0;
448
449
for (i = 0; i < sizeof(cache) / sizeof(cache[0]); i++) {
450
/* invalidate this element */
451
cache[i].address = 0;
452
}
453
}
454
455
/**
456
* This function implements a very simple caching scheme to accelerate the reading of small objects.
457
* A more sophisticated scheme is implemented in the Java code. This cache allows us to bypass
458
* the relatively expensive call-in to Java for most objects. During a normal jextract run we expect
459
* the cache to have a hit rate of over 90%.
460
*/
461
static void
462
readCachedMemory(UDATA address, void *structure, UDATA size, UDATA *bytesRead)
463
{
464
#if DEBUG_CACHE_STATISTICS
465
static UDATA hits = 0;
466
static UDATA total = 0;
467
#endif /* DEBUG_CACHE_STATISTICS */
468
469
dbgCacheElement* thisElement = NULL;
470
UDATA lineStart = address & ~(UDATA)(sizeof(thisElement->data) - 1);
471
UDATA endAddress = address + size;
472
473
#if DEBUG_CACHE_STATISTICS
474
if (((++total) % (16 * 1024)) == 0) {
475
dbgPrint("Cache hit rate: %.2f\n", (float)hits / (float)total);
476
}
477
#endif /* DEBUG_CACHE_STATISTICS */
478
479
*bytesRead = 0;
480
481
if (((lineStart + sizeof(thisElement->data)) >= endAddress) &&
482
(endAddress > address)) { /* check for arithmetic overflow */
483
UDATA cacheBytesRead = 0;
484
485
thisElement = &cache[(lineStart / sizeof(thisElement->data)) % CACHE_SIZE];
486
487
/* is the data cached at this slot? */
488
if (thisElement->address == lineStart) {
489
memcpy(structure, thisElement->data + (address - lineStart), size);
490
*bytesRead = size;
491
#if DEBUG_CACHE_STATISTICS
492
hits += 1;
493
#endif /* DEBUG_CACHE_STATISTICS */
494
return;
495
}
496
497
/* it wasn't -- cache it now */
498
callGetMemoryBytes(lineStart, thisElement->data, sizeof(thisElement->data), &cacheBytesRead);
499
if (cacheBytesRead == sizeof(thisElement->data)) {
500
thisElement->address = lineStart;
501
memcpy(structure, thisElement->data + (address - lineStart), size);
502
*bytesRead = size;
503
} else {
504
/* invalidate this element */
505
thisElement->address = 0;
506
}
507
}
508
}
509
510