functionality. * cygheap.cc (init_cheap): Move cygheap_max calculation to _csbrk. (_csbrk): Reorganize to not assume first allocation is <= 1 page. (cygheap_setup_for_child): Mark protected handle as inheritable. * cygheap.h (cygheap_debug): New struct. (init_cygheap): Add new structure when debugging. * dcrt0.cc (dll_crt0_1): Remove call to debug_init. Close ppid_handle here, if appropriate. Don't protect subproc_ready, since it is already protected in the parent. Call memory_init prior to ProtectHandle to ensure that cygheap is set up. Call debug_fixup_after_fork_exec when appropriate. (_dll_crt0): Don't close ppid_handle here. * debug.cc: Use cygheap debug structure rather than static elements throughout. (add_handle): Don't issue a warning if attempt to protect handle in exactly the same way from exactly the same place. Add pid info to warning output. Accept additional argument controlling whether handle is to be inherited. Add pid to stored information. (debug_fixup_after_fork_exec): Renamed from debug_fixup_after_fork. Reorganize to avoid erroneously skipping handles. (mark_closed): Add pid info to warning output. (setclexec): Rename from setclexec_pid. * fhandler.cc (fhandler_base::get_default_fmode): Minor reorg. (fhandler_base::fstat): Add debugging output. (fhandler_base::set_inheritance): Call setclexec rather than setclexec_pid. (fhandler_base::fork_fixup): Ditto. * fhandler_console.cc (get_tty_stuff): Mark protected handle as inheritable. * fhandler_tty.cc (fhandler_tty_slave::open): Ditto. * tty.cc (tty::make_pipes): Ditto. (tty::common_init): Ditto. * fork.cc (fork_parent): Ditto. (fork_child): Close protected handles with correct name. Remove debug_fixup_after_fork call. * fhandler_socket.cc (fhandler_socket::create_secret_event): Mark protected handle as inheritable/non-inheritable, as appropriate. * shared.cc (memory_init): Mark protected handle as inheritable. Call debug_init here. * sigproc.cc (wait_sig): Close protected handle with correct name. * spawn.cc (spawn_guts): Rename spr to subproc_ready and mark it as inheritable. * exceptions.cc (debugger_command): Try to run dumper.exe, if found. * syscalls.cc (fstat64): Don't follow symlinks for path_conv lookup since path is already resolved.
382 lines
8.5 KiB
C++
382 lines
8.5 KiB
C++
/* debug.cc
|
|
|
|
Copyright 1998, 1999, 2000, 2001, 2002 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"
|
|
#ifdef DEBUGGING
|
|
#include <errno.h>
|
|
#include "fhandler.h"
|
|
#include "path.h"
|
|
#include "dtable.h"
|
|
#include "cygheap.h"
|
|
#endif
|
|
|
|
#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]; // increase as necessary
|
|
#define NTHREADS (sizeof (threads) / sizeof (threads[0]))
|
|
|
|
void
|
|
threadname_init ()
|
|
{
|
|
new_muto (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);
|
|
|
|
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 <stdlib.h>
|
|
#define NFREEH (sizeof (cygheap->debug.freeh) / sizeof (cygheap->debug.freeh[0]))
|
|
|
|
void debug_init ();
|
|
|
|
class lock_debug
|
|
{
|
|
static muto *locker;
|
|
bool acquired;
|
|
public:
|
|
lock_debug () : acquired (0)
|
|
{
|
|
if (locker)
|
|
acquired = !!locker->acquire (INFINITE);
|
|
}
|
|
void unlock ()
|
|
{
|
|
if (locker && acquired)
|
|
{
|
|
locker->release ();
|
|
acquired = false;
|
|
}
|
|
}
|
|
~lock_debug () {unlock ();}
|
|
friend void debug_init ();
|
|
};
|
|
|
|
muto NO_COPY *lock_debug::locker = NULL;
|
|
|
|
static bool __stdcall mark_closed (const char *, int, HANDLE, const char *, BOOL);
|
|
|
|
void
|
|
debug_init ()
|
|
{
|
|
muto *debug_lock_muto;
|
|
lock_debug::locker = new_muto (debug_lock_muto);
|
|
}
|
|
|
|
/* Find a registered handle in the linked list of handles. */
|
|
static handle_list * __stdcall
|
|
find_handle (HANDLE h)
|
|
{
|
|
handle_list *hl;
|
|
for (hl = &cygheap->debug.starth; hl->next != NULL; hl = hl->next)
|
|
if (hl->next->h == h)
|
|
goto out;
|
|
cygheap->debug.endh = hl;
|
|
hl = NULL;
|
|
|
|
out:
|
|
return hl;
|
|
}
|
|
|
|
void
|
|
setclexec (HANDLE oh, HANDLE nh, bool setit)
|
|
{
|
|
handle_list *hl = find_handle (oh);
|
|
if (hl)
|
|
{
|
|
hl->clexec = setit;
|
|
hl->h = nh;
|
|
}
|
|
}
|
|
|
|
/* Create a new handle record */
|
|
static handle_list * __stdcall
|
|
newh ()
|
|
{
|
|
handle_list *hl;
|
|
lock_debug here;
|
|
|
|
for (hl = cygheap->debug.freeh; hl < cygheap->debug.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:
|
|
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, bool inh)
|
|
{
|
|
handle_list *hl;
|
|
lock_debug here;
|
|
|
|
if ((hl = find_handle (h)))
|
|
{
|
|
hl = hl->next;
|
|
if (hl->name == name && hl->func == func && hl->ln == ln)
|
|
return;
|
|
system_printf ("%s:%d - multiple attempts to add handle %s<%p>", func,
|
|
ln, name, h);
|
|
system_printf (" previously allocated by %s:%d(%s<%p>) winpid %d",
|
|
hl->func, hl->ln, hl->name, hl->h, hl->pid);
|
|
return;
|
|
}
|
|
|
|
if ((hl = newh ()) == NULL)
|
|
{
|
|
here.unlock ();
|
|
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;
|
|
hl->clexec = !inh;
|
|
hl->pid = GetCurrentProcessId ();
|
|
cygheap->debug.endh->next = hl;
|
|
cygheap->debug.endh = hl;
|
|
debug_printf ("protecting handle '%s', clexec flag %d", hl->name, hl->clexec);
|
|
|
|
return;
|
|
}
|
|
|
|
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_exec ()
|
|
{
|
|
/* No lock needed at this point */
|
|
handle_list *hl;
|
|
for (hl = &cygheap->debug.starth; hl->next != NULL; /* nothing */)
|
|
if (!hl->next->clexec)
|
|
hl = hl->next;
|
|
else
|
|
{
|
|
debug_printf ("nuking handle '%s'", hl->next->name);
|
|
delete_handle (hl); // removes hl->next
|
|
}
|
|
}
|
|
|
|
static bool __stdcall
|
|
mark_closed (const char *func, int ln, HANDLE h, const char *name, BOOL force)
|
|
{
|
|
handle_list *hl;
|
|
lock_debug here;
|
|
|
|
if ((hl = find_handle (h)) && !force)
|
|
{
|
|
hl = hl->next;
|
|
here.unlock (); // race here
|
|
system_printf ("attempt to close protected handle %s:%d(%s<%p>) winpid %d",
|
|
hl->func, hl->ln, hl->name, hl->h, hl->pid);
|
|
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);
|
|
|
|
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 here;
|
|
|
|
if (!mark_closed (func, ln, h, name, force))
|
|
return FALSE;
|
|
|
|
ret = CloseHandle (h);
|
|
|
|
#if 0 /* Uncomment to see CloseHandle failures */
|
|
if (!ret)
|
|
small_printf ("CloseHandle(%s) failed %s:%d\n", name, func, ln);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
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*/
|