[PATCH 3/3] Cygwin: updates to wire in profiler, gmondump

2021-07-15 Thread Mark Geisert
These are updates to wire into the build tree the new tools profiler and
gmondump, and to supply documentation for the tools.

The documentation for profiler and ssp now mention each other but do not
discuss their similarities or differences.  That will be handled in a
future update to the "Profiling Cygwin Programs" section of the Cygwin
User's Guide, to be supplied.

---
 winsup/cygwin/release/3.2.1 |   7 ++
 winsup/doc/utils.xml| 123 
 winsup/utils/Makefile.am|   5 ++
 3 files changed, 135 insertions(+)

diff --git a/winsup/cygwin/release/3.2.1 b/winsup/cygwin/release/3.2.1
index 99c65ce30..4f4db622a 100644
--- a/winsup/cygwin/release/3.2.1
+++ b/winsup/cygwin/release/3.2.1
@@ -1,6 +1,13 @@
 What's new:
 ---
 
+- An IP-sampling profiler named 'profiler' has been added.  It can be used
+  to profile any Cygwin program along with any DLLs loaded.
+
+- A new tool 'gmondump' has been added.  It can dump the raw information
+  of any "gmon.out" file created by profiler, ssp, or use of the gcc/g++
+  option '-pg'.  (Continue using gprof to get symbolic profile displays.)
+
 
 What changed:
 -
diff --git a/winsup/doc/utils.xml b/winsup/doc/utils.xml
index 1d9b8488c..0b7b5d0ea 100644
--- a/winsup/doc/utils.xml
+++ b/winsup/doc/utils.xml
@@ -793,6 +793,56 @@ line separates the ACLs for each file.
 
   
 
+  
+
+  gmondump
+  1
+  Cygwin Utilities
+
+
+
+  gmondump
+  Display formatted contents of profile data files
+
+
+
+
+gmondump [OPTION]... FILENAME...
+
+
+
+
+  Options
+  
+  -h, --help Display usage information and exit
+  -v, --verbose  Display more file details (toggle: default false)
+  -V, --version  Display version information and exit
+
+
+
+
+  Description
+The gmondump utility displays the contents of
+  one or more profile data files. Such files usually have names starting
+  with "gmon.out" and are created by a profiling program such as
+  profiler or ssp. Compiling your
+  gcc/g++ programs with option -pg also works.
+ By default, summary information is shown. You can use the
+  option -v to get more detailed displays.
+Note that gmondump just displays the raw data;
+  one would usually use gprof to display the data in
+  a useful form incorporating symbolic info such as function names and
+  source line numbers.
+Here is an example of gmondump operation:
+
+$ gmondump gmon.out.21900.zstd.exe
+file gmon.out.21900.zstd.exe, gmon version 0x51879, sample rate 100
+  address range 0x0x100401000..0x0x1004cc668
+  numbuckets 208282, hitbuckets 1199, hitcount 12124, numrawarcs 0
+
+
+  
+
   
 
   kill
@@ -2127,6 +2177,75 @@ specifying an empty password.
 
   
 
