예제 #1
0
// Trap a particular thread
BOOL
TrapThread(DWORD dwProcessId, DWORD dwThreadId)
{
    PPROCESS_INFO pProcessInfo;
    PTHREAD_INFO pThreadInfo;
    HANDLE hProcess;
    HANDLE hThread;

    /* FIXME: synchronize access to globals */

    pProcessInfo = &g_Processes[dwProcessId];
    hProcess = pProcessInfo->hProcess;
    assert(hProcess);

    pThreadInfo = &pProcessInfo->Threads[dwThreadId];
    hThread = pThreadInfo->hThread;
    assert(hThread);

    DWORD dwRet = SuspendThread(hThread);
    if (dwRet != (DWORD)-1) {
        CONTEXT Context;
        if (getThreadContext(hProcess, hThread, &Context)) {
            dumpStack(hProcess, hThread, &Context);
        }
        writeDump(dwProcessId, pProcessInfo, nullptr);

        // TODO: Flag fTerminating

        exit(3);
    }

    TerminateProcess(hProcess, 3);

    return TRUE;
}
예제 #2
0
// onShipKilled
void EventManager::fireEvent(EventType eventType, Ship *ship, BfObject *damagingObject, BfObject *shooter)
{
   if(suppressEvents(eventType))
      return;

   lua_State *L = LuaScriptRunner::getL();

   TNLAssert(lua_gettop(L) == 0 || dumpStack(L), "Stack dirty!");

   for(S32 i = 0; i < subscriptions[eventType].size(); i++)
   {
      ship->push(L);                // -- ship

      if(damagingObject)
         damagingObject->push(L);   // -- ship, damagingObject
      else
         lua_pushnil(L);

      if(shooter)
         shooter->push(L);          // -- ship, damagingObject, shooter
      else
         lua_pushnil(L);

      fire(L, subscriptions[eventType][i].subscriber, eventDefs[eventType].function, subscriptions[eventType][i].context);
   }
}
예제 #3
0
파일: main.c 프로젝트: ted-lin/algo
int main()
{
	int size = sizeof(test1)/sizeof(int);
	int i;
	Stack *s = createStack();

	for (i = 0; i < size; ++i)
		push(s, createNode(test1[i]));

	dumpStack(s);
	for (i = 0; i < size; ++i) {
		free(pop(s));
		dumpStack(s);
	}
	return 0;
}
예제 #4
0
// onShipEnteredZone, onShipLeftZone
void EventManager::fireEvent(EventType eventType, Ship *ship, Zone *zone)
{
   if(suppressEvents(eventType))   
      return;

   lua_State *L = LuaScriptRunner::getL();

   TNLAssert(lua_gettop(L) == 0 || dumpStack(L), "Stack dirty!");

   for(S32 i = 0; i < subscriptions[eventType].size(); i++)
   {
      try   
      {
         // Passing ship, zone, zoneType, zoneId
         ship->push(L);                                     // -- ship
         zone->push(L);                                     // -- ship, zone   
         lua_pushinteger(L, zone->getObjectTypeNumber());   // -- ship, zone, zone->objTypeNumber
         lua_pushinteger(L, zone->getUserAssignedId());     // -- ship, zone, zone->objTypeNumber, zone->id

         fire(L, subscriptions[eventType][i].subscriber, eventDefs[eventType].function, subscriptions[eventType][i].context);
      }
      catch(LuaException &e)
      {
         handleEventFiringError(L, subscriptions[eventType][i], eventType, e.what());
         clearStack(L);
         return;
      }
   }
}
예제 #5
0
// Note that player can be NULL, in which case we'll pass nil to the listeners
// callerId will be NULL when player sends message
void EventManager::fireEvent(LuaScriptRunner *sender, EventType eventType, const char *message, LuaPlayerInfo *playerInfo, bool global)
{
   if(suppressEvents(eventType))   
      return;

   lua_State *L = LuaScriptRunner::getL();

   TNLAssert(lua_gettop(L) == 0 || dumpStack(L), "Stack dirty!");

   for(S32 i = 0; i < subscriptions[eventType].size(); i++)
   {
      if(sender == subscriptions[eventType][i].subscriber)    // Don't alert sender about own message!
         continue;

      lua_pushstring(L, message);   // -- message

      if(playerInfo)
         playerInfo->push(L);       // -- message, playerInfo
      else
         lua_pushnil(L);            

      lua_pushboolean(L, global);   // -- message, player, isGlobal

      fire(L, subscriptions[eventType][i].subscriber, eventDefs[eventType].function, subscriptions[eventType][i].context);
   }
}
예제 #6
0
void
CwshExprStack::
push(const std::string &value)
{
  if (debug_) {
    dumpStack();
    std::cerr << "Push Value: " << value << std::endl;
  }

  CwshExprStackNode *stack_node = new CwshExprStackNode(value);

  push(stack_node);

  if (debug_)
    dumpStack();
}
예제 #7
0
void
CwshExprStack::
push(CwshExprOperator *opr)
{
  if (debug_) {
    dumpStack();
    std::cerr << "Push Opr: " << opr->getToken() << std::endl;
  }

  CwshExprStackNode *stack_node = new CwshExprStackNode(opr);

  push(stack_node);

  if (debug_)
    dumpStack();
}
예제 #8
0
void handler(int sig) 
{
   unsigned long addr = (unsigned long)&sig ;
   unsigned long page = addr & ~0xFFF ; // 4K
   unsigned long size = page+0x1000-addr ;
   hexDumper_t dumpStack( &sig, size ); // just dump this page
   while( dumpStack.nextLine() )
      fprintf( stderr, "%s\n", dumpStack.getLine() );

   void *btArray[128];
   int const btSize = backtrace(btArray, sizeof(btArray) / sizeof(void *) );

   fprintf( stderr, "########## Backtrace ##########\n"
                    "Number of elements in backtrace: %u\n", btSize );

   if (btSize > 0)
      backtrace_symbols_fd( btArray, btSize, fileno(stdout) );

   fprintf( stderr, "Handler done.\n" );
   fflush( stderr );
   if( oldint.sa_handler )
      oldint.sa_handler( sig );

   exit( 1 );
}
예제 #9
0
Node *pop(void)
{
  assert(stackPointer > stack);
#ifdef DEBUG
  dumpStack();  fprintf(stderr, " POP\n");
#endif
  return *stackPointer--;
}
예제 #10
0
std::ostream& operator <<(std::ostream& out, const DebugStackObject& obj) {
    if (obj.impl.empty())
        return out;
    auto value = obj.impl.top();
    obj.impl.pop();
    dumpStack(out, obj.impl);
    out << DebugObject { value };
    return out;
}
예제 #11
0
///// Initialize levelgen specific stuff
bool LuaLevelGenerator::prepareEnvironment()
{
   if(!LuaScriptRunner::prepareEnvironment())
      return false;

   // Set this first so we have this object available in the helper functions in case we need overrides
   setSelf(L, this, "levelgen");

   TNLAssert(lua_gettop(L) == 0 || dumpStack(L), "Stack dirty!");

   if(!loadCompileRunEnvironmentScript("timer.lua") || !loadAndRunGlobalFunction(L, LEVELGEN_HELPER_FUNCTIONS_KEY, LevelgenContext))
      return false;

   // Stack is dirty here
   TNLAssert(lua_gettop(L) == 0 || dumpStack(L), "Stack dirty!");

   return true;
}
예제 #12
0
void dumpStack(std::ostream& out, std::stack<ObjectPtr>& stack) {
    if (stack.empty())
        return;
    auto value = stack.top();
    stack.pop();
    dumpStack(out, stack);
    out << DebugObject{ value } << " ";
    stack.push(value);
}
예제 #13
0
Node *push(Node *node)
{
  assert(node);
  assert(stackPointer < stack + 1023);
#ifdef DEBUG
  dumpStack();  fprintf(stderr, " PUSH ");  Node_print(node);  fprintf(stderr, "\n");
#endif
  return *++stackPointer= node;
}
예제 #14
0
파일: debugger.cpp 프로젝트: xGreat/drmingw
static void
refreshSymbolsAndDumpStack(HANDLE hProcess, HANDLE hThread)
{
    assert(hProcess);
    assert(hThread);

    loadSymbols(hProcess);

    dumpStack(hProcess, hThread, NULL);
}
예제 #15
0
static void
refreshSymbolsAndDumpStack(HANDLE hProcess, HANDLE hThread)
{
    assert(hProcess);
    assert(hThread);

    // XXX: Deferred symbols don't get loaded without this
    SymRefreshModuleList(hProcess);

    dumpStack(hProcess, hThread, NULL);
}
예제 #16
0
파일: Exception.cpp 프로젝트: imace/kkS
static void defaultSiganlHandler(int sig)
{
	try
	{
		dumpStack("SignalException", false);
	}
	catch(...)
	{

	}
}
예제 #17
0
void popWarnHandler()
/* Revert to old warn handler. */
{
struct perThreadAbortVars *ptav = getThreadVars();
if (ptav->warnIx <= 0)
    {
    if (ptav->debugPushPopErr)
        dumpStack("popWarnHandler underflow");
    errAbort("Too few popWarnHandlers");
    }
--ptav->warnIx;
}
예제 #18
0
void pushAbortHandler(AbortHandler handler)
/* Set abort handler */
{
struct perThreadAbortVars *ptav = getThreadVars();
if (ptav->abortIx >= maxAbortHandlers-1)
    {
    if (ptav->debugPushPopErr)
        dumpStack("pushAbortHandler overflow");
    errAbort("Too many pushAbortHandlers, can only handle %d", maxAbortHandlers-1);
    }
ptav->abortArray[++ptav->abortIx] = handler;
}
예제 #19
0
void popAbortHandler()
/* Revert to old abort handler. */
{
struct perThreadAbortVars *ptav = getThreadVars();
if (ptav->abortIx <= 0)
    {
    if (ptav->debugPushPopErr)
        dumpStack("popAbortHandler underflow");
    errAbort("Too many popAbortHandlers\n");
    }
--ptav->abortIx;
}
예제 #20
0
// onNexusOpened, onNexusClosed
void EventManager::fireEvent(EventType eventType)
{
   if(suppressEvents(eventType))   
      return;

   lua_State *L = LuaScriptRunner::getL();

   TNLAssert(lua_gettop(L) == 0 || dumpStack(L), "Stack dirty!");

   for(S32 i = 0; i < subscriptions[eventType].size(); i++)
      fire(L, subscriptions[eventType][i].subscriber, eventDefs[eventType].function, subscriptions[eventType][i].context);
}
static void hDumpStackAbortHandler()
/* abort handle that prints stack dump then invokes the previous abort
 * handler on the stack. */
{
if (!stackDumpDisabled)
    {
    stackDumpDisabled = TRUE;
    popWarnHandler(); // remove us from the stack
    dumpStack("\nStack dump:");
    // continue with next abort handler
    noWarnAbort();
    }
}
예제 #22
0
bool
CwshExprStack::
pop(std::string &value)
{
  if (debug_)
    dumpStack();

  if (pstack_node_ != stack_nodes_.end() &&
      (*pstack_node_)->getType() == CwshExprStackNodeType::VALUE) {
    value = (*pstack_node_)->getValue();

    remove(*pstack_node_);

    if (debug_) {
      std::cerr << "Pop Value: " << value << std::endl;
      dumpStack();
    }

    return true;
  }
  else
    return false;
}
예제 #23
0
bool
CwshExprStack::
pop(CwshExprOperator **opr)
{
  if (debug_)
    dumpStack();

  if (pstack_node_ != stack_nodes_.end() &&
      (*pstack_node_)->getType() == CwshExprStackNodeType::OPERATOR) {
    *opr = (*pstack_node_)->getOperator();

    remove(*pstack_node_);

    if (debug_) {
      std::cerr << "Pop Opr: " << (*opr)->getToken() << std::endl;
      dumpStack();
    }

    return true;
  }
  else
    return false;
}
예제 #24
0
// TODO: Merge with luaLevelGenerator version, which is almost identical
bool Console::prepareEnvironment()  
{ 
#ifndef BF_NO_CONSOLE

   if(!Parent::prepareEnvironment())
      return false;

   setScriptContext(L, ConsoleContext);

   TNLAssert(lua_gettop(L) == 0 || dumpStack(L), "Stack not cleared!");

#endif

   return true;
}
예제 #25
0
파일: main.c 프로젝트: Marfle-Bark/hwvm
int main() {
  printf("\n\n***Launching HWVM...\n\n\n");
  init();
  
  int cycles = 0;

  while (running) {
    eval();
    cycles++;
  }

  printf("\n\n***Closing HWVM...\n");
  printf("***VM ran for %i cycles.\n", cycles);
  dumpRegisters();
  dumpStack();

  return 0;
}
예제 #26
0
// onPlayerJoined, onPlayerLeft, onPlayerTeamChanged
void EventManager::fireEvent(LuaScriptRunner *player, EventType eventType, LuaPlayerInfo *playerInfo)
{
   if(suppressEvents(eventType))   
      return;

   lua_State *L = LuaScriptRunner::getL();

   TNLAssert(lua_gettop(L) == 0 || dumpStack(L), "Stack dirty!");

   for(S32 i = 0; i < subscriptions[eventType].size(); i++)
   {
      if(player == subscriptions[eventType][i].subscriber)    // Don't trouble player with own joinage or leavage!
         continue;

      playerInfo->push(L);          // -- playerInfo
      fire(L, subscriptions[eventType][i].subscriber, eventDefs[eventType].function, subscriptions[eventType][i].context);
   }
}
예제 #27
0
// onTick
void EventManager::fireEvent(EventType eventType, U32 deltaT)
{
   if(suppressEvents(eventType))   
      return;

   if(eventType == TickEvent)
      mStepCount--;   

   lua_State *L = LuaScriptRunner::getL();

   TNLAssert(lua_gettop(L) == 0 || dumpStack(L), "Stack dirty!");

   for(S32 i = 0; i < subscriptions[eventType].size(); i++)
   {
      lua_pushinteger(L, deltaT);   // -- deltaT
      fire(L, subscriptions[eventType][i].subscriber, eventDefs[eventType].function, subscriptions[eventType][i].context);
   }
}
예제 #28
0
  int main() {
        int *stack, ch, data;

        /* get the size of the stack from the user */
        printf("Enter the size of the stack:");
        scanf("%d", &size);

        /* allocate memory for the stack */
        stack = (int *)malloc(sizeof(int) * size);

        while (1) {
                printf("1. Push\n2. Pop\n3. Dump Stack\n");
                printf("4. Exit\nEnter your choice:");
                scanf("%d", &ch);

                switch (ch) {
                        case 1:
                                /* push given data into the stack */
                                printf("Enter your i/p to push:");
                                scanf("%d", &data);
                                push(stack, data);
                                break;
                        case 2:
                                /* pops first element from the stack */
                                pop(stack);
                                break;
                        case 3:
                                /* dump the stack contents */
                                dumpStack(stack);
                                break;

                        case 4:
                                exit(0);
                        default:
                                printf("Wrong Option!!\n");
                                break;
                }
        }

        return 0;
  }
