Index: /tags/ipp-unions-20250528/Ohana/src/opihi/include/pcontrol.h
===================================================================
--- /tags/ipp-unions-20250528/Ohana/src/opihi/include/pcontrol.h	(revision 42894)
+++ /tags/ipp-unions-20250528/Ohana/src/opihi/include/pcontrol.h	(revision 42895)
@@ -343,4 +343,13 @@
 void QuitCheckSystemThread (void);
 
+void Stats_CheckBusyJobs (int reset);
+void Stats_CheckDoneJobs (int reset);
+void Stats_CheckKillJobs (int reset);
+void Stats_CheckRespHosts (int reset);
+void Stats_CheckDoneHosts (int reset);
+void Stats_CheckDownHosts (int reset);
+void Stats_CheckIdleHosts (int reset);
+void Stats_CheckLiveHosts (int reset);
+
 // Job   *FindJobByID (IDtype JobID, int *StackID);
 // Job   *FindJobInStackByID (int StackID, int ID);
Index: /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/Makefile
===================================================================
--- /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/Makefile	(revision 42894)
+++ /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/Makefile	(revision 42895)
@@ -83,4 +83,5 @@
 $(SRC)/controller_output.$(ARCH).o \
 $(SRC)/controller_pulse.$(ARCH).o \
+$(SRC)/controller_pcstats.$(ARCH).o \
 $(SRC)/task.$(ARCH).o \
 $(SRC)/task_host.$(ARCH).o \
Index: /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/controller.c
===================================================================
--- /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/controller.c	(revision 42894)
+++ /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/controller.c	(revision 42895)
@@ -14,4 +14,5 @@
 int controller_check   	  PROTO((int, char **));
 int controller_output  	  PROTO((int, char **));
+int controller_pcstats 	  PROTO((int, char **));
 int controller_pulse   	  PROTO((int, char **));
 
@@ -32,4 +33,5 @@
   {1, "verbose",   controller_verbose,    "set controller verbosity"},
   {1, "version",   controller_version,    "show controller version"},
+  {1, "pcstats",   controller_pcstats,    "show pcontrol throughput stats"},
   {1, "pulse",     controller_pulse,      "set controller pulse"},
 };
Index: /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/controller_hoststack.c
===================================================================
--- /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/controller_hoststack.c	(revision 42894)
+++ /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/controller_hoststack.c	(revision 42895)
@@ -9,5 +9,5 @@
   if (argc != 2) {
     gprint (GP_ERR, "USAGE: controller hoststack (hoststack)\n");
-    gprint (GP_ERR, "       (hoststack) : pending, busy, exit, crash, hung, done\n");
+    gprint (GP_ERR, "       (hoststack) : idle, busy, done, down, off\n");
     return (FALSE);
   }
Index: /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/controller_pcstats.c
===================================================================
--- /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/controller_pcstats.c	(revision 42895)
+++ /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/controller_pcstats.c	(revision 42895)
@@ -0,0 +1,50 @@
+# include "pantasks.h"
+
+int controller_pcstats (int argc, char **argv) {
+  OHANA_UNUSED_PARAM(argv);
+
+  int N, status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (get_argument (argc, argv, "-h")) goto help;
+  if (get_argument (argc, argv, "-help")) goto help;
+  if (get_argument (argc, argv, "--help")) goto help;
+
+  int Reset = FALSE;
+  if ((N = get_argument (argc, argv, "-reset"))) {
+    remove_argument (N, &argc, argv);
+    Reset = TRUE;
+  }
+
+  if (argc != 1) goto help;
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+  if (Reset) {
+    sprintf (command, "pcstats -reset");
+  } else {
+    sprintf (command, "pcstats");
+  }
+  
+  InitIOBuffer (&buffer, 0x100);
+
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not communicating\n");
+  }
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+
+help:
+  gprint (GP_LOG, "USAGE: controller pcstats [-reset]\n");
+  return (FALSE);
+}
Index: /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/CheckSystem.c
===================================================================
--- /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/CheckSystem.c	(revision 42894)
+++ /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/CheckSystem.c	(revision 42895)
@@ -155,4 +155,23 @@
 }
 
