tgt_active.c 32 KB
Newer Older
1 2 3
/*
 * Wine debugger - back-end for an active target
 *
Eric Pouech's avatar
Eric Pouech committed
4
 * Copyright 2000-2006 Eric Pouech
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20 21 22 23 24 25 26 27 28
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#include "debugger.h"
29
#include "psapi.h"
30 31 32 33 34 35
#include "winternl.h"
#include "wine/debug.h"
#include "wine/exception.h"

WINE_DEFAULT_DEBUG_CHANNEL(winedbg);

36
static char*            dbg_last_cmd_line;
Eric Pouech's avatar
Eric Pouech committed
37
static struct be_process_io be_process_active_io;
Eric Pouech's avatar
Eric Pouech committed
38 39 40 41 42 43 44 45 46 47 48 49 50

static void dbg_init_current_process(void)
{
}

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)) 
        {
51
	    ADDRESS64   addr;
Eric Pouech's avatar
Eric Pouech committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

            break_set_xpoints(FALSE);
	    addr.Mode   = AddrModeFlat;
	    addr.Offset = (DWORD)start;
	    break_add_break(&addr, TRUE, TRUE);
	    break_set_xpoints(TRUE);
	}
    } 
}

static unsigned dbg_handle_debug_event(DEBUG_EVENT* de);

/******************************************************************
 *		dbg_attach_debuggee
 *
 * Sets the debuggee to <pid>
 * cofe instructs winedbg what to do when first exception is received 
 * (break=FALSE, continue=TRUE)
 * wfe is set to TRUE if dbg_attach_debuggee should also proceed with all debug events
 * until the first exception is received (aka: attach to an already running process)
 */
73
BOOL dbg_attach_debuggee(DWORD pid, BOOL cofe)
Eric Pouech's avatar
Eric Pouech committed
74
{
Eric Pouech's avatar
Eric Pouech committed
75
    if (!(dbg_curr_process = dbg_add_process(&be_process_active_io, pid, 0))) return FALSE;
Eric Pouech's avatar
Eric Pouech committed
76 77 78

    if (!DebugActiveProcess(pid)) 
    {
79
        dbg_printf("Can't attach process %04x: error %u\n", pid, GetLastError());
Eric Pouech's avatar
Eric Pouech committed
80 81 82 83 84
        dbg_del_process(dbg_curr_process);
	return FALSE;
    }
    dbg_curr_process->continue_on_first_exception = cofe;

85 86
    SetEnvironmentVariableA("DBGHELP_NOLIVE", NULL);

87
    dbg_curr_process->active_debuggee = TRUE;
Eric Pouech's avatar
Eric Pouech committed
88 89 90 91 92 93
    return TRUE;
}

static unsigned dbg_fetch_context(void)
{
    dbg_context.ContextFlags = CONTEXT_CONTROL
94
        | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT
Eric Pouech's avatar
Eric Pouech committed
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
#ifdef CONTEXT_SEGMENTS
        | CONTEXT_SEGMENTS
#endif
#ifdef CONTEXT_DEBUG_REGISTERS
        | CONTEXT_DEBUG_REGISTERS
#endif
        ;
    if (!GetThreadContext(dbg_curr_thread->handle, &dbg_context))
    {
        WINE_WARN("Can't get thread's context\n");
        return FALSE;
    }
    return TRUE;
}

110 111 112 113 114 115 116 117
/***********************************************************************
 *              dbg_exception_prolog
 *
 * Examine exception and decide if interactive mode is entered(return TRUE)
 * or exception is silently continued(return FALSE)
 * is_debug means the exception is a breakpoint or single step exception
 */
