itlibthread: add pthreadperthread mode and use under ASAN - plan9port - [fork] Plan 9 from user space Err mx1.adamsgaard.dk 70 hgit clone git://src.adamsgaard.dk/plan9port URL:git://src.adamsgaard.dk/plan9port mx1.adamsgaard.dk 70 1Log /src/plan9port/log.gph mx1.adamsgaard.dk 70 1Files /src/plan9port/files.gph mx1.adamsgaard.dk 70 1Refs /src/plan9port/refs.gph mx1.adamsgaard.dk 70 1README /src/plan9port/file/README.md.gph mx1.adamsgaard.dk 70 1LICENSE /src/plan9port/file/LICENSE.gph mx1.adamsgaard.dk 70 i--- Err mx1.adamsgaard.dk 70 1commit baef953da253314657be9adea8f371bfbf4ba09e /src/plan9port/commit/baef953da253314657be9adea8f371bfbf4ba09e.gph mx1.adamsgaard.dk 70 1parent 06687f70ba7a5836c2e872648a85a724a5a1d486 /src/plan9port/commit/06687f70ba7a5836c2e872648a85a724a5a1d486.gph mx1.adamsgaard.dk 70 hAuthor: Russ Cox URL:mailto:rsc@swtch.com mx1.adamsgaard.dk 70 iDate: Sun, 17 May 2020 08:24:54 -0400 Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 ilibthread: add pthreadperthread mode and use under ASAN Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 iASAN can't deal with the coroutine stacks. Err mx1.adamsgaard.dk 70 iIn theory we can call into ASAN runtime to let it know about them, Err mx1.adamsgaard.dk 70 ibut ASAN still has problems with fork or exit happening from a Err mx1.adamsgaard.dk 70 inon-system stack. Bypass all possible problems by just having Err mx1.adamsgaard.dk 70 ia full OS thread for each libthread thread. The threads are still Err mx1.adamsgaard.dk 70 icooperatively scheduled within a proc (in thos mode, a group of OS threads). Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 iSetting the environment variable LIBTHREAD=pthreadperthread Err mx1.adamsgaard.dk 70 iwill enable the pthreadperthread mode, as will building with Err mx1.adamsgaard.dk 70 iCC9FLAGS='-fsanitize=address' in $PLAN9/config. Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 iThis solution is much more general than ASAN - for example if Err mx1.adamsgaard.dk 70 iyou are trying to find all the thread stacks in a reproducible crash Err mx1.adamsgaard.dk 70 iyou can use pthreadperthread mode with any debugger that Err mx1.adamsgaard.dk 70 iknows only about OS threads. Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 iDiffstat: Err mx1.adamsgaard.dk 70 i M src/libthread/channel.c | 2 +- Err mx1.adamsgaard.dk 70 i M src/libthread/pthread.c | 33 +++++++++++++++++++++++++++++++ Err mx1.adamsgaard.dk 70 i M src/libthread/thread.c | 70 ++++++++++++++++++++++++++++--- Err mx1.adamsgaard.dk 70 i M src/libthread/threadimpl.h | 33 +++++++++++++++++++++----------- Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i4 files changed, 120 insertions(+), 18 deletions(-) Err mx1.adamsgaard.dk 70 i--- Err mx1.adamsgaard.dk 70 1diff --git a/src/libthread/channel.c b/src/libthread/channel.c /src/plan9port/file/src/libthread/channel.c.gph mx1.adamsgaard.dk 70 it@@ -141,7 +141,7 @@ altdequeue(Alt *a) Err mx1.adamsgaard.dk 70 i delarray(ar, i); Err mx1.adamsgaard.dk 70 i return; Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i- fprint(2, "cannot find self in altdq\n"); Err mx1.adamsgaard.dk 70 i+ fprint(2, "cannot find self in altdequeue\n"); Err mx1.adamsgaard.dk 70 i abort(); Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 1diff --git a/src/libthread/pthread.c b/src/libthread/pthread.c /src/plan9port/file/src/libthread/pthread.c.gph mx1.adamsgaard.dk 70 it@@ -99,6 +99,23 @@ startprocfn(void *v) Err mx1.adamsgaard.dk 70 i pthread_exit(0); Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i+static void Err mx1.adamsgaard.dk 70 i+startpthreadfn(void *v) Err mx1.adamsgaard.dk 70 i+{ Err mx1.adamsgaard.dk 70 i+ void **a; Err mx1.adamsgaard.dk 70 i+ Proc *p; Err mx1.adamsgaard.dk 70 i+ _Thread *t; Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i+ a = (void**)v; Err mx1.adamsgaard.dk 70 i+ p = a[0]; Err mx1.adamsgaard.dk 70 i+ t = a[1]; Err mx1.adamsgaard.dk 70 i+ free(a); Err mx1.adamsgaard.dk 70 i+ t->osprocid = pthread_self(); Err mx1.adamsgaard.dk 70 i+ pthread_detach(t->osprocid); Err mx1.adamsgaard.dk 70 i+ _threadpthreadmain(p, t); Err mx1.adamsgaard.dk 70 i+ pthread_exit(0); Err mx1.adamsgaard.dk 70 i+} Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i void Err mx1.adamsgaard.dk 70 i _procstart(Proc *p, void (*fn)(Proc*)) Err mx1.adamsgaard.dk 70 i { Err mx1.adamsgaard.dk 70 it@@ -116,6 +133,22 @@ _procstart(Proc *p, void (*fn)(Proc*)) Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i+void Err mx1.adamsgaard.dk 70 i+_threadpthreadstart(Proc *p, _Thread *t) Err mx1.adamsgaard.dk 70 i+{ Err mx1.adamsgaard.dk 70 i+ void **a; Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i+ a = malloc(3*sizeof a[0]); Err mx1.adamsgaard.dk 70 i+ if(a == nil) Err mx1.adamsgaard.dk 70 i+ sysfatal("_pthreadstart malloc: %r"); Err mx1.adamsgaard.dk 70 i+ a[0] = p; Err mx1.adamsgaard.dk 70 i+ a[1] = t; Err mx1.adamsgaard.dk 70 i+ if(pthread_create(&t->osprocid, nil, (void*(*)(void*))startpthreadfn, (void*)a) < 0){ Err mx1.adamsgaard.dk 70 i+ fprint(2, "pthread_create: %r\n"); Err mx1.adamsgaard.dk 70 i+ abort(); Err mx1.adamsgaard.dk 70 i+ } Err mx1.adamsgaard.dk 70 i+} Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i static pthread_key_t prockey; Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i Proc* Err mx1.adamsgaard.dk 70 1diff --git a/src/libthread/thread.c b/src/libthread/thread.c /src/plan9port/file/src/libthread/thread.c.gph mx1.adamsgaard.dk 70 it@@ -7,6 +7,7 @@ static uint threadnsysproc; Err mx1.adamsgaard.dk 70 i static Lock threadnproclock; Err mx1.adamsgaard.dk 70 i static Ref threadidref; Err mx1.adamsgaard.dk 70 i static Proc *threadmainproc; Err mx1.adamsgaard.dk 70 i+static int pthreadperthread; Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i static void addproc(Proc*); Err mx1.adamsgaard.dk 70 i static void delproc(Proc*); Err mx1.adamsgaard.dk 70 it@@ -176,12 +177,16 @@ _threadcreate(Proc *p, void (*fn)(void*), void *arg, uint stack) Err mx1.adamsgaard.dk 70 i if(stack < (256<<10)) Err mx1.adamsgaard.dk 70 i stack = 256<<10; Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i- if(p->nthread == 0) Err mx1.adamsgaard.dk 70 i+ if(p->nthread == 0 || pthreadperthread) Err mx1.adamsgaard.dk 70 i stack = 0; // not using it Err mx1.adamsgaard.dk 70 i t = threadalloc(fn, arg, stack); Err mx1.adamsgaard.dk 70 i t->proc = p; Err mx1.adamsgaard.dk 70 i- addthreadinproc(p, t); Err mx1.adamsgaard.dk 70 i+ if(p->nthread == 0) Err mx1.adamsgaard.dk 70 i+ p->thread0 = t; Err mx1.adamsgaard.dk 70 i+ else if(pthreadperthread) Err mx1.adamsgaard.dk 70 i+ _threadpthreadstart(p, t); Err mx1.adamsgaard.dk 70 i p->nthread++; Err mx1.adamsgaard.dk 70 i+ addthreadinproc(p, t); Err mx1.adamsgaard.dk 70 i _threadready(t); Err mx1.adamsgaard.dk 70 i return t; Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 it@@ -209,6 +214,29 @@ proccreate(void (*fn)(void*), void *arg, uint stack) Err mx1.adamsgaard.dk 70 i return id; Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i+// For pthreadperthread mode, procswitch flips Err mx1.adamsgaard.dk 70 i+// between the threads. Err mx1.adamsgaard.dk 70 i+static void Err mx1.adamsgaard.dk 70 i+procswitch(Proc *p, _Thread *from, _Thread *to) Err mx1.adamsgaard.dk 70 i+{ Err mx1.adamsgaard.dk 70 i+// fprint(2, "procswitch %p %d %d\n", p, from?from->id:-1, to?to->id:-1); Err mx1.adamsgaard.dk 70 i+ lock(&p->schedlock); Err mx1.adamsgaard.dk 70 i+ from->schedrend.l = &p->schedlock; Err mx1.adamsgaard.dk 70 i+ if(to) { Err mx1.adamsgaard.dk 70 i+ p->schedthread = to; Err mx1.adamsgaard.dk 70 i+ to->schedrend.l = &p->schedlock; Err mx1.adamsgaard.dk 70 i+ _procwakeup(&to->schedrend); Err mx1.adamsgaard.dk 70 i+ } Err mx1.adamsgaard.dk 70 i+ if(p->schedthread != from) { Err mx1.adamsgaard.dk 70 i+ if(from->exiting) { Err mx1.adamsgaard.dk 70 i+ unlock(&p->schedlock); Err mx1.adamsgaard.dk 70 i+ _threadpexit(); Err mx1.adamsgaard.dk 70 i+ } Err mx1.adamsgaard.dk 70 i+ _procsleep(&from->schedrend); Err mx1.adamsgaard.dk 70 i+ } Err mx1.adamsgaard.dk 70 i+ unlock(&p->schedlock); Err mx1.adamsgaard.dk 70 i+} Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i void Err mx1.adamsgaard.dk 70 i _threadswitch(void) Err mx1.adamsgaard.dk 70 i { Err mx1.adamsgaard.dk 70 it@@ -216,9 +244,13 @@ _threadswitch(void) Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i needstack(0); Err mx1.adamsgaard.dk 70 i p = proc(); Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i /*print("threadswtch %p\n", p); */ Err mx1.adamsgaard.dk 70 i- if(p->thread->stk == nil) Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i+ if(p->thread == p->thread0) Err mx1.adamsgaard.dk 70 i procscheduler(p); Err mx1.adamsgaard.dk 70 i+ else if(pthreadperthread) Err mx1.adamsgaard.dk 70 i+ procswitch(p, p->thread, p->thread0); Err mx1.adamsgaard.dk 70 i else Err mx1.adamsgaard.dk 70 i contextswitch(&p->thread->context, &p->schedcontext); Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 it@@ -346,6 +378,15 @@ procmain(Proc *p) Err mx1.adamsgaard.dk 70 i threadexits(nil); Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i+void Err mx1.adamsgaard.dk 70 i+_threadpthreadmain(Proc *p, _Thread *t) Err mx1.adamsgaard.dk 70 i+{ Err mx1.adamsgaard.dk 70 i+ _threadsetproc(p); Err mx1.adamsgaard.dk 70 i+ procswitch(p, t, nil); Err mx1.adamsgaard.dk 70 i+ t->startfn(t->startarg); Err mx1.adamsgaard.dk 70 i+ threadexits(nil); Err mx1.adamsgaard.dk 70 i+} Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i static void Err mx1.adamsgaard.dk 70 i procscheduler(Proc *p) Err mx1.adamsgaard.dk 70 i { Err mx1.adamsgaard.dk 70 it@@ -401,9 +442,12 @@ Top: Err mx1.adamsgaard.dk 70 i p->nswitch++; Err mx1.adamsgaard.dk 70 i _threaddebug("run %d (%s)", t->id, t->name); Err mx1.adamsgaard.dk 70 i //print("run %p %p %p %p\n", t, *(uintptr*)(t->context.uc.mc.sp), t->context.uc.mc.di, t->context.uc.mc.si); Err mx1.adamsgaard.dk 70 i- if(t->stk == nil) Err mx1.adamsgaard.dk 70 i+ if(t == p->thread0) Err mx1.adamsgaard.dk 70 i return; Err mx1.adamsgaard.dk 70 i- contextswitch(&p->schedcontext, &t->context); Err mx1.adamsgaard.dk 70 i+ if(pthreadperthread) Err mx1.adamsgaard.dk 70 i+ procswitch(p, p->thread0, t); Err mx1.adamsgaard.dk 70 i+ else Err mx1.adamsgaard.dk 70 i+ contextswitch(&p->schedcontext, &t->context); Err mx1.adamsgaard.dk 70 i /*print("back in scheduler\n"); */ Err mx1.adamsgaard.dk 70 i goto Top; Err mx1.adamsgaard.dk 70 i } Err mx1.adamsgaard.dk 70 it@@ -757,10 +801,24 @@ int Err mx1.adamsgaard.dk 70 i main(int argc, char **argv) Err mx1.adamsgaard.dk 70 i { Err mx1.adamsgaard.dk 70 i Proc *p; Err mx1.adamsgaard.dk 70 i+ char *opts; Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i argv0 = argv[0]; Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i- if(getenv("NOLIBTHREADDAEMONIZE") == nil) Err mx1.adamsgaard.dk 70 i+ opts = getenv("LIBTHREAD"); Err mx1.adamsgaard.dk 70 i+ if(opts == nil) Err mx1.adamsgaard.dk 70 i+ opts = ""; Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i+ pthreadperthread = (strstr(opts, "pthreadperthread") != nil); Err mx1.adamsgaard.dk 70 i+#ifdef PLAN9PORT_ASAN Err mx1.adamsgaard.dk 70 i+ // ASAN can't deal with the coroutine stack switches. Err mx1.adamsgaard.dk 70 i+ // In theory it has support for informing it about stack switches, Err mx1.adamsgaard.dk 70 i+ // but even with those calls added it can't deal with things Err mx1.adamsgaard.dk 70 i+ // like fork or exit from a coroutine stack. Err mx1.adamsgaard.dk 70 i+ // Easier to just run in pthread-per-thread mode. Err mx1.adamsgaard.dk 70 i+ pthreadperthread = 1; Err mx1.adamsgaard.dk 70 i+#endif Err mx1.adamsgaard.dk 70 i+ if(strstr(opts, "nodaemon") || getenv("NOLIBTHREADDAEMONIZE") == nil) Err mx1.adamsgaard.dk 70 i _threadsetupdaemonize(); Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i threadargc = argc; Err mx1.adamsgaard.dk 70 1diff --git a/src/libthread/threadimpl.h b/src/libthread/threadimpl.h /src/plan9port/file/src/libthread/threadimpl.h.gph mx1.adamsgaard.dk 70 it@@ -102,6 +102,17 @@ struct Execjob Err mx1.adamsgaard.dk 70 i Channel *c; Err mx1.adamsgaard.dk 70 i }; Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i+struct _Procrendez Err mx1.adamsgaard.dk 70 i+{ Err mx1.adamsgaard.dk 70 i+ Lock *l; Err mx1.adamsgaard.dk 70 i+ int asleep; Err mx1.adamsgaard.dk 70 i+#ifdef PLAN9PORT_USING_PTHREADS Err mx1.adamsgaard.dk 70 i+ pthread_cond_t cond; Err mx1.adamsgaard.dk 70 i+#else Err mx1.adamsgaard.dk 70 i+ int pid; Err mx1.adamsgaard.dk 70 i+#endif Err mx1.adamsgaard.dk 70 i+}; Err mx1.adamsgaard.dk 70 i+ Err mx1.adamsgaard.dk 70 i struct _Thread Err mx1.adamsgaard.dk 70 i { Err mx1.adamsgaard.dk 70 i _Thread *next; Err mx1.adamsgaard.dk 70 it@@ -112,6 +123,11 @@ struct _Thread Err mx1.adamsgaard.dk 70 i void (*startfn)(void*); Err mx1.adamsgaard.dk 70 i void *startarg; Err mx1.adamsgaard.dk 70 i uint id; Err mx1.adamsgaard.dk 70 i+#ifdef PLAN9PORT_USING_PTHREADS Err mx1.adamsgaard.dk 70 i+ pthread_t osprocid; Err mx1.adamsgaard.dk 70 i+#else Err mx1.adamsgaard.dk 70 i+ int osprocid; Err mx1.adamsgaard.dk 70 i+#endif Err mx1.adamsgaard.dk 70 i uchar *stk; Err mx1.adamsgaard.dk 70 i uint stksize; Err mx1.adamsgaard.dk 70 i int exiting; Err mx1.adamsgaard.dk 70 it@@ -120,17 +136,7 @@ struct _Thread Err mx1.adamsgaard.dk 70 i char state[256]; Err mx1.adamsgaard.dk 70 i void *udata; Err mx1.adamsgaard.dk 70 i Alt *alt; Err mx1.adamsgaard.dk 70 i-}; Err mx1.adamsgaard.dk 70 i- Err mx1.adamsgaard.dk 70 i-struct _Procrendez Err mx1.adamsgaard.dk 70 i-{ Err mx1.adamsgaard.dk 70 i- Lock *l; Err mx1.adamsgaard.dk 70 i- int asleep; Err mx1.adamsgaard.dk 70 i-#ifdef PLAN9PORT_USING_PTHREADS Err mx1.adamsgaard.dk 70 i- pthread_cond_t cond; Err mx1.adamsgaard.dk 70 i-#else Err mx1.adamsgaard.dk 70 i- int pid; Err mx1.adamsgaard.dk 70 i-#endif Err mx1.adamsgaard.dk 70 i+ _Procrendez schedrend; Err mx1.adamsgaard.dk 70 i }; Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i extern void _procsleep(_Procrendez*); Err mx1.adamsgaard.dk 70 it@@ -149,6 +155,7 @@ struct Proc Err mx1.adamsgaard.dk 70 i #endif Err mx1.adamsgaard.dk 70 i Lock lock; Err mx1.adamsgaard.dk 70 i int nswitch; Err mx1.adamsgaard.dk 70 i+ _Thread *thread0; Err mx1.adamsgaard.dk 70 i _Thread *thread; Err mx1.adamsgaard.dk 70 i _Thread *pinthread; Err mx1.adamsgaard.dk 70 i _Threadlist runqueue; Err mx1.adamsgaard.dk 70 it@@ -157,6 +164,8 @@ struct Proc Err mx1.adamsgaard.dk 70 i uint nthread; Err mx1.adamsgaard.dk 70 i uint sysproc; Err mx1.adamsgaard.dk 70 i _Procrendez runrend; Err mx1.adamsgaard.dk 70 i+ Lock schedlock; Err mx1.adamsgaard.dk 70 i+ _Thread *schedthread; Err mx1.adamsgaard.dk 70 i Context schedcontext; Err mx1.adamsgaard.dk 70 i void *udata; Err mx1.adamsgaard.dk 70 i Jmp sigjmp; Err mx1.adamsgaard.dk 70 it@@ -188,6 +197,8 @@ extern void _threadpexit(void); Err mx1.adamsgaard.dk 70 i extern void _threaddaemonize(void); Err mx1.adamsgaard.dk 70 i extern void *_threadstkalloc(int); Err mx1.adamsgaard.dk 70 i extern void _threadstkfree(void*, int); Err mx1.adamsgaard.dk 70 i+extern void _threadpthreadmain(Proc*, _Thread*); Err mx1.adamsgaard.dk 70 i+extern void _threadpthreadstart(Proc*, _Thread*); Err mx1.adamsgaard.dk 70 i Err mx1.adamsgaard.dk 70 i #define USPALIGN(ucp, align) \ Err mx1.adamsgaard.dk 70 i (void*)((((uintptr)(ucp)->uc_stack.ss_sp+(ucp)->uc_stack.ss_size)-(align))&~((align)-1)) Err mx1.adamsgaard.dk 70 .