[PATCH] gprof profiling of multi-threaded Cygwin programs, ver 2
Mark Geisert
mark@maxrnd.com
Sat Feb 20 08:16:00 GMT 2016
Version 2 incorporating review comments of version 1.
Change log relative to winsup/cygwin:
* include/sys/cygwin.h: Add CW_CYGHEAP_PROFTHR_ALL.
* cygheap.cc (cygheap_profthr_all): New C-callable function that
runs cygheap's threadlist handing each pthread's thread handle in
turn to profthr_byhandle().
* external.cc (cygwin_internal): Add case CW_CYGHEAP_PROFTHR_ALL.
* gmon.c (_mcleanup): Add support for multiple simultaneous
gmon.out* files named via environment variable GMON_OUT_PREFIX.
* gmon.h (struct gmonparam): Make state decl volatile.
* mcount.c (_MCOUNT_DECL): Change stores into gmonparam.state to use
Interlocked operations. Add #include "winsup.h", update commentary.
* profil.c (profthr_byhandle): New function abstracting out the
updating of profile counters based on a thread handle.
(profthr_func): Update to call profthr_byhandle() to sample the main
thread then call cygheap_profthr_all() indirectly through
cygwin_internal(CW_CYGHEAP_PROFTHR_ALL) to sample all other threads.
Thanks,
..mark
-------------- next part --------------
---
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 <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 eb41c08..9f183af 100644
--- a/winsup/cygwin/profil.c
+++ b/winsup/cygwin/profil.c
@@ -18,6 +18,7 @@
#endif
#include <windows.h>
#include <stdio.h>
+#include <sys/cygwin.h>
#include <sys/types.h>
#include <errno.h>
#include <math.h>
@@ -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
More information about the Cygwin-patches
mailing list