static unsigned dbg_exception_prolog(BOOL is_debug, BOOL first_chance, const EXCEPTION_RECORD* rec)
Eric Pouech's avatar
Eric Pouech committed
118
{
119
    ADDRESS64   addr;
Eric Pouech's avatar
Eric Pouech committed
120
    BOOL        is_break;
121
    char        hexbuf[MAX_OFFSET_TO_STR_LEN];
Eric Pouech's avatar
Eric Pouech committed
122 123 124 125 126 127 128 129 130 131

    memory_get_current_pc(&addr);
    break_suspend_execution();
    dbg_curr_thread->excpt_record = *rec;
    dbg_curr_thread->in_exception = TRUE;

    if (!is_debug)
    {
        switch (addr.Mode)
        {
132 133 134 135 136 137 138 139 140 141 142 143 144
        case AddrModeFlat:
            dbg_printf(" in 32-bit code (%s)",
                       memory_offset_to_string(hexbuf, addr.Offset, 0));
            break;
        case AddrModeReal:
            dbg_printf(" in vm86 code (%04x:%04x)", addr.Segment, (unsigned) addr.Offset);
            break;
        case AddrMode1616:
            dbg_printf(" in 16-bit code (%04x:%04x)", addr.Segment, (unsigned) addr.Offset);
            break;
        case AddrMode1632:
            dbg_printf(" in 32-bit code (%04x:%08lx)", addr.Segment, (unsigned long) addr.Offset);
            break;
Eric Pouech's avatar
Eric Pouech committed
145 146 147 148 149 150 151 152
        default: dbg_printf(" bad address");
        }
	dbg_printf(".\n");
    }

    /* this will resynchronize builtin dbghelp's internal ELF module list */
    SymLoadModule(dbg_curr_process->handle, 0, 0, 0, 0, 0);

153
    if (is_debug) break_adjust_pc(&addr, rec->ExceptionCode, first_chance, &is_break);
Eric Pouech's avatar
Eric Pouech committed
154 155 156 157 158
    /*
     * Do a quiet backtrace so that we have an idea of what the situation
     * is WRT the source files.
     */
    stack_fetch_frames();
159 160

    if (is_debug && !is_break && break_should_continue(&addr, rec->ExceptionCode))
Eric Pouech's avatar
Eric Pouech committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
	return FALSE;

    if (addr.Mode != dbg_curr_thread->addr_mode)
    {
        const char* name = NULL;
        
        switch (addr.Mode)
        {
        case AddrMode1616: name = "16 bit";     break;
        case AddrMode1632: name = "32 bit";     break;
        case AddrModeReal: name = "vm86";       break;
        case AddrModeFlat: name = "32 bit";     break;
        }
        
        dbg_printf("In %s mode.\n", name);
        dbg_curr_thread->addr_mode = addr.Mode;
    }
    display_print();

    if (!is_debug)
    {
	/* This is a real crash, dump some info */
183
	be_cpu->print_context(dbg_curr_thread->handle, &dbg_context, 0);
Eric Pouech's avatar
Eric Pouech committed
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
	stack_info();
        be_cpu->print_segment_info(dbg_curr_thread->handle, &dbg_context);
	stack_backtrace(dbg_curr_tid);
    }
    else
    {
        static char*        last_name;
        static char*        last_file;

        char                buffer[sizeof(SYMBOL_INFO) + 256];
        SYMBOL_INFO*        si = (SYMBOL_INFO*)buffer;
        void*               lin = memory_to_linear_addr(&addr);
        DWORD64             disp64;
        IMAGEHLP_LINE       il;
        DWORD               disp;

        si->SizeOfStruct = sizeof(*si);
        si->MaxNameLen   = 256;
        il.SizeOfStruct = sizeof(il);
        if (SymFromAddr(dbg_curr_process->handle, (DWORD_PTR)lin, &disp64, si) &&
            SymGetLineFromAddr(dbg_curr_process->handle, (DWORD_PTR)lin, &disp, &il))
        {
            if ((!last_name || strcmp(last_name, si->Name)) ||
                (!last_file || strcmp(last_file, il.FileName)))
            {
                HeapFree(GetProcessHeap(), 0, last_name);
                HeapFree(GetProcessHeap(), 0, last_file);
                last_name = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(si->Name) + 1), si->Name);
                last_file = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(il.FileName) + 1), il.FileName);
213
                dbg_printf("%s () at %s:%u\n", last_name, last_file, il.LineNumber);
Eric Pouech's avatar
Eric Pouech committed
214 215 216 217 218 219 220
            }
        }
    }
    if (!is_debug || is_break ||
        dbg_curr_thread->exec_mode == dbg_exec_step_over_insn ||
        dbg_curr_thread->exec_mode == dbg_exec_step_into_insn)
    {
221
        ADDRESS64 tmp = addr;
Eric Pouech's avatar
Eric Pouech committed
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
        /* Show where we crashed */
        memory_disasm_one_insn(&tmp);
    }
    source_list_from_addr(&addr, 0);

    return TRUE;
}

static void dbg_exception_epilog(void)
{
    break_restart_execution(dbg_curr_thread->exec_count);
    /*
     * This will have gotten absorbed into the breakpoint info
     * if it was used.  Otherwise it would have been ignored.
     * In any case, we don't mess with it any more.
     */
    if (dbg_curr_thread->exec_mode == dbg_exec_cont)
	dbg_curr_thread->exec_count = 0;
    dbg_curr_thread->in_exception = FALSE;
}

