tgt_active.c 32.8 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
#include "resource.h"
31 32 33
#include "winternl.h"
#include "wine/debug.h"
#include "wine/exception.h"
34
#include "wine/unicode.h"
35 36 37

WINE_DEFAULT_DEBUG_CHANNEL(winedbg);

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

static void dbg_init_current_process(void)
{
}

static void dbg_init_current_thread(void* start)
{
    if (start)
    {
49
	if (dbg_curr_process->threads &&
Eric Pouech's avatar
Eric Pouech committed
50
            !dbg_curr_process->threads->next && /* first thread ? */
51
	    DBG_IVAR(BreakAllThreadsStartup))
Eric Pouech's avatar
Eric Pouech committed
52
        {
53
	    ADDRESS64   addr;
Eric Pouech's avatar
Eric Pouech committed
54 55 56

            break_set_xpoints(FALSE);
	    addr.Mode   = AddrModeFlat;
57
	    addr.Offset = (DWORD_PTR)start;
Eric Pouech's avatar
Eric Pouech committed
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
	    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)
 */
75
BOOL dbg_attach_debuggee(DWORD pid, BOOL cofe)
Eric Pouech's avatar
Eric Pouech committed
76
{
Eric Pouech's avatar
Eric Pouech committed
77
    if (!(dbg_curr_process = dbg_add_process(&be_process_active_io, pid, 0))) return FALSE;
Eric Pouech's avatar
Eric Pouech committed
78 79 80

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

87 88
    SetEnvironmentVariableA("DBGHELP_NOLIVE", NULL);

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

static unsigned dbg_fetch_context(void)
{
    dbg_context.ContextFlags = CONTEXT_CONTROL
96
        | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT
Eric Pouech's avatar
Eric Pouech committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
#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;
}

112 113 114 115 116 117 118 119
/***********************************************************************
 *              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
120
{
121
    ADDRESS64   addr;
Eric Pouech's avatar
Eric Pouech committed
122
    BOOL        is_break;
123
    char        hexbuf[MAX_OFFSET_TO_STR_LEN];
Eric Pouech's avatar
Eric Pouech committed
124 125 126 127 128 129 130 131 132 133

    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)
        {
134 135 136 137 138 139 140 141 142 143 144 145 146
        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
147 148 149 150 151 152 153 154
        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);

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

    if (is_debug && !is_break && break_should_continue(&addr, rec->ExceptionCode))
Eric Pouech's avatar
Eric Pouech committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
	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 */
185
	be_cpu->print_context(dbg_curr_thread->handle, &dbg_context, 0);
Eric Pouech's avatar
Eric Pouech committed
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 213 214
	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);
215
                dbg_printf("%s () at %s:%u\n", last_name, last_file, il.LineNumber);
Eric Pouech's avatar
Eric Pouech committed
216 217 218 219 220 221 222
            }
        }
    }
    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)
    {
223
        ADDRESS64 tmp = addr;
Eric Pouech's avatar
Eric Pouech committed
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 251 252
        /* 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);

253
    WINE_TRACE("exception=%x first_chance=%c\n",
Eric Pouech's avatar
Eric Pouech committed
254 255 256 257 258 259 260 261 262 263 264 265 266 267
               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);
268 269
        if(!pThread)
        {
270
            dbg_printf("Thread ID=%04x not in our list of threads -> can't rename\n", pThreadName->dwThreadID);
271 272
            return DBG_CONTINUE;
        }
Eric Pouech's avatar
Eric Pouech committed
273
        if (dbg_read_memory(pThreadName->szName, pThread->name, 9))
274
            dbg_printf("Thread ID=%04x renamed using MS VC6 extension (name==\"%.9s\")\n",
Eric Pouech's avatar
Eric Pouech committed
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 312 313
                       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",
314 315
                           rec->ExceptionInformation[0] == EXCEPTION_WRITE_FAULT ? "write" :
                           rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT ? "execute" : "read",
Eric Pouech's avatar
Eric Pouech committed
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
                           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:
        {
331
            ADDRESS64       addr;
Eric Pouech's avatar
Eric Pouech committed
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 383 384

            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:
385
            dbg_printf("floating point overflow");
Eric Pouech's avatar
Eric Pouech committed
386 387
            break;
	case EXCEPTION_FLT_UNDERFLOW:
388
            dbg_printf("floating point underflow");
Eric Pouech's avatar
Eric Pouech committed
389 390 391 392
            break;
	case EXCEPTION_FLT_STACK_CHECK:
            dbg_printf("floating point stack check");
            break;
393 394 395 396 397 398 399 400
        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
401
        default:
402
            dbg_printf("0x%08x", rec->ExceptionCode);
Eric Pouech's avatar
Eric Pouech committed
403 404 405 406 407 408 409
            break;
        }
    }
    if( (rec->ExceptionFlags & EH_STACK_INVALID) ) {
        dbg_printf( ", invalid program stack" );
    }

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

    return DBG_CONTINUE;
}

420 421
static BOOL tgt_process_active_close_process(struct dbg_process* pcs, BOOL kill);

422
static void fetch_module_name(void* name_addr, BOOL unicode, void* mod_addr,
423
                              WCHAR* buffer, size_t bufsz, BOOL is_pcs)
424
{
425 426 427
    static WCHAR        pcspid[] = {'P','r','o','c','e','s','s','_','%','0','8','x',0};
    static WCHAR        dlladdr[] = {'D','L','L','_','%','0','8','l','x',0};

428 429
    memory_get_string_indirect(dbg_curr_process, name_addr, unicode, buffer, bufsz);
    if (!buffer[0] &&
430
        !GetModuleFileNameExW(dbg_curr_process->handle, mod_addr, buffer, bufsz))
431 432 433 434
    {
        if (is_pcs)
        {
            HMODULE h;
435
            WORD (WINAPI *gpif)(HANDLE, LPWSTR, DWORD);
436 437 438 439 440 441 442 443

            /* 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")) ||
444
                !(gpif = (void*)GetProcAddress(h, "GetProcessImageFileNameW")) ||
445
                !(gpif)(dbg_curr_process->handle, buffer, bufsz))
446
                snprintfW(buffer, bufsz, pcspid, dbg_curr_pid);
447 448
        }
        else
449
            snprintfW(buffer, bufsz, dlladdr, (unsigned long)mod_addr);
450 451 452
    }
}

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

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

512
        WINE_TRACE("%04x:%04x: create process '%s'/%p @%p (%u<%u>)\n",
Eric Pouech's avatar
Eric Pouech committed
513
                   de->dwProcessId, de->dwThreadId,
514 515
                   wine_dbgstr_w(u.buffer),
                   de->u.CreateProcessInfo.lpImageName,
516
                   de->u.CreateProcessInfo.lpStartAddress,
Eric Pouech's avatar
Eric Pouech committed
517 518
                   de->u.CreateProcessInfo.dwDebugInfoFileOffset,
                   de->u.CreateProcessInfo.nDebugInfoSize);
519
        dbg_set_process_name(dbg_curr_process, u.buffer);
Eric Pouech's avatar
Eric Pouech committed
520

521
        if (!dbg_init(dbg_curr_process->handle, u.buffer, FALSE))
Eric Pouech's avatar
Eric Pouech committed
522
            dbg_printf("Couldn't initiate DbgHelp\n");
523 524
        if (!dbg_load_module(dbg_curr_process->handle, de->u.CreateProcessInfo.hFile, u.buffer,
                             (DWORD_PTR)de->u.CreateProcessInfo.lpBaseOfImage, 0))
525
            dbg_printf("couldn't load main module (%u)\n", GetLastError());
Eric Pouech's avatar
Eric Pouech committed
526

527 528
        WINE_TRACE("%04x:%04x: create thread I @%p\n",
                   de->dwProcessId, de->dwThreadId, de->u.CreateProcessInfo.lpStartAddress);
Eric Pouech's avatar
Eric Pouech committed
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543

        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:
544
        WINE_TRACE("%04x:%04x: exit process (%d)\n",
Eric Pouech's avatar
Eric Pouech committed
545 546 547 548 549 550 551
                   de->dwProcessId, de->dwThreadId, de->u.ExitProcess.dwExitCode);

        if (dbg_curr_process == NULL)
        {
            WINE_ERR("Unknown process\n");
            break;
        }
552
        tgt_process_active_close_process(dbg_curr_process, FALSE);
553
        dbg_printf("Process of pid=%04x has terminated\n", de->dwProcessId);
Eric Pouech's avatar
Eric Pouech committed
554 555 556
        break;

    case CREATE_THREAD_DEBUG_EVENT:
557 558
        WINE_TRACE("%04x:%04x: create thread D @%p\n",
                   de->dwProcessId, de->dwThreadId, de->u.CreateThread.lpStartAddress);
Eric Pouech's avatar
Eric Pouech committed
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583

        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:
584
        WINE_TRACE("%04x:%04x: exit thread (%d)\n",
Eric Pouech's avatar
Eric Pouech committed
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
                   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;
        }
602 603 604
        fetch_module_name(de->u.LoadDll.lpImageName,
                          de->u.LoadDll.fUnicode,
                          de->u.LoadDll.lpBaseOfDll,
605
                          u.buffer, sizeof(u.buffer) / sizeof(WCHAR), FALSE);
Eric Pouech's avatar
Eric Pouech committed
606

607
        WINE_TRACE("%04x:%04x: loads DLL %s @%p (%u<%u>)\n",
Eric Pouech's avatar
Eric Pouech committed
608
                   de->dwProcessId, de->dwThreadId,
609
                   wine_dbgstr_w(u.buffer), de->u.LoadDll.lpBaseOfDll,
Eric Pouech's avatar
Eric Pouech committed
610 611
                   de->u.LoadDll.dwDebugInfoFileOffset,
                   de->u.LoadDll.nDebugInfoSize);
612 613
        dbg_load_module(dbg_curr_process->handle, de->u.LoadDll.hFile, u.buffer,
                        (DWORD_PTR)de->u.LoadDll.lpBaseOfDll, 0);
Eric Pouech's avatar
Eric Pouech committed
614 615 616 617 618 619
        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",
620
                       dbg_W2A(u.buffer, -1), (unsigned long)de->u.LoadDll.lpBaseOfDll);
Eric Pouech's avatar
Eric Pouech committed
621 622 623 624 625
            if (dbg_fetch_context()) cont = 0;
        }
        break;

    case UNLOAD_DLL_DEBUG_EVENT:
626
        WINE_TRACE("%04x:%04x: unload DLL @%p\n",
Eric Pouech's avatar
Eric Pouech committed
627
                   de->dwProcessId, de->dwThreadId,
628
                   de->u.UnloadDll.lpBaseOfDll);
Eric Pouech's avatar
Eric Pouech committed
629 630 631 632 633 634 635 636 637 638 639 640 641 642
        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,
643
                          de->u.DebugString.fUnicode, u.bufferA, sizeof(u.bufferA));
644
        WINE_TRACE("%04x:%04x: output debug string (%s)\n",
645
                   de->dwProcessId, de->dwThreadId, u.bufferA);
Eric Pouech's avatar
Eric Pouech committed
646 647 648
        break;

    case RIP_EVENT:
649
        WINE_TRACE("%04x:%04x: rip error=%u type=%u\n",
Eric Pouech's avatar
Eric Pouech committed
650 651 652 653 654
                   de->dwProcessId, de->dwThreadId, de->u.RipInfo.dwError,
                   de->u.RipInfo.dwType);
        break;

    default:
655
        WINE_TRACE("%04x:%04x: unknown event (%x)\n",
Eric Pouech's avatar
Eric Pouech committed
656 657 658 659 660 661 662 663 664 665 666
                   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)
    {
667 668
        ADDRESS64       addr;
        char            hexbuf[MAX_OFFSET_TO_STR_LEN];
Eric Pouech's avatar
Eric Pouech committed
669 670 671

        dbg_exception_epilog();
        memory_get_current_pc(&addr);
672 673 674
        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
675 676 677 678
                   dbg_curr_thread->exec_count);
        if (dbg_curr_thread)
        {
            if (!SetThreadContext(dbg_curr_thread->handle, &dbg_context))
679
                dbg_printf("Cannot set ctx on %04x\n", dbg_curr_tid);
Eric Pouech's avatar
Eric Pouech committed
680 681 682 683
        }
    }
    dbg_interactiveP = FALSE;
    if (!ContinueDebugEvent(dbg_curr_pid, dbg_curr_tid, cont))
684
        dbg_printf("Cannot continue on %04x (%08x)\n", dbg_curr_tid, cont);
Eric Pouech's avatar
Eric Pouech committed
685 686
}

687 688 689 690
static void wait_exception(void)
{
    DEBUG_EVENT		de;

691
    while (dbg_num_processes() && WaitForDebugEvent(&de, INFINITE))
692 693 694 695 696 697
    {
        if (dbg_handle_debug_event(&de)) break;
    }
    dbg_interactiveP = TRUE;
}

Eric Pouech's avatar
Eric Pouech committed
698 699
void dbg_wait_next_exception(DWORD cont, int count, int mode)
{
700 701
    ADDRESS64           addr;
    char                hexbuf[MAX_OFFSET_TO_STR_LEN];
Eric Pouech's avatar
Eric Pouech committed
702 703 704 705 706 707 708 709

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

710
    wait_exception();
Eric Pouech's avatar
Eric Pouech committed
711 712 713
    if (!dbg_curr_process) return;

    memory_get_current_pc(&addr);
714 715 716
    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
717 718 719
               dbg_curr_thread->exec_count);
}

720
void     dbg_active_wait_for_first_exception(void)
Eric Pouech's avatar
Eric Pouech committed
721
{
722
    dbg_interactiveP = FALSE;
Eric Pouech's avatar
Eric Pouech committed
723
    /* wait for first exception */
724
    wait_exception();
Eric Pouech's avatar
Eric Pouech committed
725 726
}

