tgt_active.c 31.2 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 (list_count(&dbg_curr_process->threads) == 1 /* first thread ? */ &&
50
	    DBG_IVAR(BreakAllThreadsStartup))
Eric Pouech's avatar
Eric Pouech committed
51
        {
52
	    ADDRESS64   addr;
Eric Pouech's avatar
Eric Pouech committed
53 54 55

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

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

86 87
    SetEnvironmentVariableA("DBGHELP_NOLIVE", NULL);

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

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

114 115 116 117 118 119 120
/***********************************************************************
 *              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
 */
121
static unsigned dbg_exception_prolog(BOOL is_debug, const EXCEPTION_RECORD* rec)
Eric Pouech's avatar
Eric Pouech committed
122
{
123
    ADDRESS64   addr;
Eric Pouech's avatar
Eric Pouech committed
124 125 126 127 128 129 130 131
    BOOL        is_break;

    memory_get_current_pc(&addr);
    break_suspend_execution();

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

132
    if (is_debug) break_adjust_pc(&addr, rec->ExceptionCode, dbg_curr_thread->first_chance, &is_break);
Eric Pouech's avatar
Eric Pouech committed
133 134 135 136
    /*
     * Do a quiet backtrace so that we have an idea of what the situation
     * is WRT the source files.
     */
137
    stack_fetch_frames(&dbg_context);
138 139

    if (is_debug && !is_break && break_should_continue(&addr, rec->ExceptionCode))
Eric Pouech's avatar
Eric Pouech committed
140 141 142 143 144
	return FALSE;

    if (addr.Mode != dbg_curr_thread->addr_mode)
    {
        const char* name = NULL;
145

Eric Pouech's avatar
Eric Pouech committed
146 147 148
        switch (addr.Mode)
        {
        case AddrMode1616: name = "16 bit";     break;
149
        case AddrMode1632: name = "segmented 32 bit"; break;
Eric Pouech's avatar
Eric Pouech committed
150
        case AddrModeReal: name = "vm86";       break;
151
        case AddrModeFlat: name = be_cpu->pointer_size == 4 ? "32 bit" : "64 bit"; break;
Eric Pouech's avatar
Eric Pouech committed
152 153 154 155 156 157 158 159 160
        }
        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 */
161
	be_cpu->print_context(dbg_curr_thread->handle, &dbg_context, 0);
162
	stack_info(-1);
Eric Pouech's avatar
Eric Pouech committed
163 164 165 166 167 168 169 170 171 172 173 174
        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;
175
        IMAGEHLP_LINE64     il;
Eric Pouech's avatar
Eric Pouech committed
176 177 178 179 180 181
        DWORD               disp;

        si->SizeOfStruct = sizeof(*si);
        si->MaxNameLen   = 256;
        il.SizeOfStruct = sizeof(il);
        if (SymFromAddr(dbg_curr_process->handle, (DWORD_PTR)lin, &disp64, si) &&
182
            SymGetLineFromAddr64(dbg_curr_process->handle, (DWORD_PTR)lin, &disp, &il))
Eric Pouech's avatar
Eric Pouech committed
183 184 185 186 187 188 189 190
        {
            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);
191
                dbg_printf("%s () at %s:%u\n", last_name, last_file, il.LineNumber);
Eric Pouech's avatar
Eric Pouech committed
192 193 194 195 196 197 198
            }
        }
    }
    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)
    {
199
        ADDRESS64 tmp = addr;
Eric Pouech's avatar
Eric Pouech committed
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
        /* 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;
224
    const THREADNAME_INFO*    pThreadName;
Eric Pouech's avatar
Eric Pouech committed
225 226 227 228
    struct dbg_thread*  pThread;

    assert(dbg_curr_thread);

229
    WINE_TRACE("exception=%x first_chance=%c\n",
Eric Pouech's avatar
Eric Pouech committed
230 231 232 233 234 235 236 237 238
               rec->ExceptionCode, first_chance ? 'Y' : 'N');

    switch (rec->ExceptionCode)
    {
    case EXCEPTION_BREAKPOINT:
    case EXCEPTION_SINGLE_STEP:
        is_debug = TRUE;
        break;
    case EXCEPTION_NAME_THREAD:
239
        pThreadName = (const THREADNAME_INFO*)(rec->ExceptionInformation);
Eric Pouech's avatar
Eric Pouech committed
240 241 242 243
        if (pThreadName->dwThreadID == -1)
            pThread = dbg_curr_thread;
        else
            pThread = dbg_get_thread(dbg_curr_process, pThreadName->dwThreadID);
244 245
        if(!pThread)
        {
246
            dbg_printf("Thread ID=%04x not in our list of threads -> can't rename\n", pThreadName->dwThreadID);
247 248
            return DBG_CONTINUE;
        }
Eric Pouech's avatar
Eric Pouech committed
249
        if (dbg_read_memory(pThreadName->szName, pThread->name, 9))
250
            dbg_printf("Thread ID=%04x renamed using MS VC6 extension (name==\"%.9s\")\n",
Eric Pouech's avatar
Eric Pouech committed
251 252 253 254 255 256 257 258 259 260 261
                       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;
    }

262 263 264
    dbg_curr_thread->excpt_record = *rec;
    dbg_curr_thread->in_exception = TRUE;
    dbg_curr_thread->first_chance = first_chance;
Eric Pouech's avatar
Eric Pouech committed
265

266
    if (!is_debug) info_win32_exception();
Eric Pouech's avatar
Eric Pouech committed
267

268 269 270 271
    if (rec->ExceptionCode == STATUS_POSSIBLE_DEADLOCK && !DBG_IVAR(BreakOnCritSectTimeOut))
    {
        dbg_curr_thread->in_exception = FALSE;
        return DBG_EXCEPTION_NOT_HANDLED;
Eric Pouech's avatar
Eric Pouech committed
272 273
    }

274
    if (dbg_exception_prolog(is_debug, rec))
Eric Pouech's avatar
Eric Pouech committed
275 276 277 278 279 280 281 282 283
    {
	dbg_interactiveP = TRUE;
        return 0;
    }
    dbg_exception_epilog();

    return DBG_CONTINUE;
}

284 285
static BOOL tgt_process_active_close_process(struct dbg_process* pcs, BOOL kill);

286
static void fetch_module_name(void* name_addr, BOOL unicode, void* mod_addr,
287
                              WCHAR* buffer, size_t bufsz, BOOL is_pcs)
288
{
289 290 291
    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};

292 293
    memory_get_string_indirect(dbg_curr_process, name_addr, unicode, buffer, bufsz);
    if (!buffer[0] &&
294
        !GetModuleFileNameExW(dbg_curr_process->handle, mod_addr, buffer, bufsz))
295 296 297 298
    {
        if (is_pcs)
        {
            HMODULE h;
299
            WORD (WINAPI *gpif)(HANDLE, LPWSTR, DWORD);
300 301 302 303 304 305 306 307

            /* 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")) ||
308
                !(gpif = (void*)GetProcAddress(h, "GetProcessImageFileNameW")) ||
309
                !(gpif)(dbg_curr_process->handle, buffer, bufsz))
310
                snprintfW(buffer, bufsz, pcspid, dbg_curr_pid);
311 312
        }
        else
313
            snprintfW(buffer, bufsz, dlladdr, (unsigned long)mod_addr);
314 315 316
    }
}

Eric Pouech's avatar
Eric Pouech committed
317 318
static unsigned dbg_handle_debug_event(DEBUG_EVENT* de)
{
319 320 321 322
    union {
        char	bufferA[256];
        WCHAR	buffer[256];
    } u;
Eric Pouech's avatar
Eric Pouech committed
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
    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)
        {
338
            WINE_ERR("%04x:%04x: not a registered process or thread (perhaps a 16 bit one ?)\n",
Eric Pouech's avatar
Eric Pouech committed
339 340 341 342
                     de->dwProcessId, de->dwThreadId);
            break;
        }

343
        WINE_TRACE("%04x:%04x: exception code=%08x\n",
Eric Pouech's avatar
Eric Pouech committed
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
                   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
364
        dbg_curr_process = dbg_add_process(&be_process_active_io, de->dwProcessId,
Eric Pouech's avatar
Eric Pouech committed
365 366 367 368 369 370
                                           de->u.CreateProcessInfo.hProcess);
        if (dbg_curr_process == NULL)
        {
            WINE_ERR("Couldn't create process\n");
            break;
        }
371 372 373
        fetch_module_name(de->u.CreateProcessInfo.lpImageName,
                          de->u.CreateProcessInfo.fUnicode,
                          de->u.CreateProcessInfo.lpBaseOfImage,
374
                          u.buffer, sizeof(u.buffer) / sizeof(WCHAR), TRUE);
Eric Pouech's avatar
Eric Pouech committed
375

376
        WINE_TRACE("%04x:%04x: create process '%s'/%p @%p (%u<%u>)\n",
Eric Pouech's avatar
Eric Pouech committed
377
                   de->dwProcessId, de->dwThreadId,
378 379
                   wine_dbgstr_w(u.buffer),
                   de->u.CreateProcessInfo.lpImageName,
380
                   de->u.CreateProcessInfo.lpStartAddress,
Eric Pouech's avatar
Eric Pouech committed
381 382
                   de->u.CreateProcessInfo.dwDebugInfoFileOffset,
                   de->u.CreateProcessInfo.nDebugInfoSize);
383
        dbg_set_process_name(dbg_curr_process, u.buffer);
Eric Pouech's avatar
Eric Pouech committed
384

385
        if (!dbg_init(dbg_curr_process->handle, u.buffer, FALSE))
Eric Pouech's avatar
Eric Pouech committed
386
            dbg_printf("Couldn't initiate DbgHelp\n");
387 388
        if (!dbg_load_module(dbg_curr_process->handle, de->u.CreateProcessInfo.hFile, u.buffer,
                             (DWORD_PTR)de->u.CreateProcessInfo.lpBaseOfImage, 0))
389
            dbg_printf("couldn't load main module (%u)\n", GetLastError());
Eric Pouech's avatar
Eric Pouech committed
390

391 392
        WINE_TRACE("%04x:%04x: create thread I @%p\n",
                   de->dwProcessId, de->dwThreadId, de->u.CreateProcessInfo.lpStartAddress);
Eric Pouech's avatar
Eric Pouech committed
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407

        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:
408
        WINE_TRACE("%04x:%04x: exit process (%d)\n",
Eric Pouech's avatar
Eric Pouech committed
409 410 411 412 413 414 415
                   de->dwProcessId, de->dwThreadId, de->u.ExitProcess.dwExitCode);

        if (dbg_curr_process == NULL)
        {
            WINE_ERR("Unknown process\n");
            break;
        }
416
        tgt_process_active_close_process(dbg_curr_process, FALSE);
417
        dbg_printf("Process of pid=%04x has terminated\n", de->dwProcessId);
Eric Pouech's avatar
Eric Pouech committed
418 419 420
        break;

    case CREATE_THREAD_DEBUG_EVENT:
421 422
        WINE_TRACE("%04x:%04x: create thread D @%p\n",
                   de->dwProcessId, de->dwThreadId, de->u.CreateThread.lpStartAddress);
Eric Pouech's avatar
Eric Pouech committed
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447

        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:
448
        WINE_TRACE("%04x:%04x: exit thread (%d)\n",
Eric Pouech's avatar
Eric Pouech committed
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
                   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;
        }
466 467 468
        fetch_module_name(de->u.LoadDll.lpImageName,
                          de->u.LoadDll.fUnicode,
                          de->u.LoadDll.lpBaseOfDll,
469
                          u.buffer, sizeof(u.buffer) / sizeof(WCHAR), FALSE);
Eric Pouech's avatar
Eric Pouech committed
470

471
        WINE_TRACE("%04x:%04x: loads DLL %s @%p (%u<%u>)\n",
Eric Pouech's avatar
Eric Pouech committed
472
                   de->dwProcessId, de->dwThreadId,
473
                   wine_dbgstr_w(u.buffer), de->u.LoadDll.lpBaseOfDll,
Eric Pouech's avatar
Eric Pouech committed
474 475
                   de->u.LoadDll.dwDebugInfoFileOffset,
                   de->u.LoadDll.nDebugInfoSize);
476 477
        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
478 479 480 481 482
        break_set_xpoints(FALSE);
        break_check_delayed_bp();
        break_set_xpoints(TRUE);
        if (DBG_IVAR(BreakOnDllLoad))
        {
483 484
            dbg_printf("Stopping on DLL %s loading at %p\n",
                       dbg_W2A(u.buffer, -1), de->u.LoadDll.lpBaseOfDll);
Eric Pouech's avatar
Eric Pouech committed
485 486 487 488 489
            if (dbg_fetch_context()) cont = 0;
        }
        break;

    case UNLOAD_DLL_DEBUG_EVENT:
490
        WINE_TRACE("%04x:%04x: unload DLL @%p\n",
Eric Pouech's avatar
Eric Pouech committed
491
                   de->dwProcessId, de->dwThreadId,
492
                   de->u.UnloadDll.lpBaseOfDll);
493 494
        break_delete_xpoints_from_module((DWORD_PTR)de->u.UnloadDll.lpBaseOfDll);
        SymUnloadModule64(dbg_curr_process->handle, (DWORD_PTR)de->u.UnloadDll.lpBaseOfDll);
Eric Pouech's avatar
Eric Pouech committed
495 496 497 498 499 500 501 502 503 504 505
        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,
506
                          de->u.DebugString.fUnicode, u.bufferA, sizeof(u.bufferA));
507
        WINE_TRACE("%04x:%04x: output debug string (%s)\n",
508
                   de->dwProcessId, de->dwThreadId, u.bufferA);
Eric Pouech's avatar
Eric Pouech committed
509 510 511
        break;

    case RIP_EVENT:
512
        WINE_TRACE("%04x:%04x: rip error=%u type=%u\n",
Eric Pouech's avatar
Eric Pouech committed
513 514 515 516 517
                   de->dwProcessId, de->dwThreadId, de->u.RipInfo.dwError,
                   de->u.RipInfo.dwType);
        break;

    default:
518
        WINE_TRACE("%04x:%04x: unknown event (%x)\n",
Eric Pouech's avatar
Eric Pouech committed
519 520 521 522 523 524 525 526 527 528 529
                   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)
    {
530 531
        ADDRESS64       addr;
        char            hexbuf[MAX_OFFSET_TO_STR_LEN];
Eric Pouech's avatar
Eric Pouech committed
532 533 534

        dbg_exception_epilog();
        memory_get_current_pc(&addr);
535 536 537
        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
538 539 540 541
                   dbg_curr_thread->exec_count);
        if (dbg_curr_thread)
        {
            if (!SetThreadContext(dbg_curr_thread->handle, &dbg_context))
542
                dbg_printf("Cannot set ctx on %04lx\n", dbg_curr_tid);
Eric Pouech's avatar
Eric Pouech committed
543 544 545 546
        }
    }
    dbg_interactiveP = FALSE;
    if (!ContinueDebugEvent(dbg_curr_pid, dbg_curr_tid, cont))
547
        dbg_printf("Cannot continue on %04lx (%08x)\n", dbg_curr_tid, cont);
Eric Pouech's avatar
Eric Pouech committed
548 549
}

550 551 552 553
static void wait_exception(void)
{
    DEBUG_EVENT		de;

554
    while (dbg_num_processes() && WaitForDebugEvent(&de, INFINITE))
555 556 557 558 559 560
    {
        if (dbg_handle_debug_event(&de)) break;
    }
    dbg_interactiveP = TRUE;
}

Eric Pouech's avatar
Eric Pouech committed
561 562
void dbg_wait_next_exception(DWORD cont, int count, int mode)
{
563 564
    ADDRESS64           addr;
    char                hexbuf[MAX_OFFSET_TO_STR_LEN];
Eric Pouech's avatar
Eric Pouech committed
565 566 567 568 569 570 571 572

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

573
    wait_exception();
Eric Pouech's avatar
Eric Pouech committed
574 575 576
    if (!dbg_curr_process) return;

    memory_get_current_pc(&addr);
577 578 579
    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
580 581 582
               dbg_curr_thread->exec_count);
}

583
void     dbg_active_wait_for_first_exception(void)
Eric Pouech's avatar
Eric Pouech committed
584
{
585
    dbg_interactiveP = FALSE;
Eric Pouech's avatar
Eric Pouech committed
586
    /* wait for first exception */
587
    wait_exception();
Eric Pouech's avatar
Eric Pouech committed
588 589
}

