debugger.c 15.9 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
#include "wine/exception.h"
32

33
WINE_DEFAULT_DEBUG_CHANNEL(debugstr);
34 35 36


/******************************************************************************
37
 *           WaitForDebugEvent   (KERNEL32.@)
38
 *
Andrew Johnston's avatar
Andrew Johnston committed
39 40
 *  Waits for a debugging event to occur in a process being debugged before
 *  filling out the debug event structure.
41
 *
42 43 44 45
 * 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
46
 * RETURNS
47
 *
Andrew Johnston's avatar
Andrew Johnston committed
48
 *  Returns true if a debug event occurred and false if the call timed out.
49
 */
Andrew Johnston's avatar
Andrew Johnston committed
50
BOOL WINAPI WaitForDebugEvent(
51 52
    LPDEBUG_EVENT event,
    DWORD         timeout)
53
{
54
    BOOL ret;
55 56
    DWORD res;
    int i;
57 58

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

68
            if (!wine_server_reply_size(reply))  /* timeout */
69
            {
70
                wait = wine_server_ptr_handle( reply->wait );
71 72 73
                ret = FALSE;
                goto done;
            }
74 75 76 77
            event->dwDebugEventCode = data.code;
            event->dwProcessId      = (DWORD)reply->pid;
            event->dwThreadId       = (DWORD)reply->tid;
            switch(data.code)
78 79
            {
            case EXCEPTION_DEBUG_EVENT:
80 81 82 83 84 85 86 87
                if (data.exception.exc_code == DBG_PRINTEXCEPTION_C && data.exception.nb_params >= 2)
                {
                    event->dwDebugEventCode = OUTPUT_DEBUG_STRING_EVENT;
                    event->u.DebugString.lpDebugStringData  = wine_server_get_ptr( data.exception.params[1] );
                    event->u.DebugString.fUnicode           = FALSE;
                    event->u.DebugString.nDebugStringLength = data.exception.params[0];
                    break;
                }
88 89 90 91 92 93 94
                else if (data.exception.exc_code == DBG_RIPEXCEPTION && data.exception.nb_params >= 2)
                {
                    event->dwDebugEventCode = RIP_EVENT;
                    event->u.RipInfo.dwError = data.exception.params[0];
                    event->u.RipInfo.dwType  = data.exception.params[1];
                    break;
                }
95 96 97 98 99 100 101 102
                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];
103 104
                break;
            case CREATE_THREAD_DEBUG_EVENT:
105 106 107
                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 );
108 109
                break;
            case CREATE_PROCESS_DEBUG_EVENT:
110 111 112 113 114 115 116 117 118 119
                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;
120 121
                break;
            case EXIT_THREAD_DEBUG_EVENT:
122
                event->u.ExitThread.dwExitCode = data.exit.exit_code;
123 124
                break;
            case EXIT_PROCESS_DEBUG_EVENT:
125
                event->u.ExitProcess.dwExitCode = data.exit.exit_code;
126 127
                break;
            case LOAD_DLL_DEBUG_EVENT:
128 129 130 131 132 133
                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;
134 135
                break;
            case UNLOAD_DLL_DEBUG_EVENT:
136
                event->u.UnloadDll.lpBaseOfDll = wine_server_get_ptr( data.unload_dll.base );
137 138 139
                break;
            }
        done:
140
            /* nothing */ ;
141
        }
142
        SERVER_END_REQ;
143 144 145 146 147
        if (ret) return TRUE;
        if (!wait) break;
        res = WaitForSingleObject( wait, timeout );
        CloseHandle( wait );
        if (res != STATUS_WAIT_0) break;
148
    }
149 150
    SetLastError( ERROR_SEM_TIMEOUT );
    return FALSE;
151 152 153 154
}


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


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

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

237 238 239 240 241
static LONG WINAPI debug_exception_handler( EXCEPTION_POINTERS *eptr )
{
    EXCEPTION_RECORD *rec = eptr->ExceptionRecord;
    return (rec->ExceptionCode == DBG_PRINTEXCEPTION_C) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
}
242 243

/***********************************************************************
244
 *           OutputDebugStringA   (KERNEL32.@)
Andrew Johnston's avatar
Andrew Johnston committed
245
 *
246
 *  Output by an application of an ascii string to a debugger (if attached)
Andrew Johnston's avatar
Andrew Johnston committed
247
 *  and program log.
248 249 250 251 252 253 254
 *
 * PARAMS
 *  str [I] The message to be logged and given to the debugger.
 *
 * RETURNS
 *
 *  Nothing.
255
 */
