/* gmondump.c Displays summary info about given profile data file(s). Written by Mark Geisert . 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 #include #include #include #include #include #include #include #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[BUFSIZ]; 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[BUFSIZ]; 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; char buf[BUFSIZ]; va_start (args, fmt); vsprintf (buf, fmt, args); va_end (args); warn (geterrno, "%s", buf); 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; }