Mercurial > hg > graal-compiler
diff agent/src/os/win32/SwDbgSub.cpp @ 0:a61af66fc99e jdk7-b24
Initial load
author | duke |
---|---|
date | Sat, 01 Dec 2007 00:00:00 +0000 |
parents | |
children | c18cbe5936b8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/src/os/win32/SwDbgSub.cpp Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,883 @@ +/* + * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code 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 code 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 in the LICENSE file that + * accompanied this code). + * + * 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 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This is the source code for the subprocess forked by the Simple +// Windows Debug Server. It assumes most of the responsibility for the +// debug session, and processes all of the commands sent by clients. + +// Disable too-long symbol warnings +#pragma warning ( disable : 4786 ) + +#include <iostream> +#include <vector> +#include <stdlib.h> +#include <assert.h> +// Must come before windows.h +#include <winsock2.h> +#include <windows.h> +#include "IOBuf.hpp" +#include "libInfo.hpp" +#include "LockableList.hpp" +#include "Message.hpp" +#include "Monitor.hpp" +#include "nt4internals.hpp" + +// Uncomment the #define below to get messages on stderr +// #define DEBUGGING + +using namespace std; + +DWORD pid; +HANDLE procHandle; +IOBuf* ioBuf; + +// State flags indicating whether the attach to the remote process +// definitively succeeded or failed +volatile bool attachFailed = false; +volatile bool attachSucceeded = false; + +// State flag indicating whether the target process is suspended. +// Modified by suspend()/resume(), viewed by debug thread, but only +// under cover of the threads lock. +volatile bool suspended = false; + +// State flags indicating whether we are considered to be attached to +// the target process and are therefore queuing up events to be sent +// back to the debug server. These flags are only accessed and +// modified under the cover of the eventLock. +Monitor* eventLock; +// The following is set to true when a client is attached to this process +volatile bool generateDebugEvents = false; +// Pointer to current debug event; non-NULL indicates a debug event is +// waiting to be sent to the client. Main thread sets this to NULL to +// indicate that the event has been consumed; also sets +// passEventToClient, below. +volatile DEBUG_EVENT* curDebugEvent = NULL; +// Set by main thread to indicate whether the most recently posted +// debug event should be passed on to the target process. +volatile bool passEventToClient = true; + +void conditionalPostDebugEvent(DEBUG_EVENT* ev, DWORD* continueOrNotHandledFlag) { + // FIXME: make it possible for the client to enable and disable + // certain types of events (have to do so in a platform-independent + // manner) + switch (ev->dwDebugEventCode) { + case EXCEPTION_DEBUG_EVENT: + switch (ev->u.Exception.ExceptionRecord.ExceptionCode) { + case EXCEPTION_BREAKPOINT: break; + case EXCEPTION_SINGLE_STEP: break; + case EXCEPTION_ACCESS_VIOLATION: break; + default: return; + } + } + eventLock->lock(); + if (generateDebugEvents) { + curDebugEvent = ev; + while (curDebugEvent != NULL) { + eventLock->wait(); + } + if (passEventToClient) { + *continueOrNotHandledFlag = DBG_EXCEPTION_NOT_HANDLED; + } else { + *continueOrNotHandledFlag = DBG_CONTINUE; + } + } + eventLock->unlock(); +} + + +//---------------------------------------------------------------------- +// Module list +// + +vector<LibInfo> libs; + +//---------------------------------------------------------------------- +// Thread list +// + +struct ThreadInfo { + DWORD tid; + HANDLE thread; + + ThreadInfo(DWORD tid, HANDLE thread) { + this->tid = tid; + this->thread = thread; + } +}; + +class ThreadList : public LockableList<ThreadInfo> { +public: + bool removeByThreadID(DWORD tid) { + for (InternalListType::iterator iter = internalList.begin(); + iter != internalList.end(); iter++) { + if ((*iter).tid == tid) { + internalList.erase(iter); + return true; + } + } + return false; + } + HANDLE threadIDToHandle(DWORD tid) { + for (InternalListType::iterator iter = internalList.begin(); + iter != internalList.end(); iter++) { + if ((*iter).tid == tid) { + return (*iter).thread; + } + } + return NULL; + } +}; + +ThreadList threads; + +//---------------------------------------------------------------------- +// INITIALIZATION AND TERMINATION +// + +void +printError(const char* prefix) { + DWORD detail = GetLastError(); + LPTSTR message; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + detail, + 0, + (LPTSTR) &message, + 1, + NULL); + // FIXME: This is signaling an error: "The handle is invalid." ? + // Do I have to do all of my WaitForDebugEvent calls from the same thread? + cerr << prefix << ": " << message << endl; + LocalFree(message); +} + +void +endProcess(bool waitForProcess = true) { + NT4::unloadNTDLL(); + if (waitForProcess) { + // Though we're exiting because of an error, do not tear down the + // target process. + WaitForSingleObject(procHandle, INFINITE); + } + CloseHandle(procHandle); + exit(0); +} + +DWORD WINAPI +debugThreadEntry(void*) { +#ifdef DEBUGGING + DWORD lastMsgId = 0; + int count = 0; +#endif + + if (!DebugActiveProcess(pid)) { + attachFailed = true; + return 0; + } + + // Wait for debug events. We keep the information from some of these + // on the side in anticipation of later queries by the client. NOTE + // that we leave the process running. The main thread is responsible + // for suspending and resuming all currently-active threads upon + // client attach and detach. + + while (true) { + DEBUG_EVENT ev; + if (!WaitForDebugEvent(&ev, INFINITE)) { +#ifdef DEBUGGING + if (++count < 10) { + // FIXME: This is signaling an error: "The handle is invalid." ? + // Do I have to do all of my WaitForDebugEvent calls from the same thread? + printError("WaitForDebugEvent failed"); + } +#endif + } else { + +#ifdef DEBUGGING + if (ev.dwDebugEventCode != lastMsgId) { + lastMsgId = ev.dwDebugEventCode; + count = 0; + cerr << "Debug thread received event " << ev.dwDebugEventCode << endl; + } else { + if (++count < 10) { + cerr << "Debug thread received event " << ev.dwDebugEventCode << endl; + } + } +#endif + + DWORD dbgContinueMode = DBG_CONTINUE; + + switch (ev.dwDebugEventCode) { + case LOAD_DLL_DEBUG_EVENT: + conditionalPostDebugEvent(&ev, &dbgContinueMode); + break; + + case UNLOAD_DLL_DEBUG_EVENT: + conditionalPostDebugEvent(&ev, &dbgContinueMode); + break; + + case CREATE_PROCESS_DEBUG_EVENT: + threads.lock(); + // FIXME: will this deal properly with child processes? If + // not, is it possible to make it do so? +#ifdef DEBUGGING + cerr << "CREATE_PROCESS_DEBUG_EVENT " << ev.dwThreadId + << " " << ev.u.CreateProcessInfo.hThread << endl; +#endif + if (ev.u.CreateProcessInfo.hThread != NULL) { + threads.add(ThreadInfo(ev.dwThreadId, ev.u.CreateProcessInfo.hThread)); + } + threads.unlock(); + break; + + case CREATE_THREAD_DEBUG_EVENT: + threads.lock(); +#ifdef DEBUGGING + cerr << "CREATE_THREAD_DEBUG_EVENT " << ev.dwThreadId + << " " << ev.u.CreateThread.hThread << endl; +#endif + if (suspended) { + // Suspend this thread before adding it to the thread list + SuspendThread(ev.u.CreateThread.hThread); + } + threads.add(ThreadInfo(ev.dwThreadId, ev.u.CreateThread.hThread)); + threads.unlock(); + break; + + case EXIT_THREAD_DEBUG_EVENT: + threads.lock(); +#ifdef DEBUGGING + cerr << "EXIT_THREAD_DEBUG_EVENT " << ev.dwThreadId << endl; +#endif + threads.removeByThreadID(ev.dwThreadId); + threads.unlock(); + break; + + case EXCEPTION_DEBUG_EVENT: + // cerr << "EXCEPTION_DEBUG_EVENT" << endl; + switch (ev.u.Exception.ExceptionRecord.ExceptionCode) { + case EXCEPTION_BREAKPOINT: + // cerr << "EXCEPTION_BREAKPOINT" << endl; + if (!attachSucceeded && !attachFailed) { + attachSucceeded = true; + } + break; + + default: + dbgContinueMode = DBG_EXCEPTION_NOT_HANDLED; + break; + } + conditionalPostDebugEvent(&ev, &dbgContinueMode); + break; + + case EXIT_PROCESS_DEBUG_EVENT: + endProcess(false); + // NOT REACHED + break; + + default: +#ifdef DEBUGGING + cerr << "Received debug event " << ev.dwDebugEventCode << endl; +#endif + break; + } + + ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, dbgContinueMode); + } + } +} + +bool +attachToProcess() { + // Create event lock + eventLock = new Monitor(); + + // Get a process handle for later + procHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (procHandle == NULL) { + return false; + } + + // Start up the debug thread + DWORD debugThreadId; + if (CreateThread(NULL, 0, &debugThreadEntry, NULL, 0, &debugThreadId) == NULL) { + // Failed to make background debug thread. Fail. + return false; + } + + while ((!attachSucceeded) && (!attachFailed)) { + Sleep(1); + } + + if (attachFailed) { + return false; + } + + assert(attachSucceeded); + + return true; +} + +bool +readMessage(Message* msg) { + DWORD numRead; + if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), + msg, + sizeof(Message), + &numRead, + NULL)) { + return false; + } + if (numRead != sizeof(Message)) { + return false; + } + // For "poke" messages, must follow up by reading raw data + if (msg->type == Message::POKE) { + char* dataBuf = new char[msg->pokeArg.numBytes]; + if (dataBuf == NULL) { + return false; + } + if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), + dataBuf, + msg->pokeArg.numBytes, + &numRead, + NULL)) { + delete[] dataBuf; + return false; + } + if (numRead != msg->pokeArg.numBytes) { + delete[] dataBuf; + return false; + } + msg->pokeArg.data = (void *) dataBuf; + } + return true; +} + +void +handlePeek(Message* msg) { +#ifdef DEBUGGING + cerr << "Entering handlePeek()" << endl; +#endif + + char* memBuf = new char[msg->peekArg.numBytes]; + if (memBuf == NULL) { + ioBuf->writeString("B"); + ioBuf->writeBinChar(0); + ioBuf->flush(); + delete[] memBuf; + return; + } + + // Try fast case first + DWORD numRead; + BOOL res = ReadProcessMemory(procHandle, + (LPCVOID) msg->peekArg.address, + memBuf, + msg->peekArg.numBytes, + &numRead); + if (res && (numRead == msg->peekArg.numBytes)) { + + // OK, complete success. Phew. +#ifdef DEBUGGING + cerr << "Peek success case" << endl; +#endif + ioBuf->writeString("B"); + ioBuf->writeBinChar(1); + ioBuf->writeBinUnsignedInt(numRead); + ioBuf->writeBinChar(1); + ioBuf->writeBinBuf(memBuf, numRead); + } else { +#ifdef DEBUGGING + cerr << "*** Peek slow case ***" << endl; +#endif + + ioBuf->writeString("B"); + ioBuf->writeBinChar(1); + + // Use VirtualQuery to speed things up a bit + DWORD numLeft = msg->peekArg.numBytes; + char* curAddr = (char*) msg->peekArg.address; + while (numLeft > 0) { + MEMORY_BASIC_INFORMATION memInfo; + VirtualQueryEx(procHandle, curAddr, &memInfo, sizeof(memInfo)); + DWORD numToRead = memInfo.RegionSize; + if (numToRead > numLeft) { + numToRead = numLeft; + } + DWORD numRead; + if (memInfo.State == MEM_COMMIT) { + // Read the process memory at this address for this length + // FIXME: should check the result of this read + ReadProcessMemory(procHandle, curAddr, memBuf, + numToRead, &numRead); + // Write this out +#ifdef DEBUGGING + cerr << "*** Writing " << numToRead << " bytes as mapped ***" << endl; +#endif + ioBuf->writeBinUnsignedInt(numToRead); + ioBuf->writeBinChar(1); + ioBuf->writeBinBuf(memBuf, numToRead); + } else { + // Indicate region is free +#ifdef DEBUGGING + cerr << "*** Writing " << numToRead << " bytes as unmapped ***" << endl; +#endif + ioBuf->writeBinUnsignedInt(numToRead); + ioBuf->writeBinChar(0); + } + curAddr += numToRead; + numLeft -= numToRead; + } + } + + ioBuf->flush(); + delete[] memBuf; +#ifdef DEBUGGING + cerr << "Exiting handlePeek()" << endl; +#endif +} + +void +handlePoke(Message* msg) { +#ifdef DEBUGGING + cerr << "Entering handlePoke()" << endl; +#endif + DWORD numWritten; + BOOL res = WriteProcessMemory(procHandle, + (LPVOID) msg->pokeArg.address, + msg->pokeArg.data, + msg->pokeArg.numBytes, + &numWritten); + if (res && (numWritten == msg->pokeArg.numBytes)) { + // Success + ioBuf->writeBoolAsInt(true); +#ifdef DEBUGGING + cerr << " (Succeeded)" << endl; +#endif + } else { + // Failure + ioBuf->writeBoolAsInt(false); +#ifdef DEBUGGING + cerr << " (Failed)" << endl; +#endif + } + ioBuf->writeEOL(); + ioBuf->flush(); + // We clean up the data + char* dataBuf = (char*) msg->pokeArg.data; + delete[] dataBuf; +#ifdef DEBUGGING + cerr << "Exiting handlePoke()" << endl; +#endif +} + +bool +suspend() { + if (suspended) { + return false; + } + // Before we suspend, we must take a snapshot of the loaded module + // names and base addresses, since acquiring this snapshot requires + // starting and exiting a thread in the remote process (at least on + // NT 4). + libs.clear(); +#ifdef DEBUGGING + cerr << "Starting suspension" << endl; +#endif + libInfo(pid, libs); +#ifdef DEBUGGING + cerr << " Got lib info" << endl; +#endif + threads.lock(); +#ifdef DEBUGGING + cerr << " Got thread lock" << endl; +#endif + suspended = true; + int j = 0; + for (int i = 0; i < threads.size(); i++) { + j++; + SuspendThread(threads.get(i).thread); + } +#ifdef DEBUGGING + cerr << "Suspended " << j << " threads" << endl; +#endif + threads.unlock(); + return true; +} + +bool +resume() { + if (!suspended) { + return false; + } + threads.lock(); + suspended = false; + for (int i = 0; i < threads.size(); i++) { + ResumeThread(threads.get(i).thread); + } + threads.unlock(); +#ifdef DEBUGGING + cerr << "Resumed process" << endl; +#endif + return true; +} + +int +main(int argc, char **argv) +{ + if (argc != 2) { + // Should only be used by performing CreateProcess within SwDbgSrv + exit(1); + } + + if (sscanf(argv[1], "%u", &pid) != 1) { + exit(1); + } + + // Try to attach to process + if (!attachToProcess()) { + // Attach failed. Notify parent by writing result to stdout file + // handle. + char res = 0; + DWORD numBytes; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &res, sizeof(res), + &numBytes, NULL); + exit(1); + } + + // Server is expecting success result back. + char res = 1; + DWORD numBytes; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &res, sizeof(res), + &numBytes, NULL); + + // Initialize our I/O buffer + ioBuf = new IOBuf(32768, 131072); + ioBuf->setOutputFileHandle(GetStdHandle(STD_OUTPUT_HANDLE)); + + // At this point we are attached. Enter our main loop which services + // requests from the server. Note that in order to handle attach/ + // detach properly (i.e., resumption of process upon "detach") we + // will need another thread which handles debug events. + while (true) { + // Read a message from the server + Message msg; + if (!readMessage(&msg)) { + endProcess(); + } + +#ifdef DEBUGGING + cerr << "Main thread read message: " << msg.type << endl; +#endif + + switch (msg.type) { + // ATTACH and DETACH messages MUST come in pairs + case Message::ATTACH: + suspend(); + eventLock->lock(); + generateDebugEvents = true; + eventLock->unlock(); + break; + + case Message::DETACH: + eventLock->lock(); + generateDebugEvents = false; + // Flush remaining event if any + if (curDebugEvent != NULL) { + curDebugEvent = NULL; + eventLock->notifyAll(); + } + eventLock->unlock(); + resume(); + break; + + case Message::LIBINFO: + { + if (!suspended) { + ioBuf->writeInt(0); + } else { + // Send back formatted text + ioBuf->writeInt(libs.size()); + for (int i = 0; i < libs.size(); i++) { + ioBuf->writeSpace(); + ioBuf->writeInt(1); + ioBuf->writeSpace(); + ioBuf->writeInt(libs[i].name.size()); + ioBuf->writeSpace(); + ioBuf->writeString(libs[i].name.c_str()); + ioBuf->writeSpace(); + ioBuf->writeAddress(libs[i].base); + } + } + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + + case Message::PEEK: + handlePeek(&msg); + break; + + case Message::POKE: + handlePoke(&msg); + break; + + case Message::THREADLIST: + { + if (!suspended) { + ioBuf->writeInt(0); + } else { + threads.lock(); + ioBuf->writeInt(threads.size()); + for (int i = 0; i < threads.size(); i++) { + ioBuf->writeSpace(); + ioBuf->writeAddress((void*) threads.get(i).thread); + } + threads.unlock(); + } + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + + case Message::DUPHANDLE: + { + HANDLE dup; + if (DuplicateHandle(procHandle, + msg.handleArg.handle, + GetCurrentProcess(), + &dup, + 0, + FALSE, + DUPLICATE_SAME_ACCESS)) { + ioBuf->writeBoolAsInt(true); + ioBuf->writeSpace(); + ioBuf->writeAddress((void*) dup); + } else { + ioBuf->writeBoolAsInt(false); + } + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + + case Message::CLOSEHANDLE: + { + CloseHandle(msg.handleArg.handle); + break; + } + + case Message::GETCONTEXT: + { + if (!suspended) { + ioBuf->writeBoolAsInt(false); + } else { + CONTEXT context; + context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; + if (GetThreadContext(msg.handleArg.handle, &context)) { + ioBuf->writeBoolAsInt(true); + // EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP, EIP, DS, ES, FS, GS, + // CS, SS, EFLAGS, DR0, DR1, DR2, DR3, DR6, DR7 + // See README-commands.txt + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Eax); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Ebx); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Ecx); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Edx); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Esi); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Edi); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Ebp); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Esp); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Eip); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegDs); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegEs); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegFs); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegGs); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegCs); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegSs); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.EFlags); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr0); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr1); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr2); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr3); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr6); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr7); + } else { + ioBuf->writeBoolAsInt(false); + } + } + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + + case Message::SETCONTEXT: + { + if (!suspended) { + ioBuf->writeBoolAsInt(false); + } else { + CONTEXT context; + context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; + context.Eax = msg.setContextArg.Eax; + context.Ebx = msg.setContextArg.Ebx; + context.Ecx = msg.setContextArg.Ecx; + context.Edx = msg.setContextArg.Edx; + context.Esi = msg.setContextArg.Esi; + context.Edi = msg.setContextArg.Edi; + context.Ebp = msg.setContextArg.Ebp; + context.Esp = msg.setContextArg.Esp; + context.Eip = msg.setContextArg.Eip; + context.SegDs = msg.setContextArg.Ds; + context.SegEs = msg.setContextArg.Es; + context.SegFs = msg.setContextArg.Fs; + context.SegGs = msg.setContextArg.Gs; + context.SegCs = msg.setContextArg.Cs; + context.SegSs = msg.setContextArg.Ss; + context.EFlags = msg.setContextArg.EFlags; + context.Dr0 = msg.setContextArg.Dr0; + context.Dr1 = msg.setContextArg.Dr1; + context.Dr2 = msg.setContextArg.Dr2; + context.Dr3 = msg.setContextArg.Dr3; + context.Dr6 = msg.setContextArg.Dr6; + context.Dr7 = msg.setContextArg.Dr7; + if (SetThreadContext(msg.setContextArg.handle, &context)) { + ioBuf->writeBoolAsInt(true); + } else { + ioBuf->writeBoolAsInt(false); + } + } + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + + case Message::SELECTORENTRY: + { + LDT_ENTRY entry; + + if (GetThreadSelectorEntry(msg.selectorArg.handle, + msg.selectorArg.selector, + &entry)) { + ioBuf->writeBoolAsInt(true); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.LimitLow); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.BaseLow); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.HighWord.Bytes.BaseMid); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.HighWord.Bytes.Flags1); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.HighWord.Bytes.Flags2); + ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.HighWord.Bytes.BaseHi); + } else { + ioBuf->writeBoolAsInt(false); + } + + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + + case Message::SUSPEND: + suspend(); + break; + + case Message::RESUME: + resume(); + break; + + case Message::POLLEVENT: + eventLock->lock(); + if (curDebugEvent == NULL) { + ioBuf->writeBoolAsInt(false); + } else { + ioBuf->writeBoolAsInt(true); + ioBuf->writeSpace(); + threads.lock(); + ioBuf->writeAddress((void*) threads.threadIDToHandle(curDebugEvent->dwThreadId)); + threads.unlock(); + ioBuf->writeSpace(); + ioBuf->writeUnsignedInt(curDebugEvent->dwDebugEventCode); + // Figure out what else to write + switch (curDebugEvent->dwDebugEventCode) { + case LOAD_DLL_DEBUG_EVENT: + ioBuf->writeSpace(); + ioBuf->writeAddress(curDebugEvent->u.LoadDll.lpBaseOfDll); + break; + + case UNLOAD_DLL_DEBUG_EVENT: + ioBuf->writeSpace(); + ioBuf->writeAddress(curDebugEvent->u.UnloadDll.lpBaseOfDll); + break; + + case EXCEPTION_DEBUG_EVENT: + { + DWORD code = curDebugEvent->u.Exception.ExceptionRecord.ExceptionCode; + ioBuf->writeSpace(); + ioBuf->writeUnsignedInt(code); + ioBuf->writeSpace(); + ioBuf->writeAddress(curDebugEvent->u.Exception.ExceptionRecord.ExceptionAddress); + switch (curDebugEvent->u.Exception.ExceptionRecord.ExceptionCode) { + case EXCEPTION_ACCESS_VIOLATION: + ioBuf->writeSpace(); + ioBuf->writeBoolAsInt(curDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0] != 0); + ioBuf->writeSpace(); + ioBuf->writeAddress((void*) curDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[1]); + break; + + default: + break; + } + break; + } + + default: + break; + } + } + eventLock->unlock(); + ioBuf->writeEOL(); + ioBuf->flush(); + break; + + case Message::CONTINUEEVENT: + eventLock->lock(); + if (curDebugEvent == NULL) { + ioBuf->writeBoolAsInt(false); + } else { + curDebugEvent = NULL; + passEventToClient = msg.boolArg.val; + ioBuf->writeBoolAsInt(true); + eventLock->notify(); + } + eventLock->unlock(); + ioBuf->writeEOL(); + ioBuf->flush(); + break; + } + } + + endProcess(); + + // NOT REACHED + return 0; +}