diff options
Diffstat (limited to 'winsup/cygwin/profil.c')
-rw-r--r-- | winsup/cygwin/profil.c | 86 |
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; |