mirror of
https://github.com/freebsd/freebsd-src.git
synced 2024-11-27 13:32:45 +00:00
Make profiling work for ELF. gprof now autodetects the format of
the executable file, so it will work for both a.out and ELF format files. I have split the object format specific code into separate source files. It's cleaner than it was before, but it's still pretty crufty. Don't cheat on your make world for this update. A lot of things have to be rebuilt for it to work, including the compiler and all of the profiled libraries.
This commit is contained in:
parent
bbfd144724
commit
5584f22bb3
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=38928
@ -405,9 +405,10 @@ do { \
|
||||
#define FUNCTION_PROFILER(FILE, LABELNO) \
|
||||
{ \
|
||||
if (flag_pic) \
|
||||
fprintf (FILE, "\tcall *mcount@GOT(%%ebx)\n"); \
|
||||
fprintf (FILE, "\tcall *%s@GOT(%%ebx)\n", \
|
||||
TARGET_AOUT ? "mcount" : ".mcount"); \
|
||||
else \
|
||||
fprintf (FILE, "\tcall mcount\n"); \
|
||||
fprintf (FILE, "\tcall %s\n", TARGET_AOUT ? "mcount" : ".mcount"); \
|
||||
}
|
||||
|
||||
#define FUNCTION_PROFILER_EPILOGUE(FILE) \
|
||||
@ -415,9 +416,11 @@ do { \
|
||||
if (TARGET_PROFILER_EPILOGUE) \
|
||||
{ \
|
||||
if (flag_pic) \
|
||||
fprintf (FILE, "\tcall *mexitcount@GOT(%%ebx)\n"); \
|
||||
fprintf (FILE, "\tcall *%s@GOT(%%ebx)\n", \
|
||||
TARGET_AOUT ? "mexitcount" : ".mexitcount"); \
|
||||
else \
|
||||
fprintf (FILE, "\tcall mexitcount\n"); \
|
||||
fprintf (FILE, "\tcall %s\n", \
|
||||
TARGET_AOUT ? "mexitcount" : ".mexitcount"); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
@ -405,9 +405,10 @@ do { \
|
||||
#define FUNCTION_PROFILER(FILE, LABELNO) \
|
||||
{ \
|
||||
if (flag_pic) \
|
||||
fprintf (FILE, "\tcall *mcount@GOT(%%ebx)\n"); \
|
||||
fprintf (FILE, "\tcall *%s@GOT(%%ebx)\n", \
|
||||
TARGET_AOUT ? "mcount" : ".mcount"); \
|
||||
else \
|
||||
fprintf (FILE, "\tcall mcount\n"); \
|
||||
fprintf (FILE, "\tcall %s\n", TARGET_AOUT ? "mcount" : ".mcount"); \
|
||||
}
|
||||
|
||||
#define FUNCTION_PROFILER_EPILOGUE(FILE) \
|
||||
@ -415,9 +416,11 @@ do { \
|
||||
if (TARGET_PROFILER_EPILOGUE) \
|
||||
{ \
|
||||
if (flag_pic) \
|
||||
fprintf (FILE, "\tcall *mexitcount@GOT(%%ebx)\n"); \
|
||||
fprintf (FILE, "\tcall *%s@GOT(%%ebx)\n", \
|
||||
TARGET_AOUT ? "mexitcount" : ".mexitcount"); \
|
||||
else \
|
||||
fprintf (FILE, "\tcall mexitcount\n"); \
|
||||
fprintf (FILE, "\tcall %s\n", \
|
||||
TARGET_AOUT ? "mexitcount" : ".mexitcount"); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
#
|
||||
# $Id: Makefile,v 1.6 1996/04/12 02:24:34 jdp Exp $
|
||||
# $Id: Makefile,v 1.1.1.1 1998/03/07 20:27:10 jdp Exp $
|
||||
#
|
||||
|
||||
SRCS= crt1.c crtbegin.c crtend.c crti.S crtn.S
|
||||
OBJS= ${SRCS:N*.h:R:S/$/.o/g}
|
||||
OBJS+= gcrt1.o
|
||||
SOBJS= crtbegin.so crtend.so
|
||||
CFLAGS+= -elf
|
||||
CFLAGS+= -Wall -Wno-unused
|
||||
@ -15,6 +16,9 @@ INTERNALLIB= true
|
||||
|
||||
all: ${OBJS} ${SOBJS}
|
||||
|
||||
gcrt1.o: crt1.c
|
||||
${CC} ${CFLAGS} -DGCRT -c -o gcrt1.o ${.CURDIR}/crt1.c
|
||||
|
||||
realinstall:
|
||||
.for file in ${OBJS} ${SOBJS}
|
||||
${INSTALL} ${COPY} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \
|
||||
|
@ -22,7 +22,7 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: crt1.c,v 1.4 1996/04/12 02:24:35 jdp Exp $
|
||||
* $Id: crt1.c,v 1.1.1.1 1998/03/07 20:27:10 jdp Exp $
|
||||
*/
|
||||
|
||||
#ifndef __GNUC__
|
||||
@ -37,6 +37,13 @@ extern void _fini(void);
|
||||
extern void _init(void);
|
||||
extern int main(int, char **, char **);
|
||||
|
||||
#ifdef GCRT
|
||||
extern void _mcleanup(void);
|
||||
extern void monstartup(void *, void *);
|
||||
extern int eprol;
|
||||
extern int etext;
|
||||
#endif
|
||||
|
||||
extern int _DYNAMIC;
|
||||
#pragma weak _DYNAMIC
|
||||
|
||||
@ -71,7 +78,19 @@ _start(char *arguments, ...)
|
||||
if(&_DYNAMIC != 0)
|
||||
atexit(rtld_cleanup);
|
||||
|
||||
#ifdef GCRT
|
||||
atexit(_mcleanup);
|
||||
#endif
|
||||
atexit(_fini);
|
||||
#ifdef GCRT
|
||||
monstartup(&eprol, &etext);
|
||||
#endif
|
||||
_init();
|
||||
exit( main(argc, argv, env) );
|
||||
}
|
||||
|
||||
#ifdef GCRT
|
||||
__asm__(".text");
|
||||
__asm__("eprol:");
|
||||
__asm__(".previous");
|
||||
#endif
|
||||
|
@ -1,9 +1,10 @@
|
||||
#
|
||||
# $Id: Makefile,v 1.6 1996/04/12 02:24:34 jdp Exp $
|
||||
# $Id: Makefile,v 1.1.1.1 1998/03/07 20:27:10 jdp Exp $
|
||||
#
|
||||
|
||||
SRCS= crt1.c crtbegin.c crtend.c crti.S crtn.S
|
||||
OBJS= ${SRCS:N*.h:R:S/$/.o/g}
|
||||
OBJS+= gcrt1.o
|
||||
SOBJS= crtbegin.so crtend.so
|
||||
CFLAGS+= -elf
|
||||
CFLAGS+= -Wall -Wno-unused
|
||||
@ -15,6 +16,9 @@ INTERNALLIB= true
|
||||
|
||||
all: ${OBJS} ${SOBJS}
|
||||
|
||||
gcrt1.o: crt1.c
|
||||
${CC} ${CFLAGS} -DGCRT -c -o gcrt1.o ${.CURDIR}/crt1.c
|
||||
|
||||
realinstall:
|
||||
.for file in ${OBJS} ${SOBJS}
|
||||
${INSTALL} ${COPY} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \
|
||||
|
@ -22,7 +22,7 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: crt1.c,v 1.4 1996/04/12 02:24:35 jdp Exp $
|
||||
* $Id: crt1.c,v 1.1.1.1 1998/03/07 20:27:10 jdp Exp $
|
||||
*/
|
||||
|
||||
#ifndef __GNUC__
|
||||
@ -37,6 +37,13 @@ extern void _fini(void);
|
||||
extern void _init(void);
|
||||
extern int main(int, char **, char **);
|
||||
|
||||
#ifdef GCRT
|
||||
extern void _mcleanup(void);
|
||||
extern void monstartup(void *, void *);
|
||||
extern int eprol;
|
||||
extern int etext;
|
||||
#endif
|
||||
|
||||
extern int _DYNAMIC;
|
||||
#pragma weak _DYNAMIC
|
||||
|
||||
@ -71,7 +78,19 @@ _start(char *arguments, ...)
|
||||
if(&_DYNAMIC != 0)
|
||||
atexit(rtld_cleanup);
|
||||
|
||||
#ifdef GCRT
|
||||
atexit(_mcleanup);
|
||||
#endif
|
||||
atexit(_fini);
|
||||
#ifdef GCRT
|
||||
monstartup(&eprol, &etext);
|
||||
#endif
|
||||
_init();
|
||||
exit( main(argc, argv, env) );
|
||||
}
|
||||
|
||||
#ifdef GCRT
|
||||
__asm__(".text");
|
||||
__asm__("eprol:");
|
||||
__asm__(".previous");
|
||||
#endif
|
||||
|
@ -31,7 +31,7 @@
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)profile.h 8.1 (Berkeley) 6/11/93
|
||||
* $Id: profile.h,v 1.16 1998/07/10 09:26:41 bde Exp $
|
||||
* $Id: profile.h,v 1.17 1998/07/14 05:09:43 bde Exp $
|
||||
*/
|
||||
|
||||
#ifndef _MACHINE_PROFILE_H_
|
||||
@ -135,8 +135,12 @@ void stopguprof __P((struct gmonparam *p));
|
||||
|
||||
__BEGIN_DECLS
|
||||
#ifdef __GNUC__
|
||||
#ifdef __ELF__
|
||||
void mcount __P((void)) __asm(".mcount");
|
||||
#else
|
||||
void mcount __P((void)) __asm("mcount");
|
||||
#endif
|
||||
#endif
|
||||
static void _mcount __P((uintfptr_t frompc, uintfptr_t selfpc));
|
||||
__END_DECLS
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)profile.h 8.1 (Berkeley) 6/11/93
|
||||
* $Id: profile.h,v 1.16 1998/07/10 09:26:41 bde Exp $
|
||||
* $Id: profile.h,v 1.17 1998/07/14 05:09:43 bde Exp $
|
||||
*/
|
||||
|
||||
#ifndef _MACHINE_PROFILE_H_
|
||||
@ -135,8 +135,12 @@ void stopguprof __P((struct gmonparam *p));
|
||||
|
||||
__BEGIN_DECLS
|
||||
#ifdef __GNUC__
|
||||
#ifdef __ELF__
|
||||
void mcount __P((void)) __asm(".mcount");
|
||||
#else
|
||||
void mcount __P((void)) __asm("mcount");
|
||||
#endif
|
||||
#endif
|
||||
static void _mcount __P((uintfptr_t frompc, uintfptr_t selfpc));
|
||||
__END_DECLS
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# @(#)Makefile 8.1 (Berkeley) 6/29/93
|
||||
|
||||
PROG= gprof
|
||||
SRCS= gprof.c arcs.c dfn.c lookup.c ${MACHINE_ARCH}.c hertz.c \
|
||||
SRCS= gprof.c aout.c arcs.c dfn.c elf.c lookup.c ${MACHINE_ARCH}.c hertz.c \
|
||||
printgprof.c printlist.c
|
||||
|
||||
beforeinstall:
|
||||
|
201
usr.bin/gprof/aout.c
Normal file
201
usr.bin/gprof/aout.c
Normal file
@ -0,0 +1,201 @@
|
||||
#include <a.out.h>
|
||||
|
||||
#include "gprof.h"
|
||||
|
||||
static void getstrtab(FILE *, const char *);
|
||||
static void getsymtab(FILE *, const char *);
|
||||
static void gettextspace(FILE *);
|
||||
static bool funcsymbol(struct nlist *);
|
||||
|
||||
static char *strtab; /* string table in core */
|
||||
static long ssiz; /* size of the string table */
|
||||
static struct exec xbuf; /* exec header of a.out */
|
||||
|
||||
/* Things which get -E excluded by default. */
|
||||
static char *excludes[] = { "mcount", "__mcleanup", NULL };
|
||||
|
||||
/*
|
||||
* Set up string and symbol tables from a.out.
|
||||
* and optionally the text space.
|
||||
* On return symbol table is sorted by value.
|
||||
*
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
aout_getnfile(const char *filename, char ***defaultEs)
|
||||
{
|
||||
FILE *nfile;
|
||||
int valcmp();
|
||||
|
||||
nfile = fopen( filename ,"r");
|
||||
if (nfile == NULL) {
|
||||
perror( filename );
|
||||
done();
|
||||
}
|
||||
fread(&xbuf, 1, sizeof(xbuf), nfile);
|
||||
if (N_BADMAG(xbuf)) {
|
||||
fclose(nfile);
|
||||
return -1;
|
||||
}
|
||||
getstrtab(nfile, filename);
|
||||
getsymtab(nfile, filename);
|
||||
gettextspace( nfile );
|
||||
fclose(nfile);
|
||||
# ifdef DEBUG
|
||||
if ( debug & AOUTDEBUG ) {
|
||||
register int j;
|
||||
|
||||
for (j = 0; j < nname; j++){
|
||||
printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name);
|
||||
}
|
||||
}
|
||||
# endif DEBUG
|
||||
*defaultEs = excludes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
getstrtab(FILE *nfile, const char *filename)
|
||||
{
|
||||
|
||||
fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
|
||||
if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
|
||||
warnx("%s: no string table (old format?)" , filename );
|
||||
done();
|
||||
}
|
||||
strtab = calloc(ssiz, 1);
|
||||
if (strtab == NULL) {
|
||||
warnx("%s: no room for %d bytes of string table", filename , ssiz);
|
||||
done();
|
||||
}
|
||||
if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
|
||||
warnx("%s: error reading string table", filename );
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read in symbol table
|
||||
*/
|
||||
static void
|
||||
getsymtab(FILE *nfile, const char *filename)
|
||||
{
|
||||
register long i;
|
||||
int askfor;
|
||||
struct nlist nbuf;
|
||||
|
||||
/* pass1 - count symbols */
|
||||
fseek(nfile, (long)N_SYMOFF(xbuf), 0);
|
||||
nname = 0;
|
||||
for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
|
||||
fread(&nbuf, sizeof(nbuf), 1, nfile);
|
||||
if ( ! funcsymbol( &nbuf ) ) {
|
||||
continue;
|
||||
}
|
||||
nname++;
|
||||
}
|
||||
if (nname == 0) {
|
||||
warnx("%s: no symbols", filename );
|
||||
done();
|
||||
}
|
||||
askfor = nname + 1;
|
||||
nl = (nltype *) calloc( askfor , sizeof(nltype) );
|
||||
if (nl == 0) {
|
||||
warnx("no room for %d bytes of symbol table", askfor * sizeof(nltype) );
|
||||
done();
|
||||
}
|
||||
|
||||
/* pass2 - read symbols */
|
||||
fseek(nfile, (long)N_SYMOFF(xbuf), 0);
|
||||
npe = nl;
|
||||
nname = 0;
|
||||
for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
|
||||
fread(&nbuf, sizeof(nbuf), 1, nfile);
|
||||
if ( ! funcsymbol( &nbuf ) ) {
|
||||
# ifdef DEBUG
|
||||
if ( debug & AOUTDEBUG ) {
|
||||
printf( "[getsymtab] rejecting: 0x%x %s\n" ,
|
||||
nbuf.n_type , strtab + nbuf.n_un.n_strx );
|
||||
}
|
||||
# endif DEBUG
|
||||
continue;
|
||||
}
|
||||
npe->value = nbuf.n_value;
|
||||
npe->name = strtab+nbuf.n_un.n_strx;
|
||||
# ifdef DEBUG
|
||||
if ( debug & AOUTDEBUG ) {
|
||||
printf( "[getsymtab] %d %s 0x%08x\n" ,
|
||||
nname , npe -> name , npe -> value );
|
||||
}
|
||||
# endif DEBUG
|
||||
npe++;
|
||||
nname++;
|
||||
}
|
||||
npe->value = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* read in the text space of an a.out file
|
||||
*/
|
||||
static void
|
||||
gettextspace(FILE *nfile)
|
||||
{
|
||||
|
||||
if ( cflag == 0 ) {
|
||||
return;
|
||||
}
|
||||
textspace = (u_char *) malloc( xbuf.a_text );
|
||||
if ( textspace == 0 ) {
|
||||
warnx("ran out room for %d bytes of text space: can't do -c" ,
|
||||
xbuf.a_text );
|
||||
return;
|
||||
}
|
||||
(void) fseek( nfile , N_TXTOFF( xbuf ) , 0 );
|
||||
if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) {
|
||||
warnx("couldn't read text space: can't do -c");
|
||||
free( textspace );
|
||||
textspace = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
funcsymbol(struct nlist *nlistp)
|
||||
{
|
||||
char *name, c;
|
||||
|
||||
/*
|
||||
* must be a text symbol,
|
||||
* and static text symbols don't qualify if aflag set.
|
||||
*/
|
||||
if ( ! ( ( nlistp -> n_type == ( N_TEXT | N_EXT ) )
|
||||
|| ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) {
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* name must start with an underscore if uflag is set.
|
||||
* can't have any `funny' characters in name,
|
||||
* where `funny' means `.' (.o file names)
|
||||
* need to make an exception for sparc .mul & co.
|
||||
* perhaps we should just drop this code entirely...
|
||||
*/
|
||||
name = strtab + nlistp -> n_un.n_strx;
|
||||
if ( uflag && *name != '_' )
|
||||
return FALSE;
|
||||
#ifdef sparc
|
||||
if ( *name == '.' ) {
|
||||
char *p = name + 1;
|
||||
if ( *p == 'u' )
|
||||
p++;
|
||||
if ( strcmp ( p, "mul" ) == 0 || strcmp ( p, "div" ) == 0 ||
|
||||
strcmp ( p, "rem" ) == 0 )
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
while ( c = *name++ ) {
|
||||
if ( c == '.' ) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
106
usr.bin/gprof/elf.c
Normal file
106
usr.bin/gprof/elf.c
Normal file
@ -0,0 +1,106 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <elf.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "gprof.h"
|
||||
|
||||
static bool wantsym(const Elf_Sym *, const char *);
|
||||
|
||||
/* Things which get -E excluded by default. */
|
||||
static char *excludes[] = { ".mcount", "_mcleanup", NULL };
|
||||
|
||||
int
|
||||
elf_getnfile(const char *filename, char ***defaultEs)
|
||||
{
|
||||
int fd;
|
||||
Elf_Ehdr h;
|
||||
struct stat s;
|
||||
void *mapbase;
|
||||
const char *base;
|
||||
const Elf_Shdr *shdrs;
|
||||
const Elf_Shdr *sh_symtab;
|
||||
const Elf_Shdr *sh_strtab;
|
||||
const char *strtab;
|
||||
const Elf_Sym *symtab;
|
||||
int symtabct;
|
||||
int i;
|
||||
|
||||
if ((fd = open(filename, O_RDONLY)) == -1)
|
||||
err(1, "%s", filename);
|
||||
if (read(fd, &h, sizeof h) != sizeof h || !IS_ELF(h)) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (fstat(fd, &s) == -1)
|
||||
err(1, "Cannot fstat %s", filename);
|
||||
if ((mapbase = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0)) ==
|
||||
MAP_FAILED)
|
||||
err(1, "Cannot mmap %s", filename);
|
||||
close(fd);
|
||||
|
||||
base = (const char *)mapbase;
|
||||
shdrs = (const Elf_Shdr *)(base + h.e_shoff);
|
||||
|
||||
/* Find the symbol table and associated string table section. */
|
||||
for (i = 1; i < h.e_shnum; i++)
|
||||
if (shdrs[i].sh_type == SHT_SYMTAB)
|
||||
break;
|
||||
if (i == h.e_shnum)
|
||||
errx(1, "%s has no symbol table", filename);
|
||||
sh_symtab = &shdrs[i];
|
||||
sh_strtab = &shdrs[sh_symtab->sh_link];
|
||||
|
||||
symtab = (const Elf_Sym *)(base + sh_symtab->sh_offset);
|
||||
symtabct = sh_symtab->sh_size / sh_symtab->sh_entsize;
|
||||
strtab = (const char *)(base + sh_strtab->sh_offset);
|
||||
|
||||
/* Count the symbols that we're interested in. */
|
||||
nname = 0;
|
||||
for (i = 1; i < symtabct; i++)
|
||||
if (wantsym(&symtab[i], strtab))
|
||||
nname++;
|
||||
|
||||
/* Allocate memory for them, plus a terminating entry. */
|
||||
if ((nl = (nltype *)calloc(nname + 1, sizeof(nltype))) == NULL)
|
||||
errx(1, "Insufficient memory for symbol table");
|
||||
|
||||
/* Read them in. */
|
||||
npe = nl;
|
||||
for (i = 1; i < symtabct; i++) {
|
||||
const Elf_Sym *sym = &symtab[i];
|
||||
|
||||
if (wantsym(sym, strtab)) {
|
||||
npe->value = sym->st_value;
|
||||
npe->name = strtab + sym->st_name;
|
||||
npe++;
|
||||
}
|
||||
}
|
||||
npe->value = -1;
|
||||
|
||||
*defaultEs = excludes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
wantsym(const Elf_Sym *sym, const char *strtab)
|
||||
{
|
||||
int type;
|
||||
int bind;
|
||||
|
||||
type = ELF_ST_TYPE(sym->st_info);
|
||||
bind = ELF_ST_BIND(sym->st_info);
|
||||
|
||||
if (type != STT_FUNC ||
|
||||
bind == STB_WEAK ||
|
||||
(aflag && bind == STB_LOCAL) ||
|
||||
(uflag && strchr(strtab + sym->st_name, '.') != NULL))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
@ -113,6 +113,7 @@ The static call graph of the program is discovered by a heuristic
|
||||
that examines the text space of the object file.
|
||||
Static-only parents or children are shown
|
||||
with call counts of 0.
|
||||
This option is not supported on some architectures.
|
||||
.It Fl C Ar count
|
||||
Find a minimal set of arcs that can be broken to eliminate all cycles with
|
||||
.Ar count
|
||||
@ -210,8 +211,14 @@ to accumulate profile data across several runs of an
|
||||
.Pa a.out
|
||||
file.
|
||||
.It Fl u
|
||||
Suppresses the printing of functions whose name does not begin with
|
||||
an underscore.
|
||||
Suppresses the printing of functions whose names are not visible to
|
||||
C programs. For the ELF object format, this means names that
|
||||
contain the
|
||||
.Ql \&.
|
||||
character. For the a.out object format, it means names that do not
|
||||
begin with a
|
||||
.Ql _
|
||||
character.
|
||||
All relevant information about such functions belongs to the
|
||||
(non-suppressed) function with the next lowest address.
|
||||
This is useful for eliminating "functions" that are just labels
|
||||
|
@ -42,19 +42,16 @@ static const char copyright[] =
|
||||
static char sccsid[] = "@(#)gprof.c 8.1 (Berkeley) 6/6/93";
|
||||
#endif
|
||||
static const char rcsid[] =
|
||||
"$Id: gprof.c,v 1.6 1997/07/15 08:04:40 charnier Exp $";
|
||||
"$Id: gprof.c,v 1.7 1998/08/08 17:48:26 jdp Exp $";
|
||||
#endif /* not lint */
|
||||
|
||||
#include <err.h>
|
||||
#include "gprof.h"
|
||||
|
||||
/*
|
||||
* things which get -E excluded by default.
|
||||
*/
|
||||
char *defaultEs[] = { "mcount" , "__mcleanup" , 0 };
|
||||
static int valcmp(const void *, const void *);
|
||||
|
||||
|
||||
static struct gmonhdr gmonhdr;
|
||||
static bool uflag;
|
||||
static int lflag;
|
||||
static int Lflag;
|
||||
|
||||
@ -64,6 +61,7 @@ main(argc, argv)
|
||||
{
|
||||
char **sp;
|
||||
nltype **timesortnlp;
|
||||
char **defaultEs;
|
||||
|
||||
--argc;
|
||||
argv++;
|
||||
@ -159,19 +157,25 @@ main(argc, argv)
|
||||
} else {
|
||||
gmonname = GMONNAME;
|
||||
}
|
||||
/*
|
||||
* get information from the executable file.
|
||||
*/
|
||||
if (elf_getnfile(a_outname, &defaultEs) == -1 &&
|
||||
aout_getnfile(a_outname, &defaultEs) == -1)
|
||||
errx(1, "%s: bad format", a_outname);
|
||||
/*
|
||||
* sort symbol table.
|
||||
*/
|
||||
qsort(nl, nname, sizeof(nltype), valcmp);
|
||||
/*
|
||||
* turn off default functions
|
||||
*/
|
||||
for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
|
||||
for ( sp = defaultEs ; *sp ; sp++ ) {
|
||||
Eflag = TRUE;
|
||||
addlist( Elist , *sp );
|
||||
eflag = TRUE;
|
||||
addlist( elist , *sp );
|
||||
}
|
||||
/*
|
||||
* get information about a.out file.
|
||||
*/
|
||||
getnfile();
|
||||
/*
|
||||
* get information about mon.out file(s).
|
||||
*/
|
||||
@ -222,146 +226,6 @@ main(argc, argv)
|
||||
done();
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up string and symbol tables from a.out.
|
||||
* and optionally the text space.
|
||||
* On return symbol table is sorted by value.
|
||||
*/
|
||||
getnfile()
|
||||
{
|
||||
FILE *nfile;
|
||||
int valcmp();
|
||||
|
||||
nfile = fopen( a_outname ,"r");
|
||||
if (nfile == NULL) {
|
||||
perror( a_outname );
|
||||
done();
|
||||
}
|
||||
fread(&xbuf, 1, sizeof(xbuf), nfile);
|
||||
if (N_BADMAG(xbuf)) {
|
||||
warnx("%s: bad format", a_outname );
|
||||
done();
|
||||
}
|
||||
getstrtab(nfile);
|
||||
getsymtab(nfile);
|
||||
gettextspace( nfile );
|
||||
qsort(nl, nname, sizeof(nltype), valcmp);
|
||||
fclose(nfile);
|
||||
# ifdef DEBUG
|
||||
if ( debug & AOUTDEBUG ) {
|
||||
register int j;
|
||||
|
||||
for (j = 0; j < nname; j++){
|
||||
printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name);
|
||||
}
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
|
||||
getstrtab(nfile)
|
||||
FILE *nfile;
|
||||
{
|
||||
|
||||
fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
|
||||
if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
|
||||
warnx("%s: no string table (old format?)" , a_outname );
|
||||
done();
|
||||
}
|
||||
strtab = calloc(ssiz, 1);
|
||||
if (strtab == NULL) {
|
||||
warnx("%s: no room for %d bytes of string table", a_outname , ssiz);
|
||||
done();
|
||||
}
|
||||
if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
|
||||
warnx("%s: error reading string table", a_outname );
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read in symbol table
|
||||
*/
|
||||
getsymtab(nfile)
|
||||
FILE *nfile;
|
||||
{
|
||||
register long i;
|
||||
int askfor;
|
||||
struct nlist nbuf;
|
||||
|
||||
/* pass1 - count symbols */
|
||||
fseek(nfile, (long)N_SYMOFF(xbuf), 0);
|
||||
nname = 0;
|
||||
for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
|
||||
fread(&nbuf, sizeof(nbuf), 1, nfile);
|
||||
if ( ! funcsymbol( &nbuf ) ) {
|
||||
continue;
|
||||
}
|
||||
nname++;
|
||||
}
|
||||
if (nname == 0) {
|
||||
warnx("%s: no symbols", a_outname );
|
||||
done();
|
||||
}
|
||||
askfor = nname + 1;
|
||||
nl = (nltype *) calloc( askfor , sizeof(nltype) );
|
||||
if (nl == 0) {
|
||||
warnx("no room for %d bytes of symbol table", askfor * sizeof(nltype) );
|
||||
done();
|
||||
}
|
||||
|
||||
/* pass2 - read symbols */
|
||||
fseek(nfile, (long)N_SYMOFF(xbuf), 0);
|
||||
npe = nl;
|
||||
nname = 0;
|
||||
for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
|
||||
fread(&nbuf, sizeof(nbuf), 1, nfile);
|
||||
if ( ! funcsymbol( &nbuf ) ) {
|
||||
# ifdef DEBUG
|
||||
if ( debug & AOUTDEBUG ) {
|
||||
printf( "[getsymtab] rejecting: 0x%x %s\n" ,
|
||||
nbuf.n_type , strtab + nbuf.n_un.n_strx );
|
||||
}
|
||||
# endif DEBUG
|
||||
continue;
|
||||
}
|
||||
npe->value = nbuf.n_value;
|
||||
npe->name = strtab+nbuf.n_un.n_strx;
|
||||
# ifdef DEBUG
|
||||
if ( debug & AOUTDEBUG ) {
|
||||
printf( "[getsymtab] %d %s 0x%08x\n" ,
|
||||
nname , npe -> name , npe -> value );
|
||||
}
|
||||
# endif DEBUG
|
||||
npe++;
|
||||
nname++;
|
||||
}
|
||||
npe->value = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* read in the text space of an a.out file
|
||||
*/
|
||||
gettextspace( nfile )
|
||||
FILE *nfile;
|
||||
{
|
||||
|
||||
if ( cflag == 0 ) {
|
||||
return;
|
||||
}
|
||||
textspace = (u_char *) malloc( xbuf.a_text );
|
||||
if ( textspace == 0 ) {
|
||||
warnx("ran out room for %d bytes of text space: can't do -c" ,
|
||||
xbuf.a_text );
|
||||
return;
|
||||
}
|
||||
(void) fseek( nfile , N_TXTOFF( xbuf ) , 0 );
|
||||
if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) {
|
||||
warnx("couldn't read text space: can't do -c");
|
||||
free( textspace );
|
||||
textspace = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* information from a gmon.out file is in two parts:
|
||||
* an array of sampling hits within pc ranges,
|
||||
@ -531,9 +395,14 @@ dumpsum( sumfile )
|
||||
fclose( sfile );
|
||||
}
|
||||
|
||||
valcmp(p1, p2)
|
||||
nltype *p1, *p2;
|
||||
static int
|
||||
valcmp(v1, v2)
|
||||
const void *v1;
|
||||
const void *v2;
|
||||
{
|
||||
const nltype *p1 = (const nltype *)v1;
|
||||
const nltype *p2 = (const nltype *)v2;
|
||||
|
||||
if ( p1 -> value < p2 -> value ) {
|
||||
return LESSTHAN;
|
||||
}
|
||||
@ -711,48 +580,6 @@ alignentries()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
funcsymbol( nlistp )
|
||||
struct nlist *nlistp;
|
||||
{
|
||||
char *name, c;
|
||||
|
||||
/*
|
||||
* must be a text symbol,
|
||||
* and static text symbols don't qualify if aflag set.
|
||||
*/
|
||||
if ( ! ( ( nlistp -> n_type == ( N_TEXT | N_EXT ) )
|
||||
|| ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) {
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* name must start with an underscore if uflag is set.
|
||||
* can't have any `funny' characters in name,
|
||||
* where `funny' means `.' (.o file names)
|
||||
* need to make an exception for sparc .mul & co.
|
||||
* perhaps we should just drop this code entirely...
|
||||
*/
|
||||
name = strtab + nlistp -> n_un.n_strx;
|
||||
if ( uflag && *name != '_' )
|
||||
return FALSE;
|
||||
#ifdef sparc
|
||||
if ( *name == '.' ) {
|
||||
char *p = name + 1;
|
||||
if ( *p == 'u' )
|
||||
p++;
|
||||
if ( strcmp ( p, "mul" ) == 0 || strcmp ( p, "div" ) == 0 ||
|
||||
strcmp ( p, "rem" ) == 0 )
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
while ( c = *name++ ) {
|
||||
if ( c == '.' ) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
done()
|
||||
{
|
||||
|
||||
|
@ -37,7 +37,6 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/gmon.h>
|
||||
|
||||
#include <a.out.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@ -121,7 +120,7 @@ typedef struct arcstruct arctype;
|
||||
* its address, the number of calls and compute its share of cpu time.
|
||||
*/
|
||||
struct nl {
|
||||
char *name; /* the name */
|
||||
const char *name; /* the name */
|
||||
unsigned long value; /* the pc entry point */
|
||||
unsigned long svalue; /* entry point aligned to histograms */
|
||||
double time; /* ticks in this routine */
|
||||
@ -217,9 +216,6 @@ double totime; /* total time for all routines */
|
||||
double printtime; /* total of time being printed */
|
||||
double scale; /* scale factor converting samples to pc
|
||||
values: each sample covers scale bytes */
|
||||
char *strtab; /* string table in core */
|
||||
long ssiz; /* size of the string table */
|
||||
struct exec xbuf; /* exec header of a.out */
|
||||
unsigned char *textspace; /* text space of a.out in core */
|
||||
int cyclethreshold; /* with -C, minimum cycle size to ignore */
|
||||
|
||||
@ -237,6 +233,7 @@ bool fflag; /* specific functions requested */
|
||||
bool Fflag; /* functions requested with time */
|
||||
bool kflag; /* arcs to be deleted */
|
||||
bool sflag; /* sum multiple gmon.out files */
|
||||
bool uflag; /* suppress symbols hidden from C */
|
||||
bool zflag; /* zero time/called functions, too */
|
||||
|
||||
/*
|
||||
@ -259,6 +256,7 @@ struct stringlist *ktolist;
|
||||
/*
|
||||
addarc();
|
||||
*/
|
||||
int aout_getnfile(const char *, char ***);
|
||||
int arccmp();
|
||||
arctype *arclookup();
|
||||
/*
|
||||
@ -280,17 +278,15 @@ bool dfn_numbered();
|
||||
nltype **doarcs();
|
||||
/*
|
||||
done();
|
||||
*/
|
||||
int elf_getnfile(const char *, char ***);
|
||||
/*
|
||||
findcalls();
|
||||
flatprofheader();
|
||||
flatprofline();
|
||||
*/
|
||||
bool funcsymbol();
|
||||
/*
|
||||
getnfile();
|
||||
getpfile();
|
||||
getstrtab();
|
||||
getsymtab();
|
||||
gettextspace();
|
||||
gprofheader();
|
||||
gprofline();
|
||||
main();
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
PROG= gprof4
|
||||
NOMAN= noman
|
||||
SRCS= gprof.c arcs.c dfn.c lookup.c ${MACHINE_ARCH}.c hertz.c \
|
||||
SRCS= gprof.c aout.c arcs.c dfn.c elf.c lookup.c ${MACHINE_ARCH}.c hertz.c \
|
||||
printgprof.c printlist.c
|
||||
CFLAGS+=-DGPROF4
|
||||
.PATH: ${.CURDIR}/../../usr.bin/gprof
|
||||
|
Loading…
Reference in New Issue
Block a user