newlib-cygwin/winsup/utils/gmondump.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;
}