]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
lib/lock: MXUser statistics are available anytime
authorVMware, Inc <>
Tue, 19 Oct 2010 20:08:01 +0000 (13:08 -0700)
committerMarcelo Vanzin <mvanzin@vmware.com>
Tue, 19 Oct 2010 20:08:01 +0000 (13:08 -0700)
All statistics memory and computation are dynamic - nothing is
done unless a lock has statistics enabled.

Signed-off-by: Marcelo Vanzin <mvanzin@vmware.com>
open-vm-tools/lib/include/userlock.h
open-vm-tools/lib/lock/ulBarrier.c
open-vm-tools/lib/lock/ulExcl.c
open-vm-tools/lib/lock/ulInt.h
open-vm-tools/lib/lock/ulRW.c
open-vm-tools/lib/lock/ulRec.c
open-vm-tools/lib/lock/ulSema.c
open-vm-tools/lib/lock/ulStats.c

index 1c6708e5a46c4e720a7b9c4b7edcc66a934279fd..6dba3e1629c7c3ced516164ffc1de0bd406a9258 100644 (file)
@@ -24,6 +24,8 @@
 #define INCLUDE_ALLOW_VMCORE
 #include "includeCheck.h"
 
+#include <stdarg.h>
+
 #include "vm_atomic.h"
 #include "vm_basic_types.h"
 #include "vm_basic_defs.h"
@@ -205,9 +207,6 @@ MXUserRecLock *MXUser_InitFromMXRec(const char *name,
                                     MX_Rank rank,
                                     Bool isBelowBull);
 
-#if defined(VMX86_STATS) 
-#define MXUSER_STATS  // stats "only inside the VMX" when requested
-#endif
 #endif
 
 #if defined(VMX86_DEBUG)
@@ -215,16 +214,19 @@ MXUserRecLock *MXUser_InitFromMXRec(const char *name,
 #endif
 
 #if defined(MXUSER_DEBUG)
-Bool MXUser_IsCurThreadHoldingLocks(void);
 void MXUser_TryAcquireFailureControl(Bool (*func)(const char *lockName));
+Bool MXUser_IsCurThreadHoldingLocks(void);
 #endif
 
-#if defined(MXUSER_STATS)
 void MXUser_StatisticsControl(double contentionRatio,
                               uint64 minCount);
 
 void MXUser_PerLockData(void);
-#endif
+void MXUser_SetStatsFunc(void *context,
+                         uint32 maxLineLength,
+                         void (*statsFunc)(void *context,
+                                           const char *fmt,
+                                           va_list ap));
 
 void MXUser_SetInPanic(void);
 Bool MXUser_InPanic(void);
index 407629b4bd01a372ede79a00cc9e805e19b60553..22371bf2ff2e4c7b14345e9c9d24b83d0ed9511e 100644 (file)
@@ -155,15 +155,14 @@ MXUser_CreateBarrier(const char *userName,  // IN:
    barrier->configCount = count;
    barrier->curContext = 0;
 
-   barrier->header.name = properName;
    barrier->header.signature = MXUSER_BARRIER_SIGNATURE;
+   barrier->header.name = properName;
    barrier->header.rank = rank;
+   barrier->header.serialNumber = MXUserAllocSerialNumber();
    barrier->header.dumpFunc = MXUserDumpBarrier;
-
-#if defined(MXUSER_STATS)
    barrier->header.statsFunc = NULL;
-   barrier->header.identifier = MXUserAllocID();
-#endif
+
+   MXUserAddToList(&barrier->header);
 
    return barrier;
 }
@@ -198,6 +197,8 @@ MXUser_DestroyBarrier(MXUserBarrier *barrier)  // IN:
                             __FUNCTION__);
       }
 
+      MXUserRemoveFromList(&barrier->header);
+
       MXUser_DestroyCondVar(barrier->contexts[0].condVar);
       MXUser_DestroyCondVar(barrier->contexts[1].condVar);
       MXUser_DestroyExclLock(barrier->lock);
index 9c486d8d9bb29108f262cbdf096533d49bef8afe..e3af02e6430aead48bec0ebbce4de46afbec49a2 100644 (file)
@@ -45,7 +45,6 @@ struct MXUserExclLock
 };
 
 
