public inbox for cygwin-patches@cygwin.com
 help / color / mirror / Atom feed
From: Mark Geisert <mark@maxrnd.com>
To: cygwin-patches@cygwin.com
Subject: [PATCH 2/3] Cygwin: New tool: gmondump
Date: Thu, 15 Jul 2021 21:49:56 -0700	[thread overview]
Message-ID: <20210716044957.5298-2-mark@maxrnd.com> (raw)
In-Reply-To: <20210716044957.5298-1-mark@maxrnd.com>

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.

---
 winsup/utils/gmondump.c | 255 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 255 insertions(+)
 create mode 100644 winsup/utils/gmondump.c

diff --git a/winsup/utils/gmondump.c b/winsup/utils/gmondump.c
new file mode 100644
index 000000000..e469f01f1
--- /dev/null
+++ b/winsup/utils/gmondump.c
@@ -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;
+}
-- 
2.31.1


  reply	other threads:[~2021-07-16  4:50 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-07-16  4:49 [PATCH 1/3] Cygwin: New tool: profiler Mark Geisert
2021-07-16  4:49 ` Mark Geisert [this message]
2021-07-16  4:49 ` [PATCH 3/3] Cygwin: updates to wire in profiler, gmondump Mark Geisert
2021-07-19 10:04 ` [PATCH 1/3] Cygwin: New tool: profiler Corinna Vinschen
2021-07-19 14:23   ` Jon Turney
2021-07-19 15:43     ` Jon Turney
2021-07-21  8:00       ` [PATCH] Cygwin: fix format warnings in profiler.cc Mark Geisert
2021-07-21  8:09         ` Corinna Vinschen
2021-07-21  8:07       ` [PATCH 1/3] Cygwin: New tool: profiler Corinna Vinschen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210716044957.5298-2-mark@maxrnd.com \
    --to=mark@maxrnd.com \
    --cc=cygwin-patches@cygwin.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).