Re: Debugging MT programs (slink) ?
cc: to the libc6 maintainer is set.
debugging multithreaded applications is only possible, if the glibc is also
compiled with a patch by the opengroup people. gdb already contains the
necessary patch.
however i tried on two systems with debian 2.0 or 2.1, and was not able to
recompile the libc6 package (2.1) or without breaking c++ applications(2.0).
tha patch is attached (it's not so big).
request to the libc6 maintainer : could you please compile a libc6 with this
patch ? itßs know to be stable (i had a machine with 2.0.4 and this patch
running for months without problems (debian 2.0)). maybe you can put the
package into experimental, so people can try ...
regards, andreas
---
diff -ru glibc-2.0.6-ORIG/linuxthreads/internals.h glibc-2.0.6/linuxthreads/internals.h
--- glibc-2.0.6-ORIG/linuxthreads/internals.h Mon Dec 8 05:39:54 1997
+++ glibc-2.0.6/linuxthreads/internals.h Tue Apr 28 20:15:36 1998
@@ -97,7 +97,7 @@
struct pthread_request {
pthread_descr req_thread; /* Thread doing the request */
enum { /* Request kind */
- REQ_CREATE, REQ_FREE, REQ_PROCESS_EXIT, REQ_MAIN_THREAD_EXIT
+ REQ_CREATE, REQ_FREE, REQ_PROCESS_EXIT, REQ_MAIN_THREAD_EXIT, REQ_DEBUG
} req_kind;
union { /* Arguments for request */
struct { /* For REQ_CREATE: */
@@ -247,6 +247,8 @@
#define ASSERT(x)
#define MSG(msg,arg...)
#endif
+
+extern volatile int __pthread_threads_debug;
/* Internal global functions */
diff -ru glibc-2.0.6-ORIG/linuxthreads/manager.c glibc-2.0.6/linuxthreads/manager.c
--- glibc-2.0.6-ORIG/linuxthreads/manager.c Mon Dec 8 05:19:54 1997
+++ glibc-2.0.6/linuxthreads/manager.c Thu Apr 30 13:43:01 1998
@@ -37,6 +37,14 @@
struct pthread_handle_struct __pthread_handles[PTHREAD_THREADS_MAX] =
{ { 0, &__pthread_initial_thread}, /* All NULLs */ };
+/* # active entries in __pthread_handles array (used for library debugging) */
+
+volatile int __pthread_handles_num = 1;
+
+/* Use debugger additional actions for thread creation */
+
+volatile int __pthread_threads_debug = 0;
+
/* Mapping from stack segment to thread descriptor. */
/* Stack segment numbers are also indices into the __pthread_handles array. */
/* Stack segment number 0 is reserved for the initial thread. */
@@ -86,10 +94,16 @@
#ifdef INIT_THREAD_SELF
INIT_THREAD_SELF(&__pthread_manager_thread);
#endif
- /* Block all signals except PTHREAD_SIG_RESTART */
+ /* Block all signals except PTHREAD_SIG_RESTART, PTHREAD_SIG_CANCEL
+ and SIGTRAP */
sigfillset(&mask);
sigdelset(&mask, PTHREAD_SIG_RESTART);
+ sigdelset(&mask, PTHREAD_SIG_CANCEL); /* for debugging new threads */
+ sigdelset(&mask, SIGTRAP); /* for debugging purposes */
sigprocmask(SIG_SETMASK, &mask, NULL);
+ /* Synchronize debugging of the thread manager */
+ n = __libc_read(reqfd, (char *)&request, sizeof(request));
+ ASSERT(n == sizeof(request) && request.req_kind == REQ_DEBUG);
/* Enter server loop */
while(1) {
FD_ZERO(&readfds);
@@ -136,6 +150,11 @@
return 0;
}
break;
+ case REQ_DEBUG:
+ if (__pthread_threads_debug)
+ raise(PTHREAD_SIG_CANCEL); /* Make debugger aware of new thread */
+ restart(request.req_thread);
+ break;
}
}
}
@@ -147,6 +166,7 @@
{
pthread_descr self = (pthread_descr) arg;
void * outcome;
+ struct pthread_request request;
/* Initialize special thread_self processing, if any. */
#ifdef INIT_THREAD_SELF
INIT_THREAD_SELF(self);
@@ -161,6 +181,13 @@
if (self->p_start_args.schedpolicy != SCHED_OTHER)
__sched_setscheduler(self->p_pid, self->p_start_args.schedpolicy,
&self->p_start_args.schedparam);
+ /* Make debugger aware of new threads */
+ if (__pthread_threads_debug) {
+ request.req_thread = self;
+ request.req_kind = REQ_DEBUG;
+ __libc_write(__pthread_manager_request, (char *) &request, sizeof(request));
+ suspend(self);
+ }
/* Run the thread code */
outcome = self->p_start_args.start_routine(self->p_start_args.arg);
/* Exit with the given return value */
@@ -191,6 +218,7 @@
/* It seems part of this segment is already mapped. Try the next. */
}
/* Allocate new thread identifier */
+ __pthread_handles_num++;
pthread_threads_counter += PTHREAD_THREADS_MAX;
new_thread_id = sseg + pthread_threads_counter;
/* Initialize the thread descriptor */
@@ -248,6 +276,7 @@
munmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),
INITIAL_STACK_SIZE);
__pthread_handles[sseg].h_descr = NULL;
+ __pthread_handles_num--;
return errno;
}
/* Insert new thread in doubly linked list of active threads */
@@ -274,6 +303,7 @@
acquire(&handle->h_spinlock);
handle->h_descr = NULL;
release(&handle->h_spinlock);
+ __pthread_handles_num--;
/* If initial thread, nothing to free */
if (th == &__pthread_initial_thread) return;
/* Free the stack and thread descriptor area */
diff -ru glibc-2.0.6-ORIG/linuxthreads/pthread.c glibc-2.0.6/linuxthreads/pthread.c
--- glibc-2.0.6-ORIG/linuxthreads/pthread.c Mon Dec 8 05:25:24 1997
+++ glibc-2.0.6/linuxthreads/pthread.c Thu Apr 30 13:40:33 1998
@@ -120,6 +120,18 @@
int __pthread_exit_requested = 0;
int __pthread_exit_code = 0;
+/* Internal values for library debugging future compatibility */
+
+const int __pthread_threads_max = PTHREAD_THREADS_MAX;
+const int __pthread_sig_restart = PTHREAD_SIG_RESTART;
+const int __pthread_sig_cancel = PTHREAD_SIG_CANCEL;
+
+const int __pthread_sizeof_handle = sizeof (struct pthread_handle_struct);
+const int __pthread_offsetof_descr = offsetof (struct pthread_handle_struct,
+ h_descr);
+const int __pthread_offsetof_pid = offsetof (struct _pthread_descr_struct,
+ p_pid);
+
/* Forward declarations */
static void pthread_exit_process(int retcode, void *arg);
@@ -206,6 +218,8 @@
__pthread_manager_request = -1;
return -1;
}
+ /* Set pid field of the thread manager. */
+ __pthread_manager_thread.p_pid = __pthread_manager_pid;
return 0;
}
@@ -217,7 +231,14 @@
pthread_descr self = thread_self();
struct pthread_request request;
if (__pthread_manager_request < 0) {
- if (pthread_initialize_manager() < 0) return EAGAIN;
+ if (pthread_initialize_manager() < 0)
+ return EAGAIN;
+ if (__pthread_threads_debug)
+ raise(PTHREAD_SIG_CANCEL); /* Make debugger aware of new thread manager */
+ /* Synchronize debugging of the thread manager */
+ request.req_thread = self;
+ request.req_kind = REQ_DEBUG;
+ __libc_write(__pthread_manager_request, (char *) &request, sizeof(request));
}
request.req_thread = self;
request.req_kind = REQ_CREATE;
@@ -327,13 +348,24 @@
}
/* The handler for the CANCEL signal checks for cancellation
- (in asynchronous mode) and for process-wide exit and exec requests. */
+ (in asynchronous mode), for process-wide exit, exec requests
+ and for new thread debugging. */
static void pthread_handle_sigcancel(int sig)
{
pthread_descr self = thread_self();
sigjmp_buf * jmpbuf;
+ if (self == &__pthread_manager_thread) {
+ /* On reception of a REQ_DEBUG request (sent by new threads created to
+ the thread manager under debugging mode), the thread manager throws
+ PTHREAD_SIG_CANCEL to itself. The debugger (if active) intercepts
+ this signal, takes into account new threads and continue execution
+ of the thread manager by propagating the signal because it doesn't
+ know what it is specifically done for. In the current implementation,
+ the thread manager simply discards it. */
+ return;
+ }
if (__pthread_exit_requested) {
/* Main thread should accumulate times for thread manager and its
children, so that timings for main thread account for all threads. */
Reply to: