debugger.c 15.2 KB
Newer Older
1 2 3 4
/*
 * Win32 debugger functions
 *
 * Copyright (C) 1999 Alexandre Julliard
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
#include "config.h"
22
#include <stdio.h>
23 24
#include <string.h>

25 26
#include "ntstatus.h"
#define WIN32_NO_STATUS
27
#include "winerror.h"
28
#include "wine/server.h"
29
#include "kernel_private.h"
30
#include "wine/debug.h"
31

32
WINE_DEFAULT_DEBUG_CHANNEL(debugstr);
33 34 35


/******************************************************************************
36
 *           WaitForDebugEvent   (KERNEL32.@)
37
 *
Andrew Johnston's avatar
Andrew Johnston committed
38 39
 *  Waits for a debugging event to occur in a process being debugged before
 *  filling out the debug event structure.
40
 *
41 42 43 44
 * PARAMS
 *  event   [O] Address of structure for event information.
 *  timeout [I] Number of milliseconds to wait for event.
 *
Andrew Johnston's avatar
Andrew Johnston committed
45
 * RETURNS
46
 *
Andrew Johnston's avatar
Andrew Johnston committed
47
 *  Returns true if a debug event occurred and false if the call timed out.
48
 */
Andrew Johnston's avatar
Andrew Johnston committed
49
BOOL WINAPI WaitForDebugEvent(
50 51
    LPDEBUG_EVENT event,
    DWORD         timeout)
52
{
53
    BOOL ret;
54 55
    DWORD res;
    int i;
56 57

    for (;;)
58
    {
59
        HANDLE wait = 0;
60 61
        debug_event_t data;
        SERVER_START_REQ( wait_debug_event )
62 63
        {
            req->get_handle = (timeout != 0);
64 65
            wine_server_set_reply( req, &data, sizeof(data) );
            if (!(ret = !wine_server_call_err( req ))) goto done;
66

67
            if (!wine_server_reply_size(reply))  /* timeout */
68
            {
69
                wait = wine_server_ptr_handle( reply->wait );
70 71 72
                ret = FALSE;
                goto done;
            }
73 74 75 76
            event->dwDebugEventCode = data.code;
            event->dwProcessId      = (DWORD)reply->pid;
            event->dwThreadId       = (DWORD)reply->tid;
            switch(data.code)
77 78
            {
            case EXCEPTION_DEBUG_EVENT:
79 80 81 82 83 84 85 86
                event->u.Exception.dwFirstChance = data.exception.first;
                event->u.Exception.ExceptionRecord.ExceptionCode    = data.exception.exc_code;
                event->u.Exception.ExceptionRecord.ExceptionFlags   = data.exception.flags;
                event->u.Exception.ExceptionRecord.ExceptionRecord  = wine_server_get_ptr( data.exception.record );
                event->u.Exception.ExceptionRecord.ExceptionAddress = wine_server_get_ptr( data.exception.address );
                event->u.Exception.ExceptionRecord.NumberParameters = data.exception.nb_params;
                for (i = 0; i < data.exception.nb_params; i++)
                    event->u.Exception.ExceptionRecord.ExceptionInformation[i] = data.exception.params[i];
87 88
                break;
            case CREATE_THREAD_DEBUG_EVENT:
89 90 91
                event->u.CreateThread.hThread           = wine_server_ptr_handle( data.create_thread.handle );
                event->u.CreateThread.lpThreadLocalBase = wine_server_get_ptr( data.create_thread.teb );
                event->u.CreateThread.lpStartAddress    = wine_server_get_ptr( data.create_thread.start );
92 93
                break;
            case CREATE_PROCESS_DEBUG_EVENT:
94 95 96 97 98 99 100 101 102 103
                event->u.CreateProcessInfo.hFile                 = wine_server_ptr_handle( data.create_process.file );
                event->u.CreateProcessInfo.hProcess              = wine_server_ptr_handle( data.create_process.process );
                event->u.CreateProcessInfo.hThread               = wine_server_ptr_handle( data.create_process.thread );
                event->u.CreateProcessInfo.lpBaseOfImage         = wine_server_get_ptr( data.create_process.base );
                event->u.CreateProcessInfo.dwDebugInfoFileOffset = data.create_process.dbg_offset;
                event->u.CreateProcessInfo.nDebugInfoSize        = data.create_process.dbg_size;
                event->u.CreateProcessInfo.lpThreadLocalBase     = wine_server_get_ptr( data.create_process.teb );
                event->u.CreateProcessInfo.lpStartAddress        = wine_server_get_ptr( data.create_process.start );
                event->u.CreateProcessInfo.lpImageName           = wine_server_get_ptr( data.create_process.name );
                event->u.CreateProcessInfo.fUnicode              = data.create_process.unicode;
104 105
                break;
            case EXIT_THREAD_DEBUG_EVENT:
106
                event->u.ExitThread.dwExitCode = data.exit.exit_code;
107 108
                break;
            case EXIT_PROCESS_DEBUG_EVENT:
109
                event->u.ExitProcess.dwExitCode = data.exit.exit_code;
110 111
                break;
            case LOAD_DLL_DEBUG_EVENT:
112 113 114 115 116 117
                event->u.LoadDll.hFile                 = wine_server_ptr_handle( data.load_dll.handle );
                event->u.LoadDll.lpBaseOfDll           = wine_server_get_ptr( data.load_dll.base );
                event->u.LoadDll.dwDebugInfoFileOffset = data.load_dll.dbg_offset;
                event->u.LoadDll.nDebugInfoSize        = data.load_dll.dbg_size;
                event->u.LoadDll.lpImageName           = wine_server_get_ptr( data.load_dll.name );
                event->u.LoadDll.fUnicode              = data.load_dll.unicode;
118 119
                break;
            case UNLOAD_DLL_DEBUG_EVENT:
120
                event->u.UnloadDll.lpBaseOfDll = wine_server_get_ptr( data.unload_dll.base );
121 122
                break;
            case OUTPUT_DEBUG_STRING_EVENT:
123
                event->u.DebugString.lpDebugStringData  = wine_server_get_ptr( data.output_string.string );
124
                event->u.DebugString.fUnicode           = FALSE;
125
                event->u.DebugString.nDebugStringLength = data.output_string.length;
126 127
                break;
            case RIP_EVENT:
128 129
                event->u.RipInfo.dwError = data.rip_info.error;
                event->u.RipInfo.dwType  = data.rip_info.type;
130 131 132
                break;
            }
        done:
133
            /* nothing */ ;
134
        }
135
        SERVER_END_REQ;
136 137 138 139 140
        if (ret) return TRUE;
        if (!wait) break;
        res = WaitForSingleObject( wait, timeout );
        CloseHandle( wait );
        if (res != STATUS_WAIT_0) break;
141
    }
142 143
    SetLastError( ERROR_SEM_TIMEOUT );
    return FALSE;
144 145 146 147
}


