summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/profil.c
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/profil.c')
-rw-r--r--winsup/cygwin/profil.c86
1 files changed, 67 insertions, 19 deletions
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;