static BOOL tgt_process_active_close_process(struct dbg_process* pcs, BOOL kill) { if (pcs == dbg_curr_process) { /* remove all set breakpoints in debuggee code */ break_set_xpoints(FALSE); /* needed for single stepping (ugly). * should this be handled inside the server ??? */ be_cpu->single_step(&dbg_context, FALSE); if (dbg_curr_thread->in_exception) { SetThreadContext(dbg_curr_thread->handle, &dbg_context); ContinueDebugEvent(dbg_curr_pid, dbg_curr_tid, DBG_CONTINUE); } } if (kill) { TerminateProcess(pcs->handle, 0); } else { if (!DebugActiveProcessStop(pcs->pid)) return FALSE; } SymCleanup(pcs->handle); dbg_del_process(pcs); return TRUE; }
static void dbg_init_current_thread(void* start) { if (start) { if (dbg_curr_process->threads && !dbg_curr_process->threads->next && /* first thread ? */ DBG_IVAR(BreakAllThreadsStartup)) { ADDRESS64 addr; break_set_xpoints(FALSE); addr.Mode = AddrModeFlat; addr.Offset = (DWORD_PTR)start; break_add_break(&addr, TRUE, TRUE); break_set_xpoints(TRUE); } } }
static unsigned dbg_handle_debug_event(DEBUG_EVENT* de) { union { char bufferA[256]; WCHAR buffer[256]; } u; DWORD cont = DBG_CONTINUE; dbg_curr_pid = de->dwProcessId; dbg_curr_tid = de->dwThreadId; if ((dbg_curr_process = dbg_get_process(de->dwProcessId)) != NULL) dbg_curr_thread = dbg_get_thread(dbg_curr_process, de->dwThreadId); else dbg_curr_thread = NULL; switch (de->dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: if (!dbg_curr_thread) { WINE_ERR("%04x:%04x: not a registered process or thread (perhaps a 16 bit one ?)\n", de->dwProcessId, de->dwThreadId); break; } WINE_TRACE("%04x:%04x: exception code=%08x\n", de->dwProcessId, de->dwThreadId, de->u.Exception.ExceptionRecord.ExceptionCode); if (dbg_curr_process->continue_on_first_exception) { dbg_curr_process->continue_on_first_exception = FALSE; if (!DBG_IVAR(BreakOnAttach)) break; } if (dbg_fetch_context()) { cont = dbg_handle_exception(&de->u.Exception.ExceptionRecord, de->u.Exception.dwFirstChance); if (cont && dbg_curr_thread) { SetThreadContext(dbg_curr_thread->handle, &dbg_context); } } break; case CREATE_PROCESS_DEBUG_EVENT: dbg_curr_process = dbg_add_process(&be_process_active_io, de->dwProcessId, de->u.CreateProcessInfo.hProcess); if (dbg_curr_process == NULL) { WINE_ERR("Couldn't create process\n"); break; } fetch_module_name(de->u.CreateProcessInfo.lpImageName, de->u.CreateProcessInfo.fUnicode, de->u.CreateProcessInfo.lpBaseOfImage, u.buffer, sizeof(u.buffer) / sizeof(WCHAR), TRUE); WINE_TRACE("%04x:%04x: create process '%s'/%p @%p (%u<%u>)\n", de->dwProcessId, de->dwThreadId, wine_dbgstr_w(u.buffer), de->u.CreateProcessInfo.lpImageName, de->u.CreateProcessInfo.lpStartAddress, de->u.CreateProcessInfo.dwDebugInfoFileOffset, de->u.CreateProcessInfo.nDebugInfoSize); dbg_set_process_name(dbg_curr_process, u.buffer); if (!dbg_init(dbg_curr_process->handle, u.buffer, FALSE)) dbg_printf("Couldn't initiate DbgHelp\n"); if (!dbg_load_module(dbg_curr_process->handle, de->u.CreateProcessInfo.hFile, u.buffer, (DWORD_PTR)de->u.CreateProcessInfo.lpBaseOfImage, 0)) dbg_printf("couldn't load main module (%u)\n", GetLastError()); WINE_TRACE("%04x:%04x: create thread I @%p\n", de->dwProcessId, de->dwThreadId, de->u.CreateProcessInfo.lpStartAddress); dbg_curr_thread = dbg_add_thread(dbg_curr_process, de->dwThreadId, de->u.CreateProcessInfo.hThread, de->u.CreateProcessInfo.lpThreadLocalBase); if (!dbg_curr_thread) { WINE_ERR("Couldn't create thread\n"); break; } dbg_init_current_process(); dbg_init_current_thread(de->u.CreateProcessInfo.lpStartAddress); break; case EXIT_PROCESS_DEBUG_EVENT: WINE_TRACE("%04x:%04x: exit process (%d)\n", de->dwProcessId, de->dwThreadId, de->u.ExitProcess.dwExitCode); if (dbg_curr_process == NULL) { WINE_ERR("Unknown process\n"); break; } tgt_process_active_close_process(dbg_curr_process, FALSE); dbg_printf("Process of pid=%04x has terminated\n", de->dwProcessId); break; case CREATE_THREAD_DEBUG_EVENT: WINE_TRACE("%04x:%04x: create thread D @%p\n", de->dwProcessId, de->dwThreadId, de->u.CreateThread.lpStartAddress); if (dbg_curr_process == NULL) { WINE_ERR("Unknown process\n"); break; } if (dbg_get_thread(dbg_curr_process, de->dwThreadId) != NULL) { WINE_TRACE("Thread already listed, skipping\n"); break; } dbg_curr_thread = dbg_add_thread(dbg_curr_process, de->dwThreadId, de->u.CreateThread.hThread, de->u.CreateThread.lpThreadLocalBase); if (!dbg_curr_thread) { WINE_ERR("Couldn't create thread\n"); break; } dbg_init_current_thread(de->u.CreateThread.lpStartAddress); break; case EXIT_THREAD_DEBUG_EVENT: WINE_TRACE("%04x:%04x: exit thread (%d)\n", de->dwProcessId, de->dwThreadId, de->u.ExitThread.dwExitCode); if (dbg_curr_thread == NULL) { WINE_ERR("Unknown thread\n"); break; } /* FIXME: remove break point set on thread startup */ dbg_del_thread(dbg_curr_thread); break; case LOAD_DLL_DEBUG_EVENT: if (dbg_curr_thread == NULL) { WINE_ERR("Unknown thread\n"); break; } fetch_module_name(de->u.LoadDll.lpImageName, de->u.LoadDll.fUnicode, de->u.LoadDll.lpBaseOfDll, u.buffer, sizeof(u.buffer) / sizeof(WCHAR), FALSE); WINE_TRACE("%04x:%04x: loads DLL %s @%p (%u<%u>)\n", de->dwProcessId, de->dwThreadId, wine_dbgstr_w(u.buffer), de->u.LoadDll.lpBaseOfDll, de->u.LoadDll.dwDebugInfoFileOffset, de->u.LoadDll.nDebugInfoSize); dbg_load_module(dbg_curr_process->handle, de->u.LoadDll.hFile, u.buffer, (DWORD_PTR)de->u.LoadDll.lpBaseOfDll, 0); break_set_xpoints(FALSE); break_check_delayed_bp(); break_set_xpoints(TRUE); if (DBG_IVAR(BreakOnDllLoad)) { dbg_printf("Stopping on DLL %s loading at 0x%08lx\n", dbg_W2A(u.buffer, -1), (unsigned long)de->u.LoadDll.lpBaseOfDll); if (dbg_fetch_context()) cont = 0; } break; case UNLOAD_DLL_DEBUG_EVENT: WINE_TRACE("%04x:%04x: unload DLL @%p\n", de->dwProcessId, de->dwThreadId, de->u.UnloadDll.lpBaseOfDll); break_delete_xpoints_from_module((unsigned long)de->u.UnloadDll.lpBaseOfDll); SymUnloadModule(dbg_curr_process->handle, (unsigned long)de->u.UnloadDll.lpBaseOfDll); break; case OUTPUT_DEBUG_STRING_EVENT: if (dbg_curr_thread == NULL) { WINE_ERR("Unknown thread\n"); break; } memory_get_string(dbg_curr_process, de->u.DebugString.lpDebugStringData, TRUE, de->u.DebugString.fUnicode, u.bufferA, sizeof(u.bufferA)); WINE_TRACE("%04x:%04x: output debug string (%s)\n", de->dwProcessId, de->dwThreadId, u.bufferA); break; case RIP_EVENT: WINE_TRACE("%04x:%04x: rip error=%u type=%u\n", de->dwProcessId, de->dwThreadId, de->u.RipInfo.dwError, de->u.RipInfo.dwType); break; default: WINE_TRACE("%04x:%04x: unknown event (%x)\n", de->dwProcessId, de->dwThreadId, de->dwDebugEventCode); } if (!cont) return TRUE; /* stop execution */ ContinueDebugEvent(de->dwProcessId, de->dwThreadId, cont); return FALSE; /* continue execution */ }
/*********************************************************************** * break_restart_execution * * Set the bp to the correct state to restart execution * in the given mode. */ void break_restart_execution(int count) { ADDRESS64 addr; enum dbg_line_status status; enum dbg_exec_mode mode, ret_mode; ADDRESS64 callee; void* linear; memory_get_current_pc(&addr); linear = memory_to_linear_addr(&addr); /* * This is the mode we will be running in after we finish. We would like * to be able to modify this in certain cases. */ ret_mode = mode = dbg_curr_thread->exec_mode; /* we've stopped on a xpoint (other than step over) */ if (dbg_curr_thread->stopped_xpoint > 0) { /* * If we have set a new value, then save it in the BP number. */ if (count != 0 && mode == dbg_exec_cont) { dbg_curr_process->bp[dbg_curr_thread->stopped_xpoint].skipcount = count; } /* If we've stopped on a breakpoint, single step over it (, then run) */ if (is_xpoint_break(dbg_curr_thread->stopped_xpoint)) mode = dbg_exec_step_into_insn; } else if (mode == dbg_exec_cont && count > 1) { dbg_printf("Not stopped at any breakpoint; argument ignored.\n"); } if (mode == dbg_exec_finish && be_cpu->is_function_return(linear)) { mode = ret_mode = dbg_exec_step_into_insn; } /* * See if the function we are stepping into has debug info * and line numbers. If not, then we step over it instead. * FIXME - we need to check for things like thunks or trampolines, * as the actual function may in fact have debug info. */ if (be_cpu->is_function_call(linear, &callee)) { status = symbol_get_function_line_status(&callee); #if 0 /* FIXME: we need to get the thunk type */ /* * Anytime we have a trampoline, step over it. */ if ((mode == EXEC_STEP_OVER || mode == EXEC_STEPI_OVER) && status == dbg_in_a_thunk) { WINE_WARN("Not stepping into trampoline at %p (no lines)\n", memory_to_linear_addr(&callee)); mode = EXEC_STEP_OVER_TRAMPOLINE; } #endif if (mode == dbg_exec_step_into_line && status == dbg_no_line_info) { WINE_WARN("Not stepping into function at %p (no lines)\n", memory_to_linear_addr(&callee)); mode = dbg_exec_step_over_line; } } if (mode == dbg_exec_step_into_line && symbol_get_function_line_status(&addr) == dbg_no_line_info) { dbg_printf("Single stepping until exit from function,\n" "which has no line number information.\n"); ret_mode = mode = dbg_exec_finish; } switch (mode) { case dbg_exec_cont: /* Continuous execution */ be_cpu->single_step(&dbg_context, FALSE); break_set_xpoints(TRUE); break; #if 0 case EXEC_STEP_OVER_TRAMPOLINE: /* * This is the means by which we step over our conversion stubs * in callfrom*.s and callto*.s. We dig the appropriate address * off the stack, and we set the breakpoint there instead of the * address just after the call. */ be_cpu->get_addr(dbg_curr_thread->handle, &dbg_context, be_cpu_addr_stack, &addr); /* FIXME: we assume stack grows as on an i386 */ addr.Offset += 2 * sizeof(unsigned int); dbg_read_memory(memory_to_linear_addr(&addr), &addr.Offset, sizeof(addr.Offset)); dbg_curr_process->bp[0].addr = addr; dbg_curr_process->bp[0].enabled = TRUE; dbg_curr_process->bp[0].refcount = 1; dbg_curr_process->bp[0].skipcount = 0; dbg_curr_process->bp[0].xpoint_type = be_xpoint_break; dbg_curr_process->bp[0].condition = NULL; be_cpu->single_step(&dbg_context, FALSE); break_set_xpoints(TRUE); break; #endif case dbg_exec_finish: case dbg_exec_step_over_insn: /* Stepping over a call */ case dbg_exec_step_over_line: /* Stepping over a call */ if (be_cpu->is_step_over_insn(linear)) { be_cpu->disasm_one_insn(&addr, FALSE); dbg_curr_process->bp[0].addr = addr; dbg_curr_process->bp[0].enabled = TRUE; dbg_curr_process->bp[0].refcount = 1; dbg_curr_process->bp[0].skipcount = 0; dbg_curr_process->bp[0].xpoint_type = be_xpoint_break; dbg_curr_process->bp[0].condition = NULL; be_cpu->single_step(&dbg_context, FALSE); break_set_xpoints(TRUE); break; } /* else fall through to single-stepping */ case dbg_exec_step_into_line: /* Single-stepping a line */ case dbg_exec_step_into_insn: /* Single-stepping an instruction */ be_cpu->single_step(&dbg_context, TRUE); break; default: RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); } dbg_curr_thread->step_over_bp = dbg_curr_process->bp[0]; dbg_curr_thread->exec_mode = ret_mode; }
/*********************************************************************** * break_suspend_execution * * Remove all bp before entering the debug loop */ void break_suspend_execution(void) { break_set_xpoints(FALSE); dbg_curr_process->bp[0] = dbg_curr_thread->step_over_bp; }