-#if defined(MXUSER_STATS)
 /*
  *-----------------------------------------------------------------------------
  *
@@ -113,7 +112,6 @@ MXUserStatsActionExcl(MXUserHeader *header)  // IN:
       }
    }
 }
-#endif
 
 
 /*
@@ -174,7 +172,6 @@ MXUser_CreateExclLock(const char *userName,  // IN:
                       MX_Rank rank)          // IN:
 {
    char *properName;
-   MXUserStats *stats;
    MXUserExclLock *lock;
 
    lock = Util_SafeCalloc(1, sizeof(*lock));
@@ -192,24 +189,24 @@ MXUser_CreateExclLock(const char *userName,  // IN:
       return NULL;
    }
 
-   lock->header.name = properName;
    lock->header.signature = MXUSER_EXCL_SIGNATURE;
+   lock->header.name = properName;
    lock->header.rank = rank;
+   lock->header.serialNumber = MXUserAllocSerialNumber();
    lock->header.dumpFunc = MXUserDumpExclLock;
 
-#if defined(MXUSER_STATS)
-   lock->header.statsFunc = MXUserStatsActionExcl;
-   lock->header.identifier = MXUserAllocID();
+   if (MXUserStatsEnabled()) {
+      MXUserStats *stats = Util_SafeCalloc(1, sizeof(*stats));
 
-   stats = Util_SafeCalloc(1, sizeof(*stats));
+      MXUserAcquisitionStatsSetUp(&stats->acquisitionStats);
+      MXUserBasicStatsSetUp(&stats->heldStats, MXUSER_STAT_CLASS_HELD);
 
-   MXUserAcquisitionStatsSetUp(&stats->acquisitionStats);
-   MXUserBasicStatsSetUp(&stats->heldStats, MXUSER_STAT_CLASS_HELD);
-#else
-   stats = NULL;
-#endif
-
-   Atomic_WritePtr(&lock->statsMem, stats);
+      lock->header.statsFunc = MXUserStatsActionExcl;
+      Atomic_WritePtr(&lock->statsMem, stats);
+   } else {
+      lock->header.statsFunc = NULL;
+      Atomic_WritePtr(&lock->statsMem, NULL);
+   }
 
    MXUserAddToList(&lock->header);
 
index b9dd2ecf433c33e86fbf7b73f3d636cb93674b28..d7759bb1655a35eeaeb4e2cc4270006b9dd10880 100644 (file)
@@ -396,15 +396,12 @@ MXUserGetNativeTID(void)
 
 typedef struct MXUserHeader {
    uint32       signature;
-   MX_Rank      rank;
    char        *name;
-   uint32       identifier;
+   MX_Rank      rank;
+   uint32       serialNumber;
    void       (*dumpFunc)(struct MXUserHeader *);
-
-#if defined(MXUSER_STATS)
    void       (*statsFunc)(struct MXUserHeader *);
    ListItem     item;
-#endif
 } MXUserHeader;
 
 
@@ -478,24 +475,12 @@ typedef struct {
    MXUserBasicStats  basicStats;       // total held statistics
 } MXUserReleaseStats;
 
-uint32 MXUserAllocID(void);
+uint32 MXUserAllocSerialNumber(void);
 
-#if defined(MXUSER_STATS)
 void MXUserAddToList(MXUserHeader *header);
 void MXUserRemoveFromList(MXUserHeader *header);
-#else
-static INLINE void
-MXUserAddToList(MXUserHeader *header)
-{
-   return;
-}
 
-static INLINE void
-MXUserRemoveFromList(MXUserHeader *header)
-{
-   return;
-}
-#endif
+Bool MXUserStatsEnabled(void);
 
 typedef struct MXUserHisto MXUserHisto;
 
index 4c5305f25798571cc64fd6213217d66e4e4cb9cb..fff666b020977932d02b7ae57ba08b825360a8eb 100644 (file)
@@ -271,7 +271,6 @@ struct MXUserRWLock
 };
 
 
-#if defined(MXUSER_STATS)
 /*
  *-----------------------------------------------------------------------------
  *
@@ -340,7 +339,6 @@ MXUserStatsActionRW(MXUserHeader *header)  // IN:
       }
    }
 }
-#endif
 
 
 /*
@@ -422,6 +420,12 @@ MXUser_CreateRWLock(const char *userName,  // IN:
       properName = Util_SafeStrdup(userName);
    }
 
+   lock->header.signature = MXUSER_RW_SIGNATURE;
+   lock->header.name = properName;
+   lock->header.rank = rank;
+   lock->header.serialNumber = MXUserAllocSerialNumber();
+   lock->header.dumpFunc = MXUserDumpRWLock;
+
    /*
     * Always attempt to use native locks when they are available. If, for some
     * reason, a native lock should be available but isn't, fall back to using
@@ -430,48 +434,32 @@ MXUser_CreateRWLock(const char *userName,  // IN:
 
    lock->useNative = useNative && MXUserNativeRWInit(&lock->nativeLock);
 
-   if (lock->useNative) {
-#if defined(MXUSER_STATS)
-      /* stats builds need an internal recursive lock for data integrity */
-
-      lockInited = MXRecLockInit(&lock->recursiveLock);
-
-      if (!lockInited) {
-         MXUserNativeRWDestroy(&lock->nativeLock);
-      }
-#else
-      lockInited = TRUE;
-#endif
-   } else {
-      lockInited = MXRecLockInit(&lock->recursiveLock);
-   }
+   lockInited = MXRecLockInit(&lock->recursiveLock);
 
    if (LIKELY(lockInited)) {
-      MXUserStats *stats;
-
       lock->holderTable = HashTable_Alloc(256,
                                           HASH_INT_KEY | HASH_FLAG_ATOMIC,
                                           MXUserFreeHashEntry);
 
-      lock->header.name = properName;
-      lock->header.signature = MXUSER_RW_SIGNATURE;
-      lock->header.rank = rank;
-      lock->header.dumpFunc = MXUserDumpRWLock;
+      if (MXUserStatsEnabled()) {
+         MXUserStats *stats = Util_SafeCalloc(1, sizeof(*stats));
 
-#if defined(MXUSER_STATS)
-      lock->header.statsFunc = MXUserStatsActionRW;
-      lock->header.identifier = MXUserAllocID();
+         MXUserAcquisitionStatsSetUp(&stats->acquisitionStats);
+         MXUserBasicStatsSetUp(&stats->heldStats, MXUSER_STAT_CLASS_HELD);
 
-      stats = Util_SafeCalloc(1, sizeof(*stats));
-
-      MXUserAcquisitionStatsSetUp(&stats->acquisitionStats);
-      MXUserBasicStatsSetUp(&stats->heldStats, MXUSER_STAT_CLASS_HELD);
-#else
-      stats = NULL;
-#endif
+         lock->header.statsFunc = MXUserStatsActionRW;
+         Atomic_WritePtr(&lock->statsMem, stats);
+      } else {
+         lock->header.statsFunc = NULL;
+         Atomic_WritePtr(&lock->statsMem, NULL);
+      }
 
       MXUserAddToList(&lock->header);
    } else {
+      if (lock->useNative) {
+         MXUserNativeRWDestroy(&lock->nativeLock);
+      }
+
       free(properName);
       free(lock);
       lock = NULL;
@@ -518,14 +506,10 @@ MXUser_DestroyRWLock(MXUserRWLock *lock)  // IN:
             MXUserDumpAndPanic(&lock->header, "%s: Internal error (%d)\n",
                                __FUNCTION__, err);
          }
