Cygwin: New tool: gmondump

This new tool was formerly part of 'profiler' but was spun out thanks to
Jon T's reasonable review comment.  Gmondump is more of a debugging tool
than something users might have need for.  Users would more likely use
gprof to make use of symbolic info like function names and source line
numbers.
This commit is contained in:
Mark Geisert 2021-07-15 21:49:56 -07:00 committed by Jon Turney
parent 9bd6c0b2b1
commit 4ad5b0ca31
No known key found for this signature in database
GPG Key ID: C7C86F0370285C81
1 changed files with 255 additions and 0 deletions

255
winsup/utils/gmondump.c Normal file
View File

@ -0,0 +1,255 @@
/*
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
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)
{
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);
if (numbuckets != (hdr.hpc - hdr.lpc) / 4)
goto notgmon;
numrawarcs = 0;
if (stat.st_size != hdr.ncnt)
{
numrawarcs = stat.st_size - hdr.ncnt;
if (numrawarcs !=
(int) sizeof (rawarc) * (numrawarcs / (int) sizeof (rawarc)))
goto notgmon;
numrawarcs /= (int) sizeof (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 0x%p..0x%p\n", hdr.lpc, hdr.hpc);
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");
char *addr = (char *) hdr.lpc;
int incr = (hdr.hpc - hdr.lpc) / numbuckets;
for (res = 0; res < numbuckets; ++bucket, ++res, addr += incr)
if (*bucket)
note (" address 0x%p, hitcount %d\n", addr, *bucket);
bucket -= numbuckets;
if (numrawarcs)
{
rawarc = (struct rawarc *) calloc (numrawarcs, sizeof (rawarc));
res = read (fd, rawarc, numrawarcs * (int) sizeof (rawarc));
if (res != numrawarcs * (int) sizeof (rawarc))
error (0, "unable to read rawarc data");
note (" rawarc data follows...\n");
for (res = 0; res < numrawarcs; ++rawarc, ++res)
note (" from 0x%p, self 0x%p, count %d\n",
rawarc->raw_frompc, rawarc->raw_selfpc, rawarc->raw_count);
}
}
note ("\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:
;
}
for (int i = optind; i < argc; i++)
gmondump1 (argv[i]);
return 0;
}