Added debug events support.
This commit is contained in:
parent
3e2be8d7ed
commit
e712e077bc
117
include/server.h
117
include/server.h
|
@ -48,6 +48,7 @@ struct new_process_request
|
|||
{
|
||||
int inherit; /* inherit flag */
|
||||
int inherit_all; /* inherit all handles from parent */
|
||||
int create_flags; /* creation flags */
|
||||
int start_flags; /* flags from startup info */
|
||||
int hstdin; /* handle for stdin */
|
||||
int hstdout; /* handle for stdout */
|
||||
|
@ -732,6 +733,119 @@ struct next_process_reply
|
|||
};
|
||||
|
||||
|
||||
/* Wait for a debug event */
|
||||
struct wait_debug_event_request
|
||||
{
|
||||
int timeout; /* timeout in ms */
|
||||
};
|
||||
struct wait_debug_event_reply
|
||||
{
|
||||
int code; /* event code */
|
||||
void* pid; /* process id */
|
||||
void* tid; /* thread id */
|
||||
/* followed by the event data (see below) */
|
||||
};
|
||||
|
||||
|
||||
/* Send a debug event */
|
||||
struct send_debug_event_request
|
||||
{
|
||||
int code; /* event code */
|
||||
/* followed by the event data (see below) */
|
||||
};
|
||||
struct send_debug_event_reply
|
||||
{
|
||||
int status; /* event continuation status */
|
||||
};
|
||||
|
||||
|
||||
/* definitions of the event data depending on the event code */
|
||||
struct debug_event_exception
|
||||
{
|
||||
int code; /* exception code */
|
||||
int flags; /* exception flags */
|
||||
void *record; /* exception record ptr */
|
||||
void *addr; /* exception address */
|
||||
int nb_params; /* exceptions parameters */
|
||||
int params[15];
|
||||
int first_chance; /* first chance to handle it? */
|
||||
};
|
||||
struct debug_event_create_thread
|
||||
{
|
||||
int handle; /* handle to the new thread */
|
||||
void *teb; /* thread teb (in debugged process address space) */
|
||||
void *start; /* thread startup routine */
|
||||
};
|
||||
struct debug_event_create_process
|
||||
{
|
||||
int file; /* handle to the process exe file */
|
||||
int process; /* handle to the new process */
|
||||
int thread; /* handle to the new thread */
|
||||
void *base; /* base of executable image */
|
||||
int dbg_offset; /* offset of debug info in file */
|
||||
int dbg_size; /* size of debug info */
|
||||
void *teb; /* thread teb (in debugged process address space) */
|
||||
void *start; /* thread startup routine */
|
||||
void *name; /* image name (optional) */
|
||||
int unicode; /* is it Unicode? */
|
||||
};
|
||||
struct debug_event_exit
|
||||
{
|
||||
int exit_code; /* thread or process exit code */
|
||||
};
|
||||
struct debug_event_load_dll
|
||||
{
|
||||
int handle; /* file handle for the dll */
|
||||
void *base; /* base address of the dll */
|
||||
int dbg_offset; /* offset of debug info in file */
|
||||
int dbg_size; /* size of debug info */
|
||||
void *name; /* image name (optional) */
|
||||
int unicode; /* is it Unicode? */
|
||||
};
|
||||
struct debug_event_unload_dll
|
||||
{
|
||||
void *base; /* base address of the dll */
|
||||
};
|
||||
struct debug_event_output_string
|
||||
{
|
||||
void *string; /* string to display (in debugged process address space) */
|
||||
int unicode; /* is it Unicode? */
|
||||
int length; /* string length */
|
||||
};
|
||||
struct debug_event_rip_info
|
||||
{
|
||||
int error; /* ??? */
|
||||
int type; /* ??? */
|
||||
};
|
||||
union debug_event_data
|
||||
{
|
||||
struct debug_event_exception exception;
|
||||
struct debug_event_create_thread create_thread;
|
||||
struct debug_event_create_process create_process;
|
||||
struct debug_event_exit exit;
|
||||
struct debug_event_load_dll load_dll;
|
||||
struct debug_event_unload_dll unload_dll;
|
||||
struct debug_event_output_string output_string;
|
||||
struct debug_event_rip_info rip_info;
|
||||
};
|
||||
|
||||
|
||||
/* Continue a debug event */
|
||||
struct continue_debug_event_request
|
||||
{
|
||||
void* pid; /* process id to continue */
|
||||
void* tid; /* thread id to continue */
|
||||
int status; /* continuation status */
|
||||
};
|
||||
|
||||
|
||||
/* Start debugging an existing process */
|
||||
struct debug_process_request
|
||||
{
|
||||
void* pid; /* id of the process to debug */
|
||||
};
|
||||
|
||||
|
||||
/* requests definitions */
|
||||
#include "server/request.h"
|
||||
|
||||
|
@ -740,14 +854,13 @@ struct next_process_reply
|
|||
#ifndef __WINE_SERVER__
|
||||
|
||||
/* client communication functions */
|
||||
extern void CLIENT_ProtocolError( const char *err, ... );
|
||||
extern void CLIENT_SendRequest( enum request req, int pass_fd,
|
||||
int n, ... /* arg_1, len_1, etc. */ );
|
||||
extern unsigned int CLIENT_WaitReply( int *len, int *passed_fd,
|
||||
int n, ... /* arg_1, len_1, etc. */ );
|
||||
extern unsigned int CLIENT_WaitSimpleReply( void *reply, int len, int *passed_fd );
|
||||
extern int CLIENT_InitServer(void);
|
||||
|
||||
struct _THDB;
|
||||
extern int CLIENT_SetDebug( int level );
|
||||
extern int CLIENT_DebuggerRequest( int op );
|
||||
extern int CLIENT_InitThread(void);
|
||||
|
|
|
@ -63,6 +63,10 @@ enum request
|
|||
REQ_CREATE_DEVICE,
|
||||
REQ_CREATE_SNAPSHOT,
|
||||
REQ_NEXT_PROCESS,
|
||||
REQ_WAIT_DEBUG_EVENT,
|
||||
REQ_SEND_DEBUG_EVENT,
|
||||
REQ_CONTINUE_DEBUG_EVENT,
|
||||
REQ_DEBUG_PROCESS,
|
||||
REQ_NB_REQUESTS
|
||||
};
|
||||
|
||||
|
@ -126,6 +130,10 @@ DECL_HANDLER(get_mapping_info);
|
|||
DECL_HANDLER(create_device);
|
||||
DECL_HANDLER(create_snapshot);
|
||||
DECL_HANDLER(next_process);
|
||||
DECL_HANDLER(wait_debug_event);
|
||||
DECL_HANDLER(send_debug_event);
|
||||
DECL_HANDLER(continue_debug_event);
|
||||
DECL_HANDLER(debug_process);
|
||||
|
||||
static const struct handler {
|
||||
void (*handler)();
|
||||
|
@ -189,6 +197,10 @@ static const struct handler {
|
|||
{ (void(*)())req_create_device, sizeof(struct create_device_request) },
|
||||
{ (void(*)())req_create_snapshot, sizeof(struct create_snapshot_request) },
|
||||
{ (void(*)())req_next_process, sizeof(struct next_process_request) },
|
||||
{ (void(*)())req_wait_debug_event, sizeof(struct wait_debug_event_request) },
|
||||
{ (void(*)())req_send_debug_event, sizeof(struct send_debug_event_request) },
|
||||
{ (void(*)())req_continue_debug_event, sizeof(struct continue_debug_event_request) },
|
||||
{ (void(*)())req_debug_process, sizeof(struct debug_process_request) },
|
||||
};
|
||||
#endif /* WANT_REQUEST_HANDLERS */
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ MODULE = server
|
|||
C_SRCS = \
|
||||
change.c \
|
||||
console.c \
|
||||
debugger.c \
|
||||
device.c \
|
||||
event.c \
|
||||
file.c \
|
||||
|
|
|
@ -0,0 +1,438 @@
|
|||
/*
|
||||
* Server-side debugger functions
|
||||
*
|
||||
* Copyright (C) 1999 Alexandre Julliard
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "winbase.h"
|
||||
#include "winerror.h"
|
||||
|
||||
#include "server.h"
|
||||
#include "handle.h"
|
||||
#include "process.h"
|
||||
#include "thread.h"
|
||||
|
||||
|
||||
struct debug_event
|
||||
{
|
||||
struct debug_event *next; /* event queue */
|
||||
struct debug_event *prev;
|
||||
struct thread *thread; /* thread which sent this event */
|
||||
int sent; /* already sent to the debugger? */
|
||||
int code; /* event code */
|
||||
union debug_event_data data; /* event data */
|
||||
};
|
||||
|
||||
struct debug_ctx
|
||||
{
|
||||
struct thread *owner; /* thread owning this debug context */
|
||||
int waiting; /* is thread waiting for an event? */
|
||||
struct timeout_user *timeout; /* timeout user for wait timeout */
|
||||
struct debug_event *event_head; /* head of pending events queue */
|
||||
struct debug_event *event_tail; /* tail of pending events queue */
|
||||
};
|
||||
|
||||
/* size of the event data */
|
||||
static const int event_sizes[] =
|
||||
{
|
||||
0,
|
||||
sizeof(struct debug_event_exception), /* EXCEPTION_DEBUG_EVENT */
|
||||
sizeof(struct debug_event_create_thread), /* CREATE_THREAD_DEBUG_EVENT */
|
||||
sizeof(struct debug_event_create_process), /* CREATE_PROCESS_DEBUG_EVENT */
|
||||
sizeof(struct debug_event_exit), /* EXIT_THREAD_DEBUG_EVENT */
|
||||
sizeof(struct debug_event_exit), /* EXIT_PROCESS_DEBUG_EVENT */
|
||||
sizeof(struct debug_event_load_dll), /* LOAD_DLL_DEBUG_EVENT */
|
||||
sizeof(struct debug_event_unload_dll), /* UNLOAD_DLL_DEBUG_EVENT */
|
||||
sizeof(struct debug_event_output_string), /* OUTPUT_DEBUG_STRING_EVENT */
|
||||
sizeof(struct debug_event_rip_info) /* RIP_EVENT */
|
||||
};
|
||||
|
||||
|
||||
/* initialise the fields that do not need to be filled by the client */
|
||||
static int fill_debug_event( struct thread *debugger, struct thread *thread,
|
||||
struct debug_event *event )
|
||||
{
|
||||
int handle;
|
||||
|
||||
/* some events need special handling */
|
||||
switch(event->code)
|
||||
{
|
||||
case CREATE_THREAD_DEBUG_EVENT:
|
||||
if ((event->data.create_thread.handle = alloc_handle( debugger->process, thread,
|
||||
THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME, FALSE )) == -1)
|
||||
return 0;
|
||||
break;
|
||||
case CREATE_PROCESS_DEBUG_EVENT:
|
||||
if ((handle = event->data.create_process.file) != -1)
|
||||
{
|
||||
if ((handle = duplicate_handle( thread->process, handle, debugger->process,
|
||||
GENERIC_READ, FALSE, 0 )) == -1)
|
||||
return 0;
|
||||
event->data.create_process.file = handle;
|
||||
}
|
||||
if ((event->data.create_process.process = alloc_handle( debugger->process, thread->process,
|
||||
PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE )) == -1)
|
||||
{
|
||||
if (handle != -1) close_handle( debugger->process, handle );
|
||||
return 0;
|
||||
}
|
||||
if ((event->data.create_process.thread = alloc_handle( debugger->process, thread,
|
||||
THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME, FALSE )) == -1)
|
||||
{
|
||||
if (handle != -1) close_handle( debugger->process, handle );
|
||||
close_handle( debugger->process, event->data.create_process.process );
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case LOAD_DLL_DEBUG_EVENT:
|
||||
if ((handle = event->data.load_dll.handle) != -1)
|
||||
{
|
||||
if ((handle = duplicate_handle( thread->process, handle, debugger->process,
|
||||
GENERIC_READ, FALSE, 0 )) == -1)
|
||||
return 0;
|
||||
event->data.load_dll.handle = handle;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* free a debug event structure */
|
||||
static void free_event( struct debug_event *event )
|
||||
{
|
||||
switch(event->code)
|
||||
{
|
||||
case CREATE_THREAD_DEBUG_EVENT:
|
||||
close_handle( event->thread->process, event->data.create_thread.handle );
|
||||
break;
|
||||
case CREATE_PROCESS_DEBUG_EVENT:
|
||||
if (event->data.create_process.file != -1)
|
||||
close_handle( event->thread->process, event->data.create_process.file );
|
||||
close_handle( event->thread->process, event->data.create_process.thread );
|
||||
close_handle( event->thread->process, event->data.create_process.process );
|
||||
break;
|
||||
case LOAD_DLL_DEBUG_EVENT:
|
||||
if (event->data.load_dll.handle != -1)
|
||||
close_handle( event->thread->process, event->data.load_dll.handle );
|
||||
break;
|
||||
}
|
||||
event->thread->debug_event = NULL;
|
||||
release_object( event->thread );
|
||||
free( event );
|
||||
}
|
||||
|
||||
/* unlink the first event from the queue */
|
||||
static void unlink_event( struct debug_ctx *debug_ctx, struct debug_event *event )
|
||||
{
|
||||
if (event->prev) event->prev->next = event->next;
|
||||
else debug_ctx->event_head = event->next;
|
||||
if (event->next) event->next->prev = event->prev;
|
||||
else debug_ctx->event_tail = event->prev;
|
||||
event->next = event->prev = NULL;
|
||||
}
|
||||
|
||||
/* link an event at the end of the queue */
|
||||
static void link_event( struct debug_ctx *debug_ctx, struct debug_event *event )
|
||||
{
|
||||
event->next = NULL;
|
||||
event->prev = debug_ctx->event_tail;
|
||||
if (event->prev) event->prev->next = event;
|
||||
else debug_ctx->event_head = event;
|
||||
debug_ctx->event_tail = event;
|
||||
}
|
||||
|
||||
/* send the first queue event as a reply */
|
||||
static void send_event_reply( struct debug_ctx *debug_ctx )
|
||||
{
|
||||
struct wait_debug_event_reply reply;
|
||||
struct debug_event *event = debug_ctx->event_head;
|
||||
struct thread *thread = event->thread;
|
||||
|
||||
assert( event );
|
||||
assert( debug_ctx->waiting );
|
||||
|
||||
unlink_event( debug_ctx, event );
|
||||
event->sent = 1;
|
||||
reply.code = event->code;
|
||||
reply.pid = thread->process;
|
||||
reply.tid = thread;
|
||||
debug_ctx->waiting = 0;
|
||||
if (debug_ctx->timeout)
|
||||
{
|
||||
remove_timeout_user( debug_ctx->timeout );
|
||||
debug_ctx->timeout = NULL;
|
||||
}
|
||||
debug_ctx->owner->error = 0;
|
||||
send_reply( debug_ctx->owner, -1, 2, &reply, sizeof(reply),
|
||||
&event->data, event_sizes[event->code] );
|
||||
}
|
||||
|
||||
/* timeout callback while waiting for a debug event */
|
||||
static void wait_event_timeout( void *ctx )
|
||||
{
|
||||
struct debug_ctx *debug_ctx = (struct debug_ctx *)ctx;
|
||||
struct wait_debug_event_reply reply;
|
||||
|
||||
assert( debug_ctx->waiting );
|
||||
|
||||
reply.code = 0;
|
||||
reply.pid = 0;
|
||||
reply.tid = 0;
|
||||
debug_ctx->waiting = 0;
|
||||
debug_ctx->timeout = NULL;
|
||||
debug_ctx->owner->error = WAIT_TIMEOUT;
|
||||
send_reply( debug_ctx->owner, -1, 1, &reply, sizeof(reply) );
|
||||
}
|
||||
|
||||
/* wait for a debug event (or send a reply at once if one is pending) */
|
||||
static int wait_for_debug_event( int timeout )
|
||||
{
|
||||
struct debug_ctx *debug_ctx = current->debug_ctx;
|
||||
struct timeval when;
|
||||
|
||||
if (!debug_ctx) /* current thread is not a debugger */
|
||||
{
|
||||
SET_ERROR( ERROR_ACCESS_DENIED ); /* FIXME */
|
||||
return 0;
|
||||
}
|
||||
assert( !debug_ctx->waiting );
|
||||
if (debug_ctx->event_head) /* already have a pending event */
|
||||
{
|
||||
debug_ctx->waiting = 1;
|
||||
send_event_reply( debug_ctx );
|
||||
return 1;
|
||||
}
|
||||
if (!timeout) /* no event and we don't want to wait */
|
||||
{
|
||||
SET_ERROR( WAIT_TIMEOUT );
|
||||
return 0;
|
||||
}
|
||||
if (timeout != -1) /* start the timeout */
|
||||
{
|
||||
make_timeout( &when, timeout );
|
||||
if (!(debug_ctx->timeout = add_timeout_user( &when, wait_event_timeout, debug_ctx )))
|
||||
return 0;
|
||||
}
|
||||
debug_ctx->waiting = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* continue a debug event */
|
||||
static int continue_debug_event( struct process *process, struct thread *thread, int status )
|
||||
{
|
||||
struct debug_event *event = thread->debug_event;
|
||||
|
||||
if (process->debugger != current || !event || !event->sent)
|
||||
{
|
||||
/* not debugging this process, or no event pending */
|
||||
SET_ERROR( ERROR_ACCESS_DENIED ); /* FIXME */
|
||||
return 0;
|
||||
}
|
||||
if (thread->state != TERMINATED)
|
||||
{
|
||||
/* only send a reply if the thread is still there */
|
||||
/* (we can get a continue on an exit thread/process event) */
|
||||
struct send_debug_event_reply reply;
|
||||
reply.status = status;
|
||||
send_reply( thread, -1, 1, &reply, sizeof(reply) );
|
||||
}
|
||||
free_event( event );
|
||||
resume_process( process );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* queue a debug event for a debugger */
|
||||
static struct debug_event *queue_debug_event( struct thread *debugger, struct thread *thread,
|
||||
int code, void *data )
|
||||
{
|
||||
struct debug_ctx *debug_ctx = debugger->debug_ctx;
|
||||
struct debug_event *event;
|
||||
|
||||
assert( debug_ctx );
|
||||
/* cannot queue a debug event for myself */
|
||||
assert( debugger->process != thread->process );
|
||||
|
||||
/* build the event */
|
||||
if (!(event = mem_alloc( sizeof(*event) - sizeof(event->data) + event_sizes[code] )))
|
||||
return NULL;
|
||||
event->sent = 0;
|
||||
event->code = code;
|
||||
event->thread = (struct thread *)grab_object( thread );
|
||||
memcpy( &event->data, data, event_sizes[code] );
|
||||
|
||||
if (!fill_debug_event( debugger, thread, event ))
|
||||
{
|
||||
release_object( event->thread );
|
||||
free( event );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (thread->debug_event)
|
||||
{
|
||||
/* only exit events can replace others */
|
||||
assert( code == EXIT_THREAD_DEBUG_EVENT || code == EXIT_PROCESS_DEBUG_EVENT );
|
||||
if (!thread->debug_event->sent) unlink_event( debug_ctx, thread->debug_event );
|
||||
free_event( thread->debug_event );
|
||||
}
|
||||
|
||||
link_event( debug_ctx, event );
|
||||
thread->debug_event = event;
|
||||
suspend_process( thread->process );
|
||||
if (debug_ctx->waiting) send_event_reply( debug_ctx );
|
||||
return event;
|
||||
}
|
||||
|
||||
/* attach a process to a debugger thread */
|
||||
int debugger_attach( struct process *process, struct thread *debugger )
|
||||
{
|
||||
struct debug_ctx *debug_ctx;
|
||||
struct thread *thread;
|
||||
|
||||
if (process->debugger) /* already being debugged */
|
||||
{
|
||||
SET_ERROR( ERROR_ACCESS_DENIED );
|
||||
return 0;
|
||||
}
|
||||
/* make sure we don't create a debugging loop */
|
||||
for (thread = debugger; thread; thread = thread->process->debugger)
|
||||
if (thread->process == process)
|
||||
{
|
||||
SET_ERROR( ERROR_ACCESS_DENIED );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!debugger->debug_ctx) /* need to allocate a context */
|
||||
{
|
||||
assert( !debugger->debug_first );
|
||||
if (!(debug_ctx = mem_alloc( sizeof(*debug_ctx) ))) return 0;
|
||||
debug_ctx->owner = current;
|
||||
debug_ctx->waiting = 0;
|
||||
debug_ctx->timeout = NULL;
|
||||
debug_ctx->event_head = NULL;
|
||||
debug_ctx->event_tail = NULL;
|
||||
debugger->debug_ctx = debug_ctx;
|
||||
}
|
||||
process->debugger = debugger;
|
||||
process->debug_prev = NULL;
|
||||
process->debug_next = debugger->debug_first;
|
||||
debugger->debug_first = process;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* detach a process from its debugger thread */
|
||||
static void debugger_detach( struct process *process )
|
||||
{
|
||||
struct thread *debugger = process->debugger;
|
||||
|
||||
assert( debugger );
|
||||
|
||||
if (process->debug_next) process->debug_next->debug_prev = process->debug_prev;
|
||||
if (process->debug_prev) process->debug_prev->debug_next = process->debug_next;
|
||||
else debugger->debug_first = process;
|
||||
process->debugger = NULL;
|
||||
}
|
||||
|
||||
/* a thread is exiting */
|
||||
void debug_exit_thread( struct thread *thread, int exit_code )
|
||||
{
|
||||
struct thread *debugger = current->process->debugger;
|
||||
struct debug_ctx *debug_ctx = thread->debug_ctx;
|
||||
|
||||
if (debugger) /* being debugged -> send an event to the debugger */
|
||||
{
|
||||
struct debug_event_exit event;
|
||||
event.exit_code = exit_code;
|
||||
if (!thread->proc_next && !thread->proc_prev)
|
||||
{
|
||||
assert( thread->process->thread_list == thread );
|
||||
/* this is the last thread, send an exit process event and cleanup */
|
||||
queue_debug_event( debugger, current, EXIT_PROCESS_DEBUG_EVENT, &event );
|
||||
debugger_detach( thread->process );
|
||||
}
|
||||
else queue_debug_event( debugger, current, EXIT_THREAD_DEBUG_EVENT, &event );
|
||||
}
|
||||
|
||||
if (debug_ctx) /* this thread is a debugger */
|
||||
{
|
||||
struct debug_event *event;
|
||||
|
||||
/* kill all debugged processes */
|
||||
while (thread->debug_first) kill_process( thread->debug_first, exit_code );
|
||||
/* free all pending events */
|
||||
while ((event = debug_ctx->event_head) != NULL)
|
||||
{
|
||||
unlink_event( debug_ctx, event );
|
||||
free_event( event );
|
||||
}
|
||||
/* remove the timeout */
|
||||
if (debug_ctx->timeout) remove_timeout_user( debug_ctx->timeout );
|
||||
thread->debug_ctx = NULL;
|
||||
free( debug_ctx );
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for a debug event */
|
||||
DECL_HANDLER(wait_debug_event)
|
||||
{
|
||||
struct wait_debug_event_reply reply;
|
||||
|
||||
if (!wait_for_debug_event( req->timeout ))
|
||||
{
|
||||
reply.code = 0;
|
||||
reply.pid = NULL;
|
||||
reply.tid = NULL;
|
||||
send_reply( current, -1, 1, &reply, sizeof(reply) );
|
||||
}
|
||||
}
|
||||
|
||||
/* Continue a debug event */
|
||||
DECL_HANDLER(continue_debug_event)
|
||||
{
|
||||
struct process *process = get_process_from_id( req->pid );
|
||||
if (process)
|
||||
{
|
||||
struct thread *thread = get_thread_from_id( req->tid );
|
||||
if (thread)
|
||||
{
|
||||
continue_debug_event( process, thread, req->status );
|
||||
release_object( thread );
|
||||
}
|
||||
release_object( process );
|
||||
}
|
||||
send_reply( current, -1, 0 );
|
||||
}
|
||||
|
||||
/* Start debugging an existing process */
|
||||
DECL_HANDLER(debug_process)
|
||||
{
|
||||
struct process *process = get_process_from_id( req->pid );
|
||||
if (process)
|
||||
{
|
||||
debugger_attach( process, current );
|
||||
/* FIXME: should notice the debugged process somehow */
|
||||
release_object( process );
|
||||
}
|
||||
send_reply( current, -1, 0 );
|
||||
}
|
||||
|
||||
/* Send a debug event */
|
||||
DECL_HANDLER(send_debug_event)
|
||||
{
|
||||
struct thread *debugger = current->process->debugger;
|
||||
struct send_debug_event_reply reply;
|
||||
|
||||
if ((req->code <= 0) || (req->code > RIP_EVENT))
|
||||
fatal_protocol_error( "send_debug_event: bad event code %d\n", req->code );
|
||||
if (len != event_sizes[req->code])
|
||||
fatal_protocol_error( "send_debug_event: bad event length %d/%d\n",
|
||||
len, event_sizes[req->code] );
|
||||
assert( !current->debug_event );
|
||||
reply.status = 0;
|
||||
if (debugger)
|
||||
{
|
||||
if (queue_debug_event( debugger, current, req->code, data ))
|
||||
return; /* don't reply now, wait for continue_debug_event */
|
||||
}
|
||||
send_reply( current, -1, 1, &reply, sizeof(reply) );
|
||||
}
|
|
@ -153,6 +153,11 @@ extern void file_set_error(void);
|
|||
|
||||
extern int create_console( int fd, struct object *obj[2] );
|
||||
|
||||
/* debugger functions */
|
||||
|
||||
extern int debugger_attach( struct process *process, struct thread *debugger );
|
||||
extern void debug_exit_thread( struct thread *thread, int exit_code );
|
||||
|
||||
extern int debug_level;
|
||||
|
||||
#endif /* __WINE_SERVER_OBJECT_H */
|
||||
|
|
|
@ -109,12 +109,21 @@ static struct process *create_process( struct new_process_request *req )
|
|||
return NULL;
|
||||
}
|
||||
init_process( process );
|
||||
if (parent->console_in) process->console_in = grab_object( parent->console_in );
|
||||
if (parent->console_out) process->console_out = grab_object( parent->console_out );
|
||||
|
||||
if (!(process->info = mem_alloc( sizeof(*process->info) ))) goto error;
|
||||
memcpy( process->info, req, sizeof(*req) );
|
||||
|
||||
/* set the process console */
|
||||
if (req->create_flags & CREATE_NEW_CONSOLE)
|
||||
{
|
||||
if (!alloc_console( process )) goto error;
|
||||
}
|
||||
else if (!(req->create_flags & DETACHED_PROCESS))
|
||||
{
|
||||
if (parent->console_in) process->console_in = grab_object( parent->console_in );
|
||||
if (parent->console_out) process->console_out = grab_object( parent->console_out );
|
||||
}
|
||||
|
||||
if (!req->inherit_all && !(req->start_flags & STARTF_USESTDHANDLES))
|
||||
{
|
||||
process->info->hstdin = duplicate_handle( parent, req->hstdin, process,
|
||||
|
@ -125,6 +134,12 @@ static struct process *create_process( struct new_process_request *req )
|
|||
0, TRUE, DUPLICATE_SAME_ACCESS );
|
||||
}
|
||||
|
||||
/* attach to the debugger if requested */
|
||||
if (req->create_flags & DEBUG_PROCESS)
|
||||
debugger_attach( process, current );
|
||||
else if (parent->debugger && !(req->create_flags & DEBUG_ONLY_THIS_PROCESS))
|
||||
debugger_attach( process, parent->debugger );
|
||||
|
||||
process->next = first_process;
|
||||
first_process->prev = process;
|
||||
first_process = process;
|
||||
|
|
|
@ -87,6 +87,7 @@ static void init_thread( struct thread *thread, int fd )
|
|||
thread->mutex = NULL;
|
||||
thread->debug_ctx = NULL;
|
||||
thread->debug_first = NULL;
|
||||
thread->debug_event = NULL;
|
||||
thread->wait = NULL;
|
||||
thread->apc = NULL;
|
||||
thread->apc_count = 0;
|
||||
|
@ -157,6 +158,7 @@ static void destroy_thread( struct object *obj )
|
|||
struct thread *thread = (struct thread *)obj;
|
||||
assert( obj->ops == &thread_ops );
|
||||
|
||||
assert( !thread->debug_ctx ); /* cannot still be debugging something */
|
||||
release_object( thread->process );
|
||||
if (thread->next) thread->next->prev = thread->prev;
|
||||
if (thread->prev) thread->prev->next = thread->next;
|
||||
|
@ -522,6 +524,7 @@ void thread_killed( struct thread *thread, int exit_code )
|
|||
thread->state = TERMINATED;
|
||||
thread->exit_code = exit_code;
|
||||
if (thread->wait) end_wait( thread );
|
||||
debug_exit_thread( thread, exit_code );
|
||||
abandon_mutexes( thread );
|
||||
remove_process_thread( thread->process, thread );
|
||||
wake_up( &thread->obj, 0 );
|
||||
|
|
|
@ -20,6 +20,7 @@ struct thread_wait;
|
|||
struct thread_apc;
|
||||
struct mutex;
|
||||
struct debug_ctx;
|
||||
struct debug_event;
|
||||
|
||||
enum run_state { STARTING, RUNNING, TERMINATED };
|
||||
|
||||
|
@ -34,6 +35,7 @@ struct thread
|
|||
struct mutex *mutex; /* list of currently owned mutexes */
|
||||
struct debug_ctx *debug_ctx; /* debugger context if this thread is a debugger */
|
||||
struct process *debug_first; /* head of debugged processes list */
|
||||
struct debug_event *debug_event; /* pending debug event for this thread */
|
||||
struct thread_wait *wait; /* current wait condition if sleeping */
|
||||
struct thread_apc *apc; /* list of async procedure calls */
|
||||
int apc_count; /* number of outstanding APCs */
|
||||
|
|
|
@ -10,6 +10,7 @@ static int dump_new_process_request( struct new_process_request *req, int len )
|
|||
{
|
||||
fprintf( stderr, " inherit=%d,", req->inherit );
|
||||
fprintf( stderr, " inherit_all=%d,", req->inherit_all );
|
||||
fprintf( stderr, " create_flags=%d,", req->create_flags );
|
||||
fprintf( stderr, " start_flags=%d,", req->start_flags );
|
||||
fprintf( stderr, " hstdin=%d,", req->hstdin );
|
||||
fprintf( stderr, " hstdout=%d,", req->hstdout );
|
||||
|
@ -682,6 +683,46 @@ static int dump_next_process_reply( struct next_process_reply *req, int len )
|
|||
return (int)sizeof(*req);
|
||||
}
|
||||
|
||||
static int dump_wait_debug_event_request( struct wait_debug_event_request *req, int len )
|
||||
{
|
||||
fprintf( stderr, " timeout=%d", req->timeout );
|
||||
return (int)sizeof(*req);
|
||||
}
|
||||
|
||||
static int dump_wait_debug_event_reply( struct wait_debug_event_reply *req, int len )
|
||||
{
|
||||
fprintf( stderr, " code=%d,", req->code );
|
||||
fprintf( stderr, " pid=%p,", req->pid );
|
||||
fprintf( stderr, " tid=%p", req->tid );
|
||||
return (int)sizeof(*req);
|
||||
}
|
||||
|
||||
static int dump_send_debug_event_request( struct send_debug_event_request *req, int len )
|
||||
{
|
||||
fprintf( stderr, " code=%d", req->code );
|
||||
return (int)sizeof(*req);
|
||||
}
|
||||
|
||||
static int dump_send_debug_event_reply( struct send_debug_event_reply *req, int len )
|
||||
{
|
||||
fprintf( stderr, " status=%d", req->status );
|
||||
return (int)sizeof(*req);
|
||||
}
|
||||
|
||||
static int dump_continue_debug_event_request( struct continue_debug_event_request *req, int len )
|
||||
{
|
||||
fprintf( stderr, " pid=%p,", req->pid );
|
||||
fprintf( stderr, " tid=%p,", req->tid );
|
||||
fprintf( stderr, " status=%d", req->status );
|
||||
return (int)sizeof(*req);
|
||||
}
|
||||
|
||||
static int dump_debug_process_request( struct debug_process_request *req, int len )
|
||||
{
|
||||
fprintf( stderr, " pid=%p", req->pid );
|
||||
return (int)sizeof(*req);
|
||||
}
|
||||
|
||||
struct dumper
|
||||
{
|
||||
int (*dump_req)( void *data, int len );
|
||||
|
@ -806,6 +847,14 @@ static const struct dumper dumpers[REQ_NB_REQUESTS] =
|
|||
(void(*)())dump_create_snapshot_reply },
|
||||
{ (int(*)(void *,int))dump_next_process_request,
|
||||
(void(*)())dump_next_process_reply },
|
||||
{ (int(*)(void *,int))dump_wait_debug_event_request,
|
||||
(void(*)())dump_wait_debug_event_reply },
|
||||
{ (int(*)(void *,int))dump_send_debug_event_request,
|
||||
(void(*)())dump_send_debug_event_reply },
|
||||
{ (int(*)(void *,int))dump_continue_debug_event_request,
|
||||
(void(*)())0 },
|
||||
{ (int(*)(void *,int))dump_debug_process_request,
|
||||
(void(*)())0 },
|
||||
};
|
||||
|
||||
static const char * const req_names[REQ_NB_REQUESTS] =
|
||||
|
@ -868,6 +917,10 @@ static const char * const req_names[REQ_NB_REQUESTS] =
|
|||
"create_device",
|
||||
"create_snapshot",
|
||||
"next_process",
|
||||
"wait_debug_event",
|
||||
"send_debug_event",
|
||||
"continue_debug_event",
|
||||
"debug_process",
|
||||
};
|
||||
|
||||
void trace_request( enum request req, void *data, int len, int fd )
|
||||
|
|
Loading…
Reference in New Issue