boolean debug_event(int event, Object *exception, const Thread *thread, const MethodRecord *method, byte *pc, int frame) { // Inform the debug thread (if any) that there has been a debug event. // return false if no debug thread is waiting. switch(debugEventOptions[event]) { case DBG_EVENT_DISABLE: return false; case DBG_EVENT_IGNORE: return true; default: break; } // Check that we have a debugger attached and that it is ready to go... if (!debug || get_monitor_count((&(debug->_super.sync))) != 0 || debugThread->state != CONDVAR_WAITING || (Debug *)debugThread->waitingOn != debug) return false; // Setup the state debug->typ = event; debug->exception = ptr2ref(exception); debug->thread = ptr2ref(thread); debug->pc = (pc ? pc - get_binary_base() : 0); debug->method = (method ? method - get_method_table(get_class_record(0)) : 0); debug->frame = frame; // Suspend all threads (except current) suspend_thread(null); // Make sure current thread is also suspended suspend_thread(currentThread); // Allow the debug thread to run resume_thread(debugThread); monitor_notify_unchecked(&debug->_super, 1); return true; }
/** * @return Method record or null. */ MethodRecord *find_method (ClassRecord *classRecord, int methodSignature) { MethodRecord* mr0 = get_method_table( classRecord); MethodRecord* mr = mr0 + get_method_cnt(classRecord); while( -- mr >= mr0) if( mr->signatureId == methodSignature) return mr; return null; }
void set_debug(Debug *_debug) { debug = _debug; debugThread = currentThread; debug->methodBase = ptr2word(get_method_table(get_class_record(0))); debug->classBase = ptr2word(get_class_base()); debug->fieldBase = ptr2word(get_field_table(get_class_record(0))); debug->threads = threads; int i; for(i = 0; i < sizeof(debugEventOptions); i++) debugEventOptions[i] = DBG_EVENT_DISABLE; }
/** * Create a compact form of the specified call stack. * One int per frame containing * the method number and current offset. If the ignore parameter is non null * then frames that have a matching this field will not be included in the * trace. This allow the frames for the creation of an exception object to * be ignored. */ Object * create_stack_trace(Thread *thread, Object *ignore) { int frameCnt = thread->stackFrameIndex; Object *stackArray; JINT *data; int i; StackFrame *topFrame = ((StackFrame *)array_start(thread->stackFrameArray)) + frameCnt; StackFrame *stackFrame = topFrame; MethodRecord *methodBase = get_method_table(get_class_record(0)); byte *pcBase = get_binary_base() + 2; // Ignore frames if required. if (ignore) { while ((STACKWORD)ignore == *(stackFrame->localsBase)) { stackFrame--; frameCnt--; } } // Try and allocate the space for the trace. stackArray = new_single_array(AI, frameCnt); if (stackArray == JNULL) return JNULL; if (thread == currentThread) topFrame->pc = getPc(); // adjust top most pc to allow for return address hack topFrame->pc += 2; // Fill in the trace. data = jint_array(stackArray); for(i = 0; i < frameCnt; i++) { data[i] = ((stackFrame->methodRecord - methodBase) << 16) | (stackFrame->pc - pcBase - stackFrame->methodRecord->codeOffset); stackFrame--; } // restore correct pc topFrame->pc -= 2; return stackArray; }
/** * @param classRecord Record for method class. * @param methodRecord Calle's method record. * @param retAddr What the PC should be upon return. * @return true iff the stack frame was pushed. */ boolean dispatch_special (MethodRecord *methodRecord, byte *retAddr) { /** * Note: This code is a little tricky, particularly when used with * a garbage collector. It manipulates the stack frame and in some cases * may need to perform memory allocation. In all cases we must take care * to ensure that if an allocation can be made then any live objects * on the stack must be below the current stack pointer. * In addition to the above we take great care so that this function can * be restarted (to allow us to wait for available memory). To enable this * we avoid making any commitments to changes to global state until both * stacks have been commited. */ #if DEBUG_METHODS int debug_ctr; #endif Object *stackFrameArray; StackFrame *stackFrame; StackFrame *stackBase; int newStackFrameIndex; STACKWORD *newStackTop; #if DEBUG_BYTECODE printf("call method %d ret %x\n", methodRecord - get_method_table(get_class_record(0)), retAddr); printf ("\n------ dispatch special - %d ------------------\n\n", methodRecord->signatureId); #endif #if DEBUG_METHODS printf ("dispatch_special: %d, %d\n", (int) methodRecord, (int) retAddr); printf ("-- signature id = %d\n", methodRecord->signatureId); printf ("-- code offset = %d\n", methodRecord->codeOffset); printf ("-- flags = %d\n", methodRecord->mflags); printf ("-- num params = %d\n", methodRecord->numParameters); //printf ("-- stack ptr = %d\n", (int) get_stack_ptr()); //printf ("-- max stack ptr= %d\n", (int) (currentThread->stackArray + (get_array_size(currentThread->stackArray))*2)); #endif // First deal with the easy case of a native call... if (is_native (methodRecord)) { #if DEBUG_METHODS printf ("-- native\n"); #endif // WARNING: Once the instruction below has been executed we may have // references on the stack that are above the stack pointer. If a GC // gets run when in this state the reference may get collected as // grabage. This means that any native functions that take a reference // parameter and that may end up allocating memory *MUST* protect that // reference before calling the allocator... pop_words_cur (methodRecord->numParameters); switch(dispatch_native (methodRecord->signatureId, get_stack_ptr_cur() + 1)) { case EXEC_RETRY: // Need to re-start the instruction, so reset the state of the stack curStackTop += methodRecord->numParameters; break; case EXEC_CONTINUE: // Normal completion return to the requested point. curPc = retAddr; break; case EXEC_RUN: // We are running new code, curPc will be set. Nothing to do. break; case EXEC_EXCEPTION: // An exception has been thrown. The PC will be set correctly and // the stack may have been adjusted... break; } // Stack frame not pushed return false; } // Now start to build the new stack frames. We start by placing the // the new stack pointer below any params. The params will become locals // in the new frame. newStackTop = get_stack_ptr_cur() - methodRecord->numParameters; newStackFrameIndex = (int)(byte)currentThread->stackFrameIndex; if (newStackFrameIndex >= 255) { throw_new_exception (JAVA_LANG_STACKOVERFLOWERROR); return false; } #if DEBUG_METHODS //for (debug_ctr = 0; debug_ctr < methodRecord->numParameters; debug_ctr++) // printf ("-- param[%d] = %ld\n", debug_ctr, (long) get_stack_ptr()[debug_ctr+1]); #endif stackFrameArray = ref2obj(currentThread->stackFrameArray); stackBase = (StackFrame *)array_start(stackFrameArray); // Setup OLD stackframe ready for return stackFrame = stackBase + (newStackFrameIndex); stackFrame->stackTop = newStackTop; stackFrame->pc = retAddr; // Push NEW stack frame // Increment size of stack frame array but do not commit to it until we have // completely built both new stacks. newStackFrameIndex++; stackFrame++; if (((byte *)stackFrame - (byte *)stackBase) >= get_array_length(stackFrameArray)) { #if FIXED_STACK_SIZE throw_new_exception (JAVA_LANG_STACKOVERFLOWERROR); return false; #else if (expand_call_stack(currentThread) < 0) return false; stackFrame = (StackFrame *)array_start(currentThread->stackFrameArray) + newStackFrameIndex; #endif } // Initialize rest of new stack frame stackFrame->methodRecord = methodRecord; stackFrame->monitor = null; stackFrame->localsBase = newStackTop + 1; // Allocate space for locals etc. newStackTop = init_sp(stackFrame, methodRecord); stackFrame->stackTop = newStackTop; currentThread->stackFrameIndex = newStackFrameIndex; // Check for stack overflow if (is_stack_overflow (newStackTop, methodRecord)) { #if FIXED_STACK_SIZE throw_new_exception (JAVA_LANG_STACKOVERFLOWERROR); return false; #else if (expand_value_stack(currentThread, methodRecord->maxOperands+methodRecord->numLocals) < 0) { currentThread->stackFrameIndex--; return false; } // NOTE at this point newStackTop is no longer valid! newStackTop = stackFrame->stackTop; #endif } // All set. So now we can finally commit to the new stack frames update_constant_registers (stackFrame); curStackTop = newStackTop; // and jump to the start of the new code curPc = get_code_ptr(methodRecord); return true; }
/** * Call a java static method from the VM * NOTE: This can only be used to call methods that have a signature * value that is less than 255. Normally it will be used to call * one of the specialsignature entries (which are all less than 255). */ void dispatch_java(byte classIndex, byte sig, byte *retAddr, byte *btAddr) { ClassRecord *classRecord = get_class_record (classIndex); MethodRecord *mRec = find_method (classRecord, sig); dispatch_special_checked(classIndex, mRec - get_method_table(classRecord), retAddr, btAddr); }
/** * Exceute the static initializer if required. Note that the ret address used * here is set such that the current instruction will be re-started when the * initialization completes. * @return An indication of how the VM should proceed */ int dispatch_static_initializer (ClassRecord *aRec, byte *retAddr) { int state = get_init_state(aRec); ClassRecord *init = aRec; ClassRecord *super = get_class_record(init->parentClass); MethodRecord *method; // Are we needed? if (state & C_INITIALIZED) return EXEC_CONTINUE; // We need to initialize all of the super classes first. So we find the // highest one that has not been initialized and deal with that. This code // will then be called again and we will init the next highest and so on // until all of the classes in the chain are done. for(;;) { // find first super class that has not been initialized while (init != super && (get_init_state(super) & C_INITIALIZED) == 0) { init = super; super = get_class_record(init->parentClass); } // Do we have an initilizer if so we have found our class if (has_clinit (init)) break; // no initializer so mark as now initialized set_init_state (init, C_INITIALIZED); // If we are at the start of the list we are done if (init == aRec) return EXEC_CONTINUE; // Otherwise go do it all again init = aRec; } state = get_init_state(init); // are we already initializing ? if (state & C_INITIALIZING) { // Is it this thread that is doing the init? if (get_sync(init)->threadId == currentThread->threadId) return EXEC_CONTINUE; // No so we must retry the current instruction curPc = retAddr; sleep_thread(1); schedule_request(REQUEST_SWITCH_THREAD); return EXEC_RETRY; } #if DEBUG_METHODS printf ("dispatch_static_initializer: has clinit: %d, %d\n", (int) aRec, (int) retAddr); #endif // Static initializer is always the first method method = get_method_table(init); if ((byte *)method == get_binary_base() || method->signatureId != _6clinit_7_4_5V) { throw_new_exception (JAVA_LANG_NOSUCHMETHODERROR); return EXEC_EXCEPTION; } // Can we run it? if (!dispatch_special (method, retAddr)) return EXEC_RETRY; // Mark for next time set_init_state(init, C_INITIALIZING); // and claim the monitor current_stackframe()->monitor = (Object *)init; enter_monitor (currentThread, (Object *)init); return EXEC_RUN; }