727
static	unsigned dbg_start_debuggee(LPSTR cmdLine)
Eric Pouech's avatar
Eric Pouech committed
728 729
{
    PROCESS_INFORMATION	info;
730
    STARTUPINFOA	startup, current;
731
    DWORD               flags;
Eric Pouech's avatar
Eric Pouech committed
732

733 734
    GetStartupInfoA(&current);

Eric Pouech's avatar
Eric Pouech committed
735 736 737
    memset(&startup, 0, sizeof(startup));
    startup.cb = sizeof(startup);
    startup.dwFlags = STARTF_USESHOWWINDOW;
738 739 740

    startup.wShowWindow = (current.dwFlags & STARTF_USESHOWWINDOW) ?
        current.wShowWindow : SW_SHOWNORMAL;
Eric Pouech's avatar
Eric Pouech committed
741 742 743 744

    /* FIXME: shouldn't need the CREATE_NEW_CONSOLE, but as usual CUI:s need it
     * while GUI:s don't
     */
745 746 747 748
    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
749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
                       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
773
    if (!(dbg_curr_process = dbg_add_process(&be_process_active_io, dbg_curr_pid, 0))) return FALSE;
774
    dbg_curr_process->active_debuggee = TRUE;
Eric Pouech's avatar
Eric Pouech committed
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793

    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);
