summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/gmon.c
diff options
context:
space:
mode:
authorMark Geisert <mark@maxrnd.com>2016-02-19 22:58:31 -0800
committerCorinna Vinschen <corinna@vinschen.de>2016-03-10 20:39:46 +0100
commit5807ba83e498d87b52675376a81d2edfe6de098d (patch)
tree6b5a3a6a891773a9c46b8b7a30af47018b7973e2 /winsup/cygwin/gmon.c
parent65231f415a9f69486b6d9890d4b736f5676aef11 (diff)
downloadcygnal-5807ba83e498d87b52675376a81d2edfe6de098d.tar.gz
cygnal-5807ba83e498d87b52675376a81d2edfe6de098d.tar.bz2
cygnal-5807ba83e498d87b52675376a81d2edfe6de098d.zip
Support profiling of multi-threaded apps.
This patch set modifies Cygwin's profiling support to sample PC values of all an app's threads, not just the main thread. There is no change to how profiling is requested: just compile and link the app with "-pg" as usual. The profiling info is dumped into file gmon.out as always. A new facility enabled via the environment variable GMON_OUT_PREFIX. This facility is intended to match an undocumented Linux glibc feature. Exporting the variable with a non-empty value such as "foo" causes the profiling info to go to a file named foo.$pid instead of the default. With that, both resulting processes of a fork() can have their profiling data captured in separate files. gprof already knows how to accumulate data from multiple files if they all pertain to the same app. There is no change to the normal Cygwin execution paths if profiling is not enabled. And when it is enabled, only the one profiling thread per profiled app is doing more work than it used to. * 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. (profile_off): Zero targthr to indicate profiling was turned off. (profile_on): Fix handle leak on failure path. (profile_child): New callback func to restart profiling in child process after a fork if the parent was being profiled. (profile_ctl): Call pthread_atfork() to set profile_child callback. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
Diffstat (limited to 'winsup/cygwin/gmon.c')
-rw-r--r--winsup/cygwin/gmon.c82
1 files changed, 34 insertions, 48 deletions
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