590
static	unsigned dbg_start_debuggee(LPSTR cmdLine)
Eric Pouech's avatar
Eric Pouech committed
591 592
{
    PROCESS_INFORMATION	info;
593
    STARTUPINFOA	startup, current;
594
    DWORD               flags;
Eric Pouech's avatar
Eric Pouech committed
595

596 597
    GetStartupInfoA(&current);

Eric Pouech's avatar
Eric Pouech committed
598 599 600
    memset(&startup, 0, sizeof(startup));
    startup.cb = sizeof(startup);
    startup.dwFlags = STARTF_USESHOWWINDOW;
601 602 603

    startup.wShowWindow = (current.dwFlags & STARTF_USESHOWWINDOW) ?
        current.wShowWindow : SW_SHOWNORMAL;
Eric Pouech's avatar
Eric Pouech committed
604 605 606 607

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

611 612
    if (!CreateProcessA(NULL, cmdLine, NULL, NULL, FALSE, flags,
                        NULL, NULL, &startup, &info))
Eric Pouech's avatar
Eric Pouech committed
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
    {
	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
636
    if (!(dbg_curr_process = dbg_add_process(&be_process_active_io, dbg_curr_pid, 0))) return FALSE;
637
    dbg_curr_process->active_debuggee = TRUE;
Eric Pouech's avatar
Eric Pouech committed
638 639 640 641 642 643 644 645

    return TRUE;
}

void	dbg_run_debuggee(const char* args)
{
    if (args)
    {
646
        WINE_FIXME("Re-running current program with %s as args is broken\n", wine_dbgstr_a(args));
Eric Pouech's avatar
Eric Pouech committed
647 648 649 650 651 652 653 654 655 656
        return;
    }
    else 
    {
	if (!dbg_last_cmd_line)
        {
	    dbg_printf("Cannot find previously used command line.\n");
	    return;
	}
	dbg_start_debuggee(dbg_last_cmd_line);
657
        dbg_active_wait_for_first_exception();
Eric Pouech's avatar
Eric Pouech committed
658 659 660 661
        source_list_from_addr(NULL, 0);
    }
}

662
static BOOL str2int(const char* str, DWORD_PTR* val)
663 664 665 666 667 668 669
{
    char*   ptr;

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

670 671 672 673 674 675 676 677 678 679
static HANDLE create_temp_file(void)
{
    static const WCHAR prefixW[] = {'w','d','b',0};
    WCHAR path[MAX_PATH], name[MAX_PATH];

    if (!GetTempPathW( MAX_PATH, path ) || !GetTempFileNameW( path, prefixW, 0, name ))
        return INVALID_HANDLE_VALUE;
    return CreateFileW( name, GENERIC_READ|GENERIC_WRITE|DELETE, FILE_SHARE_DELETE,
                        NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0 );
}
680

681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
static void output_system_info(void)
{
#ifdef __i386__
    static const char platform[] = "i386";
#elif defined(__x86_64__)
    static const char platform[] = "x86_64";
#elif defined(__sparc__)
    static const char platform[] = "sparc";
#elif defined(__powerpc__)
    static const char platform[] = "powerpc";
#elif defined(__arm__)
    static const char platform[] = "arm";
#else
# error CPU unknown
#endif

    const char *(CDECL *wine_get_build_id)(void);
    void (CDECL *wine_get_host_version)( const char **sysname, const char **release );
    BOOL is_wow64;

    wine_get_build_id = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
    wine_get_host_version = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
    if (!IsWow64Process( dbg_curr_process->handle, &is_wow64 )) is_wow64 = FALSE;

    dbg_printf( "System information:\n" );
    if (wine_get_build_id) dbg_printf( "    Wine build: %s\n", wine_get_build_id() );
    dbg_printf( "    Platform: %s%s\n", platform, is_wow64 ? " (WOW64)" : "" );
    if (wine_get_host_version)
    {
        const char *sysname, *release;
        wine_get_host_version( &sysname, &release );
        dbg_printf( "    Host system: %s\n", sysname );
        dbg_printf( "    Host version: %s\n", release );
    }
}

717 718 719 720 721 722 723 724
/******************************************************************
 *		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[])
{
725
    DWORD_PTR pid, evt;
726 727 728 729

    /* try the form <myself> pid */
    if (argc == 1 && str2int(argv[0], &pid) && pid != 0)
    {
730
        if (!dbg_attach_debuggee(pid, FALSE))
Eric Pouech's avatar
Eric Pouech committed
731
            return start_error_init;
732 733
    }
    /* try the form <myself> pid evt (Win32 JIT debugger) */
Eric Pouech's avatar
Eric Pouech committed
734 735
    else if (argc == 2 && str2int(argv[0], &pid) && pid != 0 &&
             str2int(argv[1], &evt) && evt != 0)
736
    {
737
        if (!dbg_attach_debuggee(pid, TRUE))
738 739 740 741 742 743 744
        {
            /* don't care about result */
            SetEvent((HANDLE)evt);
            return start_error_init;
        }
        if (!SetEvent((HANDLE)evt))
        {
745
            WINE_ERR("Invalid event handle: %lx\n", evt);
746 747 748 749
            return start_error_init;
        }
        CloseHandle((HANDLE)evt);
    }
Eric Pouech's avatar
Eric Pouech committed
750 751 752 753
    else return start_error_parse;

    dbg_curr_pid = pid;
    return start_ok;
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
}

/******************************************************************
 *		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
795 796 797 798 799 800 801 802

/******************************************************************
 *		dbg_active_auto
 *
 * Starts (<pid> or <pid> <evt>) in automatic mode
 */
enum dbg_start dbg_active_auto(int argc, char* argv[])
{
803
    HANDLE thread = 0, event = 0, input, output = INVALID_HANDLE_VALUE;
804 805
    enum dbg_start      ds = start_error_parse;

806 807 808 809 810 811 812 813 814
    DBG_IVAR(BreakOnDllLoad) = 0;

    /* auto mode */
    argc--; argv++;
    ds = dbg_active_attach(argc, argv);
    if (ds != start_ok) {
        msgbox_res_id(NULL, IDS_INVALID_PARAMS, IDS_AUTO_CAPTION, MB_OK);
        return ds;
    }
815 816 817 818 819

    switch (display_crash_dialog())
    {
    case ID_DEBUG:
        AllocConsole();
820 821 822
        dbg_init_console();
        dbg_start_interactive(INVALID_HANDLE_VALUE);
        return start_ok;
823
    case ID_DETAILS:
824 825 826
        event = CreateEventW( NULL, TRUE, FALSE, NULL );
        if (event) thread = display_crash_details( event );
        if (thread) dbg_houtput = output = create_temp_file();
827
        break;
828
    }
829

830 831 832
    input = parser_generate_command_file("echo Modules:", "info share",
                                         "echo Threads:", "info threads", NULL);
    if (input == INVALID_HANDLE_VALUE) return start_error_parse;
833 834 835 836 837

    if (dbg_curr_process->active_debuggee)
        dbg_active_wait_for_first_exception();

    dbg_interactiveP = TRUE;
838 839 840 841
    parser_handle(input);

    if (output != INVALID_HANDLE_VALUE)
    {
842
        output_system_info();
843 844
        SetEvent( event );
        WaitForSingleObject( thread, INFINITE );
845
        CloseHandle( output );
846 847
        CloseHandle( thread );
        CloseHandle( event );
848
    }
849

850 851
    CloseHandle( input );
    dbg_curr_process->process_io->close_process(dbg_curr_process, TRUE);
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
    return start_ok;
}

/******************************************************************
 *		dbg_active_minidump
 *
 * Starts (<pid> or <pid> <evt>) in minidump mode
 */
enum dbg_start dbg_active_minidump(int argc, char* argv[])
{
    HANDLE              hFile;
    enum dbg_start      ds = start_error_parse;
    const char*     file = NULL;
    char            tmp[8 + 1 + MAX_PATH]; /* minidump <file> */

    dbg_houtput = GetStdHandle(STD_ERROR_HANDLE);
    DBG_IVAR(BreakOnDllLoad) = 0;

    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)
878
    {
879 880 881 882 883
    case 1:
        ds = dbg_active_attach(argc, argv);
        break;
    case 2:
        if ((ds = dbg_active_attach(argc, argv)) != start_ok)
884 885 886 887
        {
            file = argv[0];
            ds = dbg_active_attach(argc - 1, argv + 1);
        }
888 889 890 891 892 893 894 895 896 897 898 899 900
        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];
901

902 903
        GetTempPathA(sizeof(path), path);
        GetTempFileNameA(path, "WD", 0, tmp + 10);
904
    }