794
        dbg_active_wait_for_first_exception();
Eric Pouech's avatar
Eric Pouech committed
795 796 797 798
        source_list_from_addr(NULL, 0);
    }
}

799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
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)
    {
821
        if (!dbg_attach_debuggee(pid, FALSE))
Eric Pouech's avatar
Eric Pouech committed
822
            return start_error_init;
823 824
    }
    /* try the form <myself> pid evt (Win32 JIT debugger) */
Eric Pouech's avatar
Eric Pouech committed
825 826
    else if (argc == 2 && str2int(argv[0], &pid) && pid != 0 &&
             str2int(argv[1], &evt) && evt != 0)
827
    {
828
        if (!dbg_attach_debuggee(pid, TRUE))
829 830 831 832 833 834 835
        {
            /* don't care about result */
            SetEvent((HANDLE)evt);
            return start_error_init;
        }
        if (!SetEvent((HANDLE)evt))
        {
836
            WINE_ERR("Invalid event handle: %x\n", evt);
837 838 839 840
            return start_error_init;
        }
        CloseHandle((HANDLE)evt);
    }
Eric Pouech's avatar
Eric Pouech committed
841 842 843 844
    else return start_error_parse;

    dbg_curr_pid = pid;
    return start_ok;
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 877 878 879 880 881 882 883 884 885
}

