diff options
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | gmon/Makefile | 8 | ||||
-rw-r--r-- | gmon/Versions | 3 | ||||
-rw-r--r-- | gmon/sys/profil.h | 63 | ||||
-rw-r--r-- | gmon/tst-sprofil.c | 172 | ||||
-rw-r--r-- | sysdeps/generic/sprofil.c | 37 | ||||
-rw-r--r-- | sysdeps/posix/sprofil.c | 357 |
7 files changed, 651 insertions, 3 deletions
@@ -1,3 +1,17 @@ +2001-03-21 Ulrich Drepper <drepper@redhat.com> + + * gmon/Makefile (routines): Add sprofil. + (tests): Add tst-sprofil. + (noprof): Add sprofil. + * gmon/Versions [libc] (GLIBC_2.2.3): Add sprofil. + +2001-03-20 David Mosberger <davidm@hpl.hp.com> + + * sysdeps/generic/sprofil.c: New file. + * sysdeps/posix/sprofil.c: New file. + * gmon/tst-sprofil.c: New file. + * gmon/sys/profil.h: New file. + 2001-03-20 Andreas Schwab <schwab@suse.de> * include/unistd.h: Adjust prototype of __gethostname. diff --git a/gmon/Makefile b/gmon/Makefile index a07fe731eb..48f367fce9 100644 --- a/gmon/Makefile +++ b/gmon/Makefile @@ -1,4 +1,4 @@ -# Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. +# Copyright (C) 1995, 1996, 1997, 2001 Free Software Foundation, Inc. # This file is part of the GNU C Library. # The GNU C Library is free software; you can redistribute it and/or @@ -23,7 +23,9 @@ subdir := gmon headers := sys/gmon.h sys/gmon_out.h distribute := machine-gmon.h profil-counter.h -routines := gmon mcount profil bb_init_func bb_exit_func prof-freq +routines := gmon mcount profil sprofil bb_init_func bb_exit_func prof-freq + +tests := tst-sprofil include ../Rules @@ -37,7 +39,7 @@ CFLAGS-mcount.c := -fno-omit-frame-pointer noprof := mcount ifeq (,$(filter profil,$(unix-syscalls))) -noprof += profil +noprof += profil sprofil endif $(noprof:%=$(objpfx)%.op): %.op: %.o diff --git a/gmon/Versions b/gmon/Versions index c39f1d1f74..d0b63334f2 100644 --- a/gmon/Versions +++ b/gmon/Versions @@ -16,4 +16,7 @@ libc { # m* moncontrol; } + GLIBC_2.2.3 { + sprofil; + } } diff --git a/gmon/sys/profil.h b/gmon/sys/profil.h new file mode 100644 index 0000000000..136bd53511 --- /dev/null +++ b/gmon/sys/profil.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2001 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang <davidm@hpl.hp.com>. + + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _PROFIL_H +#define _PROFIL_H 1 + +#include <features.h> + +#include <sys/time.h> +#include <sys/types.h> + +/* This interface is intended to follow the sprofil() system calls as + described by the sprofil(2) man page of Irix v6.5, except that: + + - there is no a priori limit on number of text sections + - pr_scale is declared as unsigned long (instead of "unsigned int") + - pr_size is declared as size_t (instead of "unsigned int") + - pr_off is declared as void * (instead of "__psunsigned_t") + - the overflow bin (pr_base==0, pr_scale==2) can appear anywhere + in the profp array + - PROF_FAST has no effect */ + +struct prof + { + void *pr_base; /* buffer base */ + size_t pr_size; /* buffer size */ + size_t pr_off; /* pc offset */ + unsigned long int pr_scale; /* pc scaling (fixed-point number) */ + }; + +enum + { + PROF_USHORT = 0, /* use 16-bit counters (default) */ + PROF_UINT = 1 << 0, /* use 32-bit counters */ + PROF_FAST = 1 << 1 /* profile faster than usual */ + }; + + +__BEGIN_DECLS + +extern int sprofil (struct prof *__profp, int __profcnt, + struct timeval *__tvp, unsigned int __flags) __THROW; + +__END_DECLS + +#endif /* profil.h */ diff --git a/gmon/tst-sprofil.c b/gmon/tst-sprofil.c new file mode 100644 index 0000000000..26d4c7c56e --- /dev/null +++ b/gmon/tst-sprofil.c @@ -0,0 +1,172 @@ +/* Copyright (C) 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by David Mosberger-Tang <davidm@hpl.hp.com>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/profil.h> + +#include <bits/wordsize.h> + +#define NELEMS(a) (sizeof (a)/sizeof ((a)[0])) + +size_t taddr[] = + { + 0x00001000, /* elf32/hppa */ + 0x08048000, /* Linux elf32/x86 */ + 0x80000000, /* Linux elf32/m68k */ + 0x00400000, /* Linux elf32/mips */ + 0x01800000, /* Linux elf32/ppc */ + 0x00010000 /* Linux elf32/sparc */ +#if __WORDSIZE > 32 + , + 0x4000000000000000, /* Linux elf64/ia64 */ + 0x0000000120000000, /* Linux elf64/alpha */ + 0x4000000000001000, /* elf64/hppa */ + 0x0000000100000000 /* Linux elf64/sparc */ +#endif + }; + +unsigned int buf[NELEMS (taddr)][0x10000 / sizeof (int)]; +unsigned int bshort[5][0x100 / sizeof (int)]; +unsigned int blong[1][0x1000 / sizeof (int)]; +unsigned int vlong[1][0x2000 / sizeof (int)]; + +long int +fac (long int n) +{ + if (n == 0) + return 1; + return n * fac (n - 1); +} + +int +main (int argc, char **argv) +{ + unsigned int ovfl = 0, profcnt = 0; + struct timeval tv, start; + struct prof prof[32]; + double t_tick, delta; + long int sum = 0; + int i, j; + + for (i = 0; i < NELEMS (taddr); ++i) + { + prof[profcnt].pr_base = buf[i]; + prof[profcnt].pr_size = sizeof (buf[i]); + prof[profcnt].pr_off = taddr[i]; + prof[profcnt].pr_scale = 0x10000; + ++profcnt; + } + + prof[profcnt].pr_base = blong[0]; + prof[profcnt].pr_size = sizeof (blong[0]); + prof[profcnt].pr_off = 0x80001000; + prof[profcnt].pr_scale = 0x10000; + ++profcnt; + + prof[profcnt].pr_base = bshort[0]; + prof[profcnt].pr_size = sizeof (bshort[0]); + prof[profcnt].pr_off = 0x80000080; + prof[profcnt].pr_scale = 0x10000; + ++profcnt; + + prof[profcnt].pr_base = bshort[1]; + prof[profcnt].pr_size = sizeof (bshort[1]); + prof[profcnt].pr_off = 0x80000f80; + prof[profcnt].pr_scale = 0x10000; + ++profcnt; + + prof[profcnt].pr_base = bshort[2]; + prof[profcnt].pr_size = sizeof (bshort[2]); + prof[profcnt].pr_off = 0x80001080; + prof[profcnt].pr_scale = 0x10000; + ++profcnt; + + prof[profcnt].pr_base = bshort[3]; + prof[profcnt].pr_size = sizeof (bshort[3]); + prof[profcnt].pr_off = 0x80001f80; + prof[profcnt].pr_scale = 0x10000; + ++profcnt; + + prof[profcnt].pr_base = bshort[4]; + prof[profcnt].pr_size = sizeof (bshort[4]); + prof[profcnt].pr_off = 0x80002080; + prof[profcnt].pr_scale = 0x10000; + ++profcnt; + + prof[profcnt].pr_base = vlong[0]; + prof[profcnt].pr_size = sizeof (vlong[0]); + prof[profcnt].pr_off = 0x80000080; + prof[profcnt].pr_scale = 0x10000; + ++profcnt; + + /* Set up overflow counter (must be last on Irix). */ + prof[profcnt].pr_base = &ovfl; + prof[profcnt].pr_size = sizeof (ovfl); + prof[profcnt].pr_off = 0; + prof[profcnt].pr_scale = 2; + ++profcnt; + + /* Turn it on. */ + if (sprofil (prof, profcnt, &tv, PROF_UINT) < 0) + { + if (errno == ENOSYS) + exit (0); + perror ("sprofil"); + exit (1); + } + + t_tick = tv.tv_sec + 1e-6 * tv.tv_usec; + printf ("profiling period = %g ms\n", 1e3 * t_tick); + + gettimeofday (&start, NULL); + do + { + for (i = 0; i < 21; ++i) + sum += fac (i); + + gettimeofday (&tv, NULL); + timersub (&tv, &start, &tv); + delta = tv.tv_sec + 1e-6 * tv.tv_usec; + } + while (delta < 1000 * t_tick); + + printf ("sum = 0x%lx\n", sum); + + /* Turn it off. */ + if (sprofil (NULL, 0, NULL, 0) < 0) + { + if (errno == ENOSYS) + exit (0); + perror ("sprofil"); + exit (1); + } + + printf ("overflow = %u\n", ovfl); + for (i = 0; i < NELEMS (taddr); ++i) + for (j = 0; j < 0x10000 / sizeof (int); ++j) + if (buf[i][j] != 0) + printf ("%0*Zx\t%u\t(buffer %d)\n", + (int) (sizeof (size_t) * 2), + (taddr[i] + (char *) &buf[i][j] - (char *) &buf[i][0]), + buf[i][j], i); + + return 0; +} diff --git a/sysdeps/generic/sprofil.c b/sysdeps/generic/sprofil.c new file mode 100644 index 0000000000..d8e094591b --- /dev/null +++ b/sysdeps/generic/sprofil.c @@ -0,0 +1,37 @@ +/* Copyright (C) 2001 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang <davidm@hpl.hp.com>. + + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> + +#include <sys/profil.h> +#include <sys/time.h> + +int +__sprofil (struct prof *profp, int profcnt, struct timeval *tvp, + unsigned int flags) +{ + if (profcnt == 0) + return 0; + + __set_errno (ENOSYS); + return -1; +} + +weak_alias (__sprofil, sprofil) diff --git a/sysdeps/posix/sprofil.c b/sysdeps/posix/sprofil.c new file mode 100644 index 0000000000..c653253ed6 --- /dev/null +++ b/sysdeps/posix/sprofil.c @@ -0,0 +1,357 @@ +/* Copyright (C) 2001 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang <davidm@hpl.hp.com>. + + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <assert.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/time.h> +#include <sys/profil.h> + +#ifndef SIGPROF +# include <sysdeps/generic/sprofil.c> +#else + +#include <libc-internal.h> + +struct region + { + size_t offset; + size_t nsamples; + unsigned int scale; + union + { + void *vp; + unsigned short *us; + unsigned int *ui; + } + sample; + size_t start; + size_t end; + }; + +struct prof_info + { + unsigned int num_regions; + struct region *region; + struct region *last, *overflow; + struct itimerval saved_timer; + struct sigaction saved_action; + }; + +static unsigned int overflow_counter; + +static struct region default_overflow_region = + { + offset: 0, + nsamples: 1, + scale: 2, + sample: { &overflow_counter }, + start: 0, + end: ~(size_t) 0 + }; + +static struct prof_info prof_info; + +static inline unsigned long int +pc_to_index (size_t pc, size_t offset, unsigned int scale, int prof_uint) +{ + size_t i = (pc - offset) / (prof_uint ? sizeof (int) : sizeof (short)); + + if (sizeof (unsigned long long int) > sizeof (size_t)) + return (unsigned long long int) i * scale / 65536; + else + return i / 65536 * scale + i % 65536 * scale / 65536; +} + +static inline size_t +index_to_pc (unsigned long int n, size_t offset, unsigned int scale, + int prof_uint) +{ + size_t pc, bin_size = (prof_uint ? sizeof (int) : sizeof (short)); + + if (sizeof (unsigned long long int) > sizeof (size_t)) + pc = offset + (unsigned long long int) n * bin_size * 65536ull / scale; + else + pc = (offset + n * bin_size / scale * 65536 + + n * bin_size % scale * 65536 / scale); + + if (pc_to_index (pc, offset, scale, prof_uint) < n) + /* Adjust for rounding error. */ + ++pc; + + assert (pc_to_index (pc - 1, offset, scale, prof_uint) < n + && pc_to_index (pc, offset, scale, prof_uint) >= n); + + return pc; +} + +static inline void +profil_count (void *pcp, int prof_uint) +{ + struct region *region, *r = prof_info.last; + size_t lo, hi, mid, pc = (unsigned long int) pcp; + unsigned long int i; + + /* Fast path: pc is in same region as before. */ + if (pc >= r->start && pc < r->end) + region = r; + else + { + /* Slow path: do a binary search for the right region. */ + lo = 0; hi = prof_info.num_regions - 1; + while (lo <= hi) + { + mid = (lo + hi) / 2; + + r = prof_info.region + mid; + if (pc >= r->start && pc < r->end) + { + prof_info.last = r; + region = r; + break; + } + + if (pc < r->start) + hi = mid - 1; + else + lo = mid + 1; + } + + /* No matching region: increment overflow count. There is no point + in updating the cache here, as it won't hit anyhow. */ + region = prof_info.overflow; + } + + i = pc_to_index (pc, region->offset, region->scale, prof_uint); + if (i < r->nsamples) + { + if (prof_uint) + { + if (r->sample.ui[i] < (unsigned int) ~0) + ++r->sample.ui[i]; + } + else + { + if (r->sample.us[i] < (unsigned short) ~0) + ++r->sample.us[i]; + } + } + else + { + if (prof_uint) + ++prof_info.overflow->sample.ui[0]; + else + ++prof_info.overflow->sample.us[0]; + } +} + +static inline void +profil_count_ushort (void *pcp) +{ + profil_count (pcp, 0); +} + +static inline void +profil_count_uint (void *pcp) +{ + profil_count (pcp, 1); +} + +/* Get the machine-dependent definition of `profil_counter', the signal + handler for SIGPROF. It calls `profil_count' (above) with the PC of the + interrupted code. */ +#define profil_counter profil_counter_ushort +#define profil_count(pc) profil_count (pc, 0) +#include "profil-counter.h" + +#undef profil_counter +#undef profil_count + +#define profil_counter profil_counter_uint +#define profil_count(pc) profil_count (pc, 1) +#include "profil-counter.h" + +static int +insert (int i, unsigned long int start, unsigned long int end, struct prof *p, + int prof_uint) +{ + struct region *r; + size_t to_copy; + + if (start >= end) + return 0; /* don't bother with empty regions */ + + if (prof_info.num_regions == 0) + r = malloc (sizeof (*r)); + else + r = realloc (prof_info.region, (prof_info.num_regions + 1) * sizeof (*r)); + if (r == NULL) + return -1; + + to_copy = prof_info.num_regions - i; + if (to_copy > 0) + memmove (r + i + 1, r + i, to_copy * sizeof (*r)); + + r[i].offset = p->pr_off; + r[i].nsamples = p->pr_size / (prof_uint ? sizeof (int) : sizeof (short)); + r[i].scale = p->pr_scale; + r[i].sample.vp = p->pr_base; + r[i].start = start; + r[i].end = end; + + prof_info.region = r; + ++prof_info.num_regions; + + if (p->pr_off == 0 && p->pr_scale == 2) + prof_info.overflow = r; + + return 0; +} + +/* Add a new profiling region. If the new region overlaps with + existing ones, this may add multiple subregions so that the final + data structure is free of overlaps. The absence of overlaps makes + it possible to use a binary search in profil_count(). Note that + this function depends on new regions being presented in DECREASING + ORDER of starting address. */ + +static int +add_region (struct prof *p, int prof_uint) +{ + unsigned long int nsamples; + size_t start, end; + int i; + + if (p->pr_scale < 2) + return 0; + + nsamples = p->pr_size / (prof_uint ? sizeof (int) : sizeof (short)); + + start = p->pr_off; + end = index_to_pc (nsamples, p->pr_off, p->pr_scale, prof_uint); + + /* Merge with existing regions. */ + for (i = 0; i < prof_info.num_regions; ++i) + { + if (start < prof_info.region[i].start) + { + if (end < prof_info.region[i].start) + break; + else if (insert (i, start, prof_info.region[i].start, p, prof_uint) + < 0) + return -1; + } + start = prof_info.region[i].end; + } + return insert (i, start, end, p, prof_uint); +} + +static int +pcmp (const void *left, const void *right) +{ + struct prof *l = *(struct prof **) left; + struct prof *r = *(struct prof **) right; + + if (l->pr_off < r->pr_off) + return 1; + else if (l->pr_off > r->pr_off) + return -1; + return 0; +} + +int +__sprofil (struct prof *profp, int profcnt, struct timeval *tvp, + unsigned int flags) +{ + struct prof *p[profcnt]; + struct itimerval timer; + struct sigaction act; + int i; + + if (tvp != NULL) + { + /* Return profiling period. */ + unsigned long int t = 1000000 / __profile_frequency (); + tvp->tv_sec = t / 1000000; + tvp->tv_usec = t % 1000000; + } + + if (prof_info.num_regions > 0) + { + /* Disable profiling. */ + if (__setitimer (ITIMER_PROF, &prof_info.saved_timer, NULL) < 0) + return -1; + + if (__sigaction (SIGPROF, &prof_info.saved_action, NULL) < 0) + return -1; + + free (prof_info.region); + return 0; + } + + prof_info.num_regions = 0; + prof_info.region = NULL; + prof_info.overflow = &default_overflow_region; + + for (i = 0; i < profcnt; ++i) + p[i] = profp + i; + + /* Sort in order of decreasing starting address: */ + qsort (p, profcnt, sizeof (p[0]), pcmp); + + /* Add regions in order of decreasing starting address: */ + for (i = 0; i < profcnt; ++i) + if (add_region (p[i], (flags & PROF_UINT) != 0) < 0) + { + if (prof_info.region) + free (prof_info.region); + prof_info.num_regions = 0; + prof_info.region = NULL; + return -1; + } + + if (prof_info.num_regions == 0) + return 0; + + prof_info.last = prof_info.region; + + /* Install SIGPROF handler. */ + if (flags & PROF_UINT) + act.sa_handler = (sighandler_t) &profil_counter_uint; + else + act.sa_handler = (sighandler_t) &profil_counter_ushort; + act.sa_flags = SA_RESTART; + sigfillset (&act.sa_mask); + if (__sigaction (SIGPROF, &act, &prof_info.saved_action) < 0) + return -1; + + /* Setup profiling timer. */ + timer.it_value.tv_sec = 0; + timer.it_value.tv_usec = 1; + timer.it_interval = timer.it_value; + return __setitimer (ITIMER_PROF, &timer, &prof_info.saved_timer); +} + +weak_alias (__sprofil, sprofil) + +#endif /* SIGPROF */ |