+// CheckBusyJobs : Stats
+
+static int   Nt_CheckBusyJobs = 0;
+static int   Np_CheckBusyJobs = 0;
+static float dT_CheckBusyJobs = 0.0;
+
+void Stats_CheckBusyJobs (int reset) {
+
+  gprint (GP_LOG, "CBJ: Npass: %d, Ntest: %d, rate: %f\n", Np_CheckBusyJobs, Nt_CheckBusyJobs, Nt_CheckBusyJobs / dT_CheckBusyJobs);
+
+  if (reset) {
+    Nt_CheckBusyJobs = 0;
+    Np_CheckBusyJobs = 0;
+    dT_CheckBusyJobs = 0.0;
+  }
+}
+
+// CheckBusyJobs : Func
+
 int CheckBusyJobs (float MaxDelay) {
 
@@ -195,6 +214,32 @@
   }
   if (DEBUG && (Nobject > 0)) gprint (GP_ERR, "checked %d of %d jobs\n", i, Nobject);
+
+  Np_CheckBusyJobs ++;
+  Nt_CheckBusyJobs += i;
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  dT_CheckBusyJobs += dtime;
   return (i);
 }
+
+// CheckDoneJobs : Stats
+
+static int   Nt_CheckDoneJobs = 0;
+static int   Np_CheckDoneJobs = 0;
+static float dT_CheckDoneJobs = 0.0;
+
+void Stats_CheckDoneJobs (int reset) {
+
+  gprint (GP_LOG, "CDJ: Npass: %d, Ntest: %d, rate: %f\n", Np_CheckDoneJobs, Nt_CheckDoneJobs, Nt_CheckDoneJobs / dT_CheckDoneJobs);
+
+  if (reset) {
+    Nt_CheckDoneJobs = 0;
+    Np_CheckDoneJobs = 0;
+    dT_CheckDoneJobs = 0.0;
+  }
+}
+
+// CheckDoneJobs : Func
 
 int CheckDoneJobs (float MaxDelay) {
@@ -234,6 +279,32 @@
   }
   if (DEBUG && (Nobject > 0)) gprint (GP_ERR, "checked %d of %d jobs\n", i, Nobject);
+
+  Np_CheckDoneJobs ++;
+  Nt_CheckDoneJobs += i;
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  dT_CheckDoneJobs += dtime;
   return (i);
 }
+
+// CheckKillJobs : Stats
+
+static int   Nt_CheckKillJobs = 0;
+static int   Np_CheckKillJobs = 0;
+static float dT_CheckKillJobs = 0.0;
+
+void Stats_CheckKillJobs (int reset) {
+
+  gprint (GP_LOG, "CKJ: Npass: %d, Ntest: %d, rate: %f\n", Np_CheckKillJobs, Nt_CheckKillJobs, Nt_CheckKillJobs / dT_CheckKillJobs);
+
+  if (reset) {
+    Nt_CheckKillJobs = 0;
+    Np_CheckKillJobs = 0;
+    dT_CheckKillJobs = 0.0;
+  }
+}
+
+// CheckKillJobs : Func
 
 int CheckKillJobs (float MaxDelay) {
@@ -273,6 +344,32 @@
   }
   if (DEBUG && (Nobject > 0)) gprint (GP_ERR, "checked %d of %d jobs\n", i, Nobject);
+
+  Np_CheckKillJobs ++;
+  Nt_CheckKillJobs += i;
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  dT_CheckKillJobs += dtime;
   return (i);
 }