static DWORD dbg_handle_exception(const EXCEPTION_RECORD* rec, BOOL first_chance)
{
    BOOL                is_debug = FALSE;
    THREADNAME_INFO*    pThreadName;
    struct dbg_thread*  pThread;

    assert(dbg_curr_thread);

251
    WINE_TRACE("exception=%x first_chance=%c\n",
Eric Pouech's avatar
Eric Pouech committed
252 253 254 255 256 257 258 259 260 261 262 263 264 265
               rec->ExceptionCode, first_chance ? 'Y' : 'N');

    switch (rec->ExceptionCode)
    {
    case EXCEPTION_BREAKPOINT:
    case EXCEPTION_SINGLE_STEP:
        is_debug = TRUE;
        break;
    case EXCEPTION_NAME_THREAD:
        pThreadName = (THREADNAME_INFO*)(rec->ExceptionInformation);
        if (pThreadName->dwThreadID == -1)
            pThread = dbg_curr_thread;
        else
            pThread = dbg_get_thread(dbg_curr_process, pThreadName->dwThreadID);
266 267
        if(!pThread)
        {
268
            dbg_printf("Thread ID=%04x not in our list of threads -> can't rename\n", pThreadName->dwThreadID);
269 270
            return DBG_CONTINUE;
        }
Eric Pouech's avatar
Eric Pouech committed
271
        if (dbg_read_memory(pThreadName->szName, pThread->name, 9))
272
            dbg_printf("Thread ID=%04x renamed using MS VC6 extension (name==\"%s\")\n",
Eric Pouech's avatar
Eric Pouech committed
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
                       pThread->tid, pThread->name);
        return DBG_CONTINUE;
    }

    if (first_chance && !is_debug && !DBG_IVAR(BreakOnFirstChance) &&
	!(rec->ExceptionFlags & EH_STACK_INVALID))
    {
        /* pass exception to program except for debug exceptions */
        return DBG_EXCEPTION_NOT_HANDLED;
    }

    if (!is_debug)
    {
        /* print some infos */
        dbg_printf("%s: ",
                   first_chance ? "First chance exception" : "Unhandled exception");
        switch (rec->ExceptionCode)
        {
        case EXCEPTION_INT_DIVIDE_BY_ZERO:
            dbg_printf("divide by zero");
            break;
        case EXCEPTION_INT_OVERFLOW:
            dbg_printf("overflow");
            break;
        case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
            dbg_printf("array bounds");
            break;
        case EXCEPTION_ILLEGAL_INSTRUCTION:
            dbg_printf("illegal instruction");
            break;
        case EXCEPTION_STACK_OVERFLOW:
            dbg_printf("stack overflow");
            break;
        case EXCEPTION_PRIV_INSTRUCTION:
            dbg_printf("privileged instruction");
            break;
        case EXCEPTION_ACCESS_VIOLATION:
            if (rec->NumberParameters == 2)
                dbg_printf("page fault on %s access to 0x%08lx",
312 313
                           rec->ExceptionInformation[0] == EXCEPTION_WRITE_FAULT ? "write" :
                           rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT ? "execute" : "read",
Eric Pouech's avatar
Eric Pouech committed
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
                           rec->ExceptionInformation[1]);
            else
                dbg_printf("page fault");
            break;
        case EXCEPTION_DATATYPE_MISALIGNMENT:
            dbg_printf("Alignment");
            break;
	case DBG_CONTROL_C:
            dbg_printf("^C");
            break;
        case CONTROL_C_EXIT:
            dbg_printf("^C");
            break;
        case STATUS_POSSIBLE_DEADLOCK:
        {
329
            ADDRESS64       addr;
Eric Pouech's avatar
Eric Pouech committed
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382

            addr.Mode   = AddrModeFlat;
            addr.Offset = rec->ExceptionInformation[0];

            dbg_printf("wait failed on critical section ");
            print_address(&addr, FALSE);
        }
        if (!DBG_IVAR(BreakOnCritSectTimeOut))
        {
            dbg_printf("\n");
            return DBG_EXCEPTION_NOT_HANDLED;
        }
        break;
        case EXCEPTION_WINE_STUB:
        {
            char dll[32], name[64];
            memory_get_string(dbg_curr_process,
                              (void*)rec->ExceptionInformation[0], TRUE, FALSE,
                              dll, sizeof(dll));
            if (HIWORD(rec->ExceptionInformation[1]))
                memory_get_string(dbg_curr_process,
                                  (void*)rec->ExceptionInformation[1], TRUE, FALSE,
                                  name, sizeof(name));
            else
                sprintf( name, "%ld", rec->ExceptionInformation[1] );
            dbg_printf("unimplemented function %s.%s called", dll, name);
        }
        break;
        case EXCEPTION_WINE_ASSERTION:
            dbg_printf("assertion failed");
            break;
        case EXCEPTION_VM86_INTx:
            dbg_printf("interrupt %02lx in vm86 mode", rec->ExceptionInformation[0]);
            break;
        case EXCEPTION_VM86_STI:
            dbg_printf("sti in vm86 mode");
            break;
        case EXCEPTION_VM86_PICRETURN:
            dbg_printf("PIC return in vm86 mode");
            break;
	case EXCEPTION_FLT_DENORMAL_OPERAND:
            dbg_printf("denormal float operand");
            break;
	case EXCEPTION_FLT_DIVIDE_BY_ZERO:
            dbg_printf("divide by zero");
            break;
	case EXCEPTION_FLT_INEXACT_RESULT:
            dbg_printf("inexact float result");
            break;
	case EXCEPTION_FLT_INVALID_OPERATION:
            dbg_printf("invalid float operation");
            break;
	case EXCEPTION_FLT_OVERFLOW:
383
            dbg_printf("floating point overflow");
Eric Pouech's avatar
Eric Pouech committed
384 385
            break;
	case EXCEPTION_FLT_UNDERFLOW:
386
            dbg_printf("floating point underflow");
Eric Pouech's avatar
Eric Pouech committed
387 388 389 390
            break;
	case EXCEPTION_FLT_STACK_CHECK:
            dbg_printf("floating point stack check");
            break;
391 392 393 394 395 396 397 398
        case CXX_EXCEPTION:
            if(rec->NumberParameters == 3 && rec->ExceptionInformation[0] == CXX_FRAME_MAGIC)
                dbg_printf("C++ exception(object = 0x%08lx, type = 0x%08lx)",
                           rec->ExceptionInformation[1], rec->ExceptionInformation[2]);
            else
                dbg_printf("C++ exception with strange parameter count %d or magic 0x%08lx",
                           rec->NumberParameters, rec->ExceptionInformation[0]);
            break;
Eric Pouech's avatar
Eric Pouech committed
399
        default:
400
            dbg_printf("0x%08x", rec->ExceptionCode);
Eric Pouech's avatar
Eric Pouech committed
401 402 403 404 405 406 407
            break;
        }
    }
    if( (rec->ExceptionFlags & EH_STACK_INVALID) ) {
        dbg_printf( ", invalid program stack" );
    }

408
    if (dbg_exception_prolog(is_debug, first_chance, rec))
Eric Pouech's avatar
Eric Pouech committed
409 410 411 412 413 414 415 416 417
    {
	dbg_interactiveP = TRUE;
        return 0;
    }
    dbg_exception_epilog();

    return DBG_CONTINUE;
}