/******************************************************************
 *		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
886 887 888 889 890 891 892 893

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

897 898 899 900 901
    if (!strcmp(argv[0], "--auto"))
    {
        /* auto mode */
        argc--; argv++;
        ds = dbg_active_attach(argc, argv);
902 903 904 905 906 907 908 909 910 911
        if (ds != start_ok) {
            msgbox_res_id(NULL, IDS_INVALID_PARAMS, IDS_AUTO_CAPTION, MB_OK);
            return ds;
        }
        if (!display_crash_dialog()) {
            dbg_init_console();
            dbg_start_interactive(INVALID_HANDLE_VALUE);
            return start_ok;
        }

912 913
        hFile = parser_generate_command_file("echo Modules:", "info share",
                                             "echo Threads:", "info threads",
914
                                             "backtrace", "detach", NULL);
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 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
    }
    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);
        }
963
        hFile = parser_generate_command_file(tmp, "detach", NULL);
964 965 966
    }
    else return start_error_parse;
    if (hFile == INVALID_HANDLE_VALUE) return start_error_parse;
Eric Pouech's avatar
Eric Pouech committed
967

968 969 970
    if (dbg_curr_process->active_debuggee)
        dbg_active_wait_for_first_exception();

Eric Pouech's avatar
Eric Pouech committed
971 972 973
    dbg_interactiveP = TRUE;
    parser_handle(hFile);

974
    return start_ok;
Eric Pouech's avatar
Eric Pouech committed
975
}
976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991

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);
        }
992 993 994 995 996 997 998 999
    }
    if (kill)
    {
        TerminateProcess(pcs->handle, 0);
    }
    else
    {
        if (!DebugActiveProcessStop(pcs->pid)) return FALSE;
1000 1001 1002 1003 1004 1005 1006
    }
    SymCleanup(pcs->handle);
    dbg_del_process(pcs);

    return TRUE;
}

Eric Pouech's avatar
Eric Pouech committed
1007
static struct be_process_io be_process_active_io =
1008 1009 1010 1011
{
    tgt_process_active_close_process,
    ReadProcessMemory,
    WriteProcessMemory,
1012
    GetThreadSelectorEntry,
1013
};