905 906 907 908 909 910 911 912
    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);
    }
    hFile = parser_generate_command_file(tmp, "detach", NULL);
913
    if (hFile == INVALID_HANDLE_VALUE) return start_error_parse;
Eric Pouech's avatar
Eric Pouech committed
914

915 916 917
    if (dbg_curr_process->active_debuggee)
        dbg_active_wait_for_first_exception();

Eric Pouech's avatar
Eric Pouech committed
918 919 920
    dbg_interactiveP = TRUE;
    parser_handle(hFile);

921
    return start_ok;
Eric Pouech's avatar
Eric Pouech committed
922
}
923 924 925

static BOOL tgt_process_active_close_process(struct dbg_process* pcs, BOOL kill)
{
926 927 928 929 930 931 932 933 934 935
    if (kill)
    {
        DWORD exit_code = 0;

        if (pcs == dbg_curr_process && dbg_curr_thread->in_exception)
            exit_code = dbg_curr_thread->excpt_record.ExceptionCode;

        TerminateProcess(pcs->handle, exit_code);
    }
    else if (pcs == dbg_curr_process)
936 937 938 939 940 941 942 943 944 945 946 947
    {
        /* 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);
        }
948 949 950 951
    }
    else
    {
        if (!DebugActiveProcessStop(pcs->pid)) return FALSE;
952 953 954 955 956 957 958
    }
    SymCleanup(pcs->handle);
    dbg_del_process(pcs);

    return TRUE;
}

959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975
static BOOL tgt_process_active_read(HANDLE hProcess, const void* addr,
                                    void* buffer, SIZE_T len, SIZE_T* rlen)
{
    return ReadProcessMemory( hProcess, addr, buffer, len, rlen );
}

static BOOL tgt_process_active_write(HANDLE hProcess, void* addr,
                                     const void* buffer, SIZE_T len, SIZE_T* wlen)
{
    return WriteProcessMemory( hProcess, addr, buffer, len, wlen );
}

static BOOL tgt_process_active_get_selector(HANDLE hThread, DWORD sel, LDT_ENTRY* le)
{
    return GetThreadSelectorEntry( hThread, sel, le );
}

Eric Pouech's avatar
Eric Pouech committed
976
static struct be_process_io be_process_active_io =
977 978
{
    tgt_process_active_close_process,
979 980 981
    tgt_process_active_read,
    tgt_process_active_write,
    tgt_process_active_get_selector
982
};