+  
+
+  profiler
+  1
+  Cygwin Utilities
+
+
+
+  profiler
+  Sampling profiler of Cygwin programs with their 
DLLs
+
+
+
+
+profiler [OPTION]... PROGRAM [ARG]...
+profiler [OPTION]... -p PID
+
+
+
+
+  Options
+  
+  -d, --debugDisplay debugging messages (toggle: default false)
+  -e, --events   Display Windows DEBUG_EVENTS (toggle: default false)
+  -f, --fork-profile Profiles child processes (toggle: default false)
+  -h, --help Display usage information and exit
+  -o, --output=FILENAME  Write output to file FILENAME rather than stdout
+  -p, --pid=NAttach to running program with Cygwin pid N
+ ...or with Windows pid -N
+  -s, --sample-rate=NSet IP sampling rate to N Hz (default 100)
+  -v, --verbose  Display more status messages (toggle: default false)
+  -V, --version  Display version information and exit
+  -w, --new-window   Launch given command in a new window
+
+
+
+
+  Description
+The profiler utility executes a given program, and
+  optionally the children of that program, collecting the location of the
+  CPU instruction pointer (IP) many times per second. This gives a profile
+  of the program's execution, showing where the most time is being spent.
+  This profiling technique is called "IP sampling".
+
+A novel feature of profiler is that time spent in
+  DLLs loaded with or by your program is profiled too. You use
+  gprof to process and display the resulting profile
+  information. In this fashion you can determine whether your own code,
+  the Cygwin DLL, or another DLL has "hot spots" that might benefit from
+  tuning.
+
+(See also ssp, another profiler that
+  operates in a different fashion: stepping by instruction. This can
+  provide a different view on your program's operation.)
+
+Here is an example of profiler operation:
+
+$ profiler du -khs .
+22G .
+97 samples across 83 buckets written to gmon.out.5908.cygwin1.dll
+4 samples 

[PATCH 2/3] Cygwin: New tool: gmondump

2021-07-15 Thread Mark Geisert
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 0..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 .
+
+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
+note (const char *fmt, ...)
+{
+  va_list args;
+  charbuf[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;
+  charbuf[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;
+  intfd;
+  struct gmonhdr hdr;
+  inthitbuckets;
+  inthitcount;
+  intnumbuckets;
+  intnumrawarcs;
+  struct rawarc *rawarc = NULL;
+  intres;
+  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 = (stru

[PATCH 1/3] Cygwin: New tool: profiler

2021-07-15 Thread Mark Geisert
The new tool formerly known as cygmon is renamed to 'profiler'.  For the
name I considered 'ipsampler' and could not think of any others.  I'm open
to a different name if any is suggested.

I decided that a discussion of the pros and cons of this profiler vs the
existing ssp should probably be in the "Profiling Cygwin Programs" section
of the Cygwin User's Guide rather than in the help for either.  That
material will be supplied at some point.

CONTEXT buffers are made child-specific and thus thread-specific since
there is one profiler thread for each child program being profiled.

The SetThreadPriority() warning comment has been expanded.

chmod() works on Cygwin so the "//XXX ineffective" comment is gone.

I decided to make the "sample all executable sections" and "sample
dynamically generated code" suggestions simply expanded comments for now.

The profiler program is now a Cygwin exe rather than a native exe.

---
 winsup/utils/profiler.cc | 1109 ++
 1 file changed, 1109 insertions(+)
 create mode 100644 winsup/utils/profiler.cc

diff --git a/winsup/utils/profiler.cc b/winsup/utils/profiler.cc
new file mode 100644
index 0..d1a01c3a2
--- /dev/null
+++ b/winsup/utils/profiler.cc
@@ -0,0 +1,1109 @@
+/*
+profiler.cc
+Periodically samples IP of a process and its DLLs; writes gprof data files.
+
+Written by Mark Geisert , who admits to
+copying pretty liberally from strace.cc.  h/t to cgf for strace!
+
+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.
+*/
+
+#define WIN32_LEAN_AND_MEAN
+#include 
+
+#define cygwin_internal cygwin_internal_dontuse
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "cygwin/version.h"
+#include "cygtls_padsize.h"
+#include "gcc_seh.h"
+typedef unsigned short ushort;
+typedef uint16_t u_int16_t; // Non-standard sized type needed by ancient gmon.h
+#define NO_GLOBALS_H
+#include "gmon.h"
+#include "path.h"
+#undef cygwin_internal
+
+/* Undo this #define from winsup.h. */
+#ifdef ExitThread
+#undef ExitThread
+#endif
+
+#define SCALE_SHIFT 2 // == 4 bytes of address space per bucket
+#define MS_VC_EXCEPTION 0x406D1388 // thread name notification from child
+
+DWORD   child_pid;
+int debugging = 0;
+void   *drive_map;
+int events = 0;
+int forkprofile = 0;
+int new_window;
+int numprocesses;
+FILE   *ofile = stdout;
+const char *pgm;
+char   *prefix = (char *) "gmon.out";
+int samplerate = 100; // in Hz; up to 1000 might work
+int verbose = 0;
+
+void __attribute__ ((__noreturn__))
+usage (FILE *where = stderr)
+{
+  fprintf (where, "\
+Usage: %s [OPTIONS] \n\
+   or: %s [OPTIONS] -p \n\
+\n\
+Profiles a command or process by sampling its IP (instruction pointer).\n\
+OPTIONS are:\n\
+\n\
+  -d, --debugDisplay debugging messages (toggle: default false)\n\
+  -e, --events   Display Windows DEBUG_EVENTS (toggle: default 
false)\n\
+  -f, --fork-profile Profile child processes (toggle: default false)\n\
+  -h, --help Display usage information and exit\n\
+  -o, --output=FILENAME  Write output to file FILENAME rather than stdout\n\
+  -p, --pid=NAttach to running program with Cygwin pid N\n\
+ ...or with Windows pid -N\n\
+  -s, --sample-rate=NSet IP sampling rate to N Hz (default 100)\n\
+  -v, --verbose  Display more status messages (toggle: default 
false)\n\
+  -V, --version  Display version information and exit\n\
+  -w, --new-window   Launch given command in a new window\n\
+\n", pgm, pgm);
+
+  exit (where == stderr ? 1 : 0 );
+}
+
+/* A span is a memory address range covering an EXE's or DLL's .text segment. 
*/
+struct span_list
+{
+  WCHAR  *name;
+  LPVOID  base;
+  size_t  textlo;
+  size_t  texthi;
+  int hitcount;
+  int hitbuckets;
+  int numbuckets;
+  int*buckets;
+  struct span_list *next;
+};
+
+/* A thread. */
+struct thread_list
+{
+  DWORD   tid;
+  HANDLE  hthread;
+  WCHAR  *name;
+  struct thread_list *next;
+};
+
+/* A child is any process being sampled in this profiler run. */
+struct child_list
+{
+  DWORD  pid;
+  volatile int profiling;
+  HANDLE hproc;
+  HANDLE hquitevt;
+  HANDLE hprofthr;
+  CONTEXT*context;
+  struct thread_list *threads;
+  struct span_list   *spans;
+  struct child_list  *next;
+};
+
+child_list children;
+typedef struct child_list child;
+
+void
+note (const char *fmt, ...)
+{
+  va_list args;
+  charbuf[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;
+  charbuf[4096];
+
+  va_start (args, fmt