예제 #29
0
// onScoreChanged
void EventManager::fireEvent(EventType eventType, S32 score, S32 team, LuaPlayerInfo *playerInfo)
{
   if(suppressEvents(eventType))
         return;

   lua_State *L = LuaScriptRunner::getL();

   TNLAssert(lua_gettop(L) == 0 || dumpStack(L), "Stack dirty!");

   for(S32 i = 0; i < subscriptions[eventType].size(); i++)
   {
      lua_pushinteger(L, score);   // -- score
      lua_pushinteger(L, team);    // -- score, team

      if(playerInfo)
         playerInfo->push(L);      // -- score, team, playerInfo
      else
         lua_pushnil(L);

      fire(L, subscriptions[eventType][i].subscriber, eventDefs[eventType].function, subscriptions[eventType][i].context);
   }
}
예제 #30
0
BOOL DebugMainLoop(const DebugOptions *pOptions)
{
    BOOL fFinished = FALSE;
    BOOL fBreakpointSignalled = FALSE;
    BOOL fWowBreakpointSignalled = FALSE;
    BOOL fTerminating = FALSE;

    while(!fFinished)
    {
        DEBUG_EVENT DebugEvent;            // debugging event information
        DWORD dwContinueStatus = DBG_CONTINUE;    // exception continuation
        PPROCESS_INFO pProcessInfo;
        PTHREAD_INFO pThreadInfo;
        HANDLE hProcess;

        // Wait for a debugging event to occur. The second parameter indicates
        // that the function does not return until a debugging event occurs.
        if(!WaitForDebugEvent(&DebugEvent, INFINITE))
        {
            OutputDebug("WaitForDebugEvent: 0x%08lx", GetLastError());

            return FALSE;
        }

        // Process the debugging event code.
        switch (DebugEvent.dwDebugEventCode) {
        case EXCEPTION_DEBUG_EVENT: {
            PEXCEPTION_RECORD pExceptionRecord = &DebugEvent.u.Exception.ExceptionRecord;
            NTSTATUS ExceptionCode = pExceptionRecord->ExceptionCode;

            // Process the exception code. When handling
            // exceptions, remember to set the continuation
            // status parameter (dwContinueStatus). This value
            // is used by the ContinueDebugEvent function.
            if (pOptions->verbose_flag) {
                lprintf("EXCEPTION PID=%lu TID=%lu ExceptionCode=0x%lx dwFirstChance=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        pExceptionRecord->ExceptionCode,
                        DebugEvent.u.Exception.dwFirstChance
                );
            }

            // Find the process in the process list
            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];

            dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;

            if (DebugEvent.u.Exception.dwFirstChance) {
                if (pExceptionRecord->ExceptionCode == (DWORD)STATUS_BREAKPOINT) {
                    // Signal the aedebug event
                    if (!fBreakpointSignalled) {
                        fBreakpointSignalled = TRUE;

                        if (pOptions->hEvent) {
                            SetEvent(pOptions->hEvent);
                            CloseHandle(pOptions->hEvent);
                        }

                        if (pOptions->dwThreadId) {
                            DWORD dwThreadId = pOptions->dwThreadId;
                            const DWORD dwFailed = (DWORD)-1;
                            DWORD dwRet = dwFailed;
                            pThreadInfo = &pProcessInfo->Threads[dwThreadId];
                            HANDLE hThread = pThreadInfo->hThread;
                            if (hThread != NULL) {
                                dwRet = ResumeThread(hThread);
                            }
                            if (dwRet == dwFailed) {
                                lprintf("error: failed to resume thread %lu\n", dwThreadId);
                            }
                        }

                        /*
                         * We ignore first-chance breakpoints by default.
                         *
                         * We get one of these whenever we attach to a process.
                         * But in some cases, we never get a second-chance, e.g.,
                         * when we're attached through MSVCRT's abort().
                         */
                        if (!pOptions->breakpoint_flag) {
                            dwContinueStatus = DBG_CONTINUE;
                            break;
                        }
                    }
                }

                if (ExceptionCode == STATUS_WX86_BREAKPOINT) {
                    if (!fWowBreakpointSignalled) {
                        fWowBreakpointSignalled = TRUE;
                        dwContinueStatus = DBG_CONTINUE;
                        break;
                    }
                }

               /*
                 * Ignore thread naming exception.
                 *
                 * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
                 *
                 * TODO: Note down the thread name
                 */
                if (ExceptionCode == 0x406d1388) {
                    dwContinueStatus = DBG_CONTINUE;
                    break;
                }

                if (!pOptions->first_chance) {
                    break;
                }
            }

            // XXX: Deferred symbols don't get loaded without this
            SymRefreshModuleList(pProcessInfo->hProcess);

            dumpException(pProcessInfo->hProcess,
                          &DebugEvent.u.Exception.ExceptionRecord);

            // Find the thread in the thread list
            THREAD_INFO_LIST::const_iterator it;
            for (it = pProcessInfo->Threads.begin(); it != pProcessInfo->Threads.end(); ++it) {
                DWORD dwThreadId = it->first;
                HANDLE hThread = it->second.hThread;
                if (dwThreadId != DebugEvent.dwThreadId &&
                    ExceptionCode != STATUS_BREAKPOINT &&
                    ExceptionCode != STATUS_WX86_BREAKPOINT) {
                        continue;
                }

                dumpStack(pProcessInfo->hProcess, hThread, NULL);
            }

            if (!DebugEvent.u.Exception.dwFirstChance) {
                /*
                 * Terminate the process. As continuing would cause the JIT debugger
                 * to be invoked again.
                 */
                fTerminating = TRUE;
                TerminateProcess(pProcessInfo->hProcess, (UINT)ExceptionCode);
            }

            break;
        }

        case CREATE_THREAD_DEBUG_EVENT:
            if (pOptions->verbose_flag) {
                lprintf("CREATE_THREAD PID=%lu TID=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId
                );
            }

            // Add the thread to the thread list
            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];
            pThreadInfo = &pProcessInfo->Threads[DebugEvent.dwThreadId];
            pThreadInfo->hThread = DebugEvent.u.CreateThread.hThread;
            break;

        case CREATE_PROCESS_DEBUG_EVENT: {
            HANDLE hFile = DebugEvent.u.CreateProcessInfo.hFile;

            char szImageName[MAX_PATH];
            char *lpImageName = NULL;
            if (hFile && GetFileNameFromHandle(hFile, szImageName, _countof(szImageName))) {
                lpImageName = szImageName;
            }

            if (pOptions->verbose_flag) {
                PCSTR lpModuleName = lpImageName ? getBaseName(lpImageName) : "";

                lprintf("CREATE_PROCESS PID=%lu TID=%lu lpBaseOfImage=%p %s\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        DebugEvent.u.CreateProcessInfo.lpBaseOfImage,
                        lpModuleName
                );
            }

            hProcess = DebugEvent.u.CreateProcessInfo.hProcess;

            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];
            pProcessInfo->hProcess = hProcess;

            pThreadInfo = &pProcessInfo->Threads[DebugEvent.dwThreadId];
            pThreadInfo->hThread = DebugEvent.u.CreateProcessInfo.hThread;

            if (!InitializeSym(hProcess, FALSE)) {
                OutputDebug("error: SymInitialize failed: 0x%08lx\n", GetLastError());
                exit(EXIT_FAILURE);
            }

            SymRegisterCallback64(hProcess, &symCallback, 0);

            loadModule(hProcess, hFile, lpImageName, DebugEvent.u.CreateProcessInfo.lpBaseOfImage);

            break;
        }

        case EXIT_THREAD_DEBUG_EVENT:
            if (pOptions->verbose_flag) {
                lprintf("EXIT_THREAD PID=%lu TID=%lu dwExitCode=0x%lx\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        DebugEvent.u.ExitThread.dwExitCode
                );
            }

            // Remove the thread from the thread list
            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];
            hProcess = pProcessInfo->hProcess;

            // Dump the stack on abort()
            if (!fTerminating && isAbnormalExitCode(DebugEvent.u.ExitThread.dwExitCode)) {
                pThreadInfo = &pProcessInfo->Threads[DebugEvent.dwThreadId];
                refreshSymbolsAndDumpStack(hProcess, pThreadInfo->hThread);
            }

            pProcessInfo->Threads.erase(DebugEvent.dwThreadId);
            break;

        case EXIT_PROCESS_DEBUG_EVENT: {
            if (pOptions->verbose_flag) {
                lprintf("EXIT_PROCESS PID=%lu TID=%lu dwExitCode=0x%lx\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        DebugEvent.u.ExitProcess.dwExitCode
                );
            }

            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];
            hProcess = pProcessInfo->hProcess;

            // Dump the stack on abort()
            if (!fTerminating && isAbnormalExitCode(DebugEvent.u.ExitThread.dwExitCode)) {
                pThreadInfo = &pProcessInfo->Threads[DebugEvent.dwThreadId];
                refreshSymbolsAndDumpStack(hProcess, pThreadInfo->hThread);
            }

            // Remove the process from the process list
            g_Processes.erase(DebugEvent.dwProcessId);

            if (!SymCleanup(hProcess)) {
                OutputDebug("SymCleanup failed with 0x%08lx\n", GetLastError());
            }

            if (g_Processes.empty()) {
                fFinished = TRUE;
            }

            break;
        }

        case LOAD_DLL_DEBUG_EVENT: {
            HANDLE hFile = DebugEvent.u.LoadDll.hFile;

            char szImageName[MAX_PATH];
            char *lpImageName = NULL;
            if (hFile && GetFileNameFromHandle(hFile, szImageName, _countof(szImageName))) {
                lpImageName = szImageName;
            }

            if (pOptions->verbose_flag) {
                PCSTR lpModuleName = lpImageName ? getBaseName(lpImageName) : "";

                lprintf("LOAD_DLL PID=%lu TID=%lu lpBaseOfDll=%p %s\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        DebugEvent.u.LoadDll.lpBaseOfDll,
                        lpModuleName
                );
            }

            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];
            hProcess = pProcessInfo->hProcess;

            loadModule(hProcess, hFile, lpImageName, DebugEvent.u.LoadDll.lpBaseOfDll);

            break;
        }

        case UNLOAD_DLL_DEBUG_EVENT:
            if (pOptions->verbose_flag) {
                lprintf("UNLOAD_DLL PID=%lu TID=%lu lpBaseOfDll=%p\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        DebugEvent.u.UnloadDll.lpBaseOfDll
                );
            }

            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];
            hProcess = pProcessInfo->hProcess;

            SymUnloadModule64(hProcess, (UINT_PTR)DebugEvent.u.UnloadDll.lpBaseOfDll);

            break;

        case OUTPUT_DEBUG_STRING_EVENT: {
            if (pOptions->verbose_flag) {
                lprintf("OUTPUT_DEBUG_STRING PID=%lu TID=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId
                );
            }

            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];

            assert(!DebugEvent.u.DebugString.fUnicode);

            LPSTR lpDebugStringData = readProcessString(pProcessInfo->hProcess,
                                                        DebugEvent.u.DebugString.lpDebugStringData,
                                                        DebugEvent.u.DebugString.nDebugStringLength);

            lprintf("%s", lpDebugStringData);

            free(lpDebugStringData);
            break;
        }

        case RIP_EVENT:
            if (pOptions->verbose_flag) {
                lprintf("RIP PID=%lu TID=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId
                );
            }
            break;

        default:
            if (pOptions->verbose_flag) {
                lprintf("EVENT%lu PID=%lu TID=%lu\r\n",
                    DebugEvent.dwDebugEventCode,
                    DebugEvent.dwProcessId,
                    DebugEvent.dwThreadId
                );
            }
            break;
        }

        // Resume executing the thread that reported the debugging event.
        ContinueDebugEvent(
            DebugEvent.dwProcessId,
            DebugEvent.dwThreadId,
            dwContinueStatus
        );
    }

    return TRUE;
}