418 419
static BOOL tgt_process_active_close_process(struct dbg_process* pcs, BOOL kill);

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
static void fetch_module_name(void* name_addr, BOOL unicode, void* mod_addr,
                              char* buffer, size_t bufsz, BOOL is_pcs)
{
    memory_get_string_indirect(dbg_curr_process, name_addr, unicode, buffer, bufsz);
    if (!buffer[0] &&
        !GetModuleFileNameExA(dbg_curr_process->handle, mod_addr, buffer, bufsz))
    {
        if (is_pcs)
        {
            HMODULE h;
            WORD (WINAPI *gpif)(HANDLE, LPSTR, DWORD);

            /* On Windows, when we get the process creation debug event for a process
             * created by winedbg, the modules' list is not initialized yet. Hence,
             * GetModuleFileNameExA (on the main module) will generate an error.
             * Psapi (starting on XP) provides GetProcessImageFileName() which should
             * give us the expected result
             */
            if (!(h = GetModuleHandleA("psapi")) ||
                !(gpif = (void*)GetProcAddress(h, "GetProcessImageFileName")) ||
                !(gpif)(dbg_curr_process->handle, buffer, bufsz))
441
                snprintf(buffer, bufsz, "Process_%08x", dbg_curr_pid);
442 443 444 445 446 447
        }
        else
            snprintf(buffer, bufsz, "DLL_%p", mod_addr);
    }
}

Eric Pouech's avatar
Eric Pouech committed
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
static unsigned dbg_handle_debug_event(DEBUG_EVENT* de)
{
    char	buffer[256];
    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)
        {
466
            WINE_ERR("%04x:%04x: not a registered process or thread (perhaps a 16 bit one ?)\n",
Eric Pouech's avatar
Eric Pouech committed
467 468 469 470
                     de->dwProcessId, de->dwThreadId);
            break;
        }