/**********************************************************************
148
 *           ContinueDebugEvent   (KERNEL32.@)
Andrew Johnston's avatar
Andrew Johnston committed
149 150 151
 *
 *  Enables a thread that previously produced a debug event to continue.
 *
152 153 154 155 156
 * PARAMS
 *  pid    [I] The id of the process to continue.
 *  tid    [I] The id of the thread to continue.
 *  status [I] The rule to apply to unhandled exeptions.
 *
Andrew Johnston's avatar
Andrew Johnston committed
157 158 159 160
 * RETURNS
 *
 *  True if the debugger is listed as the processes owner and the process
 *  and thread are valid.
161
 */
Andrew Johnston's avatar
Andrew Johnston committed
162
BOOL WINAPI ContinueDebugEvent(
163 164 165
    DWORD pid,
    DWORD tid,
    DWORD status)
166
{
167
    BOOL ret;
168
    SERVER_START_REQ( continue_debug_event )
169
    {
170 171
        req->pid    = pid;
        req->tid    = tid;
172
        req->status = status;
173
        ret = !wine_server_call_err( req );
174 175 176
    }
    SERVER_END_REQ;
    return ret;
177 178 179 180
}


/**********************************************************************
181
 *           DebugActiveProcess   (KERNEL32.@)
Andrew Johnston's avatar
Andrew Johnston committed
182
 *
183
 *  Attempts to attach the debugger to a process.
Andrew Johnston's avatar
Andrew Johnston committed
184
 *
185 186 187
 * PARAMS
 *  pid [I] The process to be debugged.
 *
Andrew Johnston's avatar
Andrew Johnston committed
188 189 190
 * RETURNS
 *
 *  True if the debugger was attached to process.
191
 */
192
BOOL WINAPI DebugActiveProcess( DWORD pid )
193
{
194
    BOOL ret;
195
    SERVER_START_REQ( debug_process )
196
    {
197
        req->pid = pid;
198 199 200 201 202 203 204 205 206 207 208 209
        req->attach = 1;
        ret = !wine_server_call_err( req );
    }
    SERVER_END_REQ;
    return ret;
}

/**********************************************************************
 *           DebugActiveProcessStop   (KERNEL32.@)
 *
 *  Attempts to detach the debugger from a process.
 *
210 211 212
 * PARAMS
 *  pid [I] The process to be detached.
 *
213 214 215 216
 * RETURNS
 *
 *  True if the debugger was detached from the process.
 */
