Made the winedbg an external and WineLib program.
[wine.git] / debugger / winedbg.c
blob0bdc6d5ac26de77b66a2deabef0934e50554b59c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /* Wine internal debugger
4 * Interface to Windows debugger API
5 * Eric Pouech (c) 2000
6 */
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include "debugger.h"
13 #include "thread.h"
14 #include "process.h"
15 #include "wingdi.h"
16 #include "winuser.h"
18 #include "winreg.h"
20 #ifdef DBG_need_heap
21 HANDLE dbg_heap = 0;
22 #endif
24 DBG_PROCESS* DEBUG_CurrProcess = NULL;
25 DBG_THREAD* DEBUG_CurrThread = NULL;
26 CONTEXT DEBUG_context;
28 static DBG_PROCESS* proc = NULL;
30 /* build internal vars table */
31 #define INTERNAL_VAR(_var,_val) int DBG_IVAR(_var) = _val;
32 #include "intvar.h"
33 #undef INTERNAL_VAR
35 int DEBUG_Printf(int chn, const char* format, ...)
37 char buf[1024];
38 va_list valist;
40 va_start(valist, format);
41 vsprintf(buf, format, valist);
42 va_end(valist);
43 #if 0
44 if (DBG_IVAR(ChannelMask) & chn)
45 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, strlen(buf), NULL, NULL);
46 if (chn == DBG_CHN_MESG) fwrite(buf, strlen(buf), 1, stderr);
47 #else
48 if (DBG_IVAR(ChannelMask) & chn) fwrite(buf, strlen(buf), 1, stderr);
49 #endif
50 return strlen(buf);
53 static BOOL DEBUG_Init(void)
55 HKEY hkey;
56 DWORD type;
57 DWORD val;
58 DWORD count = sizeof(val);
60 if (!RegOpenKey(HKEY_CURRENT_USER, "Software\\Wine\\WineDbg", &hkey)) {
61 #define INTERNAL_VAR(_var,_val) \
62 if (!RegQueryValueEx(hkey, #_var, 0, &type, (LPSTR)&val, &count)) \
63 DBG_IVAR(_var) = val;
64 #include "intvar.h"
65 #undef INTERNAL_VAR
66 RegCloseKey(hkey);
68 return TRUE;
71 static WINE_EXCEPTION_FILTER(wine_dbg)
73 DEBUG_ExternalDebugger();
74 DEBUG_Printf(DBG_CHN_MESG, "\nwine_dbg: Exception %lx\n", GetExceptionCode());
75 return EXCEPTION_EXECUTE_HANDLER;
78 static DBG_PROCESS* DEBUG_GetProcess(DWORD pid)
80 DBG_PROCESS* p;
82 for (p = proc; p; p = p->next)
83 if (p->pid == pid) break;
84 return p;
87 static DBG_PROCESS* DEBUG_AddProcess(DWORD pid, HANDLE h)
89 DBG_PROCESS* p = DBG_alloc(sizeof(DBG_PROCESS));
90 if (!p)
91 return NULL;
92 p->handle = h;
93 p->pid = pid;
94 p->threads = NULL;
95 p->num_threads = 0;
96 p->modules = NULL;
97 p->next_index = 0;
99 p->next = proc;
100 p->prev = NULL;
101 if (proc) proc->prev = p;
102 proc = p;
103 return p;
106 static void DEBUG_DelThread(DBG_THREAD* p);
108 static void DEBUG_DelProcess(DBG_PROCESS* p)
110 if (p->threads != NULL) {
111 DEBUG_Printf(DBG_CHN_ERR, "Shouldn't happen\n");
112 while (p->threads) DEBUG_DelThread(p->threads);
114 if (p->prev) p->prev->next = p->next;
115 if (p->next) p->next->prev = p->prev;
116 if (p == proc) proc = p->next;
117 DBG_free(p);
120 static void DEBUG_InitCurrProcess(void)
122 #ifdef DBG_need_heap
124 * Initialize the debugger heap.
126 dbg_heap = HeapCreate(HEAP_NO_SERIALIZE, 0x1000, 0x8000000); /* 128MB */
127 #endif
130 * Initialize the type handling stuff.
132 DEBUG_InitTypes();
133 DEBUG_InitCVDataTypes();
136 static BOOL DEBUG_ProcessGetString(char* buffer, int size, HANDLE hp, LPSTR addr)
138 DWORD sz;
139 *(WCHAR*)buffer = 0;
140 return (addr && ReadProcessMemory(hp, addr, buffer, size, &sz));
143 static BOOL DEBUG_ProcessGetStringIndirect(char* buffer, int size, HANDLE hp, LPVOID addr)
145 LPVOID ad;
146 DWORD sz;
148 if ( addr
149 && ReadProcessMemory(hp, addr, &ad, sizeof(ad), &sz)
150 && sz == sizeof(ad)
151 && ad
152 && ReadProcessMemory(hp, ad, buffer, size, &sz))
153 return TRUE;
154 *(WCHAR*)buffer = 0;
155 return FALSE;
158 static DBG_THREAD* DEBUG_GetThread(DBG_PROCESS* p, DWORD tid)
160 DBG_THREAD* t;
162 for (t = p->threads; t; t = t->next)
163 if (t->tid == tid) break;
164 return t;
167 static DBG_THREAD* DEBUG_AddThread(DBG_PROCESS* p, DWORD tid,
168 HANDLE h, LPVOID start, LPVOID teb)
170 DBG_THREAD* t = DBG_alloc(sizeof(DBG_THREAD));
171 if (!t)
172 return NULL;
174 t->handle = h;
175 t->tid = tid;
176 t->start = start;
177 t->teb = teb;
178 t->process = p;
179 t->wait_for_first_exception = 0;
180 t->dbg_exec_mode = EXEC_CONT;
181 t->dbg_exec_count = 0;
183 p->num_threads++;
184 t->next = p->threads;
185 t->prev = NULL;
186 if (p->threads) p->threads->prev = t;
187 p->threads = t;
189 return t;
192 static void DEBUG_InitCurrThread(void)
194 if (DEBUG_CurrThread->start) {
195 if (DEBUG_CurrThread->process->num_threads == 1 ||
196 DBG_IVAR(BreakAllThreadsStartup)) {
197 DBG_VALUE value;
199 DEBUG_SetBreakpoints(FALSE);
200 value.type = NULL;
201 value.cookie = DV_TARGET;
202 value.addr.seg = 0;
203 value.addr.off = (DWORD)DEBUG_CurrThread->start;
204 DEBUG_AddBreakpoint(&value);
205 DEBUG_SetBreakpoints(TRUE);
207 } else {
208 DEBUG_CurrThread->wait_for_first_exception = 1;
212 static void DEBUG_DelThread(DBG_THREAD* t)
214 if (t->prev) t->prev->next = t->next;
215 if (t->next) t->next->prev = t->prev;
216 if (t == t->process->threads) t->process->threads = t->next;
217 t->process->num_threads--;
218 DBG_free(t);
221 static BOOL DEBUG_HandleException( EXCEPTION_RECORD *rec, BOOL first_chance, BOOL force )
223 BOOL is_debug = FALSE;
224 BOOL ret;
226 /* FIXME: need for a configuration var ? */
227 /* pass to app first ??? */
228 /* if (first_chance && !force) return 0; */
230 switch (rec->ExceptionCode)
232 case EXCEPTION_BREAKPOINT:
233 case EXCEPTION_SINGLE_STEP:
234 is_debug = TRUE;
235 break;
238 if (!is_debug)
240 /* print some infos */
241 DEBUG_Printf( DBG_CHN_MESG, "%s: ",
242 first_chance ? "First chance exception" : "Unhandled exception" );
243 switch(rec->ExceptionCode)
245 case EXCEPTION_INT_DIVIDE_BY_ZERO:
246 DEBUG_Printf( DBG_CHN_MESG, "divide by zero" );
247 break;
248 case EXCEPTION_INT_OVERFLOW:
249 DEBUG_Printf( DBG_CHN_MESG, "overflow" );
250 break;
251 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
252 DEBUG_Printf( DBG_CHN_MESG, "array bounds " );
253 break;
254 case EXCEPTION_ILLEGAL_INSTRUCTION:
255 DEBUG_Printf( DBG_CHN_MESG, "illegal instruction" );
256 break;
257 case EXCEPTION_STACK_OVERFLOW:
258 DEBUG_Printf( DBG_CHN_MESG, "stack overflow" );
259 break;
260 case EXCEPTION_PRIV_INSTRUCTION:
261 DEBUG_Printf( DBG_CHN_MESG, "priviledged instruction" );
262 break;
263 case EXCEPTION_ACCESS_VIOLATION:
264 if (rec->NumberParameters == 2)
265 DEBUG_Printf( DBG_CHN_MESG, "page fault on %s access to 0x%08lx",
266 rec->ExceptionInformation[0] ? "write" : "read",
267 rec->ExceptionInformation[1] );
268 else
269 DEBUG_Printf( DBG_CHN_MESG, "page fault" );
270 break;
271 case EXCEPTION_DATATYPE_MISALIGNMENT:
272 DEBUG_Printf( DBG_CHN_MESG, "Alignment" );
273 break;
274 case CONTROL_C_EXIT:
275 DEBUG_Printf( DBG_CHN_MESG, "^C" );
276 break;
277 case EXCEPTION_CRITICAL_SECTION_WAIT:
278 DEBUG_Printf( DBG_CHN_MESG, "critical section %08lx wait failed",
279 rec->ExceptionInformation[0] );
280 break;
281 default:
282 DEBUG_Printf( DBG_CHN_MESG, "%08lx", rec->ExceptionCode );
283 break;
287 DEBUG_Printf(DBG_CHN_TRACE,
288 "Entering debugger PC=%lx EFL=%08lx mode=%d count=%d\n",
289 DEBUG_context.Eip, DEBUG_context.EFlags,
290 DEBUG_CurrThread->dbg_exec_mode, DEBUG_CurrThread->dbg_exec_count);
292 ret = DEBUG_Main( is_debug, force, rec->ExceptionCode );
294 DEBUG_Printf(DBG_CHN_TRACE,
295 "Exiting debugger PC=%lx EFL=%08lx mode=%d count=%d\n",
296 DEBUG_context.Eip, DEBUG_context.EFlags,
297 DEBUG_CurrThread->dbg_exec_mode, DEBUG_CurrThread->dbg_exec_count);
299 return ret;
302 static BOOL DEBUG_HandleDebugEvent(DEBUG_EVENT* de, LPDWORD cont)
304 char buffer[256];
305 BOOL ret;
307 __TRY {
308 ret = TRUE;
309 *cont = 0L;
311 if ((DEBUG_CurrProcess = DEBUG_GetProcess(de->dwProcessId)) != NULL)
312 DEBUG_CurrThread = DEBUG_GetThread(DEBUG_CurrProcess, de->dwThreadId);
313 else
314 DEBUG_CurrThread = NULL;
316 switch (de->dwDebugEventCode) {
317 case EXCEPTION_DEBUG_EVENT:
318 if (!DEBUG_CurrThread) {
319 DEBUG_Printf(DBG_CHN_ERR, "%08lx:%08lx: not a registered process or thread (perhaps a 16 bit one ?)\n",
320 de->dwProcessId, de->dwThreadId);
321 break;
324 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: exception code=%08lx %d\n",
325 de->dwProcessId, de->dwThreadId,
326 de->u.Exception.ExceptionRecord.ExceptionCode,
327 DEBUG_CurrThread->wait_for_first_exception);
329 DEBUG_context.ContextFlags = CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_SEGMENTS|CONTEXT_DEBUG_REGISTERS;
330 if (!GetThreadContext(DEBUG_CurrThread->handle, &DEBUG_context)) {
331 DEBUG_Printf(DBG_CHN_WARN, "Can't get thread's context\n");
332 break;
335 DEBUG_Printf(DBG_CHN_TRACE, "%p:%p\n", de->u.Exception.ExceptionRecord.ExceptionAddress,
336 (void*)DEBUG_context.Eip);
338 *cont = DEBUG_HandleException(&de->u.Exception.ExceptionRecord,
339 de->u.Exception.dwFirstChance,
340 DEBUG_CurrThread->wait_for_first_exception);
341 if (DEBUG_CurrThread->dbg_exec_mode == EXEC_KILL) {
342 ret = FALSE;
343 } else {
344 DEBUG_CurrThread->wait_for_first_exception = 0;
345 SetThreadContext(DEBUG_CurrThread->handle, &DEBUG_context);
347 break;
349 case CREATE_THREAD_DEBUG_EVENT:
350 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: create thread D @%p\n", de->dwProcessId, de->dwThreadId,
351 de->u.CreateThread.lpStartAddress);
353 if (DEBUG_CurrProcess == NULL) {
354 DEBUG_Printf(DBG_CHN_ERR, "Unknown process\n");
355 break;
357 if (DEBUG_GetThread(DEBUG_CurrProcess, de->dwThreadId) != NULL) {
358 DEBUG_Printf(DBG_CHN_TRACE, "Thread already listed, skipping\n");
359 break;
362 DEBUG_CurrThread = DEBUG_AddThread(DEBUG_CurrProcess,
363 de->dwThreadId,
364 de->u.CreateThread.hThread,
365 de->u.CreateThread.lpStartAddress,
366 de->u.CreateThread.lpThreadLocalBase);
367 if (!DEBUG_CurrThread) {
368 DEBUG_Printf(DBG_CHN_ERR, "Couldn't create thread\n");
369 break;
371 DEBUG_InitCurrThread();
372 break;
374 case CREATE_PROCESS_DEBUG_EVENT:
375 DEBUG_ProcessGetStringIndirect(buffer, sizeof(buffer),
376 de->u.CreateProcessInfo.hProcess,
377 de->u.CreateProcessInfo.lpImageName);
379 /* FIXME unicode ? de->u.CreateProcessInfo.fUnicode */
380 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: create process %s @%p (%ld<%ld>)\n",
381 de->dwProcessId, de->dwThreadId,
382 buffer,
383 de->u.CreateProcessInfo.lpStartAddress,
384 de->u.CreateProcessInfo.dwDebugInfoFileOffset,
385 de->u.CreateProcessInfo.nDebugInfoSize);
387 if (DEBUG_GetProcess(de->dwProcessId) != NULL) {
388 DEBUG_Printf(DBG_CHN_TRACE, "Skipping already defined process\n");
389 break;
391 DEBUG_CurrProcess = DEBUG_AddProcess(de->dwProcessId,
392 de->u.CreateProcessInfo.hProcess);
393 if (DEBUG_CurrProcess == NULL) {
394 DEBUG_Printf(DBG_CHN_ERR, "Unknown process\n");
395 break;
398 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: create thread I @%p\n",
399 de->dwProcessId, de->dwThreadId,
400 de->u.CreateProcessInfo.lpStartAddress);
402 DEBUG_CurrThread = DEBUG_AddThread(DEBUG_CurrProcess,
403 de->dwThreadId,
404 de->u.CreateProcessInfo.hThread,
405 de->u.CreateProcessInfo.lpStartAddress,
406 de->u.CreateProcessInfo.lpThreadLocalBase);
407 if (!DEBUG_CurrThread) {
408 DEBUG_Printf(DBG_CHN_ERR, "Couldn't create thread\n");
409 break;
412 DEBUG_InitCurrProcess();
413 DEBUG_InitCurrThread();
414 /* so far, process name is not set */
415 DEBUG_LoadModule32("<Debugged process>", de->u.CreateProcessInfo.hFile,
416 (DWORD)de->u.CreateProcessInfo.lpBaseOfImage);
417 break;
419 case EXIT_THREAD_DEBUG_EVENT:
420 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: exit thread (%ld)\n",
421 de->dwProcessId, de->dwThreadId, de->u.ExitThread.dwExitCode);
423 if (DEBUG_CurrThread == NULL) {
424 DEBUG_Printf(DBG_CHN_ERR, "Unknown thread\n");
425 break;
427 /* FIXME: remove break point set on thread startup */
428 DEBUG_DelThread(DEBUG_CurrThread);
429 break;
431 case EXIT_PROCESS_DEBUG_EVENT:
432 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: exit process (%ld)\n",
433 de->dwProcessId, de->dwThreadId, de->u.ExitProcess.dwExitCode);
435 if (DEBUG_CurrProcess == NULL) {
436 DEBUG_Printf(DBG_CHN_ERR, "Unknown process\n");
437 break;
439 /* just in case */
440 DEBUG_SetBreakpoints(FALSE);
441 /* kill last thread */
442 DEBUG_DelThread(DEBUG_CurrProcess->threads);
443 DEBUG_DelProcess(DEBUG_CurrProcess);
444 ret = FALSE;
445 break;
447 case LOAD_DLL_DEBUG_EVENT:
448 if (DEBUG_CurrThread == NULL) {
449 DEBUG_Printf(DBG_CHN_ERR, "Unknown thread\n");
450 break;
452 DEBUG_ProcessGetStringIndirect(buffer, sizeof(buffer),
453 DEBUG_CurrThread->process->handle,
454 de->u.LoadDll.lpImageName);
456 /* FIXME unicode: de->u.LoadDll.fUnicode */
457 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: loads DLL %s @%p (%ld<%ld>)\n",
458 de->dwProcessId, de->dwThreadId,
459 buffer, de->u.LoadDll.lpBaseOfDll,
460 de->u.LoadDll.dwDebugInfoFileOffset,
461 de->u.LoadDll.nDebugInfoSize);
462 CharUpper(buffer);
463 DEBUG_LoadModule32(buffer, de->u.LoadDll.hFile, (DWORD)de->u.LoadDll.lpBaseOfDll);
464 break;
466 case UNLOAD_DLL_DEBUG_EVENT:
467 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: unload DLL @%p\n", de->dwProcessId, de->dwThreadId,
468 de->u.UnloadDll.lpBaseOfDll);
469 break;
471 case OUTPUT_DEBUG_STRING_EVENT:
472 if (DEBUG_CurrThread == NULL) {
473 DEBUG_Printf(DBG_CHN_ERR, "Unknown thread\n");
474 break;
477 DEBUG_ProcessGetString(buffer, sizeof(buffer),
478 DEBUG_CurrThread->process->handle,
479 de->u.DebugString.lpDebugStringData);
481 /* fixme unicode de->u.DebugString.fUnicode ? */
482 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: output debug string (%s)\n",
483 de->dwProcessId, de->dwThreadId, buffer);
484 break;
486 case RIP_EVENT:
487 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: rip error=%ld type=%ld\n",
488 de->dwProcessId, de->dwThreadId, de->u.RipInfo.dwError,
489 de->u.RipInfo.dwType);
490 break;
492 default:
493 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: unknown event (%ld)\n",
494 de->dwProcessId, de->dwThreadId, de->dwDebugEventCode);
497 } __EXCEPT(wine_dbg) {
498 *cont = 0;
499 ret = TRUE;
501 __ENDTRY;
503 return ret;
506 static DWORD CALLBACK DEBUG_MainLoop(DWORD pid)
508 DEBUG_EVENT de;
509 DWORD cont;
510 BOOL ret = TRUE;
512 DEBUG_Printf(DBG_CHN_MESG, " on pid %ld\n", pid);
514 DEBUG_Init();
516 while (ret && WaitForDebugEvent(&de, INFINITE)) {
517 ret = DEBUG_HandleDebugEvent(&de, &cont);
518 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, cont);
521 DEBUG_Printf(DBG_CHN_MESG, "WineDbg terminated on pid %ld\n", pid);
523 ExitProcess(0);
526 int PASCAL WinMain(HINSTANCE hInst, HINSTANCE prev, LPSTR _cmdline, int show)
528 char* argv[5];
529 char* cmdline = strdup(_cmdline);
530 char* ptr = cmdline;
531 int instr = FALSE;
532 int argc = 0;
534 while ((*ptr == ' ' || *ptr == '\t') && *ptr != 0) ptr++;
535 argv[argc++] = ptr;
536 for (; *ptr; ptr++) {
537 if ((*ptr == ' ' || *ptr == '\t') && !instr) {
538 *ptr++ = 0;
539 while (*ptr == ' ' || *ptr == '\t') ptr++;
540 if (*ptr) argv[argc++] = ptr;
541 if (argc >= sizeof(argv) / sizeof(argv[0])) return 0;
542 } else if (*ptr == '"') {
543 instr = !instr;
547 #if 0
548 /* would require to change .spec with a cuiexe type */
549 /* keep it as a guiexe for now, so that Wine won't touch the Unix stdin,
550 * stdout and stderr streams
552 if (1 /*DBG_IVAR(UseXterm)*/) {
553 COORD pos;
555 /* This is a hack: it forces creation of an xterm, not done by default */
556 pos.x = 0; pos.y = 1;
557 SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
559 #endif
561 DEBUG_Printf(DBG_CHN_MESG, "Starting WineDbg... ");
562 if (argc == 2) {
563 DWORD pid = atoi(argv[0]);
564 HANDLE hEvent = atoi(argv[1]);
566 if (pid != 0 && hEvent != 0) {
567 free(cmdline);
569 if (!DebugActiveProcess(pid)) {
570 DEBUG_Printf(DBG_CHN_ERR, "Can't attach process %ld: %ld\n",
571 pid, GetLastError());
572 return 0;
574 SetEvent(hEvent);
575 return DEBUG_MainLoop(pid);
578 do {
579 PROCESS_INFORMATION info;
580 STARTUPINFOA startup;
582 free(cmdline);
584 memset(&startup, 0, sizeof(startup));
585 startup.cb = sizeof(startup);
586 startup.dwFlags = STARTF_USESHOWWINDOW;
587 startup.wShowWindow = SW_SHOWNORMAL;
589 if (CreateProcess(NULL, _cmdline, NULL, NULL,
590 FALSE, DEBUG_PROCESS, NULL, NULL, &startup, &info)) {
591 return DEBUG_MainLoop(info.dwProcessId);
593 DEBUG_Printf(DBG_CHN_MESG, "Couldn't start process '%s'\n", _cmdline);
594 } while (0);
595 return 0;