+
+// CheckRespHosts : Stats
+
+static int   Nt_CheckRespHosts = 0;
+static int   Np_CheckRespHosts = 0;
+static float dT_CheckRespHosts = 0.0;
+
+void Stats_CheckRespHosts (int reset) {
+
+  gprint (GP_LOG, "CRH: Npass: %d, Ntest: %d, rate: %f\n", Np_CheckRespHosts, Nt_CheckRespHosts, Nt_CheckRespHosts / dT_CheckRespHosts);
+
+  if (reset) {
+    Nt_CheckRespHosts = 0;
+    Np_CheckRespHosts = 0;
+    dT_CheckRespHosts = 0.0;
+  }
+}
+
+// CheckRespHosts : Func
 
 int CheckRespHosts (float MaxDelay) {
@@ -314,6 +411,32 @@
   }
   if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+
+  Np_CheckRespHosts ++;
+  Nt_CheckRespHosts += i;
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  dT_CheckRespHosts += dtime;
   return (i);
 }
+
+// CheckDoneHosts : Stats
+
+static int   Nt_CheckDoneHosts = 0;
+static int   Np_CheckDoneHosts = 0;
+static float dT_CheckDoneHosts = 0.0;
+
+void Stats_CheckDoneHosts (int reset) {
+
+  gprint (GP_LOG, "CDH: Npass: %d, Ntest: %d, rate: %f\n", Np_CheckDoneHosts, Nt_CheckDoneHosts, Nt_CheckDoneHosts / dT_CheckDoneHosts);
+
+  if (reset) {
+    Nt_CheckDoneHosts = 0;
+    Np_CheckDoneHosts = 0;
+    dT_CheckDoneHosts = 0.0;
+  }
+}
+
+// CheckDoneHosts : Func
 
 int CheckDoneHosts (float MaxDelay) {
@@ -340,6 +463,32 @@
   }
   if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+
+  Np_CheckDoneHosts ++;
+  Nt_CheckDoneHosts += i;
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  dT_CheckDoneHosts += dtime;
   return (i);
 }
+
+// CheckDownHosts : Stats
+
+static int   Nt_CheckDownHosts = 0;
+static int   Np_CheckDownHosts = 0;
+static float dT_CheckDownHosts = 0.0;
+
+void Stats_CheckDownHosts (int reset) {
+
+  gprint (GP_LOG, "CNH: Npass: %d, Ntest: %d, rate: %f\n", Np_CheckDownHosts, Nt_CheckDownHosts, Nt_CheckDownHosts / dT_CheckDownHosts);
+
+  if (reset) {
+    Nt_CheckDownHosts = 0;
+    Np_CheckDownHosts = 0;
+    dT_CheckDownHosts = 0.0;
+  }
+}
+
+// CheckDownHosts : Func
 
 int CheckDownHosts (float MaxDelay) {
@@ -380,6 +529,32 @@
   }
   if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+
+  Np_CheckDownHosts ++;
+  Nt_CheckDownHosts += i;
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  dT_CheckDownHosts += dtime;
   return (i);
 }
+
+// CheckIdleHosts : Stats
+
+static int   Nt_CheckIdleHosts = 0;
+static int   Np_CheckIdleHosts = 0;
+static float dT_CheckIdleHosts = 0.0;
+
+void Stats_CheckIdleHosts (int reset) {
+
+  gprint (GP_LOG, "CIH: Npass: %d, Ntest: %d, rate: %f\n", Np_CheckIdleHosts, Nt_CheckIdleHosts, Nt_CheckIdleHosts / dT_CheckIdleHosts);
+
+  if (reset) {
+    Nt_CheckIdleHosts = 0;
+    Np_CheckIdleHosts = 0;
+    dT_CheckIdleHosts = 0.0;
+  }
+}
+
+// CheckIdleHosts : Func
 
 // if we have any IDLE hosts, check if there are jobs to be launched 
@@ -399,4 +574,5 @@
   if (!stack[0].Nobject && (Stage != PCONTROL_JOB_STAGE_NEEDHOST)) return (0);
 