471
        WINE_TRACE("%04x:%04x: exception code=%08x\n",
Eric Pouech's avatar
Eric Pouech committed
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
                   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:
Eric Pouech's avatar
Eric Pouech committed
492
        dbg_curr_process = dbg_add_process(&be_process_active_io, de->dwProcessId,
Eric Pouech's avatar
Eric Pouech committed
493 494 495 496 497 498
                                           de->u.CreateProcessInfo.hProcess);
        if (dbg_curr_process == NULL)
        {
            WINE_ERR("Couldn't create process\n");
            break;
        }
499 500 501 502
        fetch_module_name(de->u.CreateProcessInfo.lpImageName,
                          de->u.CreateProcessInfo.fUnicode,
                          de->u.CreateProcessInfo.lpBaseOfImage,
                          buffer, sizeof(buffer), TRUE);
Eric Pouech's avatar
Eric Pouech committed
503

504
        WINE_TRACE("%04x:%04x: create process '%s'/%p @%p (%u<%u>)\n",
Eric Pouech's avatar
Eric Pouech committed
505 506
                   de->dwProcessId, de->dwThreadId,
                   buffer, de->u.CreateProcessInfo.lpImageName,
507
                   de->u.CreateProcessInfo.lpStartAddress,
Eric Pouech's avatar
Eric Pouech committed
508 509 510 511
                   de->u.CreateProcessInfo.dwDebugInfoFileOffset,
                   de->u.CreateProcessInfo.nDebugInfoSize);
        dbg_set_process_name(dbg_curr_process, buffer);

512
        if (!dbg_init(dbg_curr_process->handle, buffer, FALSE))
Eric Pouech's avatar
Eric Pouech committed
513
            dbg_printf("Couldn't initiate DbgHelp\n");
514 515
        if (!SymLoadModule(dbg_curr_process->handle, de->u.CreateProcessInfo.hFile, buffer, NULL,
                           (unsigned long)de->u.CreateProcessInfo.lpBaseOfImage, 0))
516
            dbg_printf("couldn't load main module (%u)\n", GetLastError());
Eric Pouech's avatar
Eric Pouech committed
517

518 519
        WINE_TRACE("%04x:%04x: create thread I @%p\n",
                   de->dwProcessId, de->dwThreadId, de->u.CreateProcessInfo.lpStartAddress);
Eric Pouech's avatar
Eric Pouech committed
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534

        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:
535
        WINE_TRACE("%04x:%04x: exit process (%d)\n",
Eric Pouech's avatar
Eric Pouech committed
536 537 538 539 540 541 542
                   de->dwProcessId, de->dwThreadId, de->u.ExitProcess.dwExitCode);

        if (dbg_curr_process == NULL)
        {
            WINE_ERR("Unknown process\n");
            break;
        }
543
        tgt_process_active_close_process(dbg_curr_process, FALSE);
544
        dbg_printf("Process of pid=%04x has terminated\n", de->dwProcessId);
Eric Pouech's avatar
Eric Pouech committed
545 546 547
        break;

    case CREATE_THREAD_DEBUG_EVENT:
548 549
        WINE_TRACE("%04x:%04x: create thread D @%p\n",
                   de->dwProcessId, de->dwThreadId, de->u.CreateThread.lpStartAddress);
Eric Pouech's avatar
Eric Pouech committed
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574

        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:
575
        WINE_TRACE("%04x:%04x: exit thread (%d)\n",
Eric Pouech's avatar
Eric Pouech committed
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
                   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;
        }
593 594 595 596
        fetch_module_name(de->u.LoadDll.lpImageName,
                          de->u.LoadDll.fUnicode,
                          de->u.LoadDll.lpBaseOfDll,
                          buffer, sizeof(buffer), FALSE);
Eric Pouech's avatar
Eric Pouech committed
597

598
        WINE_TRACE("%04x:%04x: loads DLL %s @%p (%u<%u>)\n",
Eric Pouech's avatar
Eric Pouech committed
599
                   de->dwProcessId, de->dwThreadId,
600
                   buffer, de->u.LoadDll.lpBaseOfDll,
Eric Pouech's avatar
Eric Pouech committed
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
                   de->u.LoadDll.dwDebugInfoFileOffset,
                   de->u.LoadDll.nDebugInfoSize);
        SymLoadModule(dbg_curr_process->handle, de->u.LoadDll.hFile, buffer, NULL,
                      (unsigned long)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",
                       buffer, (unsigned long)de->u.LoadDll.lpBaseOfDll);
            if (dbg_fetch_context()) cont = 0;
        }
        break;

    case UNLOAD_DLL_DEBUG_EVENT:
617
        WINE_TRACE("%04x:%04x: unload DLL @%p\n",
Eric Pouech's avatar
Eric Pouech committed
618
                   de->dwProcessId, de->dwThreadId,
619
                   de->u.UnloadDll.lpBaseOfDll);
Eric Pouech's avatar
Eric Pouech committed
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
        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, buffer, sizeof(buffer));
635
        WINE_TRACE("%04x:%04x: output debug string (%s)\n",
Eric Pouech's avatar
Eric Pouech committed
636 637 638 639
                   de->dwProcessId, de->dwThreadId, buffer);
        break;

    case RIP_EVENT:
640
        WINE_TRACE("%04x:%04x: rip error=%u type=%u\n",
Eric Pouech's avatar
Eric Pouech committed
641 642 643 644 645
                   de->dwProcessId, de->dwThreadId, de->u.RipInfo.dwError,
                   de->u.RipInfo.dwType);
        break;

    default:
646
        WINE_TRACE("%04x:%04x: unknown event (%x)\n",
Eric Pouech's avatar
Eric Pouech committed
647 648 649 650 651 652 653 654 655 656 657
                   de->dwProcessId, de->dwThreadId, de->dwDebugEventCode);
    }
    if (!cont) return TRUE;  /* stop execution */
    ContinueDebugEvent(de->dwProcessId, de->dwThreadId, cont);
    return FALSE;  /* continue execution */
}

static void dbg_resume_debuggee(DWORD cont)
{
    if (dbg_curr_thread->in_exception)
    {
658 659
        ADDRESS64       addr;
        char            hexbuf[MAX_OFFSET_TO_STR_LEN];
Eric Pouech's avatar
Eric Pouech committed
660 661 662

        dbg_exception_epilog();
        memory_get_current_pc(&addr);
663 664 665
        WINE_TRACE("Exiting debugger      PC=%s mode=%d count=%d\n",
                   memory_offset_to_string(hexbuf, addr.Offset, 0),
                   dbg_curr_thread->exec_mode,
Eric Pouech's avatar
Eric Pouech committed
666 667 668 669
                   dbg_curr_thread->exec_count);
        if (dbg_curr_thread)
        {
            if (!SetThreadContext(dbg_curr_thread->handle, &dbg_context))
670
                dbg_printf("Cannot set ctx on %04x\n", dbg_curr_tid);
Eric Pouech's avatar
Eric Pouech committed
671 672 673 674
        }
    }
    dbg_interactiveP = FALSE;
    if (!ContinueDebugEvent(dbg_curr_pid, dbg_curr_tid, cont))
675
        dbg_printf("Cannot continue on %04x (%08x)\n", dbg_curr_tid, cont);
Eric Pouech's avatar
Eric Pouech committed
676 677
}

678 679 680 681
static void wait_exception(void)
{
    DEBUG_EVENT		de;

682
    while (dbg_num_processes() && WaitForDebugEvent(&de, INFINITE))
683 684 685 686 687 688
    {
        if (dbg_handle_debug_event(&de)) break;
    }
    dbg_interactiveP = TRUE;
}

Eric Pouech's avatar
Eric Pouech committed
689 690
void dbg_wait_next_exception(DWORD cont, int count, int mode)
{
691 692
    ADDRESS64           addr;
    char                hexbuf[MAX_OFFSET_TO_STR_LEN];
Eric Pouech's avatar
Eric Pouech committed
693 694 695 696 697 698 699 700

    if (cont == DBG_CONTINUE)
    {
        dbg_curr_thread->exec_count = count;
        dbg_curr_thread->exec_mode = mode;
    }
    dbg_resume_debuggee(cont);

701
    wait_exception();
Eric Pouech's avatar
Eric Pouech committed
702 703 704
    if (!dbg_curr_process) return;

    memory_get_current_pc(&addr);
705 706 707
    WINE_TRACE("Entering debugger     PC=%s mode=%d count=%d\n",
               memory_offset_to_string(hexbuf, addr.Offset, 0),
               dbg_curr_thread->exec_mode,
Eric Pouech's avatar
Eric Pouech committed
708 709 710
               dbg_curr_thread->exec_count);
}

711
void     dbg_active_wait_for_first_exception(void)
Eric Pouech's avatar
Eric Pouech committed
712
{
713
    dbg_interactiveP = FALSE;
Eric Pouech's avatar
Eric Pouech committed
714
    /* wait for first exception */
715
    wait_exception();
Eric Pouech's avatar
Eric Pouech committed
716 717
}