217
BOOL WINAPI DebugActiveProcessStop( DWORD pid )
218 219 220 221
{
    BOOL ret;
    SERVER_START_REQ( debug_process )
    {
222
        req->pid = pid;
223
        req->attach = 0;
224
        ret = !wine_server_call_err( req );
225 226 227
    }
    SERVER_END_REQ;
    return ret;
228 229 230 231
}


/***********************************************************************
232
 *           OutputDebugStringA   (KERNEL32.@)
Andrew Johnston's avatar
Andrew Johnston committed
233
 *
234
 *  Output by an application of an ascii string to a debugger (if attached)
Andrew Johnston's avatar
Andrew Johnston committed
235
 *  and program log.
236 237 238 239 240 241 242
 *
 * PARAMS
 *  str [I] The message to be logged and given to the debugger.
 *
 * RETURNS
 *
 *  Nothing.
243
 */
244
void WINAPI OutputDebugStringA( LPCSTR str )
245
{
246 247 248
    static HANDLE DBWinMutex = NULL;
    static BOOL mutex_inited = FALSE;

249 250
    if (!str) str = "";

251
    /* send string to attached debugger */
252
    SERVER_START_REQ( output_debug_string )
253
    {
254
        req->string  = wine_server_client_ptr( str );
255
        req->length  = strlen(str) + 1;
256
        wine_server_call( req );
257 258
    }
    SERVER_END_REQ;
259

260
    WARN("%s\n", debugstr_a(str));
261 262 263 264 265 266 267 268 269 270 271 272 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

    /* send string to a system-wide monitor */
    /* FIXME should only send to monitor if no debuggers are attached */

    if (!mutex_inited)
    {
        /* first call to OutputDebugString, initialize mutex handle */
        static const WCHAR mutexname[] = {'D','B','W','i','n','M','u','t','e','x',0};
        HANDLE mutex = CreateMutexExW( NULL, mutexname, 0, SYNCHRONIZE );
        if (mutex)
        {
            if (InterlockedCompareExchangePointer( &DBWinMutex, mutex, 0 ) != 0)
            {
                /* someone beat us here... */
                CloseHandle( mutex );
            }
        }
        mutex_inited = TRUE;
    }

    if (DBWinMutex)
    {
        static const WCHAR shmname[] = {'D','B','W','I','N','_','B','U','F','F','E','R',0};
        static const WCHAR eventbuffername[] = {'D','B','W','I','N','_','B','U','F','F','E','R','_','R','E','A','D','Y',0};
        static const WCHAR eventdataname[] = {'D','B','W','I','N','_','D','A','T','A','_','R','E','A','D','Y',0};
        HANDLE mapping;

        mapping = OpenFileMappingW( FILE_MAP_WRITE, FALSE, shmname );
        if (mapping)
        {
            LPVOID buffer;
            HANDLE eventbuffer, eventdata;

            buffer = MapViewOfFile( mapping, FILE_MAP_WRITE, 0, 0, 0 );
            eventbuffer = OpenEventW( SYNCHRONIZE, FALSE, eventbuffername );
            eventdata = OpenEventW( EVENT_MODIFY_STATE, FALSE, eventdataname );

            if (buffer && eventbuffer && eventdata)
            {
300
                /* monitor is present, synchronize with other OutputDebugString invocations */
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
                WaitForSingleObject( DBWinMutex, INFINITE );

                /* acquire control over the buffer */
                if (WaitForSingleObject( eventbuffer, 10000 ) == WAIT_OBJECT_0)
                {
                    int str_len;
                    struct _mon_buffer_t {
                        DWORD pid;
                        char buffer[1];
                    } *mon_buffer = (struct _mon_buffer_t*) buffer;

                    str_len = strlen( str );
                    if (str_len > (4096 - sizeof(DWORD) - 1))
                        str_len = 4096 - sizeof(DWORD) - 1;

                    mon_buffer->pid = GetCurrentProcessId();
                    memcpy( mon_buffer->buffer, str, str_len );
                    mon_buffer->buffer[str_len] = 0;

                    /* signal data ready */
                    SetEvent( eventdata );
                }
                ReleaseMutex( DBWinMutex );
            }

            if (buffer)
                UnmapViewOfFile( buffer );
            if (eventbuffer)
                CloseHandle( eventbuffer );
            if (eventdata)
                CloseHandle( eventdata );
            CloseHandle( mapping );
        }
    }
335 336 337 338
}