-
-#if defined(MXUSER_STATS)
-         MXRecLockDestroy(&lock->recursiveLock);  
-#endif
-      } else {
-         MXRecLockDestroy(&lock->recursiveLock);  
       }
 
+      MXRecLockDestroy(&lock->recursiveLock);  
+
       MXUserRemoveFromList(&lock->header);
 
       stats = (MXUserStats *) Atomic_ReadPtr(&lock->statsMem);
index 2363521c996067cee1a2e85c4b0461c585295cfc..82ecca379150679e2aadf26be4445ccfc081beda 100644 (file)
@@ -57,7 +57,7 @@ struct MXUserRecLock
    struct MX_MutexRec  *vmmLock;
 };
 
-#if defined(MXUSER_STATS)
+
 /*
  *-----------------------------------------------------------------------------
  *
@@ -125,7 +125,6 @@ MXUserStatsActionRec(MXUserHeader *header)  // IN:
       }
    }
 }
-#endif
 
 
 /*
@@ -202,7 +201,6 @@ MXUserCreateRecLock(const char *userName,  // IN:
                     Bool beSilent)         // IN:
 {
    char *properName;
-   MXUserStats *stats;
    MXUserRecLock *lock;
 
    lock = Util_SafeCalloc(1, sizeof(*lock));
@@ -222,30 +220,24 @@ MXUserCreateRecLock(const char *userName,  // IN:
 
    lock->vmmLock = NULL;
 
-   lock->header.name = properName;
    lock->header.signature = MXUSER_REC_SIGNATURE;
+   lock->header.name = properName;
    lock->header.rank = rank;
+   lock->header.serialNumber = MXUserAllocSerialNumber();
    lock->header.dumpFunc = MXUserDumpRecLock;
 
-#if defined(MXUSER_STATS)
-   lock->header.statsFunc = MXUserStatsActionRec;
-   lock->header.identifier = MXUserAllocID();
-#endif
-
-   if (beSilent) {
-      stats = NULL;
+   if (beSilent || !MXUserStatsEnabled()) {
+      lock->header.statsFunc = NULL;
+      Atomic_WritePtr(&lock->statsMem, NULL);
    } else {
-#if defined(MXUSER_STATS)
-      stats = Util_SafeCalloc(1, sizeof(*stats));
+      MXUserStats *stats = Util_SafeCalloc(1, sizeof(*stats));
 
       MXUserAcquisitionStatsSetUp(&stats->acquisitionStats);
       MXUserBasicStatsSetUp(&stats->heldStats, MXUSER_STAT_CLASS_HELD);
-#else
-      stats = NULL;
-#endif
-   }
 
-   Atomic_WritePtr(&lock->statsMem, stats);
+      lock->header.statsFunc = MXUserStatsActionRec;
+      Atomic_WritePtr(&lock->statsMem, stats);
+   }
 
    MXUserAddToList(&lock->header);
 
@@ -915,16 +907,12 @@ MXUser_BindMXMutexRec(struct MX_MutexRec *mutex,  // IN:
 
    lock = Util_SafeCalloc(1, sizeof(*lock));
 
-   lock->header.name = Str_SafeAsprintf(NULL, "MX_%p", mutex);
-
    lock->header.signature = MXUSER_REC_SIGNATURE;
+   lock->header.name = Str_SafeAsprintf(NULL, "MX_%p", mutex);
    lock->header.rank = rank;
+   lock->header.serialNumber = MXUserAllocSerialNumber();
    lock->header.dumpFunc = NULL;
-
-#if defined(MXUSER_STATS)
    lock->header.statsFunc = NULL;
-   lock->header.identifier = MXUserAllocID();
-#endif
 
    Atomic_WritePtr(&lock->statsMem, NULL);
 
index 2bf0b86d98ac3258ea2a8f177856dd0faa367aa2..da8d3c4653e94461d0a67b80049b3c93c93fecfc 100644 (file)
@@ -384,7 +384,6 @@ MXUserUp(NativeSemaphore *sema)  // IN:
 #endif  // _WIN32
 
 
-#if defined(MXUSER_STATS)
 /*
  *-----------------------------------------------------------------------------
  *
@@ -442,7 +441,6 @@ MXUserStatsActionSema(MXUserHeader *header)  // IN:
       }
    }
 }
-#endif
 
 
 /*
@@ -514,23 +512,24 @@ MXUser_CreateSemaphore(const char *userName,  // IN:
    if (LIKELY(MXUserInit(&sema->nativeSemaphore) == 0)) {
       MXUserStats *stats;
 
-      sema->header.name = properName;
       sema->header.signature = MXUSER_SEMA_SIGNATURE;
+      sema->header.name = properName;
       sema->header.rank = rank;
+      sema->header.serialNumber = MXUserAllocSerialNumber();
       sema->header.dumpFunc = MXUserDumpSemaphore;
 
-#if defined(MXUSER_STATS)
-      sema->header.statsFunc = MXUserStatsActionSema;
-      sema->header.identifier = MXUserAllocID();
+      if (MXUserStatsEnabled()) {
+         sema->header.statsFunc = MXUserStatsActionSema;
 
-      stats = Util_SafeCalloc(1, sizeof(*stats));
+         stats = Util_SafeCalloc(1, sizeof(*stats));
 
-      MXUserAcquisitionStatsSetUp(&stats->acquisitionStats);
-#else
-      stats = NULL;
-#endif
+         MXUserAcquisitionStatsSetUp(&stats->acquisitionStats);
 
-      Atomic_WritePtr(&sema->statsMem, stats);
+         Atomic_WritePtr(&sema->statsMem, stats);
+      } else {
+         sema->header.statsFunc = NULL;
+         Atomic_WritePtr(&sema->statsMem, NULL);
+      }
 
       MXUserAddToList(&sema->header);
    } else {
index 739e20ae4f450e593d715b16c57e10f7822c2819..d19c14c961eceba6f55e000ca84d370bce42bafd 100644 (file)
 #include "hostinfo.h"
 #include "log.h"
 #include "logFixed.h"
-#if defined(MXUSER_STATS)
 #include "statsLog.h"
-#endif
 
 #define BINS_PER_DECADE 100
 
 static double mxUserContentionRatio = 0.0;  // always "off"
 static uint64 mxUserContentionCount = 0;    // always "off"
 
-#if defined(MXUSER_STATS)
 static Atomic_Ptr mxLockMemPtr;   // internal singleton lock
 static ListItem *mxUserLockList;  // list of all MXUser locks
-#endif
 
 typedef struct {
    void   *address;
@@ -61,8 +57,14 @@ struct MXUserHisto {
    TopOwner  ownerArray[TOPOWNERS];  // List of top owners
 };
 
+static char   *mxUserHistoLine = NULL;
+static uint32  mxUserMaxLineLength = 0;
+static void   *mxUserStatsContext = NULL;
+static void  (*mxUserStatsFunc)(void *context,
+                               const char *fmt,
+                               va_list ap) = NULL;
+
 
-#if defined(MXUSER_STATS)
 /*
  *-----------------------------------------------------------------------------
  *
@@ -121,7 +123,6 @@ MXUserRemoveFromList(MXUserHeader *header)  // IN:
       MXRecLockRelease(listLock);
    }
 }
-#endif
 
 
 /*
@@ -304,6 +305,36 @@ MXUserHistoSample(MXUserHisto *histo,  // IN/OUT:
 }
 
 
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserStatsLog --
+ *
+ *      Output the statistics data
+ *
+ * Results:
+ *      As above
+ *
+ * Side effects:
+ *      None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void
+MXUserStatsLog(const char *fmt,  // IN:
+               ...)              // IN:
+{
+   va_list ap;
+
+   ASSERT(mxUserStatsFunc);
+
+   va_start(ap, fmt);
+   (*mxUserStatsFunc)(mxUserStatsContext, fmt, ap);
+   va_end(ap);
+}
+
+
 /*
  *-----------------------------------------------------------------------------
  *
@@ -327,30 +358,16 @@ MXUserHistoDump(MXUserHisto *histo,    // IN:
    ASSERT(header);
    ASSERT(histo);
 
-#if defined(MXUSER_STATS)
    if (histo->totalSamples) {
       char *p;
       uint32 i;
       uint32 spaceLeft;
 
-      static uint32 maxLine = 0;
-      static char *histoLine = NULL;
-
-      /*
-       * Statistics are reported from a single thread. This avoids allocating
-       * a potentially large buffer on the stack.
-       */ 
-
-      if (maxLine == 0) {
-         maxLine = Log_MaxLineLength();  // includes terminating NUL
-         ASSERT(maxLine >= 1024);        // assert a rational minimum
+      ASSERT(mxUserHistoLine);
 
-         histoLine = Util_SafeMalloc(maxLine);
-      }
-
-      i = Str_Sprintf(histoLine, maxLine,
+      i = Str_Sprintf(mxUserHistoLine, mxUserMaxLineLength,
                       "MXUser: h l=%u t=%s min=%"FMT64"u max=%"FMT64"u\n",
-                      header->identifier, histo->typeName, histo->minValue,
+                      header->serialNumber, histo->typeName, histo->minValue,
                       histo->maxValue);
 
       /*
@@ -359,8 +376,8 @@ MXUserHistoDump(MXUserHisto *histo,    // IN:
        * properly terminated no matter what happens.
        */
 
-      p = &histoLine[i - 1];
-      spaceLeft = maxLine - i - 2;
+      p = &mxUserHistoLine[i - 1];
+      spaceLeft = mxUserMaxLineLength - i - 2;
 
       /* Add as many histogram bins as possible within the line limitations */
       for (i = 0; i < histo->numBins; i++) {
@@ -389,13 +406,14 @@ MXUserHistoDump(MXUserHisto *histo,    // IN:
          }
       }
 
-      StatsLog(histoLine);
+      MXUserStatsLog("%s", mxUserHistoLine);
 
-      i = Str_Sprintf(histoLine, maxLine, "MXUser: ht l=%u t=%s\n",
-                      header->identifier, histo->typeName);
+      i = Str_Sprintf(mxUserHistoLine, mxUserMaxLineLength,
+                      "MXUser: ht l=%u t=%s\n", header->serialNumber,
+                      histo->typeName);
 
-      p = &histoLine[i - 1];
-      spaceLeft = maxLine - i - 2;
+      p = &mxUserHistoLine[i - 1];
+      spaceLeft = mxUserMaxLineLength - i - 2;
 
       for (i = 0; i < TOPOWNERS; i++) {
          if (histo->ownerArray[i].address != NULL) {
@@ -425,9 +443,8 @@ MXUserHistoDump(MXUserHisto *histo,    // IN:
          }
       }
 
-      StatsLog("%s", histoLine);
+      MXUserStatsLog("%s", mxUserHistoLine);
    }
-#endif
 }
 
 
