mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-01-15 19:09:58 +08:00
5d97040501
* child_info.h (CURR_CHILD_INFO_MAGIC): Reset. (child_info::dwProcessId): Delete. (child_info::straced): New variable. (child_info::handle_fork): New member function. * dcrt0.cc (in_forkee): New global variable. (__cygwin_user_data::forkee): Mark as obsolete. (do_global_ctors): Use in_forkee rather than user_data->forkee. (get_cygwin_startup_info): Ditto. Deal with new straced field to allow strace to deal with children of attached processes. (initial_env): Accommodate changes to strace::hello. (child_info_fork::handle_fork): Rename from plain old 'handle_fork'. Move alloc_stack() call elsewhere. (dll_crt0_0): Fill out more of user_data. Reference handle_fork via fork_info. Add some debugging output. (_dll_crt0): Don't wait for sync thread if sync_startup is invalid. Zero sync_startup here. Call alloc_stack() here, if appropriate. (dll_crt0_1): Use in_forkee rather than user_data->forkee. (dll_crt0): Ditto. * malloc_wrapper.cc (malloc_init): Ditto. * dll_init.cc (in_forkee): Remove local static version of this variable. (dll_list::load_after_fork): Don't set in_forkee here. * external.cc (cygwin_internal): Use strace method rather than accessing field directly. * fhandler.cc (fhandler_base::read): Ditto. * fhandler_tty.cc (fhandler_tty_common::__acquire_output_mutex): Ditto. * fork.cc (frok::parent): Invoke strace write_childpid to communicate with potential strace. (child_copy): Add more detail to debugging output. * init.cc (calibration_id): New static variable. (prime_threads): Set sync_startup to invalid handle if we already know about thread_func_ix. Use static calibration_id to hold calibration thread id. * munge_threadfunc (munge_threadfunc): Don't try to debug if we don't find threadfunc_ix. (dll_entry): Avoid calling munge_threadfunc and _cygtls::remove on non-cygwin threads invoked during process startup. * pinfo.cc (set_myself): Always call strace.hello here regardless of DEBUGGING. * sigproc.cc (child_info::child_info): Remove spurious handling of dwProcessId. Set straced as appropriate. * spawn.cc (spawn_guts): Rename ciresrv to ch. Invoke strace write_childpid to communicate with potential strace. * strace.cc: Include child_info.h. (strace::hello): Remove inited test. Use active() method to test if strace has been activated. Handle case where we are started before (mypid): New function. (strace::vsprntf): Try to deal more intelligently with case where progname may not be filled out. Put pid in parentheses if it is a windows pid rather than a cygwin pid. myself has been filled out. (strace::write_childpid): New function for notifying strace about the creation of children. (strace::vprntf): Use strace method rather than accessing field directly. (strace_printf): Ditto. (strace::wm): Ditto. * winsup.h (in_forkee): Declare. * include/sys/strace.h (strace::write_childpid): Declare new function. (strace::attached): Define new function. (strace::active): Ditto. (strace::active_val): Ditto. (_STRACE_ON): Delete. (_STRACE_OFF): Ditto. (define_strace0): Use strace method rather than accessing field directly. (strace_printf_wrap): Ditto. (strace_printf_wrap1): Ditto. *** cygwin utils changes: * strace.cc (nprocesses): Make static global. (quiet): New variable. (strace_active): Ditto. (add_child): Increment nprocesses here. Don't add a child if it is already added (windows bug?). Report on child if not quiet. (get_child): Just return NULL if child not found. (remove_child): Report on child if not quiet. (attach_process): Don't complain if given a windows process. Use windows pid in error. (handle_output_debug_string): Issue error if trying to manipulate a process that we don't know about. Handle _STRACE_CHILD_PID - attach to reported child when we get this. (proc_child): Move nprocesses to file scope. Report on exceptions. (longopts): Implement "--quiet". (opts): Implement "-q". (main): Manipulate quiet flag. * utils.sgml (strace): Add words describing '-q'.
271 lines
6.4 KiB
C++
271 lines
6.4 KiB
C++
/* cygtls.cc
|
|
|
|
Copyright 2003, 2004, 2005 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 <sys/time.h>
|
|
#define USE_SYS_TYPES_FD_SET
|
|
#include <winsock.h>
|
|
#include "thread.h"
|
|
#include "cygtls.h"
|
|
#include "assert.h"
|
|
#include <syslog.h>
|
|
#include <signal.h>
|
|
#include <malloc.h>
|
|
#include "exceptions.h"
|
|
#include "sync.h"
|
|
#include "cygerrno.h"
|
|
#include "path.h"
|
|
#include "fhandler.h"
|
|
#include "dtable.h"
|
|
#include "cygheap.h"
|
|
#include "pinfo.h"
|
|
#include "sigproc.h"
|
|
|
|
class sentry
|
|
{
|
|
static muto lock;
|
|
int destroy;
|
|
public:
|
|
void init ();
|
|
bool acquired () {return lock.acquired ();}
|
|
sentry () {destroy = 0;}
|
|
sentry (DWORD wait) {destroy = lock.acquire (wait);}
|
|
~sentry () {if (destroy) lock.release ();}
|
|
friend void _cygtls::init ();
|
|
};
|
|
|
|
muto NO_COPY sentry::lock;
|
|
|
|
static size_t NO_COPY nthreads;
|
|
|
|
#define THREADLIST_CHUNK 256
|
|
|
|
void
|
|
_cygtls::init ()
|
|
{
|
|
if (cygheap->threadlist)
|
|
memset (cygheap->threadlist, 0, cygheap->sthreads * sizeof (cygheap->threadlist[0]));
|
|
else
|
|
{
|
|
cygheap->sthreads = THREADLIST_CHUNK;
|
|
cygheap->threadlist = (_cygtls **) ccalloc (HEAP_TLS, cygheap->sthreads,
|
|
sizeof (cygheap->threadlist[0]));
|
|
}
|
|
sentry::lock.init ("sentry_lock");
|
|
}
|
|
|
|
/* Two calls to get the stack right... */
|
|
void
|
|
_cygtls::call (DWORD (*func) (void *, void *), void *arg)
|
|
{
|
|
char buf[CYGTLS_PADSIZE];
|
|
call2 (func, arg, buf);
|
|
}
|
|
|
|
void
|
|
_cygtls::call2 (DWORD (*func) (void *, void *), void *arg, void *buf)
|
|
{
|
|
_my_tls.init_thread (buf, func);
|
|
DWORD res = func (arg, buf);
|
|
_my_tls.remove (INFINITE);
|
|
ExitThread (res);
|
|
}
|
|
|
|
void
|
|
_cygtls::init_thread (void *x, DWORD (*func) (void *, void *))
|
|
{
|
|
if (x)
|
|
{
|
|
memset (this, 0, CYGTLS_PADSIZE);
|
|
stackptr = stack;
|
|
if (_GLOBAL_REENT)
|
|
{
|
|
local_clib._stdin = _GLOBAL_REENT->_stdin;
|
|
local_clib._stdout = _GLOBAL_REENT->_stdout;
|
|
local_clib._stderr = _GLOBAL_REENT->_stderr;
|
|
local_clib.__sdidinit = _GLOBAL_REENT->__sdidinit ? -1 : 0;
|
|
local_clib.__cleanup = _GLOBAL_REENT->__cleanup;
|
|
local_clib.__sglue._niobs = 3;
|
|
local_clib.__sglue._iobs = &_GLOBAL_REENT->__sf[0];
|
|
}
|
|
local_clib._current_locale = "C";
|
|
locals.process_logmask = LOG_UPTO (LOG_DEBUG);
|
|
/* Initialize this thread's ability to respond to things like
|
|
SIGSEGV or SIGFPE. */
|
|
init_exception_handler (handle_exceptions);
|
|
}
|
|
|
|
initialized = CYGTLS_INITIALIZED;
|
|
locals.exitsock = INVALID_SOCKET;
|
|
errno_addr = &(local_clib._errno);
|
|
|
|
if ((void *) func == (void *) cygthread::stub
|
|
|| (void *) func == (void *) cygthread::simplestub)
|
|
return;
|
|
|
|
if (wincap.has_security ())
|
|
cygheap->user.reimpersonate ();
|
|
|
|
sentry here (INFINITE);
|
|
if (nthreads >= cygheap->sthreads)
|
|
{
|
|
cygheap->threadlist = (_cygtls **)
|
|
crealloc (cygheap->threadlist, (cygheap->sthreads += THREADLIST_CHUNK)
|
|
* sizeof (cygheap->threadlist[0]));
|
|
memset (cygheap->threadlist + nthreads, 0, THREADLIST_CHUNK * sizeof (cygheap->threadlist[0]));
|
|
}
|
|
|
|
cygheap->threadlist[nthreads++] = this;
|
|
}
|
|
|
|
void
|
|
_cygtls::fixup_after_fork ()
|
|
{
|
|
if (sig)
|
|
{
|
|
pop ();
|
|
sig = 0;
|
|
}
|
|
stacklock = spinning = 0;
|
|
locals.exitsock = INVALID_SOCKET;
|
|
wq.thread_ev = NULL;
|
|
}
|
|
|
|
#define free_local(x) \
|
|
if (locals.x) \
|
|
{ \
|
|
free (locals.x); \
|
|
locals.x = NULL; \
|
|
}
|
|
|
|
void
|
|
_cygtls::remove (DWORD wait)
|
|
{
|
|
debug_printf ("wait %p", wait);
|
|
if (1 || !isinitialized () || !locals.exitsock || exit_state >= ES_FINAL)
|
|
return;
|
|
if (wait)
|
|
{
|
|
/* FIXME: Need some sort of atthreadexit function to allow things like
|
|
select to control this themselves. */
|
|
if (locals.exitsock != INVALID_SOCKET)
|
|
{
|
|
closesocket (locals.exitsock);
|
|
locals.exitsock = (SOCKET) NULL;
|
|
}
|
|
free_local (process_ident);
|
|
free_local (ntoa_buf);
|
|
free_local (protoent_buf);
|
|
free_local (servent_buf);
|
|
free_local (hostent_buf);
|
|
}
|
|
|
|
do
|
|
{
|
|
sentry here (wait);
|
|
if (here.acquired ())
|
|
{
|
|
for (size_t i = 0; i < nthreads; i++)
|
|
if (this == cygheap->threadlist[i])
|
|
{
|
|
if (i < --nthreads)
|
|
cygheap->threadlist[i] = cygheap->threadlist[nthreads];
|
|
debug_printf ("removed %p element %d", this, i);
|
|
break;
|
|
}
|
|
}
|
|
} while (0);
|
|
remove_wq (wait);
|
|
}
|
|
|
|
void
|
|
_cygtls::push (__stack_t addr)
|
|
{
|
|
*stackptr++ = (__stack_t) addr;
|
|
}
|
|
|
|
#define BAD_IX ((size_t) -1)
|
|
static size_t NO_COPY threadlist_ix = BAD_IX;
|
|
|
|
_cygtls *
|
|
_cygtls::find_tls (int sig)
|
|
{
|
|
debug_printf ("sig %d\n", sig);
|
|
sentry here (INFINITE);
|
|
__asm__ volatile (".equ _threadlist_exception_return,.");
|
|
_cygtls *res = NULL;
|
|
for (threadlist_ix = 0; threadlist_ix < nthreads; threadlist_ix++)
|
|
if (sigismember (&(cygheap->threadlist[threadlist_ix]->sigwait_mask), sig))
|
|
{
|
|
res = cygheap->threadlist[threadlist_ix];
|
|
break;
|
|
}
|
|
threadlist_ix = BAD_IX;
|
|
return res;
|
|
}
|
|
|
|
void
|
|
_cygtls::set_siginfo (sigpacket *pack)
|
|
{
|
|
infodata = pack->si;
|
|
}
|
|
|
|
extern "C" DWORD __stdcall RtlUnwind (void *, void *, void *, DWORD);
|
|
int
|
|
_cygtls::handle_threadlist_exception (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *c, void *)
|
|
{
|
|
if (e->ExceptionCode != STATUS_ACCESS_VIOLATION)
|
|
{
|
|
system_printf ("unhandled exception %p at %p", e->ExceptionCode, c->Eip);
|
|
return 1;
|
|
}
|
|
|
|
sentry here;
|
|
if (threadlist_ix == BAD_IX)
|
|
{
|
|
api_fatal ("called with threadlist_ix %d", BAD_IX);
|
|
return 1;
|
|
}
|
|
|
|
if (!here.acquired ())
|
|
{
|
|
system_printf ("couldn't aquire muto");
|
|
return 1;
|
|
}
|
|
|
|
extern void *threadlist_exception_return;
|
|
cygheap->threadlist[threadlist_ix]->remove (INFINITE);
|
|
threadlist_ix = 0;
|
|
RtlUnwind (frame, threadlist_exception_return, e, 0);
|
|
return 0;
|
|
}
|
|
|
|
/* Set up the exception handler for the current thread. The PowerPC & Mips
|
|
use compiler generated tables to set up the exception handlers for each
|
|
region of code, and the kernel walks the call list until it finds a region
|
|
of code that handles exceptions. The x86 on the other hand uses segment
|
|
register fs, offset 0 to point to the current exception handler. */
|
|
|
|
extern exception_list *_except_list asm ("%fs:0");
|
|
|
|
void
|
|
_cygtls::init_exception_handler (exception_handler *eh)
|
|
{
|
|
el.handler = eh;
|
|
el.prev = _except_list;
|
|
if (!el.prev->prev && !el.prev->handler)
|
|
el.prev = ⪙
|
|
_except_list = ⪙
|
|
}
|
|
|
|
void
|
|
_cygtls::init_threadlist_exceptions ()
|
|
{
|
|
init_exception_handler (handle_threadlist_exception);
|
|
}
|