Make LockWindowUpdate return more Windows-like values.
[wine/multimedia.git] / debugger / winedbg.c
blob1df07f6f62d07cdf67d37a90ee0600da8c998a2b
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /* Wine internal debugger
4 * Interface to Windows debugger API
5 * Copyright 2000 Eric Pouech
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include "debugger.h"
28 #include "ntddk.h"
29 #include "thread.h"
30 #include "wincon.h"
31 #include "winreg.h"
32 #include "wingdi.h"
33 #include "winuser.h"
34 #include "msvcrt/excpt.h"
35 #include "wine/library.h"
37 DBG_PROCESS* DEBUG_CurrProcess = NULL;
38 DBG_THREAD* DEBUG_CurrThread = NULL;
39 DWORD DEBUG_CurrTid;
40 DWORD DEBUG_CurrPid;
41 CONTEXT DEBUG_context;
42 BOOL DEBUG_interactiveP = FALSE;
43 enum exit_mode DEBUG_ExitMode = EXIT_CONTINUE;
44 int curr_frame = 0;
45 int automatic_mode = 0;
46 static char* DEBUG_LastCmdLine = NULL;
48 static DBG_PROCESS* DEBUG_ProcessList = NULL;
49 DBG_INTVAR DEBUG_IntVars[DBG_IV_LAST];
51 void DEBUG_OutputA(int chn, const char* buffer, int len)
53 if (DBG_IVAR(ConChannelMask) & chn)
54 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, len, NULL, NULL);
55 if (DBG_IVAR(StdChannelMask) & chn)
56 fwrite(buffer, len, 1, stderr);
59 void DEBUG_OutputW(int chn, const WCHAR* buffer, int len)
61 /* FIXME: this won't work is std output isn't attached to a console */
62 if (DBG_IVAR(ConChannelMask) & chn)
63 WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), buffer, len, NULL, NULL);
64 /* simplistic Unicode to ANSI conversion */
65 if (DBG_IVAR(StdChannelMask) & chn)
66 while (len--) fputc((char)*buffer++, stderr);
69 int DEBUG_Printf(int chn, const char* format, ...)
71 static char buf[4*1024];
72 va_list valist;
73 int len;
75 va_start(valist, format);
76 len = vsnprintf(buf, sizeof(buf), format, valist);
77 va_end(valist);
79 if (len <= -1 || len >= sizeof(buf)) {
80 len = sizeof(buf) - 1;
81 buf[len] = 0;
82 buf[len - 1] = buf[len - 2] = buf[len - 3] = '.';
84 DEBUG_OutputA(chn, buf, len);
85 return len;
88 static BOOL DEBUG_IntVarsRW(int read)
90 HKEY hkey;
91 DWORD type = REG_DWORD;
92 DWORD val;
93 DWORD count = sizeof(val);
94 int i;
95 DBG_INTVAR* div = DEBUG_IntVars;
97 if (read) {
98 /* initializes internal vars table */
99 #define INTERNAL_VAR(_var,_val,_ref,_typ) \
100 div->val = _val; div->name = #_var; div->pval = _ref; \
101 div->type = DEBUG_GetBasicType(_typ); div++;
102 #include "intvar.h"
103 #undef INTERNAL_VAR
106 if (RegCreateKeyA(HKEY_CURRENT_USER, "Software\\Wine\\WineDbg", &hkey)) {
107 /* since the IVars are not yet setup, DEBUG_Printf doesn't work,
108 * so don't use it */
109 fprintf(stderr, "Cannot create WineDbg key in registry\n");
110 return FALSE;
113 for (i = 0; i < DBG_IV_LAST; i++) {
114 if (read) {
115 if (!DEBUG_IntVars[i].pval) {
116 if (!RegQueryValueEx(hkey, DEBUG_IntVars[i].name, 0,
117 &type, (LPSTR)&val, &count))
118 DEBUG_IntVars[i].val = val;
119 DEBUG_IntVars[i].pval = &DEBUG_IntVars[i].val;
120 } else {
121 *DEBUG_IntVars[i].pval = 0;
123 } else {
124 /* FIXME: type should be infered from basic type -if any- of intvar */
125 if (DEBUG_IntVars[i].pval == &DEBUG_IntVars[i].val)
126 RegSetValueEx(hkey, DEBUG_IntVars[i].name, 0,
127 type, (LPCVOID)DEBUG_IntVars[i].pval, count);
130 RegCloseKey(hkey);
131 return TRUE;
134 DBG_INTVAR* DEBUG_GetIntVar(const char* name)
136 int i;
138 for (i = 0; i < DBG_IV_LAST; i++) {
139 if (!strcmp(DEBUG_IntVars[i].name, name))
140 return &DEBUG_IntVars[i];
142 return NULL;
145 static WINE_EXCEPTION_FILTER(wine_dbg)
147 DEBUG_Printf(DBG_CHN_MESG, "\nwine_dbg: Exception (%lx) inside debugger, continuing...\n", GetExceptionCode());
148 DEBUG_ExternalDebugger();
149 return EXCEPTION_EXECUTE_HANDLER;
152 DBG_PROCESS* DEBUG_GetProcess(DWORD pid)
154 DBG_PROCESS* p;
156 for (p = DEBUG_ProcessList; p; p = p->next)
157 if (p->pid == pid) break;
158 return p;
161 static DBG_PROCESS* DEBUG_AddProcess(DWORD pid, HANDLE h, const char* imageName)
163 DBG_PROCESS* p = DBG_alloc(sizeof(DBG_PROCESS));
164 if (!p)
165 return NULL;
166 p->handle = h;
167 p->pid = pid;
168 p->imageName = imageName ? DBG_strdup(imageName) : NULL;
169 p->threads = NULL;
170 p->num_threads = 0;
171 p->continue_on_first_exception = FALSE;
172 p->modules = NULL;
173 p->num_modules = 0;
174 p->next_index = 0;
175 p->dbg_hdr_addr = 0;
176 p->delayed_bp = NULL;
177 p->num_delayed_bp = 0;
179 p->next = DEBUG_ProcessList;
180 p->prev = NULL;
181 if (DEBUG_ProcessList) DEBUG_ProcessList->prev = p;
182 DEBUG_ProcessList = p;
183 return p;
186 static void DEBUG_DelThread(DBG_THREAD* p);
188 static void DEBUG_DelProcess(DBG_PROCESS* p)
190 int i;
192 if (p->threads != NULL) {
193 DEBUG_Printf(DBG_CHN_ERR, "Shouldn't happen\n");
194 while (p->threads) DEBUG_DelThread(p->threads);
196 for (i = 0; i < p->num_delayed_bp; i++) {
197 DBG_free(p->delayed_bp[i].name);
199 DBG_free(p->delayed_bp);
200 if (p->prev) p->prev->next = p->next;
201 if (p->next) p->next->prev = p->prev;
202 if (p == DEBUG_ProcessList) DEBUG_ProcessList = p->next;
203 if (p == DEBUG_CurrProcess) DEBUG_CurrProcess = NULL;
204 DBG_free((char*)p->imageName);
205 DBG_free(p);
208 static void DEBUG_InitCurrProcess(void)
212 static BOOL DEBUG_ProcessGetString(char* buffer, int size, HANDLE hp, LPSTR addr)
214 DWORD sz;
215 *(WCHAR*)buffer = 0;
216 return (addr && ReadProcessMemory(hp, addr, buffer, size, &sz));
219 static BOOL DEBUG_ProcessGetStringIndirect(char* buffer, int size, HANDLE hp, LPVOID addr)
221 LPVOID ad;
222 DWORD sz;
224 if ( addr
225 && ReadProcessMemory(hp, addr, &ad, sizeof(ad), &sz)
226 && sz == sizeof(ad)
227 && ad
228 && ReadProcessMemory(hp, ad, buffer, size, &sz))
229 return TRUE;
230 *(WCHAR*)buffer = 0;
231 return FALSE;
234 DBG_THREAD* DEBUG_GetThread(DBG_PROCESS* p, DWORD tid)
236 DBG_THREAD* t;
238 for (t = p->threads; t; t = t->next)
239 if (t->tid == tid) break;
240 return t;
243 static DBG_THREAD* DEBUG_AddThread(DBG_PROCESS* p, DWORD tid,
244 HANDLE h, LPVOID start, LPVOID teb)
246 DBG_THREAD* t = DBG_alloc(sizeof(DBG_THREAD));
247 if (!t)
248 return NULL;
250 t->handle = h;
251 t->tid = tid;
252 t->start = start;
253 t->teb = teb;
254 t->process = p;
255 t->wait_for_first_exception = 0;
256 t->exec_mode = EXEC_CONT;
257 t->exec_count = 0;
259 sprintf(t->name, "%08lx", tid);
261 p->num_threads++;
262 t->next = p->threads;
263 t->prev = NULL;
264 if (p->threads) p->threads->prev = t;
265 p->threads = t;
267 return t;
270 static void DEBUG_InitCurrThread(void)
272 if (DEBUG_CurrThread->start) {
273 if (DEBUG_CurrThread->process->num_threads == 1 ||
274 DBG_IVAR(BreakAllThreadsStartup)) {
275 DBG_VALUE value;
277 DEBUG_SetBreakpoints(FALSE);
278 value.type = NULL;
279 value.cookie = DV_TARGET;
280 value.addr.seg = 0;
281 value.addr.off = (DWORD)DEBUG_CurrThread->start;
282 DEBUG_AddBreakpoint(&value, NULL);
283 DEBUG_SetBreakpoints(TRUE);
285 } else {
286 DEBUG_CurrThread->wait_for_first_exception = 1;
290 static void DEBUG_DelThread(DBG_THREAD* t)
292 if (t->prev) t->prev->next = t->next;
293 if (t->next) t->next->prev = t->prev;
294 if (t == t->process->threads) t->process->threads = t->next;
295 t->process->num_threads--;
296 if (t == DEBUG_CurrThread) DEBUG_CurrThread = NULL;
297 DBG_free(t);
300 BOOL DEBUG_Attach(DWORD pid, BOOL cofe)
302 if (!(DEBUG_CurrProcess = DEBUG_AddProcess(pid, 0, NULL))) return FALSE;
304 if (!DebugActiveProcess(pid)) {
305 DEBUG_Printf(DBG_CHN_MESG, "Can't attach process %lx: error %ld\n", pid, GetLastError());
306 DEBUG_DelProcess(DEBUG_CurrProcess);
307 DEBUG_CurrProcess = NULL;
308 return FALSE;
310 DEBUG_CurrProcess->continue_on_first_exception = cofe;
311 return TRUE;
314 BOOL DEBUG_Detach(void)
316 /* remove all set breakpoints in debuggee code */
317 DEBUG_SetBreakpoints(FALSE);
318 /* needed for single stepping (ugly).
319 * should this be handled inside the server ??? */
320 #ifdef __i386__
321 DEBUG_context.EFlags &= ~STEP_FLAG;
322 #endif
323 SetThreadContext(DEBUG_CurrThread->handle, &DEBUG_context);
324 DebugActiveProcessStop(DEBUG_CurrProcess->pid);
325 DEBUG_DelProcess(DEBUG_CurrProcess);
326 DEBUG_CurrProcess = NULL;
327 /* FIXME: should zero out the symbol table too */
328 return TRUE;
331 static BOOL DEBUG_ExceptionProlog(BOOL is_debug, BOOL force, DWORD code)
333 DBG_ADDR addr;
334 int newmode;
336 DEBUG_GetCurrentAddress(&addr);
337 DEBUG_SuspendExecution();
339 if (!is_debug) {
340 if (!addr.seg)
341 DEBUG_Printf(DBG_CHN_MESG, " in 32-bit code (0x%08lx)", addr.off);
342 else
343 switch(DEBUG_GetSelectorType(addr.seg))
345 case MODE_32:
346 DEBUG_Printf(DBG_CHN_MESG, " in 32-bit code (%04lx:%08lx)", addr.seg, addr.off);
347 break;
348 case MODE_16:
349 DEBUG_Printf(DBG_CHN_MESG, " in 16-bit code (%04lx:%04lx)", addr.seg, addr.off);
350 break;
351 case MODE_VM86:
352 DEBUG_Printf(DBG_CHN_MESG, " in vm86 code (%04lx:%04lx)", addr.seg, addr.off);
353 break;
354 case MODE_INVALID:
355 DEBUG_Printf(DBG_CHN_MESG, " bad CS (%lx)", addr.seg);
356 break;
358 DEBUG_Printf(DBG_CHN_MESG, ".\n");
361 DEBUG_LoadEntryPoints("Loading new modules symbols:\n");
363 if (!force && is_debug &&
364 DEBUG_ShouldContinue(&addr, code,
365 &DEBUG_CurrThread->exec_count))
366 return FALSE;
368 if ((newmode = DEBUG_GetSelectorType(addr.seg)) == MODE_INVALID) newmode = MODE_32;
369 if (newmode != DEBUG_CurrThread->dbg_mode)
371 static const char * const names[] = { "???", "16-bit", "32-bit", "vm86" };
372 DEBUG_Printf(DBG_CHN_MESG,"In %s mode.\n", names[newmode] );
373 DEBUG_CurrThread->dbg_mode = newmode;
376 DEBUG_DoDisplay();
378 if (is_debug || force) {
380 * Do a quiet backtrace so that we have an idea of what the situation
381 * is WRT the source files.
383 DEBUG_BackTrace(DEBUG_CurrTid, FALSE);
384 } else {
385 /* This is a real crash, dump some info */
386 DEBUG_InfoRegisters();
387 DEBUG_InfoStack();
388 #ifdef __i386__
389 if (DEBUG_CurrThread->dbg_mode == MODE_16) {
390 DEBUG_InfoSegments(DEBUG_context.SegDs >> 3, 1);
391 if (DEBUG_context.SegEs != DEBUG_context.SegDs)
392 DEBUG_InfoSegments(DEBUG_context.SegEs >> 3, 1);
394 DEBUG_InfoSegments(DEBUG_context.SegFs >> 3, 1);
395 #endif
396 DEBUG_BackTrace(DEBUG_CurrTid, TRUE);
399 if (!is_debug ||
400 (DEBUG_CurrThread->exec_mode == EXEC_STEPI_OVER) ||
401 (DEBUG_CurrThread->exec_mode == EXEC_STEPI_INSTR)) {
403 struct list_id list;
405 /* Show where we crashed */
406 curr_frame = 0;
407 DEBUG_DisassembleInstruction(&addr);
409 /* resets list internal arguments so we can look at source code when needed */
410 DEBUG_FindNearestSymbol(&addr, TRUE, NULL, 0, &list);
411 if (list.sourcefile) DEBUG_List(&list, NULL, 0);
413 return TRUE;
416 static void DEBUG_ExceptionEpilog(void)
418 DEBUG_RestartExecution(DEBUG_CurrThread->exec_count);
420 * This will have gotten absorbed into the breakpoint info
421 * if it was used. Otherwise it would have been ignored.
422 * In any case, we don't mess with it any more.
424 if (DEBUG_CurrThread->exec_mode == EXEC_CONT)
425 DEBUG_CurrThread->exec_count = 0;
428 static void DEBUG_HandleException(EXCEPTION_RECORD *rec, BOOL first_chance, BOOL force)
430 BOOL is_debug = FALSE;
431 THREADNAME_INFO *pThreadName;
432 DBG_THREAD *pThread;
434 assert(DEBUG_CurrThread);
436 DEBUG_ExitMode = EXIT_CONTINUE;
438 switch (rec->ExceptionCode)
440 case EXCEPTION_BREAKPOINT:
441 case EXCEPTION_SINGLE_STEP:
442 is_debug = TRUE;
443 break;
444 case EXCEPTION_NAME_THREAD:
445 pThreadName = (THREADNAME_INFO*)(rec->ExceptionInformation);
446 if (pThreadName->dwThreadID == -1)
447 pThread = DEBUG_CurrThread;
448 else
449 pThread = DEBUG_GetThread(DEBUG_CurrProcess, pThreadName->dwThreadID);
451 if (ReadProcessMemory(DEBUG_CurrThread->process->handle, pThreadName->szName,
452 pThread->name, 9, NULL))
453 DEBUG_Printf (DBG_CHN_MESG,
454 "Thread ID=0x%lx renamed using MS VC6 extension (name==\"%s\")\n",
455 pThread->tid, pThread->name);
456 return;
459 if (first_chance && !is_debug && !force && !DBG_IVAR(BreakOnFirstChance))
461 /* pass exception to program except for debug exceptions */
462 if (!is_debug) DEBUG_ExitMode = EXIT_PASS;
463 return;
466 if (!is_debug)
468 /* print some infos */
469 DEBUG_Printf(DBG_CHN_MESG, "%s: ",
470 first_chance ? "First chance exception" : "Unhandled exception");
471 switch (rec->ExceptionCode)
473 case EXCEPTION_INT_DIVIDE_BY_ZERO:
474 DEBUG_Printf(DBG_CHN_MESG, "divide by zero");
475 break;
476 case EXCEPTION_INT_OVERFLOW:
477 DEBUG_Printf(DBG_CHN_MESG, "overflow");
478 break;
479 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
480 DEBUG_Printf(DBG_CHN_MESG, "array bounds ");
481 break;
482 case EXCEPTION_ILLEGAL_INSTRUCTION:
483 DEBUG_Printf(DBG_CHN_MESG, "illegal instruction");
484 break;
485 case EXCEPTION_STACK_OVERFLOW:
486 DEBUG_Printf(DBG_CHN_MESG, "stack overflow");
487 break;
488 case EXCEPTION_PRIV_INSTRUCTION:
489 DEBUG_Printf(DBG_CHN_MESG, "privileged instruction");
490 break;
491 case EXCEPTION_ACCESS_VIOLATION:
492 if (rec->NumberParameters == 2)
493 DEBUG_Printf(DBG_CHN_MESG, "page fault on %s access to 0x%08lx",
494 rec->ExceptionInformation[0] ? "write" : "read",
495 rec->ExceptionInformation[1]);
496 else
497 DEBUG_Printf(DBG_CHN_MESG, "page fault");
498 break;
499 case EXCEPTION_DATATYPE_MISALIGNMENT:
500 DEBUG_Printf(DBG_CHN_MESG, "Alignment");
501 break;
502 case DBG_CONTROL_C:
503 DEBUG_Printf(DBG_CHN_MESG, "^C");
504 break;
505 case CONTROL_C_EXIT:
506 DEBUG_Printf(DBG_CHN_MESG, "^C");
507 break;
508 case EXCEPTION_CRITICAL_SECTION_WAIT:
510 DBG_ADDR addr;
512 addr.seg = 0;
513 addr.off = rec->ExceptionInformation[0];
515 DEBUG_Printf(DBG_CHN_MESG, "wait failed on critical section ");
516 DEBUG_PrintAddress(&addr, DEBUG_CurrThread->dbg_mode, FALSE);
518 if (!DBG_IVAR(BreakOnCritSectTimeOut))
520 DEBUG_Printf(DBG_CHN_MESG, "\n");
521 return;
523 break;
524 case EXCEPTION_WINE_STUB:
526 char dll[32], name[64];
527 DEBUG_ProcessGetString( dll, sizeof(dll), DEBUG_CurrThread->process->handle,
528 (char *)rec->ExceptionInformation[0] );
529 DEBUG_ProcessGetString( name, sizeof(name), DEBUG_CurrThread->process->handle,
530 (char *)rec->ExceptionInformation[1] );
531 DEBUG_Printf(DBG_CHN_MESG, "unimplemented function %s.%s called", dll, name );
533 break;
534 case EXCEPTION_VM86_INTx:
535 DEBUG_Printf(DBG_CHN_MESG, "interrupt %02lx in vm86 mode",
536 rec->ExceptionInformation[0]);
537 break;
538 case EXCEPTION_VM86_STI:
539 DEBUG_Printf(DBG_CHN_MESG, "sti in vm86 mode");
540 break;
541 case EXCEPTION_VM86_PICRETURN:
542 DEBUG_Printf(DBG_CHN_MESG, "PIC return in vm86 mode");
543 break;
544 default:
545 DEBUG_Printf(DBG_CHN_MESG, "%08lx", rec->ExceptionCode);
546 break;
550 #if 0
551 DEBUG_Printf(DBG_CHN_TRACE,
552 "Entering debugger PC=%lx EFL=%08lx mode=%d count=%d\n",
553 #ifdef __i386__
554 DEBUG_context.Eip, DEBUG_context.EFlags,
555 #else
556 0L, 0L,
557 #endif
558 DEBUG_CurrThread->exec_mode, DEBUG_CurrThread->exec_count);
559 #endif
561 if (automatic_mode)
563 DEBUG_ExceptionProlog(is_debug, FALSE, rec->ExceptionCode);
564 DEBUG_ExitMode = EXIT_QUIT;
565 return; /* terminate execution */
568 if (DEBUG_ExceptionProlog(is_debug, force, rec->ExceptionCode)) {
569 DEBUG_interactiveP = TRUE;
570 for (;;)
572 DEBUG_Parser();
573 if (DEBUG_ExitMode == EXIT_QUIT || DEBUG_ExitMode == EXIT_DETACH)
574 break;
575 if (DEBUG_ValidateRegisters()) {
576 if (DEBUG_ExitMode == EXIT_CONTINUE || first_chance)
577 break;
578 DEBUG_Printf(DBG_CHN_MESG, "Cannot pass on last chance exception. You must use cont\n");
581 DEBUG_interactiveP = FALSE;
583 DEBUG_ExceptionEpilog();
585 #if 0
586 DEBUG_Printf(DBG_CHN_TRACE,
587 "Exiting debugger PC=%lx EFL=%08lx mode=%d count=%d\n",
588 #ifdef __i386__
589 DEBUG_context.Eip, DEBUG_context.EFlags,
590 #else
591 0L, 0L,
592 #endif
593 DEBUG_CurrThread->exec_mode, DEBUG_CurrThread->exec_count);
594 #endif
597 static void DEBUG_HandleDebugEvent(DEBUG_EVENT* de)
599 char buffer[256];
601 DEBUG_CurrPid = de->dwProcessId;
602 DEBUG_CurrTid = de->dwThreadId;
604 DEBUG_ExitMode = EXIT_CONTINUE;
605 __TRY {
606 if ((DEBUG_CurrProcess = DEBUG_GetProcess(de->dwProcessId)) != NULL)
607 DEBUG_CurrThread = DEBUG_GetThread(DEBUG_CurrProcess, de->dwThreadId);
608 else
609 DEBUG_CurrThread = NULL;
611 switch (de->dwDebugEventCode) {
612 case EXCEPTION_DEBUG_EVENT:
613 if (!DEBUG_CurrThread) {
614 DEBUG_Printf(DBG_CHN_ERR, "%08lx:%08lx: not a registered process or thread (perhaps a 16 bit one ?)\n",
615 de->dwProcessId, de->dwThreadId);
616 break;
619 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: exception code=%08lx\n",
620 de->dwProcessId, de->dwThreadId,
621 de->u.Exception.ExceptionRecord.ExceptionCode);
623 if (DEBUG_CurrProcess->continue_on_first_exception) {
624 DEBUG_CurrProcess->continue_on_first_exception = FALSE;
625 if (!DBG_IVAR(BreakOnAttach)) {
626 break;
630 DEBUG_context.ContextFlags = CONTEXT_CONTROL
631 | CONTEXT_INTEGER
632 #ifdef CONTEXT_SEGMENTS
633 | CONTEXT_SEGMENTS
634 #endif
635 #ifdef CONTEXT_DEBUG_REGISTERS
636 | CONTEXT_DEBUG_REGISTERS
637 #endif
640 if (!GetThreadContext(DEBUG_CurrThread->handle, &DEBUG_context)) {
641 DEBUG_Printf(DBG_CHN_WARN, "Can't get thread's context\n");
642 break;
645 DEBUG_HandleException(&de->u.Exception.ExceptionRecord,
646 de->u.Exception.dwFirstChance,
647 DEBUG_CurrThread->wait_for_first_exception);
648 if (DEBUG_CurrThread) {
649 DEBUG_CurrThread->wait_for_first_exception = 0;
650 SetThreadContext(DEBUG_CurrThread->handle, &DEBUG_context);
652 break;
654 case CREATE_THREAD_DEBUG_EVENT:
655 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: create thread D @%08lx\n", de->dwProcessId, de->dwThreadId,
656 (unsigned long)(LPVOID)de->u.CreateThread.lpStartAddress);
658 if (DEBUG_CurrProcess == NULL) {
659 DEBUG_Printf(DBG_CHN_ERR, "Unknown process\n");
660 break;
662 if (DEBUG_GetThread(DEBUG_CurrProcess, de->dwThreadId) != NULL) {
663 DEBUG_Printf(DBG_CHN_TRACE, "Thread already listed, skipping\n");
664 break;
667 DEBUG_CurrThread = DEBUG_AddThread(DEBUG_CurrProcess,
668 de->dwThreadId,
669 de->u.CreateThread.hThread,
670 de->u.CreateThread.lpStartAddress,
671 de->u.CreateThread.lpThreadLocalBase);
672 if (!DEBUG_CurrThread) {
673 DEBUG_Printf(DBG_CHN_ERR, "Couldn't create thread\n");
674 break;
676 DEBUG_InitCurrThread();
677 break;
679 case CREATE_PROCESS_DEBUG_EVENT:
680 DEBUG_ProcessGetStringIndirect(buffer, sizeof(buffer),
681 de->u.CreateProcessInfo.hProcess,
682 de->u.CreateProcessInfo.lpImageName);
684 /* FIXME unicode ? de->u.CreateProcessInfo.fUnicode */
685 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: create process '%s'/%p @%08lx (%ld<%ld>)\n",
686 de->dwProcessId, de->dwThreadId,
687 buffer, de->u.CreateProcessInfo.lpImageName,
688 (unsigned long)(LPVOID)de->u.CreateProcessInfo.lpStartAddress,
689 de->u.CreateProcessInfo.dwDebugInfoFileOffset,
690 de->u.CreateProcessInfo.nDebugInfoSize);
692 if ((DEBUG_CurrProcess = DEBUG_GetProcess(de->dwProcessId)) != NULL) {
693 if (DEBUG_CurrProcess->handle) {
694 DEBUG_Printf(DBG_CHN_ERR, "Skipping already defined process\n");
695 break;
697 DEBUG_CurrProcess->handle = de->u.CreateProcessInfo.hProcess;
698 if (DEBUG_CurrProcess->imageName == NULL)
699 DEBUG_CurrProcess->imageName = DBG_strdup(buffer[0] ? buffer : "<Debugged Process>");
701 } else {
702 DEBUG_CurrProcess = DEBUG_AddProcess(de->dwProcessId,
703 de->u.CreateProcessInfo.hProcess,
704 buffer[0] ? buffer : "<Debugged Process>");
705 if (DEBUG_CurrProcess == NULL) {
706 DEBUG_Printf(DBG_CHN_ERR, "Unknown process\n");
707 break;
711 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: create thread I @%08lx\n",
712 de->dwProcessId, de->dwThreadId,
713 (unsigned long)(LPVOID)de->u.CreateProcessInfo.lpStartAddress);
715 DEBUG_CurrThread = DEBUG_AddThread(DEBUG_CurrProcess,
716 de->dwThreadId,
717 de->u.CreateProcessInfo.hThread,
718 de->u.CreateProcessInfo.lpStartAddress,
719 de->u.CreateProcessInfo.lpThreadLocalBase);
720 if (!DEBUG_CurrThread) {
721 DEBUG_Printf(DBG_CHN_ERR, "Couldn't create thread\n");
722 break;
725 DEBUG_InitCurrProcess();
726 DEBUG_InitCurrThread();
728 /* module is either PE, NE or ELF module (for WineLib), but all
729 * are loaded with wine, so load its symbols, then the main module
733 char* ptr = getenv("WINELOADER");
735 if (!ptr || DEBUG_ReadExecutableDbgInfo( ptr ) == DIL_ERROR)
736 DEBUG_ReadExecutableDbgInfo( "wine" );
737 } while (0);
739 DEBUG_LoadModule32(DEBUG_CurrProcess->imageName, de->u.CreateProcessInfo.hFile,
740 (DWORD)de->u.CreateProcessInfo.lpBaseOfImage);
742 break;
744 case EXIT_THREAD_DEBUG_EVENT:
745 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: exit thread (%ld)\n",
746 de->dwProcessId, de->dwThreadId, de->u.ExitThread.dwExitCode);
748 if (DEBUG_CurrThread == NULL) {
749 DEBUG_Printf(DBG_CHN_ERR, "Unknown thread\n");
750 break;
752 /* FIXME: remove break point set on thread startup */
753 DEBUG_DelThread(DEBUG_CurrThread);
754 break;
756 case EXIT_PROCESS_DEBUG_EVENT:
757 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: exit process (%ld)\n",
758 de->dwProcessId, de->dwThreadId, de->u.ExitProcess.dwExitCode);
760 if (DEBUG_CurrProcess == NULL) {
761 DEBUG_Printf(DBG_CHN_ERR, "Unknown process\n");
762 break;
764 /* just in case */
765 DEBUG_SetBreakpoints(FALSE);
766 /* kill last thread */
767 DEBUG_DelThread(DEBUG_CurrProcess->threads);
768 DEBUG_DelProcess(DEBUG_CurrProcess);
770 DEBUG_Printf(DBG_CHN_MESG, "Process of pid=%08lx has terminated\n", DEBUG_CurrPid);
771 break;
773 case LOAD_DLL_DEBUG_EVENT:
774 if (DEBUG_CurrThread == NULL) {
775 DEBUG_Printf(DBG_CHN_ERR, "Unknown thread\n");
776 break;
778 DEBUG_ProcessGetStringIndirect(buffer, sizeof(buffer),
779 DEBUG_CurrThread->process->handle,
780 de->u.LoadDll.lpImageName);
782 /* FIXME unicode: de->u.LoadDll.fUnicode */
783 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: loads DLL %s @%08lx (%ld<%ld>)\n",
784 de->dwProcessId, de->dwThreadId,
785 buffer, (unsigned long)de->u.LoadDll.lpBaseOfDll,
786 de->u.LoadDll.dwDebugInfoFileOffset,
787 de->u.LoadDll.nDebugInfoSize);
788 _strupr(buffer);
789 DEBUG_LoadModule32(buffer, de->u.LoadDll.hFile, (DWORD)de->u.LoadDll.lpBaseOfDll);
790 DEBUG_CheckDelayedBP();
791 if (DBG_IVAR(BreakOnDllLoad)) {
792 DEBUG_Printf(DBG_CHN_MESG, "Stopping on DLL %s loading at %08lx\n",
793 buffer, (unsigned long)de->u.LoadDll.lpBaseOfDll);
794 DEBUG_Parser();
796 break;
798 case UNLOAD_DLL_DEBUG_EVENT:
799 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: unload DLL @%08lx\n", de->dwProcessId, de->dwThreadId,
800 (unsigned long)de->u.UnloadDll.lpBaseOfDll);
801 break;
803 case OUTPUT_DEBUG_STRING_EVENT:
804 if (DEBUG_CurrThread == NULL) {
805 DEBUG_Printf(DBG_CHN_ERR, "Unknown thread\n");
806 break;
809 DEBUG_ProcessGetString(buffer, sizeof(buffer),
810 DEBUG_CurrThread->process->handle,
811 de->u.DebugString.lpDebugStringData);
813 /* FIXME unicode de->u.DebugString.fUnicode ? */
814 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: output debug string (%s)\n",
815 de->dwProcessId, de->dwThreadId, buffer);
816 break;
818 case RIP_EVENT:
819 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: rip error=%ld type=%ld\n",
820 de->dwProcessId, de->dwThreadId, de->u.RipInfo.dwError,
821 de->u.RipInfo.dwType);
822 break;
824 default:
825 DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: unknown event (%ld)\n",
826 de->dwProcessId, de->dwThreadId, de->dwDebugEventCode);
829 } __EXCEPT(wine_dbg) {
830 DEBUG_ExitMode = EXIT_CONTINUE;
832 __ENDTRY;
835 static DWORD DEBUG_MainLoop(void)
837 DEBUG_EVENT de;
839 DEBUG_Printf(DBG_CHN_MESG, " on pid %lx\n", DEBUG_CurrPid);
841 while (DEBUG_ExitMode == EXIT_CONTINUE)
843 /* wait until we get at least one loaded process */
844 while (!DEBUG_ProcessList)
846 DEBUG_Parser();
847 if (DEBUG_ExitMode == EXIT_CONTINUE || DEBUG_ExitMode == EXIT_QUIT) break;
849 if (DEBUG_ExitMode != EXIT_CONTINUE) break;
851 while ((DEBUG_ExitMode == EXIT_CONTINUE || DEBUG_ExitMode == EXIT_PASS) &&
852 DEBUG_ProcessList &&
853 WaitForDebugEvent(&de, INFINITE))
855 DEBUG_HandleDebugEvent(&de);
856 ContinueDebugEvent(de.dwProcessId, de.dwThreadId,
857 (DEBUG_ExitMode == EXIT_PASS) ? DBG_EXCEPTION_NOT_HANDLED : DBG_CONTINUE);
859 if (DEBUG_ExitMode == EXIT_DETACH && DEBUG_Detach())
861 /* DEBUG_ExitMode = EXIT_CONTINUE; */
862 /* FIXME: as we don't have a simple way to zero out the process symbol table
863 * we simply quit the debugger on detach...
865 DEBUG_ExitMode = EXIT_QUIT;
869 DEBUG_Printf(DBG_CHN_MESG, "WineDbg terminated on pid %lx\n", DEBUG_CurrPid);
871 return 0;
874 static DWORD DEBUG_AutoMode(void)
876 DEBUG_EVENT de;
878 DEBUG_Printf(DBG_CHN_MESG, " on pid %lx\n", DEBUG_CurrPid);
880 while ((DEBUG_ExitMode == EXIT_CONTINUE || DEBUG_ExitMode == EXIT_PASS) &&
881 DEBUG_ProcessList &&
882 WaitForDebugEvent(&de, INFINITE))
884 DEBUG_HandleDebugEvent(&de);
885 ContinueDebugEvent(de.dwProcessId, de.dwThreadId,
886 (DEBUG_ExitMode == EXIT_PASS) ? DBG_EXCEPTION_NOT_HANDLED : DBG_CONTINUE);
888 /* print some extra information */
889 DEBUG_Printf(DBG_CHN_MESG, "Modules:\n");
890 DEBUG_WalkModules();
891 DEBUG_Printf(DBG_CHN_MESG, "Threads:\n");
892 DEBUG_WalkThreads();
894 DEBUG_Printf(DBG_CHN_MESG, "WineDbg terminated on pid %lx\n", DEBUG_CurrPid);
895 return 0;
898 static BOOL DEBUG_Start(LPSTR cmdLine)
900 PROCESS_INFORMATION info;
901 STARTUPINFOA startup;
903 memset(&startup, 0, sizeof(startup));
904 startup.cb = sizeof(startup);
905 startup.dwFlags = STARTF_USESHOWWINDOW;
906 startup.wShowWindow = SW_SHOWNORMAL;
908 if (!CreateProcess(NULL, cmdLine, NULL, NULL,
909 FALSE, DEBUG_PROCESS|DETACHED_PROCESS, NULL, NULL, &startup, &info)) {
910 DEBUG_Printf(DBG_CHN_MESG, "Couldn't start process '%s'\n", cmdLine);
911 return FALSE;
913 DEBUG_CurrPid = info.dwProcessId;
914 if (!(DEBUG_CurrProcess = DEBUG_AddProcess(DEBUG_CurrPid, 0, NULL))) return FALSE;
916 return TRUE;
919 void DEBUG_Run(const char* args)
921 DBG_MODULE* wmod = DEBUG_GetProcessMainModule(DEBUG_CurrProcess);
922 const char* pgm = (wmod) ? wmod->module_name : "none";
924 if (args) {
925 DEBUG_Printf(DBG_CHN_MESG, "Run (%s) with '%s'\n", pgm, args);
926 } else {
927 if (!DEBUG_LastCmdLine) {
928 DEBUG_Printf(DBG_CHN_MESG, "Cannot find previously used command line.\n");
929 return;
931 DEBUG_Start(DEBUG_LastCmdLine);
935 static BOOL WINAPI DEBUG_CtrlCHandler(DWORD dwCtrlType)
937 if (dwCtrlType == CTRL_C_EVENT)
939 DEBUG_Printf(DBG_CHN_MESG, "Ctrl-C: stopping debuggee\n");
940 /* FIXME: since we likely have a single process, signal the first process
941 * in list
943 return DEBUG_ProcessList && DebugBreakProcess(DEBUG_ProcessList->handle);
945 return FALSE;
948 static void DEBUG_InitConsole(void)
950 COORD c;
951 SMALL_RECT sr;
952 DWORD mode;
954 /* keep it as a cuiexe for now, so that Wine won't touch the Unix stdin,
955 * stdout and stderr streams
957 if (DBG_IVAR(UseXTerm))
959 FreeConsole();
960 AllocConsole();
963 /* this would be nicer for output */
964 c.X = 132;
965 c.Y = 500;
966 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), c);
968 /* sets the console's window width accordingly */
969 sr.Left = 0;
970 sr.Top = 0;
971 sr.Right = c.X - 1;
972 sr.Bottom = 50;
973 SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), TRUE, &sr);
975 /* put the line editing mode with the nice emacs features (FIXME: could be triggered by a IVAR) */
976 if (GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &mode))
977 SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | WINE_ENABLE_LINE_INPUT_EMACS);
979 /* set our control-C handler */
980 SetConsoleCtrlHandler(DEBUG_CtrlCHandler, TRUE);
983 int main(int argc, char** argv)
985 DWORD retv = 0;
987 /* Initialize the type handling stuff. */
988 DEBUG_InitTypes();
989 DEBUG_InitCVDataTypes();
991 /* Initialize internal vars (types must have been initialized before) */
992 if (!DEBUG_IntVarsRW(TRUE)) return -1;
994 /* parse options */
995 while (argc > 1 && argv[1][0] == '-')
997 if (!strcmp( argv[1], "--auto" ))
999 automatic_mode = 1;
1000 /* force some internal variables */
1001 DBG_IVAR(UseXTerm) = 0;
1002 DBG_IVAR(BreakOnDllLoad) = 0;
1003 DBG_IVAR(ConChannelMask) = 0;
1004 DBG_IVAR(StdChannelMask) = DBG_CHN_MESG;
1005 argc--;
1006 argv++;
1007 continue;
1009 if (!strcmp( argv[1], "--debugmsg" ) && argv[2])
1011 wine_dbg_parse_options( argv[2] );
1012 argc -= 2;
1013 argv += 2;
1014 continue;
1016 DEBUG_Printf(DBG_CHN_MESG, "Usage: winedbg [--debugmsg dbgoptions] [--auto] cmdline\n" );
1017 return 1;
1020 DEBUG_InitConsole();
1022 DEBUG_Printf(DBG_CHN_MESG, "WineDbg starting... ");
1024 if (argc == 3) {
1025 HANDLE hEvent;
1026 DWORD pid;
1028 if ((pid = atoi(argv[1])) != 0 && (hEvent = (HANDLE)atoi(argv[2])) != 0) {
1029 if (!DEBUG_Attach(pid, TRUE)) {
1030 /* don't care about result */
1031 SetEvent(hEvent);
1032 goto leave;
1034 if (!SetEvent(hEvent)) {
1035 DEBUG_Printf(DBG_CHN_ERR, "Invalid event handle: %p\n", hEvent);
1036 goto leave;
1038 CloseHandle(hEvent);
1039 DEBUG_CurrPid = pid;
1043 if (DEBUG_CurrPid == 0 && argc > 1) {
1044 int i, len;
1045 LPSTR cmdLine;
1047 if (!(cmdLine = DBG_alloc(len = 1))) goto oom_leave;
1048 cmdLine[0] = '\0';
1050 for (i = 1; i < argc; i++) {
1051 len += strlen(argv[i]) + 1;
1052 if (!(cmdLine = DBG_realloc(cmdLine, len))) goto oom_leave;
1053 strcat(cmdLine, argv[i]);
1054 cmdLine[len - 2] = ' ';
1055 cmdLine[len - 1] = '\0';
1058 if (!DEBUG_Start(cmdLine)) {
1059 DEBUG_Printf(DBG_CHN_MESG, "Couldn't start process '%s'\n", cmdLine);
1060 goto leave;
1062 DBG_free(DEBUG_LastCmdLine);
1063 DEBUG_LastCmdLine = cmdLine;
1066 if (automatic_mode)
1068 retv = DEBUG_AutoMode();
1069 /* don't save modified variables in auto mode */
1071 else
1073 retv = DEBUG_MainLoop();
1074 /* saves modified variables */
1075 DEBUG_IntVarsRW(FALSE);
1078 leave:
1079 return retv;
1081 oom_leave:
1082 DEBUG_Printf(DBG_CHN_MESG, "Out of memory\n");
1083 goto leave;