#include "j9.h"
#include "j9protos.h"
#include "ut_j9scar.h"
#include "rommeth.h"
#include "vmhook.h"
#if defined(LINUX) && defined(J9VM_ARCH_X86) && defined(J9VM_ENV_DATA64)
#include <ucontext.h>
#define ASGCT_SUPPORTED
#endif
extern "C" {
#define AGCT_LINE_NUMBER_NATIVE_METHOD -3
enum {
ticks_no_Java_frame = 0,
ticks_no_class_load = -1,
ticks_GC_active = -2,
ticks_unknown_not_Java = -3,
ticks_not_walkable_not_Java = -4,
ticks_unknown_Java = -5,
ticks_not_walkable_Java = -6,
ticks_unknown_state = -7,
ticks_thread_exit = -8,
ticks_deopt = -9,
ticks_safepoint = -10
};
typedef struct {
jint lineno;
jmethodID method_id;
} ASGCT_CallFrame;
typedef struct {
JNIEnv *env_id;
jint num_frames;
ASGCT_CallFrame *frames;
} ASGCT_CallTrace;
#if defined(ASGCT_SUPPORTED)
extern J9JavaVM *BFUjavaVM;
#define TRIGGER_SEGV() *(UDATA*)UDATA_MAX = UDATA_MAX
static UDATA
asyncFrameIterator(J9VMThread * currentThread, J9StackWalkState * walkState)
{
ASGCT_CallFrame *frame = (ASGCT_CallFrame*)walkState->userData1;
J9Method *method = walkState->method;
J9JavaVM * vm = currentThread->javaVM;
J9Class * declaringClass = J9_CLASS_FROM_METHOD(method);
UDATA methodIndex = getMethodIndexUnchecked(method);
if (UDATA_MAX == methodIndex) {
TRIGGER_SEGV();
}
void ** jniIDs = declaringClass->jniIDs;
if (NULL == jniIDs) {
TRIGGER_SEGV();
}
J9JNIMethodID * methodID = (J9JNIMethodID*)(jniIDs[methodIndex]);
if (NULL == methodID) {
TRIGGER_SEGV();
}
frame->method_id = (jmethodID)methodID;
J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method);
if (J9_ARE_ANY_BITS_SET(romMethod->modifiers, J9AccNative)) {
frame->lineno = AGCT_LINE_NUMBER_NATIVE_METHOD;
} else {
frame->lineno = (jint)walkState->bytecodePCOffset;
}
walkState->userData1 = (void*)(frame + 1);
return J9_STACKWALK_KEEP_ITERATING;
}
static UDATA
emptySignalHandler(J9PortLibrary *portLibrary, U_32 gpType, void *gpInfo, void *handler_arg)
{
return J9PORT_SIG_EXCEPTION_RETURN;
}
typedef struct {
ASGCT_CallTrace *trace;
jint depth;
void *ucontext;
J9VMThread *currentThread;
jint num_frames;
U_8 *pc;
UDATA *sp;
UDATA *arg0EA;
J9Method *literals;
void *jitArtifactSearchCache;
UDATA privateFlags2;
} ASGCT_parms;
static UDATA
protectedASGCT(J9PortLibrary *portLib, void *arg)
{
ASGCT_parms *parms = (ASGCT_parms*)arg;
J9VMThread *targetThread = (J9VMThread*)parms->trace->env_id;
Assert_SC_true(NULL != targetThread);
J9VMThread *currentThread = BFUjavaVM->internalVMFunctions->currentVMThread(BFUjavaVM);
Assert_SC_true(targetThread == currentThread);
J9SFJITResolveFrame resolveFrame = {0};
parms->currentThread = currentThread;
parms->pc = currentThread->pc;
parms->sp = currentThread->sp;
parms->arg0EA = currentThread->arg0EA;
parms->literals = currentThread->literals;
parms->num_frames = ticks_not_walkable_Java;
parms->jitArtifactSearchCache = currentThread->jitArtifactSearchCache;
parms->privateFlags2 = currentThread->privateFlags2;
currentThread->jitArtifactSearchCache = (void*)((UDATA)currentThread->jitArtifactSearchCache | J9_STACKWALK_NO_JIT_CACHE);
currentThread->privateFlags2 |= J9_PRIVATE_FLAGS2_ASYNC_GET_CALL_TRACE;
J9JITConfig *jitConfig = BFUjavaVM->jitConfig;
if (NULL != jitConfig) {
void *ucontext = parms->ucontext;
if (NULL != ucontext) {
greg_t *regs = ((ucontext_t*)ucontext)->uc_mcontext.gregs;
greg_t rip = regs[REG_RIP];
J9JITExceptionTable *metaData = jitConfig->jitGetExceptionTableFromPC(currentThread, rip);
if (NULL != metaData) {
greg_t rsp = regs[REG_RSP];
resolveFrame.savedJITException = NULL;
resolveFrame.specialFrameFlags = J9_SSF_JIT_RESOLVE;
resolveFrame.parmCount = 0;
resolveFrame.returnAddress = (U_8*)rip;
resolveFrame.taggedRegularReturnSP = (UDATA*)(((U_8 *)rsp) + J9SF_A0_INVISIBLE_TAG);
currentThread->pc = (U_8*)J9SF_FRAME_TYPE_JIT_RESOLVE;
currentThread->arg0EA = (UDATA*)&(resolveFrame.taggedRegularReturnSP);
currentThread->literals = NULL;
currentThread->sp = (UDATA*)&resolveFrame;
}
}
}
J9StackWalkState walkState = {0};
walkState.walkThread = currentThread;
walkState.skipCount = 0;
walkState.maxFrames = parms->depth;
walkState.flags = J9_STACKWALK_INCLUDE_NATIVES | J9_STACKWALK_VISIBLE_ONLY
| J9_STACKWALK_RECORD_BYTECODE_PC_OFFSET | J9_STACKWALK_COUNT_SPECIFIED
| J9_STACKWALK_ITERATE_FRAMES | J9_STACKWALK_NO_ERROR_REPORT;
walkState.userData1 = (void*)parms->trace->frames;
walkState.frameWalkFunction = asyncFrameIterator;
UDATA result = BFUjavaVM->walkStackFrames(currentThread, &walkState);
if (J9_STACKWALK_RC_NONE == result) {
parms->num_frames = (jint)walkState.framesWalked;
}
return 0;
}
#endif
void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void *ucontext)
{
J9VMThread *currentThread = NULL;
jint num_frames = ticks_unknown_not_Java;
#if defined(ASGCT_SUPPORTED)
if (NULL != BFUjavaVM) {
Assert_SC_true(J9_EVENT_IS_HOOKED(BFUjavaVM->hookInterface, J9HOOK_VM_CLASS_LOAD));
Assert_SC_true(J9_ARE_NO_BITS_SET(BFUjavaVM->sigFlags, J9_SIG_XRS_SYNC));
PORT_ACCESS_FROM_JAVAVM(BFUjavaVM);
ASGCT_parms parms = { trace, depth, ucontext, currentThread, num_frames, NULL, NULL, NULL, NULL, NULL, 0 };
UDATA result = 0;
j9sig_protect(
protectedASGCT, (void*)&parms,
emptySignalHandler, NULL,
J9PORT_SIG_FLAG_SIGALLSYNC | J9PORT_SIG_FLAG_MAY_RETURN,
&result);
num_frames = parms.num_frames;
currentThread = parms.currentThread;
if (NULL != currentThread) {
currentThread->jitArtifactSearchCache = parms.jitArtifactSearchCache;
currentThread->privateFlags2 = parms.privateFlags2;
currentThread->pc = parms.pc;
currentThread->sp = parms.sp;
currentThread->arg0EA = parms.arg0EA;
currentThread->literals = parms.literals;
}
}
#endif
trace->num_frames = num_frames;
}
}