summaryrefslogtreecommitdiffstats
path: root/winsup
diff options
context:
space:
mode:
Diffstat (limited to 'winsup')
-rw-r--r--winsup/cygwin/cygheap.cc12
-rw-r--r--winsup/cygwin/external.cc11
-rw-r--r--winsup/cygwin/gmon.c82
-rw-r--r--winsup/cygwin/gmon.h2
-rw-r--r--winsup/cygwin/include/sys/cygwin.h2
-rw-r--r--winsup/cygwin/mcount.c12
-rw-r--r--winsup/cygwin/profil.c86
7 files changed, 134 insertions, 73 deletions
diff --git a/winsup/cygwin/cygheap.cc b/winsup/cygwin/cygheap.cc
index 6493485c3..4932cf0b1 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 e379df1d2..02335eb69 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 96b1189ee..3baf93563 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,44 @@ _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;
+ /* Covers positive pid_t values. */
+ int32_t divisor = 1000*1000*1000;
+ pid_t pid = getpid();
+ size_t len = strlen(prefix);
+
+ if (len == 0)
+ len = strlen(prefix = filename);
+ buf = alloca(len + 13);// allow for '.', 10-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 0932ed94c..b0fb479e2 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 5b7da5897..8c7128c97 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 fad672881..6111b355a 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 <sys/types.h>
#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 eb41c0844..2d7d6e394 100644
--- a/winsup/cygwin/profil.c
+++ b/winsup/cygwin/profil.c
@@ -18,9 +18,10 @@
#endif
#include <windows.h>
#include <stdio.h>
+#include <sys/cygwin.h>
#include <sys/types.h>
#include <errno.h>
-#include <math.h>
+#include <pthread.h>
#include "profil.h"
#define SLEEPTIME (1000 / PROF_HZ)
@@ -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; bail if anything amiss. */
+ if (thr == INVALID_HANDLE_VALUE)
+ return;
+ pc = get_thrpc (thr);
+ if (pc == -1)
+ return;
+
+ /* Code assumes there's only one profinfo in play: the static prof above. */
+ 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
@@ -106,6 +124,7 @@ profile_off (struct profinfo *p)
}
if (p->targthr)
CloseHandle (p->targthr);
+ p->targthr = 0;
return 0;
}
@@ -121,6 +140,7 @@ profile_on (struct profinfo *p)
GetCurrentProcess (), &p->targthr, 0, FALSE,
DUPLICATE_SAME_ACCESS))
{
+ p->targthr = 0;
errno = ESRCH;
return -1;
}
@@ -129,7 +149,7 @@ profile_on (struct profinfo *p)
if (!p->quitevt)
{
- CloseHandle (p->quitevt);
+ CloseHandle (p->targthr);
p->targthr = 0;
errno = EAGAIN;
return -1;
@@ -148,8 +168,8 @@ profile_on (struct profinfo *p)
}
/* Set profiler thread priority to highest to be sure that it gets the
- processor as soon it request it (i.e. when the Sleep terminate) to get
- the next data out of the profile. */
+ processor as soon it requests it (i.e. when the Sleep terminates) to get
+ the next data sample as quickly as possible. */
SetThreadPriority (p->profthr, THREAD_PRIORITY_TIME_CRITICAL);
@@ -157,16 +177,41 @@ profile_on (struct profinfo *p)
}
/*
- * start or stop profiling
+ * Restart profiling in child after fork.
*
- * profiling goes into the SAMPLES buffer of size SIZE (which is treated
- * as an array of u_shorts of size size/2)
+ * The profiling control info in prof needs to be selectively updated.
+ * Items counter, lowpc, highpc, and scale are correct as-is. But items
+ * targthr, profthr, and quitevt need updating because these copied HANDLE
+ * values are only valid in parent process. We also zero out the sample
+ * buffer so that samples aren't double-counted if multiple gmon.out files
+ * are aggregated. We calculate buffer's size from other data in prof.
+ */
+static void
+profile_child (void)
+{
+ /* Bail if this was a fork after profiling turned off or was never on. */
+ if (!prof.targthr)
+ return;
+
+ /* Figure out how big the sample buffer is and zero it out. */
+ size_t size = PROFIDX (prof.highpc, prof.lowpc, prof.scale) << 1;
+ memset ((char *) prof.counter, 0, size);
+
+ /* Replace stale HANDLEs in prof and create profiling thread. */
+ profile_on (&prof);
+}
+
+/*
+ * Start or stop profiling.
*
- * each bin represents a range of pc addresses from OFFSET. The number
+ * Profiling data goes into the SAMPLES buffer of size SIZE (which is treated
+ * as an array of u_shorts of size SIZE/2).
+ *
+ * Each bin represents a range of pc addresses from OFFSET. The number
* of pc addresses in a bin depends on SCALE. (A scale of 65536 maps
* each bin to two addresses, A scale of 32768 maps each bin to 4 addresses,
* a scale of 1 maps each bin to 128k addreses). Scale may be 1 - 65536,
- * or zero to turn off profiling
+ * or zero to turn off profiling.
*/
int
profile_ctl (struct profinfo * p, char *samples, size_t size,
@@ -191,6 +236,9 @@ profile_ctl (struct profinfo * p, char *samples, size_t size,
prof.highpc = PROFADDR (maxbin, offset, scale);
prof.scale = scale;
+ /* Set up callback to restart profiling in child after fork. */
+ pthread_atfork (NULL, NULL, profile_child);
+
return profile_on (p);
}
return 0;