jvmdi.c
来自「This is a resource based on j2me embedde」· C语言 代码 · 共 2,133 行 · 第 1/5 页
C
2,133 行
/* * @(#)jvmdi.c 1.140 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * *//* * Breakpoints, single-stepping and debugger event notification. * Debugger event notification/handling includes breakpoint, single-step * and exceptions. */#ifdef CVM_JVMDI#include "javavm/include/porting/ansi/stdarg.h"#include "javavm/include/defs.h"#include "javavm/include/indirectmem.h"#include "javavm/include/globalroots.h"#include "javavm/include/localroots.h"#include "javavm/include/interpreter.h"#include "javavm/include/basictypes.h"#include "javavm/include/signature.h"#include "javavm/include/globals.h"#include "javavm/include/bag.h"#include "javavm/include/common_exceptions.h"#include "javavm/include/named_sys_monitor.h"#include "javavm/include/opcodes.h"#include "generated/offsets/java_lang_Thread.h"#include "generated/jni/java_lang_reflect_Modifier.h"#include "javavm/export/jvm.h"#include "javavm/export/jni.h"#include "javavm/include/jvmdi_impl.h"#ifdef CVM_HW#include "include/hw.h"#endif/* %comment: k001 *//* (This is unused in CVM -- only used in unimplemented GetBytecodes) *//* Defined in inline.c thru an include of "opcodes.length". This is dangerous as is. There should be a header file with this extern shared by both .c files *//* extern const short opcode_length[256]; */#undef DEBUGGER_LOCK#undef DEBUGGER_UNLOCK#undef DEBUGGER_IS_LOCKED#define DEBUGGER_LOCK(ee) CVM_DEBUGGER_LOCK(ee)#define DEBUGGER_UNLOCK(ee) CVM_DEBUGGER_UNLOCK(ee)#define DEBUGGER_IS_LOCKED(ee) CVM_DEBUGGER_IS_LOCKED(ee)/* Convenience macros */#define CVM_THREAD_LOCK(ee) CVMsysMutexLock(ee, &CVMglobals.threadLock)#define CVM_THREAD_UNLOCK(ee) CVMsysMutexUnlock(ee, &CVMglobals.threadLock)/* NOTE: "Frame pop sentinels" removed in CVM version of JVMDI because we don't have two return PC slots and mangling the only available one is incorrect if an exception is thrown. See CVMjvmdiNotifyDebuggerOfFramePop and jvmdi_NotifyFramePop below. *//* #define FRAME_POP_SENTINEL ((unsigned char *)1) */#define INITIAL_BAG_SIZE 4struct bkpt { CVMUint8* pc; /* key - must be first */ CVMUint8 opcode; /* opcode to restore */ jobject classRef; /* Prevents enclosing class from being gc'ed */};struct fpop { CVMFrame* frame; /* key - must be first */ /* CVMUint8* returnpc; */ /* Was used for PC mangling in JDK version. Now just indicates set membership. */};struct fieldWatch { CVMFieldBlock* fb; /* field to watch; key - must be first */ jclass classRef; /* Prevents enclosing class from being gc'ed */};/* additional local frames to push to cover usage by client debugger */#define LOCAL_FRAME_SLOP 10#define JVMDI_EVENT_GLOBAL_MASK 0xf0000000#define JVMDI_EVENT_THREAD_MASK 0x7fffffffstatic void enableAllEvents(jint eventType, jboolean enabled) { if (enabled) { CVMglobals.jvmdiStatics.eventEnable[eventType] |= JVMDI_EVENT_GLOBAL_MASK; } else { CVMglobals.jvmdiStatics.eventEnable[eventType] &= ~JVMDI_EVENT_GLOBAL_MASK; }}static void enableThreadEvents(CVMExecEnv* ee, ThreadNode *node, jint eventType, jboolean enabled) { CVMUint32 count; CVMassert(DEBUGGER_IS_LOCKED(ee)); /* * If state is changing, update the global eventEnable word * for this thread and the per-thread boolean flag. */ if (node->eventEnabled[eventType] != enabled) { node->eventEnabled[eventType] = enabled; count = CVMglobals.jvmdiStatics.eventEnable[eventType] & JVMDI_EVENT_THREAD_MASK; if (enabled) { count++; } else { count--; } CVMglobals.jvmdiStatics.eventEnable[eventType] = (JVMDI_EVENT_GLOBAL_MASK & CVMglobals.jvmdiStatics.eventEnable[eventType]) | (JVMDI_EVENT_THREAD_MASK & count); }}static jboolean threadEnabled(jint eventType, ThreadNode *node) { return (node == NULL) ? JNI_FALSE : node->eventEnabled[eventType];}#define GLOBALLY_ENABLED(eventType) (CVMglobals.jvmdiStatics.eventEnable[eventType] & JVMDI_EVENT_GLOBAL_MASK)/* * This macro is used in notify_debugger_* to determine whether a JVMDI * event should be generated. Note that this takes an expression for the thread node * which we should avoid evaluating twice. That's why threadEnabled above * is a function, not a macro * * Also the first eventEnable[eventType] is redundant, but it serves as * a quick filter of events that are completely unreported. */#define MUST_NOTIFY(eventType, threadNodeExpr) \ (CVMglobals.jvmdiStatics.eventEnable[eventType] && \ CVMglobals.jvmdiStatics.eventHook && \ ( GLOBALLY_ENABLED(eventType) || \ threadEnabled(eventType, threadNodeExpr)))/* forward defs */static jvmdiError JNICALLjvmdi_Allocate(jlong size, jbyte** memPtr);static jvmdiError JNICALLjvmdi_Deallocate(jbyte* mem);#ifdef JDK12static voidhandleExit(void);#endifstatic ThreadNode *findThread(CVMExecEnv* ee, CVMObjectICell* thread);static ThreadNode *insertThread(CVMExecEnv* ee, CVMObjectICell* thread);/* * Initialize JVMDI - if and only if it hasn't been initialized. * Must be called before anything that accesses event structures. */static jvmdiErrorinitializeJVMDI() { CVMExecEnv *ee = CVMgetEE(); CVMBool haveFailure = CVM_FALSE; /* %comment: k003 */ if (CVMglobals.jvmdiStatics.jvmdiInitialized) { return JVMDI_ERROR_NONE; }#ifdef JDK12 CVMatExit(handleExit);#endif CVMglobals.jvmdiStatics.breakpoints = CVMbagCreateBag(sizeof(struct bkpt), INITIAL_BAG_SIZE); CVMglobals.jvmdiStatics.framePops = CVMbagCreateBag( sizeof(struct fpop), INITIAL_BAG_SIZE); CVMglobals.jvmdiStatics.watchedFieldModifications = CVMbagCreateBag(sizeof(struct fieldWatch), INITIAL_BAG_SIZE); CVMglobals.jvmdiStatics.watchedFieldAccesses = CVMbagCreateBag(sizeof(struct fieldWatch), INITIAL_BAG_SIZE); if (CVMglobals.jvmdiStatics.breakpoints == NULL || CVMglobals.jvmdiStatics.framePops == NULL || CVMglobals.jvmdiStatics.watchedFieldModifications == NULL || CVMglobals.jvmdiStatics.watchedFieldAccesses == NULL) { return JVMDI_ERROR_OUT_OF_MEMORY; } /* * Setup the events to be enabled/disabled on startup. All events * are enabled except single step, exception catch, method entry/exit. */ enableAllEvents(JVMDI_EVENT_THREAD_START, JNI_TRUE); enableAllEvents(JVMDI_EVENT_THREAD_END, JNI_TRUE); enableAllEvents(JVMDI_EVENT_CLASS_LOAD, JNI_TRUE); enableAllEvents(JVMDI_EVENT_CLASS_PREPARE, JNI_TRUE); enableAllEvents(JVMDI_EVENT_CLASS_UNLOAD, JNI_TRUE); enableAllEvents(JVMDI_EVENT_FIELD_ACCESS, JNI_TRUE); enableAllEvents(JVMDI_EVENT_FIELD_MODIFICATION, JNI_TRUE); enableAllEvents(JVMDI_EVENT_BREAKPOINT, JNI_TRUE); enableAllEvents(JVMDI_EVENT_FRAME_POP, JNI_TRUE); enableAllEvents(JVMDI_EVENT_EXCEPTION, JNI_TRUE); enableAllEvents(JVMDI_EVENT_USER_DEFINED, JNI_TRUE); /* NOTE: We must not trigger a GC while holding the thread lock. We are safe here because insertThread() can allocate global roots which can cause expansion of the global root stack, but not cause a GC. */ CVM_THREAD_LOCK(ee); /* Log all thread that were created prior to JVMDI's initialization: */ /* NOTE: We are only logging the pre-existing threads into the JVMDI threads list. We don't send currently send JVMDI_EVENT_THREAD_START for these threads that started before JVMDI was initialized. */ CVM_WALK_ALL_THREADS(ee, currentEE, { jthread thread = CVMcurrentThreadICell(currentEE); if (!haveFailure && !CVMID_icellIsNull(thread)) { ThreadNode *node = findThread(ee, thread); if (node == NULL) { node = insertThread(ee, thread); if (node == NULL) { haveFailure = CVM_TRUE; } } } }); CVM_THREAD_UNLOCK(ee); /* Abort if we detected a failure while trying to log threads: */ if (haveFailure) { return JVMDI_ERROR_OUT_OF_MEMORY; } CVMglobals.jvmdiStatics.jvmdiInitialized = CVM_TRUE; return JVMDI_ERROR_NONE;}/* * These functions maintain the linked list of currently running threads. */static ThreadNode *findThread(CVMExecEnv* ee, CVMObjectICell* thread) { ThreadNode *node; CVMBool thrEq; DEBUGGER_LOCK(ee); /* cast away volatility */ node = (ThreadNode *)CVMglobals.jvmdiStatics.threadList; while (node != NULL) { CVMID_icellSameObject(ee, node->thread, thread, thrEq); if (thrEq) { break; } node = node->next; } DEBUGGER_UNLOCK(ee); return node;}static ThreadNode *insertThread(CVMExecEnv* ee, CVMObjectICell* thread) { ThreadNode *node; /* NOTE: you could move the locking and unlocking inside the if clause in such a way as to avoid the problem with seizing both the debugger and global root lock at the same time, but it wouldn't solve the problem of removeThread, which is harder, nor the (potential) problem of the several routines which perform indirect memory accesses while holding the debugger lock; if there was ever a lock associated with those accesses there would be a problem. */ DEBUGGER_LOCK(ee); node = (ThreadNode *)malloc(sizeof(*node)); if (node != NULL) { node->thread = CVMID_getGlobalRoot(ee); if (node->thread == NULL) { goto fail0; } node->lastDetectedException = CVMID_getGlobalRoot(ee); if (node->lastDetectedException == NULL) { goto fail1; } CVMID_icellAssign(ee, node->thread, thread); memset(node->eventEnabled, 0, sizeof(node->eventEnabled)); node->startFunction = NULL; node->startFunctionArg = NULL; /* cast away volatility */ node->next = (ThreadNode *)CVMglobals.jvmdiStatics.threadList; CVMglobals.jvmdiStatics.threadList = node; } unlock: DEBUGGER_UNLOCK(ee); return node;fail1: CVMID_freeGlobalRoot(ee, node->lastDetectedException);fail0: free(node); node = NULL; goto unlock;}static jboolean removeThread(CVMObjectICell* thread) { ThreadNode *previous = NULL; ThreadNode *node; CVMExecEnv* ee = CVMgetEE(); JNIEnv* env = CVMexecEnv2JniEnv(ee); CVMBool thrEq; jboolean rc = JNI_FALSE; DEBUGGER_LOCK(ee); /* cast away volatility */ node = (ThreadNode *)CVMglobals.jvmdiStatics.threadList; while (node != NULL) { CVMID_icellSameObject(ee, node->thread, thread, thrEq); if (thrEq) { int i; if (previous == NULL) { CVMglobals.jvmdiStatics.threadList = node->next; } else { previous->next = node->next; } for (i = 0; i <= JVMDI_MAX_EVENT_TYPE_VAL; i++) { enableThreadEvents(ee, node, i, JNI_FALSE); } CVMID_freeGlobalRoot(ee, node->thread); (*env)->DeleteGlobalRef(env, node->lastDetectedException); free((void *)node); rc = JNI_TRUE; break; } previous = node; node = node->next; } DEBUGGER_UNLOCK(ee); return rc;}static voidreportException(CVMExecEnv* ee, CVMUint8 *pc, CVMObjectICell* object, CVMFrame* frame) { JVMDI_Event event; JNIEnv *env = CVMexecEnv2JniEnv(ee); CVMMethodBlock* mb = frame->mb; CVMClassBlock* exceptionCb; CVMJavaLong sz; CVMFrameIterator iter; if (mb == NULL) { return; } /* NOTE: MUST BE A JAVA METHOD */ CVMassert(!CVMmbIs(mb, NATIVE)); CVMID_objectGetClass(ee, object, exceptionCb); event.kind = JVMDI_EVENT_EXCEPTION; event.u.exception.thread = (*env)->NewLocalRef(env, CVMcurrentThreadICell(ee)); event.u.exception.clazz = (*env)->NewLocalRef(env, CVMcbJavaInstance(CVMmbClassBlock(mb))); event.u.exception.method = (jmethodID)mb; sz = CVMint2Long(pc - CVMmbJavaCode(mb)); event.u.exception.location = sz; event.u.exception.exception = (*env)->NewLocalRef(env, object); event.u.exception.catch_clazz = 0; event.u.exception.catch_method = 0; event.u.exception.catch_location = CVMlongConstZero(); /* walk up the stack to see if this exception is caught anywhere. */ CVMframeIterateInit(&iter, frame); while (CVMframeIterateNextSpecial(&iter, CVM_FALSE)) { /* %comment: k004 */ /* skip transition frames, which can't catch exceptions */ if (CVMframeIterateCanHaveJavaCatchClause(&iter)) { CVMMethodBlock* cmb = frame->mb; /* %comment: k005 */ if (cmb != NULL) { CVMUint8* pc = CVMframeIterateGetJavaPc(&iter); CVMUint8* cpc = CVMgcSafeFindPCForException(ee, &iter, exceptionCb, pc); if (cpc != NULL) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?