256
void WINAPI OutputDebugStringA( LPCSTR str )
257
{
258 259
    static HANDLE DBWinMutex = NULL;
    static BOOL mutex_inited = FALSE;
260
    BOOL caught_by_dbg = TRUE;
261

262
    if (!str) str = "";
263
    WARN("%s\n", debugstr_a(str));
264

265
    /* raise exception, WaitForDebugEvent() will generate a corresponding debug event */
266 267 268 269 270 271 272 273 274
    __TRY
    {
        ULONG_PTR args[2];
        args[0] = strlen(str) + 1;
        args[1] = (ULONG_PTR)str;
        RaiseException( DBG_PRINTEXCEPTION_C, 0, 2, args );
    }
    __EXCEPT(debug_exception_handler)
    {
275
        caught_by_dbg = FALSE;
276 277
    }
    __ENDTRY
278
    if (caught_by_dbg) return;
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 314 315

    /* send string to a system-wide monitor */
    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)
            {
316
                /* monitor is present, synchronize with other OutputDebugString invocations */
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
                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 );
        }
    }
351 352 353 354
}


/***********************************************************************
355
 *           OutputDebugStringW   (KERNEL32.@)
Andrew Johnston's avatar
Andrew Johnston committed
356
 *
357
 *  Output by an application of a unicode string to a debugger (if attached)
Andrew Johnston's avatar
Andrew Johnston committed
358
 *  and program log.
359 360 361 362 363 364 365
 *
 * PARAMS
 *  str [I] The message to be logged and given to the debugger.
 *
 * RETURNS
 *
 *  Nothing.
366
 */
367
void WINAPI OutputDebugStringW( LPCWSTR str )
368
{
369 370 371 372 373
    UNICODE_STRING strW;
    STRING strA;

    RtlInitUnicodeString( &strW, str );
    if (!RtlUnicodeStringToAnsiString( &strA, &strW, TRUE ))
374
    {
375 376
        OutputDebugStringA( strA.Buffer );
        RtlFreeAnsiString( &strA );
377
    }
378 379 380
}


381
/***********************************************************************
382
 *           DebugBreak   (KERNEL32.@)
Andrew Johnston's avatar
Andrew Johnston committed
383 384 385
 *
 *  Raises an exception so that a debugger (if attached)
 *  can take some action.
386
 */
387
#if defined(__i386__) || defined(__x86_64__)
388
__ASM_STDCALL_FUNC( DebugBreak, 0, "jmp " __ASM_NAME("DbgBreakPoint") )
389
#else
390 391 392 393
void WINAPI DebugBreak(void)
{
    DbgBreakPoint();
}
394
#endif
395

396 397 398 399 400
/***********************************************************************
 *           DebugBreakProcess   (KERNEL32.@)
 *
 *  Raises an exception so that a debugger (if attached)
 *  can take some action. Same as DebugBreak, but applies to any process.
401 402 403 404 405 406 407
 *
 * PARAMS
 *  hProc [I] Process to break into.
 *
 * RETURNS
 *
 *  True if successful.
408 409 410
 */
BOOL WINAPI DebugBreakProcess(HANDLE hProc)
{
411
    BOOL ret, self;
412

413
    TRACE("(%p)\n", hProc);
414

415
    SERVER_START_REQ( debug_break )
416
    {
417
        req->handle = wine_server_obj_handle( hProc );
418 419
        ret = !wine_server_call_err( req );
        self = ret && reply->self;
420 421
    }
    SERVER_END_REQ;
422 423
    if (self) DbgBreakPoint();
    return ret;
424 425
}

426 427

/***********************************************************************
428
 *           IsDebuggerPresent   (KERNEL32.@)
Andrew Johnston's avatar
Andrew Johnston committed
429 430 431
 *
 *  Allows a process to determine if there is a debugger attached.
 *
432 433
 * PARAMS
 *
Andrew Johnston's avatar
Andrew Johnston committed
434 435 436
 * RETURNS
 *
 *  True if there is a debugger attached.
437 438 439
 */
BOOL WINAPI IsDebuggerPresent(void)
{
440
    return NtCurrentTeb()->Peb->BeingDebugged;
441
}
442

443 444 445 446 447 448 449 450 451 452 453 454 455 456
/***********************************************************************
 *           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)
{
457 458 459
    NTSTATUS status;
    DWORD_PTR port;

460 461 462 463 464
    if(!process || !DebuggerPresent)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }
465 466 467 468 469 470 471 472 473

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

    *DebuggerPresent = !!port;
474 475
    return TRUE;
}
476

477
/***********************************************************************
478
 *           DebugSetProcessKillOnExit                    (KERNEL32.@)
479
 *
480
 * Let a debugger decide whether a debuggee will be killed upon debugger
481 482 483 484 485 486 487
 * termination.
 *
 * PARAMS
 *  kill [I] If set to true then kill the process on exit.
 *
 * RETURNS
 *  True if successful, false otherwise.
488 489 490 491 492 493 494 495 496 497 498 499 500
 */
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;
}