/* File: Debugger.c Contains: Routines to assist the registered high-level debugger. Written by: Russ Daniels Copyright: © 1987-1992 by Apple Computer, Inc., all rights reserved. Change History (most recent first): <6> 3/23/92 JSM OSEvents.h is obsolete, use Events.h. <5> 2/4/91 DFH gbm,WhiteBoard:Fixed CoreRegisterDebugger's check for initial registration vs re-registration. Kept InitExceptions from being called during registration. This check was incorrect even before change <4>. <4> 1/21/91 DFH (ksm) Fixed CoreRegisterDebugger so that it can be called to change attributes. <0> 7/10/87 RDD New Today. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Glue.h" #include "Lomem.h" #include "Data.h" #include "SysMisc.h" #include "Puppet.h" #pragma segment Debugger /* Some function prototypes that should be in (yet another) header file */ void InitDebuggerExceptions(void); void UnplugDebuggerExceptions(void); /* CoreRegisterDebugger. Register process as the current debugger. Passing kNoProcess * unregisters the debugger. * NOTE: Assumes a5 == PROCESSMGRGLOBALS. */ OSErr CoreRegisterDebugger(ProcessSerialNumberPtr pPSN, Ptr pEntryRoutine, Ptr pExitRoutine, Ptr pToAppRoutine, unsigned short newDebugControlKeyCode) { CodeEntry *pCodeEntry; PEntryPtr pProc; /* Are we are unregistering the current debugger? */ if (EqualPSNConst(kNoProcess, pPSN)) { /* Don't unregister unless there is one */ if (pDebugProcess != nil) { assert(IsProcessEntry(pDebugProcess)); /* Kill of all his debuggees first. */ pProc = pProcessList; while (pProc != nil) { PEntryPtr pNextProc; assert(IsProcessEntry(pProc)); pNextProc = pProc->p_NextProcess; if (pProc->p_state == PRNULL) { KillProcess(&pProc->p_serialNumber); } pProc = pNextProc; } UnplugDebuggerExceptions(); /* And get rid of the Debugger code segment, too */ pCodeEntry = (CodeEntry *)CoreRegisterDebugger; UnloadSeg((Ptr)pCodeEntry); ReleaseResource(GET_SCOD_HDL_FROM_FNC_PTR(pCodeEntry)); pDebugProcess = nil; } } /* We are registering or changing the debugger attributes */ else { /* locate PEntry associated with given process */ if ((pProc = PEntryFromPSN(pPSN)) == nil) return(procNotFound); /* if registering, rather than changing, put in the exception handlers */ if (pDebugProcess == nil) InitDebuggerExceptions(); /* set the procptrs in global variables */ pDebugProcess = pProc; debugEntryRoutine = pEntryRoutine; debugExitRoutine = pExitRoutine; debugToAppRoutine = pToAppRoutine; debugControlKeyCode = newDebugControlKeyCode; } return noErr; } /* swap_state. This is an attenuated copy of switch_task. It changes all of the mapped * memory, but doesn't actually switch to the "new" task. */ unsigned short swap_state(PEntryPtr pInProc, PEntryPtr pOutProc) { void save_state(PEntryPtr); unsigned short restore_state(PEntryPtr, Boolean); /* Don't need to disable here, but don't know valid processor state! */ if (pInProc == pOutProc) return(disable()); /* save outgoers state, then switch to incomer's */ save_state(pOutProc); return(restore_state(pInProc, false)); } /* CoreReadWriteProcessMemory. To read the contents of memory for a given task, we * switch in that task's memory (by calling swap_state), move the memory, then * switch the caller's memory back in. Interrupts are disabled during the process. */ OSErr CoreReadWriteProcessMemory(ProcessSerialNumberPtr pPSN, Ptr procMemPtr, u_long cBytes, Ptr callerMemPtr, Boolean doRead) { Ptr tmpPtr; PEntryPtr pProc; unsigned short ps; if ((pProc = PEntryFromPSN(pPSN)) == nil) return(procNotFound); /* Assume readin' 'till proven it's writin' */ if (!doRead) { /* Do the big el swapo */ tmpPtr = procMemPtr; procMemPtr = callerMemPtr; callerMemPtr = tmpPtr; } ps = swap_state(pProc, pCurrentProcess); BlockMove (procMemPtr, callerMemPtr, cBytes); (void)swap_state(pCurrentProcess, pProc); spl(ps); return(noErr); } /* CoreReadWriteProcessFPURegs. To read the or write the Floating Point Unit context * for a process, we go straight to where we saved them when we last switched the * process out. Of course, the caller's FPU registers can be sampled directly. * NOTE: Extremely dependant on the order in which the registers are saved. * NOTE: Assumes a5 == PROCESSMGRGLOBALS */ OSErr CoreReadWriteProcessFPURegs(ProcessSerialNumberPtr pPSN, Ptr callerMemPtr, Boolean doRead) { PEntryPtr pProc; AllRegs *pAllRegs; Ptr srcPtr, dstPtr; /* Can do nothing if there is no FPU! */ if (MachineHasFPU == false) return(hardwareConfigErr); /* Locate the victim */ if ((pProc = PEntryFromPSN(pPSN)) == nil) return(procNotFound); if (pProc == pCurrentProcess) { if (doRead) GetLiveFPURegs(callerMemPtr); else SetLiveFPURegs(callerMemPtr); } else { pAllRegs = (AllRegs *)pProc->p_sp; /* If not a full FPU context, create one by munging the stack to give the * values an FRESTORE would. */ if (!pAllRegs->fpuFrame.isFullFPUFrame) { BlockMove(pAllRegs, (Ptr)pAllRegs - sizeof(FPUFrame), sizeof(SaveRegs)); pProc->p_sp = (Ptr)pAllRegs -= sizeof(FPUFrame); /* Lower the stack */ MemClear(&pAllRegs->fpuFrame, sizeof(FPUFrame)); /* Do whole frame for alignment speed, hopefully */ pAllRegs->fpuFrame.isFullFPUFrame = true; } /* Move data the right direction */ srcPtr = (doRead) ? &pAllRegs->fpuFrame.fpuUserRegs : callerMemPtr; dstPtr = (doRead) ? callerMemPtr : &pAllRegs->fpuFrame.fpuUserRegs; BlockMove(srcPtr, dstPtr, sizeof(FPUUserRegs)); } return(noErr); } #pragma segment kernel_segment /* c_RegisterDebugger. Verify caller, then call core routine in Debugger segment. * NOTE: Assumes THEZONE != ProcessMgrZone, in that it restores THEZONE unconditionally, * instead of using SafeSetZone and SafeRestoreZone. */ pascal OSErr c_RegisterDebugger(ProcessSerialNumberPtr pPSN, Ptr pEntryRoutine, Ptr pExitRoutine, Ptr pToAppRoutine, unsigned short newDebugControlKeyCode) { short appCurMap; THz appTheZone; unsigned long olda5; short retval; CodeEntry *pCodeEntry; olda5 = ProcessMgrA5SimpleSetup(); assert(THEZONE != ProcessMgrZone); appCurMap = CURMAP; CURMAP = SYSMAP; appTheZone = THEZONE; THEZONE = SYSZONE; pCodeEntry = CoreRegisterDebugger; if (CODE_ENTRY_UNLOADED(pCodeEntry) && GET_SCOD_HDL_FROM_FNC_PTR(pCodeEntry) == nil) { if ((retval = RESERR) == noErr) retval = resNotFound; } else retval = CoreRegisterDebugger(pPSN, pEntryRoutine, pExitRoutine, pToAppRoutine, newDebugControlKeyCode); THEZONE = appTheZone; CURMAP = appCurMap; A5SimpleRestore(olda5); return retval; } /* c_ReadWriteProcessMemory. Verify caller, then call core routine in Debugger segment. */ pascal OSErr c_ReadWriteProcessMemory(ProcessSerialNumberPtr pPSN, Ptr procMemPtr, unsigned long cBytes, Ptr callerMemPtr, Boolean doRead) { OSErr retval; PEntryPtr pProc; unsigned long olda5; olda5 = ProcessMgrA5SimpleSetup(); retval = noErr; /* Pay special favor to TMON. Mr. Horwat needs to be able get the topmaphandle * of other processes in a reentrant way (since, unlike SADE, he'd like to be * able to step through context switching code). * NOTE: This should be taken care of by better debugger support in general. */ if (doRead && (cBytes == (-1)) && (StripAddress(procMemPtr) == TopMapHndl)) { if ((pProc = PEntryFromPSN(pPSN)) == nil) retval = procNotFound; else BlockMove(&((*pProc->p_pcb)->topmaphandle), callerMemPtr, sizeof(Handle)); A5SimpleRestore(olda5); return(retval); } /* Require that the caller be preregistered. Also guarantees that the Debugger * segment is loaded. */ if (pCurrentProcess == pDebugProcess) CoreReadWriteProcessMemory(pPSN, procMemPtr, cBytes, callerMemPtr, doRead); else retval = protocolErr; A5SimpleRestore(olda5); return(retval); } /* c_ReadWriteProcessFPURegs. Verify caller, then call core routine in Debugger segment. */ pascal OSErr c_ReadWriteProcessFPURegs(ProcessSerialNumberPtr pPSN, Ptr callerMemPtr, Boolean doRead) { OSErr retval; unsigned long olda5; olda5 = ProcessMgrA5SimpleSetup(); /* Require that the caller be preregistered. Also guarantees that the Debugger * segment is loaded. */ retval = (pCurrentProcess == pDebugProcess) ? CoreReadWriteProcessFPURegs(pPSN, callerMemPtr, doRead) : protocolErr; A5SimpleRestore(olda5); return(retval); }