+  // XXX note that our throughput statistics do not include this section
   /* if there are no pending jobs, check for hosts that need to be reset */
   if (!stack[0].Nobject) {
@@ -431,6 +607,32 @@
 
   if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+
+  Np_CheckIdleHosts ++;
+  Nt_CheckIdleHosts += i;
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  dT_CheckIdleHosts += dtime;
   return (i);
 }
+
+// CheckLiveHosts : Stats
+
+static int   Nt_CheckLiveHosts = 0;
+static int   Np_CheckLiveHosts = 0;
+static float dT_CheckLiveHosts = 0.0;
+
+void Stats_CheckLiveHosts (int reset) {
+
+  gprint (GP_LOG, "CLH: Npass: %d, Ntest: %d, rate: %f\n", Np_CheckLiveHosts, Nt_CheckLiveHosts, Nt_CheckLiveHosts / dT_CheckLiveHosts);
+
+  if (reset) {
+    Nt_CheckLiveHosts = 0;
+    Np_CheckLiveHosts = 0;
+    dT_CheckLiveHosts = 0.0;
+  }
+}
+
+// CheckLiveHosts : Func
 
 /* this is just a heartbeat check (only IDLE hosts) */
@@ -458,4 +660,11 @@
   }
   if (DEBUG) gprint (GP_ERR, "checked %d idle hosts\n", i);
+
+  Np_CheckLiveHosts ++;
+  Nt_CheckLiveHosts += i;
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  dT_CheckLiveHosts += dtime;
   return (TRUE);
 }
Index: /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/Makefile
===================================================================
--- /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/Makefile	(revision 42894)
+++ /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/Makefile	(revision 42895)
@@ -54,4 +54,5 @@
 $(SRC)/pulse.$(ARCH).o \
 $(SRC)/parameters.$(ARCH).o \
+$(SRC)/pcstats.$(ARCH).o \
 $(SRC)/run.$(ARCH).o \
 $(SRC)/machines.$(ARCH).o \
Index: /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/init.c
===================================================================
--- /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/init.c	(revision 42894)
+++ /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/init.c	(revision 42895)
@@ -10,4 +10,5 @@
 int machines    PROTO((int, char **));
 int parameters  PROTO((int, char **));
+int pcstats     PROTO((int, char **));
 int run         PROTO((int, char **));
 int status      PROTO((int, char **));
@@ -30,4 +31,5 @@
   {1, "machines",   machines,   "list machines"},
   {1, "parameters", parameters, "get / set system parameters"},
+  {1, "pcstats",    pcstats,    "show pcontrol througput stats"},
   {1, "run",        run,        "set controller runlevel"},
   {1, "status",     status,     "get system status"},
Index: /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/pcstats.c
===================================================================
--- /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/pcstats.c	(revision 42895)
+++ /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/pcstats.c	(revision 42895)
@@ -0,0 +1,33 @@
+# include "pcontrol.h"
+
+int pcstats (int argc, char **argv) {
+
+  int N;
+
+  if (get_argument (argc, argv, "-h")) goto help;
+  if (get_argument (argc, argv, "-help")) goto help;
+  if (get_argument (argc, argv, "--help")) goto help;
+
+  int Reset = FALSE;
+  if ((N = get_argument (argc, argv, "-reset"))) {
+    remove_argument (N, &argc, argv);
+    Reset = TRUE;
+  }
+
+  if (argc != 1) goto help;
+
+  Stats_CheckBusyJobs (Reset);
+  Stats_CheckDoneJobs (Reset);
+  Stats_CheckKillJobs (Reset);
+  Stats_CheckRespHosts (Reset);
+  Stats_CheckDoneHosts (Reset);
+  Stats_CheckDownHosts (Reset);
+  Stats_CheckIdleHosts (Reset);
+  Stats_CheckLiveHosts (Reset);
+
+  return (TRUE);
+
+help:
+  gprint (GP_LOG, "USAGE: pcstats [-reset]\n");
+  return (FALSE);
+}
