mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-01-22 23:17:28 +08:00
aa460cc0ca
Cygwin tool ssp generates gmon.out files with different address resolution than other tools do. Two address bytes per bucket rather than the usual four address bytes. Gprof can deal with the difference but gmondump can't because the latter's gmon.out header validation fails. - Remove the offending portion of the header validation code. - Make sure all code can handle differing address resolutions. - Display address resolution in verbose data dumps. - Change "rawarc" to "struct rawarc" in certain sizeof expressions to avoid buffer overrun faults. - When "-v" (verbose) is specified, note when there is missing bucket data or rawarc data.
277 lines
6.7 KiB
C
277 lines
6.7 KiB
C
/*
|
|
gmondump.c
|
|
Displays summary info about given profile data file(s).
|
|
|
|
Written by Mark Geisert <mark@maxrnd.com>.
|
|
|
|
This file is part of Cygwin.
|
|
|
|
This software is a copyrighted work licensed under the terms of the
|
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for details.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include "cygwin/version.h"
|
|
|
|
typedef unsigned short ushort;
|
|
typedef uint16_t u_int16_t; // Non-standard sized type needed by ancient gmon.h
|
|
#include "gmon.h"
|
|
|
|
FILE *ofile;
|
|
const char *pgm = "gmondump";
|
|
int verbose = 0;
|
|
|
|
void __attribute__ ((__noreturn__))
|
|
usage (FILE *where)
|
|
{
|
|
fprintf (where, "\
|
|
Usage: %s [OPTIONS] FILENAME...\n\
|
|
\n\
|
|
Display formatted contents of profile data file(s).\n\
|
|
Such files usually have names starting with \"gmon.out\".\n\
|
|
OPTIONS are:\n\
|
|
\n\
|
|
-h, --help Display usage information and exit\n\
|
|
-v, --verbose Display more file details (toggle: default false)\n\
|
|
-V, --version Display version information and exit\n\
|
|
\n", pgm);
|
|
|
|
exit (where == stderr ? 1 : 0 );
|
|
}
|
|
|
|
void __attribute__ ((__noreturn__))
|
|
usage1 (FILE *where)
|
|
{
|
|
fprintf (where, "Usage: %s [OPTIONS] FILENAME...\n", pgm);
|
|
|
|
exit (where == stderr ? 1 : 0 );
|
|
}
|
|
|
|
void
|
|
note (const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
char buf[4096];
|
|
|
|
va_start (args, fmt);
|
|
vsprintf (buf, fmt, args);
|
|
va_end (args);
|
|
|
|
fputs (buf, ofile);
|
|
fflush (ofile);
|
|
}
|
|
|
|
void
|
|
warn (int geterrno, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
char buf[4096];
|
|
|
|
va_start (args, fmt);
|
|
sprintf (buf, "%s: ", pgm);
|
|
vsprintf (strchr (buf, '\0'), fmt, args);
|
|
va_end (args);
|
|
if (geterrno)
|
|
perror (buf);
|
|
else
|
|
{
|
|
fputs (buf, ofile);
|
|
fputs ("\n", ofile);
|
|
fflush (ofile);
|
|
}
|
|
}
|
|
|
|
void __attribute__ ((noreturn))
|
|
error (int geterrno, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start (args, fmt);
|
|
warn (geterrno, fmt, args);
|
|
va_end (args);
|
|
|
|
exit (1);
|
|
}
|
|
|
|
void
|
|
gmondump1 (char *filename)
|
|
{
|
|
int addrincr;
|
|
ushort *bucket = NULL;
|
|
int fd;
|
|
struct gmonhdr hdr;
|
|
int hitbuckets;
|
|
int hitcount;
|
|
int numbuckets;
|
|
int numrawarcs;
|
|
struct rawarc *rawarc = NULL;
|
|
int res;
|
|
struct stat stat;
|
|
|
|
fd = open (filename, O_RDONLY | O_BINARY);
|
|
if (fd < 0)
|
|
{
|
|
note ("file%s %s couldn't be opened; continuing\n",
|
|
strchr (filename, '*') ? "s" : "", filename);
|
|
return;
|
|
}
|
|
|
|
/* Read and sanity-check what should be a gmon header. */
|
|
res = fstat (fd, &stat);
|
|
if (res < 0)
|
|
goto notgmon;
|
|
if (S_IFREG != (stat.st_mode & S_IFMT))
|
|
goto notgmon;
|
|
res = read (fd, &hdr, sizeof (hdr));
|
|
if (res != sizeof (hdr))
|
|
goto notgmon;
|
|
if (hdr.lpc >= hdr.hpc)
|
|
goto notgmon;
|
|
numbuckets = (hdr.ncnt - sizeof (hdr)) / sizeof (short);
|
|
addrincr = (hdr.hpc - hdr.lpc) / numbuckets;
|
|
numrawarcs = 0;
|
|
if (stat.st_size != hdr.ncnt)
|
|
{
|
|
numrawarcs = stat.st_size - hdr.ncnt;
|
|
if (numrawarcs != (int) sizeof (struct rawarc) *
|
|
(numrawarcs / (int) sizeof (struct rawarc)))
|
|
goto notgmon;
|
|
numrawarcs /= (int) sizeof (struct rawarc);
|
|
}
|
|
|
|
/* Looks good, so read and display the profiling info. */
|
|
bucket = (ushort *) calloc (numbuckets, sizeof (ushort));
|
|
res = read (fd, bucket, hdr.ncnt - sizeof (hdr));
|
|
if (res != hdr.ncnt - (int) sizeof (hdr))
|
|
goto notgmon;
|
|
hitcount = hitbuckets = 0;
|
|
for (res = 0; res < numbuckets; ++bucket, ++res)
|
|
if (*bucket)
|
|
{
|
|
++hitbuckets;
|
|
hitcount += *bucket;
|
|
}
|
|
bucket -= numbuckets;
|
|
|
|
note ("file %s, gmon version 0x%x, sample rate %d\n",
|
|
filename, hdr.version, hdr.profrate);
|
|
note (" address range %p..%p, address increment %d/bucket\n",
|
|
hdr.lpc, hdr.hpc, addrincr);
|
|
note (" numbuckets %d, hitbuckets %d, hitcount %d, numrawarcs %d\n",
|
|
numbuckets, hitbuckets, hitcount, numrawarcs);
|
|
|
|
/* If verbose is set, display contents of buckets and rawarcs arrays. */
|
|
if (verbose)
|
|
{
|
|
if (hitbuckets)
|
|
note (" bucket data follows...\n");
|
|
else
|
|
note (" no bucket data present\n");
|
|
char *addr = (char *) hdr.lpc;
|
|
for (res = 0; res < numbuckets; ++bucket, ++res, addr += addrincr)
|
|
if (*bucket)
|
|
note (" address %p, hitcount %d\n", addr, *bucket);
|
|
bucket -= numbuckets;
|
|
|
|
if (numrawarcs)
|
|
{
|
|
rawarc = (struct rawarc *) calloc (numrawarcs,
|
|
sizeof (struct rawarc));
|
|
res = read (fd, rawarc, numrawarcs * (int) sizeof (struct rawarc));
|
|
if (res != numrawarcs * (int) sizeof (struct rawarc))
|
|
error (0, "unable to read rawarc data");
|
|
note (" rawarc data follows...\n");
|
|
for (res = 0; res < numrawarcs; ++rawarc, ++res)
|
|
note (" from %p, self %p, count %d\n",
|
|
rawarc->raw_frompc, rawarc->raw_selfpc, rawarc->raw_count);
|
|
rawarc -= numrawarcs;
|
|
}
|
|
else
|
|
note (" no rawarc data present\n");
|
|
}
|
|
|
|
if (0)
|
|
{
|
|
notgmon:
|
|
note ("file %s isn't a profile data file; continuing\n", filename);
|
|
}
|
|
if (rawarc)
|
|
free (rawarc);
|
|
if (bucket)
|
|
free (bucket);
|
|
close (fd);
|
|
}
|
|
|
|
struct option longopts[] = {
|
|
{"help", no_argument, NULL, 'h'},
|
|
{"verbose", no_argument, NULL, 'v'},
|
|
{"version", no_argument, NULL, 'V'},
|
|
{NULL, 0, NULL, 0 }
|
|
};
|
|
|
|
const char *const opts = "+hvV";
|
|
|
|
void __attribute__ ((__noreturn__))
|
|
print_version ()
|
|
{
|
|
char *year_of_build = strrchr (__DATE__, ' ') + 1;
|
|
printf ("gmondump (cygwin) %d.%d.%d\n"
|
|
"Profiler data file viewer\n"
|
|
"Copyright (C) %s%s Cygwin Authors\n"
|
|
"This is free software; see the source for copying conditions. "
|
|
"There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS "
|
|
"FOR A PARTICULAR PURPOSE.\n",
|
|
CYGWIN_VERSION_DLL_MAJOR / 1000,
|
|
CYGWIN_VERSION_DLL_MAJOR % 1000,
|
|
CYGWIN_VERSION_DLL_MINOR,
|
|
strncmp (year_of_build, "2021", 4) ? "2021 - " : "",
|
|
year_of_build);
|
|
exit (0);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
ofile = stdout;
|
|
int opt;
|
|
|
|
while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
|
|
switch (opt)
|
|
{
|
|
case 'h':
|
|
/* Print help and exit. */
|
|
usage (ofile);
|
|
|
|
case 'v':
|
|
verbose ^= 1;
|
|
break;
|
|
|
|
case 'V':
|
|
/* Print version and exit. */
|
|
print_version ();
|
|
|
|
default:
|
|
;
|
|
}
|
|
|
|
if (optind >= argc)
|
|
/* Print one-line help and exit. */
|
|
usage1 (ofile);
|
|
|
|
for (int i = optind; i < argc; i++)
|
|
{
|
|
gmondump1 (argv[i]);
|
|
if ((i + 1) < argc)
|
|
note ("\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|