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.
This commit is contained in:
Ulrich Drepper 2001-03-21 20:15:55 +00:00
parent cf4431ed2d
commit 4c48b26742
7 changed files with 651 additions and 3 deletions

View File

@ -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.

View File

@ -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

View File

@ -16,4 +16,7 @@ libc {
# m*
moncontrol;
}
GLIBC_2.2.3 {
sprofil;
}
}

63
gmon/sys/profil.h Normal file
View File

@ -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 */

172
gmon/tst-sprofil.c Normal file
View File

@ -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;
}

37
sysdeps/generic/sprofil.c Normal file
View File

@ -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)

357
sysdeps/posix/sprofil.c Normal file
View File

@ -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 */