--- winsup/cygwin/cygheap.cc | 12 ++++++ winsup/cygwin/external.cc | 11 ++++++ winsup/cygwin/gmon.c | 81 ++++++++++++++++---------------------- winsup/cygwin/gmon.h | 2 +- winsup/cygwin/include/sys/cygwin.h | 2 + winsup/cygwin/mcount.c | 12 +++--- winsup/cygwin/profil.c | 38 +++++++++++++----- 7 files changed, 94 insertions(+), 64 deletions(-) diff --git a/winsup/cygwin/cygheap.cc b/winsup/cygwin/cygheap.cc index 6493485..4932cf0 100644 --- a/winsup/cygwin/cygheap.cc +++ b/winsup/cygwin/cygheap.cc @@ -744,3 +744,15 @@ init_cygheap::find_tls (int sig, bool& issig_wait) WaitForSingleObject (t->mutex, INFINITE); return t; } + +/* Called from profil.c to sample all non-main thread PC values for profiling */ +extern "C" void +cygheap_profthr_all (void (*profthr_byhandle) (HANDLE)) +{ + for (uint32_t ix = 0; ix < nthreads; ix++) + { + _cygtls *tls = cygheap->threadlist[ix].thread; + if (tls->tid) + profthr_byhandle (tls->tid->win32_obj_id); + } +} diff --git a/winsup/cygwin/external.cc b/winsup/cygwin/external.cc index e379df1..02335eb 100644 --- a/winsup/cygwin/external.cc +++ b/winsup/cygwin/external.cc @@ -702,6 +702,17 @@ cygwin_internal (cygwin_getinfo_types t, ...) } break; + case CW_CYGHEAP_PROFTHR_ALL: + { + typedef void (*func_t) (HANDLE); + extern void cygheap_profthr_all (func_t); + + func_t profthr_byhandle = va_arg(arg, func_t); + cygheap_profthr_all (profthr_byhandle); + res = 0; + } + break; + default: set_errno (ENOSYS); } diff --git a/winsup/cygwin/gmon.c b/winsup/cygwin/gmon.c index 96b1189..553f3b7 100644 --- a/winsup/cygwin/gmon.c +++ b/winsup/cygwin/gmon.c @@ -151,7 +151,6 @@ void _mcleanup (void); void _mcleanup(void) { - static char gmon_out[] = "gmon.out"; int fd; int hz; int fromindex; @@ -161,7 +160,8 @@ _mcleanup(void) struct rawarc rawarc; struct gmonparam *p = &_gmonparam; struct gmonhdr gmonhdr, *hdr; - const char *proffile; + char *filename = (char *) "gmon.out"; + char *prefix; #ifdef DEBUG int log, len; char dbuf[200]; @@ -173,58 +173,43 @@ _mcleanup(void) hz = PROF_HZ; moncontrol(0); -#ifdef nope - if ((profdir = getenv("PROFDIR")) != NULL) { - extern char *__progname; - char *s, *t, *limit; - pid_t pid; - long divisor; - - /* If PROFDIR contains a null value, no profiling - output is produced */ - if (*profdir == '\0') { - return; - } - - limit = buf + sizeof buf - 1 - 10 - 1 - - strlen(__progname) - 1; - t = buf; - s = profdir; - while((*t = *s) != '\0' && t < limit) { - t++; - s++; - } - *t++ = '/'; - - /* - * Copy and convert pid from a pid_t to a string. For - * best performance, divisor should be initialized to - * the largest power of 10 less than PID_MAX. - */ - pid = getpid(); - divisor=10000; - while (divisor > pid) divisor /= 10; /* skip leading zeros */ - do { - *t++ = (pid/divisor) + '0'; + /* We copy an undocumented glibc feature: customizing the profiler's + output file name somewhat, depending on the env var GMON_OUT_PREFIX. + if GMON_OUT_PREFIX is unspecified, the file's name is "gmon.out". + + if GMON_OUT_PREFIX is specified with at least one character, the + file's name is computed as "$GMON_OUT_PREFIX.$pid". + + if GMON_OUT_PREFIX is specified but contains no characters, the + file's name is computed as "gmon.out.$pid". Cygwin-specific. + */ + if ((prefix = getenv("GMON_OUT_PREFIX")) != NULL) { + char *buf; + long divisor = 100000; // the power of 10 bigger than PID_MAX + pid_t pid = getpid(); + size_t len = strlen(prefix); + + if (len == 0) + len = strlen(prefix = filename); + buf = alloca(len + 8); // allows for '.', 5-digit pid, NUL, +1 + + memcpy(buf, prefix, len); + buf[len++] = '.'; + + while (divisor > pid) // skip leading zeroes + divisor /= 10; + do { // convert pid to digits and store 'em + buf[len++] = (pid / divisor) + '0'; pid %= divisor; } while (divisor /= 10); - *t++ = '.'; - - s = __progname; - while ((*t++ = *s++) != '\0') - ; - proffile = buf; - } else { - proffile = gmon_out; + buf[len] = '\0'; + filename = buf; } -#else - proffile = gmon_out; -#endif - fd = open(proffile , O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0666); + fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0666); if (fd < 0) { - perror( proffile ); + perror(filename); return; } #ifdef DEBUG diff --git a/winsup/cygwin/gmon.h b/winsup/cygwin/gmon.h index 0932ed9..b0fb479 100644 --- a/winsup/cygwin/gmon.h +++ b/winsup/cygwin/gmon.h @@ -153,7 +153,7 @@ struct rawarc { * The profiling data structures are housed in this structure. */ struct gmonparam { - int state; + volatile int state; u_short *kcount; size_t kcountsize; u_short *froms; diff --git a/winsup/cygwin/include/sys/cygwin.h b/winsup/cygwin/include/sys/cygwin.h index 5b7da58..8c7128c 100644 --- a/winsup/cygwin/include/sys/cygwin.h +++ b/winsup/cygwin/include/sys/cygwin.h @@ -160,6 +160,7 @@ typedef enum CW_GETNSS_PWD_SRC, CW_GETNSS_GRP_SRC, CW_EXCEPTION_RECORD_FROM_SIGINFO_T, + CW_CYGHEAP_PROFTHR_ALL, } cygwin_getinfo_types; #define CW_LOCK_PINFO CW_LOCK_PINFO @@ -221,6 +222,7 @@ typedef enum #define CW_GETNSS_PWD_SRC CW_GETNSS_PWD_SRC #define CW_GETNSS_GRP_SRC CW_GETNSS_GRP_SRC #define CW_EXCEPTION_RECORD_FROM_SIGINFO_T CW_EXCEPTION_RECORD_FROM_SIGINFO_T +#define CW_CYGHEAP_PROFTHR_ALL CW_CYGHEAP_PROFTHR_ALL /* Token type for CW_SET_EXTERNAL_TOKEN */ enum diff --git a/winsup/cygwin/mcount.c b/winsup/cygwin/mcount.c index fad6728..6111b35 100644 --- a/winsup/cygwin/mcount.c +++ b/winsup/cygwin/mcount.c @@ -41,6 +41,7 @@ static char rcsid[] = "$OpenBSD: mcount.c,v 1.6 1997/07/23 21:11:27 kstailey Exp #endif #include #include "gmon.h" +#include "winsup.h" /* * mcount is called on entry to each function compiled with the profiling @@ -70,11 +71,12 @@ _MCOUNT_DECL (size_t frompc, size_t selfpc) p = &_gmonparam; /* * check that we are profiling - * and that we aren't recursively invoked. + * and that we aren't recursively invoked by this thread + * or entered anew by any other thread. */ - if (p->state != GMON_PROF_ON) + if (InterlockedCompareExchange ( + &p->state, GMON_PROF_BUSY, GMON_PROF_ON) != GMON_PROF_ON) return; - p->state = GMON_PROF_BUSY; /* * check that frompcindex is a reasonable pc value. * for example: signal catchers get called from the stack, @@ -162,10 +164,10 @@ _MCOUNT_DECL (size_t frompc, size_t selfpc) } } done: - p->state = GMON_PROF_ON; + InterlockedExchange (&p->state, GMON_PROF_ON); return; overflow: - p->state = GMON_PROF_ERROR; + InterlockedExchange (&p->state, GMON_PROF_ERROR); return; } diff --git a/winsup/cygwin/profil.c b/winsup/cygwin/profil.c index eb41c08..9f183af 100644 --- a/winsup/cygwin/profil.c +++ b/winsup/cygwin/profil.c @@ -18,6 +18,7 @@ #endif #include #include +#include #include #include #include @@ -65,25 +66,42 @@ print_prof (struct profinfo *p) } #endif -/* Everytime we wake up use the main thread pc to hash into the cell in the - profile buffer ARG. */ +/* Every time we wake up sample the main thread's pc to hash into the cell + in the profile buffer ARG. Then all other pthreads' pc's are sampled. */ -static void CALLBACK profthr_func (LPVOID); +static void +profthr_byhandle (HANDLE thr) +{ + size_t pc; + + // sample the pc of the thread indicated by thr, but bail if anything amiss + if (thr == INVALID_HANDLE_VALUE) + return; + pc = get_thrpc (thr); + if (pc == -1) + return; + + // code assumes there is only one profinfo in play: the static prof up top + if (pc >= prof.lowpc && pc < prof.highpc) + { + size_t idx = PROFIDX (pc, prof.lowpc, prof.scale); + prof.counter[idx]++; + } +} static void CALLBACK profthr_func (LPVOID arg) { struct profinfo *p = (struct profinfo *) arg; - size_t pc, idx; for (;;) { - pc = (size_t) get_thrpc (p->targthr); - if (pc >= p->lowpc && pc < p->highpc) - { - idx = PROFIDX (pc, p->lowpc, p->scale); - p->counter[idx]++; - } + // record profiling sample for main thread + profthr_byhandle (p->targthr); + + // record profiling samples for other pthreads, if any + cygwin_internal (CW_CYGHEAP_PROFTHR_ALL, profthr_byhandle); + #if 0 print_prof (p); #endif -- 2.7.0