@@ -515,11 +532,24 @@ MXUserBasicStatsSetUp(MXUserBasicStats *stats,  // IN/OUT:
  *-----------------------------------------------------------------------------
  */
 
+static double
+MXUserSqrt(double x)  // IN: hack until next round when FP goes away
+{
+   double xn;
+   double xn1 = x;
+
+   do {
+      xn = xn1;
+      xn1 = (xn + x/xn) / 2.0;
+   } while (fabs(xn1 - xn) > 1E-10);
+
+   return xn1;
+}
+
 void
 MXUserDumpBasicStats(MXUserBasicStats *stats,  // IN:
                      MXUserHeader *header)     // IN:
 {
-#if defined(MXUSER_STATS)
    uint64 stdDev;
 
    if (stats->numSamples < 2) {
@@ -544,15 +574,14 @@ MXUserDumpBasicStats(MXUserBasicStats *stats,  // IN:
       mean = ((double) stats->timeSum) / num;
       variance = (stats->timeSquaredSum - (num*mean*mean)) / (num - 1.0);
 
-      stdDev = (variance < 0.0) ? 0 : (uint64) (sqrt(variance) + 0.5);
+      stdDev = (variance < 0.0) ? 0 : (uint64) (MXUserSqrt(variance) + 0.5);
    }
 
-   StatsLog("MXUser: e l=%u t=%s c=%"FMT64"u min=%"FMT64"u "
-            "max=%"FMT64"u mean=%"FMT64"u sd=%"FMT64"u\n",
-            header->identifier, stats->typeName,
-            stats->numSamples, stats->minTime, stats->maxTime,
-            stats->timeSum/stats->numSamples, stdDev);
-#endif
+   MXUserStatsLog("MXUser: e l=%u t=%s c=%"FMT64"u min=%"FMT64"u "
+                  "max=%"FMT64"u mean=%"FMT64"u sd=%"FMT64"u\n",
+                  header->serialNumber, stats->typeName,
+                  stats->numSamples, stats->minTime, stats->maxTime,
+                  stats->timeSum/stats->numSamples, stdDev);
 }
 
 
@@ -673,22 +702,20 @@ void
 MXUserDumpAcquisitionStats(MXUserAcquisitionStats *stats,  // IN:
                            MXUserHeader *header)           // IN:
 {
-#if defined(MXUSER_STATS)
    if (stats->numAttempts > 0) {
       if (stats->numSuccesses > 0) {
          MXUserDumpBasicStats(&stats->basicStats, header);
       }
 
-      StatsLog("MXUser: ce l=%u a=%"FMT64"u s=%"FMT64"u sc=%"FMT64"u "
-               "sct=%"FMT64"u t=%"FMT64"u\n",
-               header->identifier,
-               stats->numAttempts,
-               stats->numSuccesses,
-               stats->numSuccessesContended,
-               stats->successContentionTime,
-               stats->totalContentionTime);
+      MXUserStatsLog("MXUser: ce l=%u a=%"FMT64"u s=%"FMT64"u sc=%"FMT64"u "
+                     "sct=%"FMT64"u t=%"FMT64"u\n",
+                     header->serialNumber,
+                     stats->numAttempts,
+                     stats->numSuccesses,
+                     stats->numSuccessesContended,
+                     stats->successContentionTime,
+                     stats->totalContentionTime);
    }
-#endif
 }
 
 
@@ -872,7 +899,68 @@ MXUserForceHisto(Atomic_Ptr *histoPtr,  // IN/OUT:
 }
 
 
-#if defined(MXUSER_STATS)
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserStatsEnabled --
+ *
+ *      Are statistics keeping enabled
+ *
+ * Results:
+ *      TRUE   Yes
+ *      FALSE  NO
+ *
+ * Side effects:
+ *      None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+MXUserStatsEnabled(void)
+{
+   return (mxUserStatsFunc != NULL) && (mxUserMaxLineLength > 0);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_SetStatsFunc --
+ *
+ *      Establish statistics taking and reporting. This is done by registering
+ *      a statistics context, a reporting function and a maximum line length.
+ *
+ *      A maxLineLength of zero (0) and/or a statsFunc of NULL will
+ *      disable/prevent statistics gathering.
+ *
+ * Results:
+ *      As above
+ *
+ * Side effects:
+ *      None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_SetStatsFunc(void *context,                    // IN:
+                    uint32 maxLineLength,             // IN:
+                    void (*statsFunc)(void *context,  // IN:
+                                      const char *fmt,
+                                      va_list ap))
+{
+   ASSERT(maxLineLength >= 1024);   // assert a rational minimum
+
+   free(mxUserHistoLine);
+   mxUserHistoLine = Util_SafeMalloc(maxLineLength);
+
+   mxUserStatsContext = context;
+   mxUserMaxLineLength = maxLineLength;
+   mxUserStatsFunc = statsFunc;
+}
+
+
 /*
  *-----------------------------------------------------------------------------
  *
@@ -904,21 +992,21 @@ MXUser_PerLockData(void)
 
    if (listLock && MXRecLockTryAcquire(listLock)) {
       ListItem *entry;
-      uint32 highestID;
-      static uint32 lastReportedID = 0;
+      uint32 highestSerialNumber;
+      static uint32 lastReportedSerialNumber = 0;
 
-      highestID = lastReportedID;
+      highestSerialNumber = lastReportedSerialNumber;
 
       LIST_SCAN(entry, mxUserLockList) {
          MXUserHeader *header = LIST_CONTAINER(entry, MXUserHeader, item);
 
          /* Log the ID information for a lock that did exist previously */
-         if (header->identifier > lastReportedID) {
-            StatsLog("MXUser: n n=%s l=%d r=0x%x\n", header->name,
-                     header->identifier, header->rank);
+         if (header->serialNumber > lastReportedSerialNumber) {
+            MXUserStatsLog("MXUser: n n=%s l=%d r=0x%x\n", header->name,
+                           header->serialNumber, header->rank);
 
-            if (header->identifier > highestID) {
-               highestID = header->identifier;
+            if (header->serialNumber > highestSerialNumber) {
+               highestSerialNumber = header->serialNumber;
             }
          }
 
@@ -931,22 +1019,21 @@ MXUser_PerLockData(void)
          }
       }
 
-      lastReportedID = highestID;
+      lastReportedSerialNumber = highestSerialNumber;
 
       MXRecLockRelease(listLock);
    }
 }
-#endif
 
 
 /*
  *-----------------------------------------------------------------------------
  *
- * MXUserAllocID --
+ * MXUserAllocSerialNumber --
  *
- *      Allocate and return an MXUser identifier
+ *      Allocate and return an MXUser serial number.
  *
- *      MXUser identifiers are never recycled.
+ *      MXUser serial numbers are never recycled.
  *
  * Results:
  *      As above.
@@ -958,9 +1045,9 @@ MXUser_PerLockData(void)
  */
 
 uint32
-MXUserAllocID(void)
+MXUserAllocSerialNumber(void)
 {
-   static Atomic_uint32 firstFreeID = { 1 };  // must start not zero
+   static Atomic_uint32 firstFreeSerialNumber = { 1 };  // must start not zero
 
-   return Atomic_FetchAndInc(&firstFreeID);
+   return Atomic_FetchAndInc(&firstFreeSerialNumber);
 }