/***********************************************************************
339
 *           OutputDebugStringW   (KERNEL32.@)
Andrew Johnston's avatar
Andrew Johnston committed
340
 *
341
 *  Output by an application of a unicode string to a debugger (if attached)
Andrew Johnston's avatar
Andrew Johnston committed
342
 *  and program log.
343 344 345 346 347 348 349
 *
 * PARAMS
 *  str [I] The message to be logged and given to the debugger.
 *
 * RETURNS
 *
 *  Nothing.
350
 */
351
void WINAPI OutputDebugStringW( LPCWSTR str )
352
{
353 354 355 356 357
    UNICODE_STRING strW;
    STRING strA;

    RtlInitUnicodeString( &strW, str );
    if (!RtlUnicodeStringToAnsiString( &strA, &strW, TRUE ))
358
    {
359 360
        OutputDebugStringA( strA.Buffer );
        RtlFreeAnsiString( &strA );
361
    }
362 363 364
}


365
/***********************************************************************
366
 *           DebugBreak   (KERNEL32.@)
Andrew Johnston's avatar
Andrew Johnston committed
367 368 369
 *
 *  Raises an exception so that a debugger (if attached)
 *  can take some action.
370
 */
371
#if defined(__i386__) || defined(__x86_64__)
372
__ASM_STDCALL_FUNC( DebugBreak, 0, "jmp " __ASM_NAME("DbgBreakPoint") )
373
#else
374 375 376 377
void WINAPI DebugBreak(void)
{
    DbgBreakPoint();
}
378
#endif
379

380 381 382 383 384
/***********************************************************************
 *           DebugBreakProcess   (KERNEL32.@)
 *
 *  Raises an exception so that a debugger (if attached)
 *  can take some action. Same as DebugBreak, but applies to any process.
385 386 387 388 389 390 391
 *
 * PARAMS
 *  hProc [I] Process to break into.
 *
 * RETURNS
 *
 *  True if successful.
392 393 394
 */
BOOL WINAPI DebugBreakProcess(HANDLE hProc)
{
395
    BOOL ret, self;
396

397
    TRACE("(%p)\n", hProc);
398

399
    SERVER_START_REQ( debug_break )
400
    {
401
        req->handle = wine_server_obj_handle( hProc );
402 403
        ret = !wine_server_call_err( req );
        self = ret && reply->self;
404 405
    }
    SERVER_END_REQ;
406 407
    if (self) DbgBreakPoint();
    return ret;
408 409
}

410 411

/***********************************************************************
412
 *           IsDebuggerPresent   (KERNEL32.@)
Andrew Johnston's avatar
Andrew Johnston committed
413 414 415
 *
 *  Allows a process to determine if there is a debugger attached.
 *
416 417
 * PARAMS
 *
Andrew Johnston's avatar
Andrew Johnston committed
418 419 420
 * RETURNS
 *
 *  True if there is a debugger attached.
421 422 423
 */
BOOL WINAPI IsDebuggerPresent(void)
{
424
    return NtCurrentTeb()->Peb->BeingDebugged;
425
}
426

427 428 429 430 431 432 433 434 435 436 437 438 439 440
/***********************************************************************
 *           CheckRemoteDebuggerPresent   (KERNEL32.@)
 *
 *  Allows a process to determine if there is a remote debugger
 *  attached.
 *
 * PARAMS
 *
 * RETURNS
 *
 *  TRUE because it is a stub.
 */
BOOL WINAPI CheckRemoteDebuggerPresent(HANDLE process, PBOOL DebuggerPresent)
{
441 442 443
    NTSTATUS status;
    DWORD_PTR port;

444 445 446 447 448
    if(!process || !DebuggerPresent)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }
449 450 451 452 453 454 455 456 457

    status = NtQueryInformationProcess(process, ProcessDebugPort, &port, sizeof(port), NULL);
    if (status != STATUS_SUCCESS)
    {
        SetLastError(RtlNtStatusToDosError(status));
        return FALSE;
    }

    *DebuggerPresent = !!port;
458 459
    return TRUE;
}
460

461
/***********************************************************************
462
 *           DebugSetProcessKillOnExit                    (KERNEL32.@)
463
 *
464
 * Let a debugger decide whether a debuggee will be killed upon debugger
465 466 467 468 469 470 471
 * termination.
 *
 * PARAMS
 *  kill [I] If set to true then kill the process on exit.
 *
 * RETURNS
 *  True if successful, false otherwise.
472 473 474 475 476 477 478 479 480 481 482 483 484
 */
BOOL WINAPI DebugSetProcessKillOnExit(BOOL kill)
{
    BOOL ret = FALSE;

    SERVER_START_REQ( set_debugger_kill_on_exit )
    {
        req->kill_on_exit = kill;
        ret = !wine_server_call_err( req );
    }
    SERVER_END_REQ;
    return ret;
}