/* debug.cc Copyright 1998, 1999, 2000, 2001 Red Hat, Inc. This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" #include "exceptions.h" #include "sync.h" #include "sigproc.h" #include "pinfo.h" #include "perthread.h" #include "perprocess.h" #include "security.h" #include "cygerrno.h" #undef CloseHandle static muto NO_COPY *threadname_lock = NULL; #define lock_threadname() \ do {if (threadname_lock) threadname_lock->acquire (INFINITE); } while (0) #define unlock_threadname() \ do {if (threadname_lock) threadname_lock->release (); } while (0) typedef struct { DWORD id; const char *name; } thread_info; static NO_COPY thread_info threads[32] = {{0, NULL}}; // increase as necessary #define NTHREADS (sizeof (threads) / sizeof (threads[0])) void threadname_init () { threadname_lock = new_muto (FALSE, "threadname_lock"); } void __stdcall regthread (const char *name, DWORD tid) { lock_threadname (); for (DWORD i = 0; i < NTHREADS; i++) if (threads[i].name == NULL || strcmp (threads[i].name, name) == 0 || threads[i].id == tid) { threads[i].name = name; threads[i].id = tid; break; } unlock_threadname (); } int __stdcall iscygthread () { DWORD tid = GetCurrentThreadId (); if (tid != mainthread.id) for (DWORD i = 0; i < NTHREADS && threads[i].name != NULL; i++) if (threads[i].id == tid) return 1; return 0; } struct thread_start { LONG notavail; LPTHREAD_START_ROUTINE func; VOID *arg; }; /* A place to store arguments to thread_stub since they can't be stored on the stack. An available element is !notavail. */ thread_start NO_COPY start_buf[NTHREADS] = {{0, NULL,NULL}}; /* Initial stub called by makethread. Performs initial per-thread initialization. */ static DWORD WINAPI thread_stub (VOID *arg) { DECLARE_TLS_STORAGE; LPTHREAD_START_ROUTINE threadfunc = ((thread_start *) arg)->func; VOID *threadarg = ((thread_start *) arg)->arg; exception_list except_entry; /* Give up our slot in the start_buf array */ (void) InterlockedExchange (&((thread_start *) arg)->notavail, 0); /* Initialize this thread's ability to respond to things like SIGSEGV or SIGFPE. */ init_exceptions (&except_entry); set_reent (user_data->impure_ptr); ExitThread (threadfunc (threadarg)); } /* Wrapper for CreateThread. Registers the thread name/id and ensures that cygwin threads are properly initialized. */ HANDLE __stdcall makethread (LPTHREAD_START_ROUTINE start, LPVOID param, DWORD flags, const char *name) { DWORD tid; HANDLE h; thread_start *info; /* Various information needed by the newly created thread */ for (;;) { /* Search the start_buf array for an empty slot to use */ for (info = start_buf; info < start_buf + NTHREADS; info++) if (!InterlockedExchange (&info->notavail, 1)) goto out; /* Should never hit here, but be defensive anyway. */ Sleep (0); } out: info->func = start; /* Real function to start */ info->arg = param; /* The single parameter to the thread */ if ((h = CreateThread (&sec_none_nih, 0, thread_stub, (VOID *) info, flags, &tid))) regthread (name, tid); /* Register for debugging output. */ return h; } /* Return the symbolic name of the current thread for debugging. */ const char * __stdcall threadname (DWORD tid, int lockit) { const char *res = NULL; if (!tid) tid = GetCurrentThreadId (); if (lockit) lock_threadname (); for (DWORD i = 0; i < NTHREADS && threads[i].name != NULL; i++) if (threads[i].id == tid) { res = threads[i].name; break; } if (lockit) unlock_threadname (); if (!res) { static char buf[30] NO_COPY = {0}; __small_sprintf (buf, "unknown (%p)", tid); res = buf; } return res; } #ifdef DEBUGGING /* Here lies extra debugging routines which help track down internal Cygwin problems when compiled with -DDEBUGGING . */ #include typedef struct _h { BOOL allocated; HANDLE h; const char *name; const char *func; int ln; DWORD clexec_pid; struct _h *next; } handle_list; static NO_COPY handle_list starth = {0, NULL, NULL, NULL, 0, 0, NULL}; static NO_COPY handle_list *endh = NULL; static handle_list NO_COPY freeh[1000] = {{0, NULL, NULL, NULL, 0, 0, NULL}}; #define NFREEH (sizeof (freeh) / sizeof (freeh[0])) static muto NO_COPY *debug_lock = NULL; #define lock_debug() \ do {if (debug_lock) debug_lock->acquire (INFINITE); } while (0) #define unlock_debug() \ do {if (debug_lock) debug_lock->release (); } while (0) static bool __stdcall mark_closed (const char *, int, HANDLE, const char *, BOOL); void debug_init () { debug_lock = new_muto (FALSE, "debug_lock"); } /* Find a registered handle in the linked list of handles. */ static handle_list * __stdcall find_handle (HANDLE h) { handle_list *hl; for (hl = &starth; hl->next != NULL; hl = hl->next) if (hl->next->h == h) goto out; endh = hl; hl = NULL; out: return hl; } void setclexec_pid (HANDLE oh, HANDLE nh, bool setit) { handle_list *hl = find_handle (oh); if (hl) { hl->clexec_pid = setit ? GetCurrentProcessId () : 0; hl->h = nh; } } /* Create a new handle record */ static handle_list * __stdcall newh () { handle_list *hl; lock_debug (); for (hl = freeh; hl < freeh + NFREEH; hl++) if (hl->name == NULL) goto out; /* All used up??? */ if ((hl = (handle_list *) malloc (sizeof *hl)) != NULL) { memset (hl, 0, sizeof (*hl)); hl->allocated = TRUE; } out: unlock_debug (); return hl; } /* Add a handle to the linked list of known handles. */ void __stdcall add_handle (const char *func, int ln, HANDLE h, const char *name) { handle_list *hl; lock_debug (); if ((hl = find_handle (h))) { hl = hl->next; system_printf ("%s:%d - multiple attempts to add handle %s<%p>", func, ln, name, h); system_printf (" previously allocated by %s:%d(%s<%p>)", hl->func, hl->ln, hl->name, hl->h); goto out; /* Already did this once */ } if ((hl = newh ()) == NULL) { unlock_debug (); system_printf ("couldn't allocate memory for %s(%d): %s(%p)", func, ln, name, h); return; } hl->h = h; hl->name = name; hl->func = func; hl->ln = ln; hl->next = NULL; endh->next = hl; endh = hl; out: unlock_debug (); } static void __stdcall delete_handle (handle_list *hl) { handle_list *hnuke = hl->next; hl->next = hl->next->next; if (hnuke->allocated) free (hnuke); else memset (hnuke, 0, sizeof (*hnuke)); } void debug_fixup_after_fork () { handle_list *hl; for (hl = &starth; hl->next != NULL; hl = hl->next) if (hl->next->clexec_pid) delete_handle (hl); } static bool __stdcall mark_closed (const char *func, int ln, HANDLE h, const char *name, BOOL force) { handle_list *hl; lock_debug (); if ((hl = find_handle (h)) && !force) { hl = hl->next; unlock_debug (); // race here system_printf ("attempt to close protected handle %s:%d(%s<%p>)", hl->func, hl->ln, hl->name, hl->h); system_printf (" by %s:%d(%s<%p>)", func, ln, name, h); return FALSE; } handle_list *hln; if (hl && (hln = hl->next) && strcmp (name, hln->name)) { system_printf ("closing protected handle %s:%d(%s<%p>)", hln->func, hln->ln, hln->name, hln->h); system_printf (" by %s:%d(%s<%p>)", func, ln, name, h); } if (hl) delete_handle (hl); unlock_debug (); return TRUE; } /* Close a known handle. Complain if !force and closing a known handle or if the name of the handle being closed does not match the registered name. */ BOOL __stdcall close_handle (const char *func, int ln, HANDLE h, const char *name, BOOL force) { BOOL ret; lock_debug (); if (!mark_closed (func, ln, h, name, force)) return FALSE; ret = CloseHandle (h); unlock_debug (); #if 0 /* Uncomment to see CloseHandle failures */ if (!ret) small_printf ("CloseHandle(%s) failed %s:%d\n", name, func, ln); #endif return ret; } /* Add a handle to the linked list of known handles. */ int __stdcall __set_errno (const char *func, int ln, int val) { debug_printf ("%s:%d val %d", func, ln, val); return _impure_ptr->_errno = val; } #endif /*DEBUGGING*/