718
static	unsigned dbg_start_debuggee(LPSTR cmdLine)
Eric Pouech's avatar
Eric Pouech committed
719 720
{
    PROCESS_INFORMATION	info;
721
    STARTUPINFOA	startup, current;
722
    DWORD               flags;
Eric Pouech's avatar
Eric Pouech committed
723

724 725
    GetStartupInfoA(&current);

Eric Pouech's avatar
Eric Pouech committed
726 727 728
    memset(&startup, 0, sizeof(startup));
    startup.cb = sizeof(startup);
    startup.dwFlags = STARTF_USESHOWWINDOW;
729 730 731

    startup.wShowWindow = (current.dwFlags & STARTF_USESHOWWINDOW) ?
        current.wShowWindow : SW_SHOWNORMAL;
Eric Pouech's avatar
Eric Pouech committed
732 733 734 735

    /* FIXME: shouldn't need the CREATE_NEW_CONSOLE, but as usual CUI:s need it
     * while GUI:s don't
     */
736 737 738 739
    flags = DEBUG_PROCESS | CREATE_NEW_CONSOLE;
    if (!DBG_IVAR(AlsoDebugProcChild)) flags |= DEBUG_ONLY_THIS_PROCESS;

    if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE, flags,
Eric Pouech's avatar
Eric Pouech committed
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
                       NULL, NULL, &startup, &info))
    {
	dbg_printf("Couldn't start process '%s'\n", cmdLine);
	return FALSE;
    }
    if (!info.dwProcessId)
    {
        /* this happens when the program being run is not a Wine binary
         * (for example, a shell wrapper around a WineLib app)
         */
        /* Current fix: list running processes and let the user attach
         * to one of them (sic)
         * FIXME: implement a real fix => grab the process (from the
         * running processes) from its name
         */
        dbg_printf("Debuggee has been started (%s)\n"
                   "But WineDbg isn't attached to it. Maybe you're trying to debug a winelib wrapper ??\n"
                   "Try to attach to one of those processes:\n", cmdLine);
        /* FIXME: (HACK) we need some time before the wrapper executes the winelib app */
        Sleep(100);
        info_win32_processes();
        return TRUE;
    }
    dbg_curr_pid = info.dwProcessId;
Eric Pouech's avatar
Eric Pouech committed
764
    if (!(dbg_curr_process = dbg_add_process(&be_process_active_io, dbg_curr_pid, 0))) return FALSE;
765
    dbg_curr_process->active_debuggee = TRUE;
Eric Pouech's avatar
Eric Pouech committed
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784

    return TRUE;
}

void	dbg_run_debuggee(const char* args)
{
    if (args)
    {
        WINE_FIXME("Re-running current program with %s as args is broken\n", args);
        return;
    }
    else 
    {
	if (!dbg_last_cmd_line)
        {
	    dbg_printf("Cannot find previously used command line.\n");
	    return;
	}
	dbg_start_debuggee(dbg_last_cmd_line);
785
        dbg_active_wait_for_first_exception();
Eric Pouech's avatar
Eric Pouech committed
786 787 788 789
        source_list_from_addr(NULL, 0);
    }
}

790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
static BOOL     str2int(const char* str, DWORD* val)
{
    char*   ptr;

    *val = strtol(str, &ptr, 10);
    return str < ptr && !*ptr;
}


/******************************************************************
 *		dbg_active_attach
 *
 * Tries to attach to a running process
 * Handles the <pid> or <pid> <evt> forms
 */
enum dbg_start  dbg_active_attach(int argc, char* argv[])
{
    DWORD       pid, evt;

    /* try the form <myself> pid */
    if (argc == 1 && str2int(argv[0], &pid) && pid != 0)
    {
812
        if (!dbg_attach_debuggee(pid, FALSE))
Eric Pouech's avatar
Eric Pouech committed
813
            return start_error_init;
814 815
    }
    /* try the form <myself> pid evt (Win32 JIT debugger) */
Eric Pouech's avatar
Eric Pouech committed
816 817
    else if (argc == 2 && str2int(argv[0], &pid) && pid != 0 &&
             str2int(argv[1], &evt) && evt != 0)
818
    {
819
        if (!dbg_attach_debuggee(pid, TRUE))
820 821 822 823 824 825 826
        {
            /* don't care about result */
            SetEvent((HANDLE)evt);
            return start_error_init;
        }
        if (!SetEvent((HANDLE)evt))
        {
827
            WINE_ERR("Invalid event handle: %x\n", evt);
828 829 830 831
            return start_error_init;
        }
        CloseHandle((HANDLE)evt);
    }
Eric Pouech's avatar
Eric Pouech committed
832 833 834 835
    else return start_error_parse;

    dbg_curr_pid = pid;
    return start_ok;
836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876
}

/******************************************************************
 *		dbg_active_launch
 *
 * Launches a debuggee (with its arguments) from argc/argv
 */
enum dbg_start    dbg_active_launch(int argc, char* argv[])
{
    int         i, len;
    LPSTR	cmd_line;

    if (argc == 0) return start_error_parse;

    if (!(cmd_line = HeapAlloc(GetProcessHeap(), 0, len = 1)))
    {
    oom_leave:
        dbg_printf("Out of memory\n");
        return start_error_init;
    }
    cmd_line[0] = '\0';

    for (i = 0; i < argc; i++)
    {
        len += strlen(argv[i]) + 1;
        if (!(cmd_line = HeapReAlloc(GetProcessHeap(), 0, cmd_line, len)))
            goto oom_leave;
        strcat(cmd_line, argv[i]);
        cmd_line[len - 2] = ' ';
        cmd_line[len - 1] = '\0';
    }

    if (!dbg_start_debuggee(cmd_line))
    {
        HeapFree(GetProcessHeap(), 0, cmd_line);
        return start_error_init;
    }
    HeapFree(GetProcessHeap(), 0, dbg_last_cmd_line);
    dbg_last_cmd_line = cmd_line;
    return start_ok;
}
Eric Pouech's avatar
Eric Pouech committed
877 878 879 880 881 882 883 884

/******************************************************************
 *		dbg_active_auto
 *
 * Starts (<pid> or <pid> <evt>) in automatic mode
 */
enum dbg_start dbg_active_auto(int argc, char* argv[])
{
885 886 887
    HANDLE              hFile;
    enum dbg_start      ds = start_error_parse;

888 889 890 891 892 893 894 895
    if (!strcmp(argv[0], "--auto"))
    {
        /* auto mode */
        argc--; argv++;
        ds = dbg_active_attach(argc, argv);
        if (ds != start_ok) return ds;
        hFile = parser_generate_command_file("echo Modules:", "info share",
                                             "echo Threads:", "info threads",
896
                                             "backtrace", "detach", NULL);
897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944
    }
    else if (!strcmp(argv[0], "--minidump"))
    {
        const char*     file = NULL;
        char            tmp[8 + 1 + MAX_PATH]; /* minidump <file> */

        argc--; argv++;
        /* hard stuff now ; we can get things like:
         * --minidump <pid>                     1 arg
         * --minidump <pid> <evt>               2 args
         * --minidump <file> <pid>              2 args
         * --minidump <file> <pid> <evt>        3 args
         */
        switch (argc)
        {
        case 1:
            ds = dbg_active_attach(argc, argv);
            break;
        case 2:
            if ((ds = dbg_active_attach(argc, argv)) != start_ok)
            {
                file = argv[0];
                ds = dbg_active_attach(argc - 1, argv + 1);
            }
            break;
        case 3:
            file = argv[0];
            ds = dbg_active_attach(argc - 1, argv + 1);
            break;
        default:
            return start_error_parse;
        }
        if (ds != start_ok) return ds;
        memcpy(tmp, "minidump \"", 10);
        if (!file)
        {
            char        path[MAX_PATH];

            GetTempPath(sizeof(path), path);
            GetTempFileName(path, "WD", 0, tmp + 10);
        }
        else strcpy(tmp + 10, file);
        strcat(tmp, "\"");
        if (!file)
        {
            /* FIXME: should generate unix name as well */
            dbg_printf("Capturing program state in %s\n", tmp + 9);
        }
945
        hFile = parser_generate_command_file(tmp, "detach", NULL);
946 947 948
    }
    else return start_error_parse;
    if (hFile == INVALID_HANDLE_VALUE) return start_error_parse;
Eric Pouech's avatar
Eric Pouech committed
949

950 951 952
    if (dbg_curr_process->active_debuggee)
        dbg_active_wait_for_first_exception();

Eric Pouech's avatar
Eric Pouech committed
953 954 955
    dbg_interactiveP = TRUE;
    parser_handle(hFile);

956
    return start_ok;
Eric Pouech's avatar
Eric Pouech committed
957
}
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981

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 && !DebugActiveProcessStop(dbg_curr_pid)) return FALSE;
    }
    SymCleanup(pcs->handle);
    dbg_del_process(pcs);

    return TRUE;
}

Eric Pouech's avatar
Eric Pouech committed
982
static struct be_process_io be_process_active_io =
983 984 985 986
{
    tgt_process_active_close_process,
    ReadProcessMemory,
    WriteProcessMemory,
987
    GetThreadSelectorEntry,
988
};