222 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			222 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright 2002, 2003 Red Hat Inc., Durham, North Carolina.
 | |
|  *
 | |
|  * All Rights Reserved.
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining
 | |
|  * a copy of this software and associated documentation files (the
 | |
|  * "Software"), to deal in the Software without restriction, including
 | |
|  * without limitation on the rights to use, copy, modify, merge,
 | |
|  * publish, distribute, sublicense, and/or sell copies of the Software,
 | |
|  * and to permit persons to whom the Software is furnished to do so,
 | |
|  * subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice (including the
 | |
|  * next paragraph) shall be included in all copies or substantial
 | |
|  * portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | |
|  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | |
|  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | |
|  * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
 | |
|  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 | |
|  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | |
|  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | |
|  * SOFTWARE.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * Authors:
 | |
|  *   Rickard E. (Rik) Faith <faith@redhat.com>
 | |
|  *
 | |
|  */
 | |
| 
 | |
| /** \file
 | |
|  *
 | |
|  * The DMX server code is written to call #dmxSync() whenever an XSync()
 | |
|  * might be necessary.  However, since XSync() requires a two way
 | |
|  * communication with the other X server, eliminating unnecessary
 | |
|  * XSync() calls is a key performance optimization.  Support for this
 | |
|  * optimization is provided in \a dmxsync.c.  This file provides routines
 | |
|  * that evaluate this optimization by counting the number of XSync()
 | |
|  * calls and monitoring their latency.  This functionality can be turned
 | |
|  * on using the -stat command-line parameter. */
 | |
| 
 | |
| #ifdef HAVE_DMX_CONFIG_H
 | |
| #include <dmx-config.h>
 | |
| #endif
 | |
| 
 | |
| #include "dmx.h"
 | |
| #include "dmxstat.h"
 | |
| #include "dmxlog.h"
 | |
| #include <X11/Xos.h>                /* For sys/time.h */
 | |
| 
 | |
| /** Used to compute a running average of value. */
 | |
| typedef struct _DMXStatAvg {
 | |
|     int           pos;
 | |
|     int           count;
 | |
|     unsigned long value[DMX_STAT_LENGTH];
 | |
| } DMXStatAvg;
 | |
| 
 | |
| /** Statistical information about XSync calls. */
 | |
| struct _DMXStatInfo {
 | |
|     unsigned long syncCount;
 | |
|     unsigned long oldSyncCount;
 | |
| 
 | |
|     DMXStatAvg    usec;
 | |
|     DMXStatAvg    pending;
 | |
| 
 | |
|     unsigned long bins[DMX_STAT_BINS];
 | |
| };
 | |
| 
 | |
| /* Interval in mS between statistic message log entries. */
 | |
|        int        dmxStatInterval;
 | |
| static int        dmxStatDisplays;
 | |
| static OsTimerPtr dmxStatTimer;
 | |
| 
 | |
| /** Return the number of microseconds as an unsigned long.
 | |
|  * Unfortunately, this is only useful for intervals < about 4 sec.  */
 | |
| static unsigned long usec(struct timeval *stop, struct timeval *start)
 | |
| {
 | |
|     return (stop->tv_sec - start->tv_sec) * 1000000
 | |
|         + stop->tv_usec - start->tv_usec;
 | |
| }
 | |
| 
 | |
| static unsigned long avg(DMXStatAvg *data, unsigned long *max)
 | |
| {
 | |
|     unsigned long sum;
 | |
|     int           i;
 | |
| 
 | |
|     *max = 0;
 | |
|     if (!data->count) return 0;
 | |
| 
 | |
|     for (i = 0, sum = 0; i < data->count; i++) {
 | |
|         if (data->value[i] > *max) *max = data->value[i];
 | |
|         sum += data->value[i];
 | |
|     }
 | |
|     return sum / data->count;
 | |
| }
 | |
| 
 | |
| /** Turn on XSync statistic gathering and printing.  Print every \a
 | |
|  * interval seconds, with lines for the first \a displays.  If \a
 | |
|  * interval is NULL, 1 will be used.  If \a displays is NULL, 0 will be
 | |
|  * used (meaning a line for every display will be printed).  Note that
 | |
|  * this function takes string arguments because it will usually be
 | |
|  * called from #ddxProcessArgument in \a dmxinit.c. */
 | |
| void dmxStatActivate(const char *interval, const char *displays)
 | |
| {
 | |
|     dmxStatInterval = (interval ? atoi(interval) : 1) * 1000;
 | |
|     dmxStatDisplays = (displays ? atoi(displays) : 0);
 | |
| 
 | |
|     if (dmxStatInterval < 1000) dmxStatInterval = 1000;
 | |
|     if (dmxStatDisplays < 0)    dmxStatDisplays = 0;
 | |
| }
 | |
| 
 | |
| /** Allocate a \a DMXStatInfo structure. */
 | |
| DMXStatInfo *dmxStatAlloc(void)
 | |
| {
 | |
|     DMXStatInfo *pt = calloc(1, sizeof(*pt));
 | |
|     return pt;
 | |
| }
 | |
| 
 | |
| /** Free the memory used by a \a DMXStatInfo structure. */
 | |
| void dmxStatFree(DMXStatInfo *pt)
 | |
| {
 | |
|     free(pt);
 | |
| }
 | |
| 
 | |
| static void dmxStatValue(DMXStatAvg *data, unsigned long value)
 | |
| {
 | |
|     if (data->count != DMX_STAT_LENGTH) ++data->count;
 | |
|     if (data->pos >= DMX_STAT_LENGTH-1) data->pos = 0;
 | |
|     data->value[data->pos++] = value;
 | |
| }
 | |
| 
 | |
| /** Note that a XSync() was just done on \a dmxScreen with the \a start
 | |
|  * and \a stop times (from gettimeofday()) and the number of
 | |
|  * pending-but-not-yet-processed XSync requests.  This routine is called
 | |
|  * from #dmxDoSync in \a dmxsync.c */
 | |
| void dmxStatSync(DMXScreenInfo *dmxScreen,
 | |
|                  struct timeval *stop, struct timeval *start,
 | |
|                  unsigned long pending)
 | |
| {
 | |
|     DMXStatInfo   *s      = dmxScreen->stat;
 | |
|     unsigned long elapsed = usec(stop, start);
 | |
|     unsigned long thresh;
 | |
|     int           i;
 | |
| 
 | |
|     ++s->syncCount;
 | |
|     dmxStatValue(&s->usec, elapsed);
 | |
|     dmxStatValue(&s->pending, pending);
 | |
|     
 | |
|     for (i = 0, thresh = DMX_STAT_BIN0; i < DMX_STAT_BINS-1; i++) {
 | |
|         if (elapsed < thresh) {
 | |
|             ++s->bins[i];
 | |
|             break;
 | |
|         }
 | |
|         thresh *= DMX_STAT_BINMULT;
 | |
|     }
 | |
|     if (i == DMX_STAT_BINS-1) ++s->bins[i];
 | |
| }
 | |
| 
 | |
| /* Actually do the work of printing out the human-readable message. */
 | |
| static CARD32 dmxStatCallback(OsTimerPtr timer, CARD32 t, pointer arg)
 | |
| {
 | |
|     int         i, j;
 | |
|     static int  header = 0;
 | |
|     int         limit = dmxNumScreens;
 | |
| 
 | |
|     if (!dmxNumScreens) {
 | |
|         header = 0;
 | |
|         return DMX_STAT_INTERVAL;
 | |
|     }
 | |
| 
 | |
|     if (!header++ || !(header % 10)) {
 | |
|         dmxLog(dmxDebug,
 | |
|                " S SyncCount  Sync/s avSync mxSync avPend mxPend | "
 | |
|                "<10ms   <1s   >1s\n");
 | |
|     }
 | |
| 
 | |
|     if (dmxStatDisplays && dmxStatDisplays < limit) limit = dmxStatDisplays;
 | |
|     for (i = 0; i < limit; i++) {
 | |
|         DMXScreenInfo *dmxScreen = &dmxScreens[i];
 | |
|         DMXStatInfo   *s         = dmxScreen->stat;
 | |
|         unsigned long aSync, mSync;
 | |
|         unsigned long aPend, mPend;
 | |
|         
 | |
|         if (!s) continue;
 | |
| 
 | |
|         aSync = avg(&s->usec,    &mSync);
 | |
|         aPend = avg(&s->pending, &mPend);
 | |
|         dmxLog(dmxDebug, "%2d %9lu %7lu %6lu %6lu %6lu %6lu |",
 | |
|                i,                                               /* S */
 | |
|                s->syncCount,                                    /* SyncCount */
 | |
|                (s->syncCount
 | |
|                 - s->oldSyncCount) * 1000 / dmxStatInterval,    /* Sync/s */
 | |
|                aSync,                                           /* us/Sync */
 | |
|                mSync,                                           /* max/Sync */
 | |
|                aPend,                                           /* avgPend */
 | |
|                mPend);                                          /* maxPend */
 | |
|         for (j = 0; j < DMX_STAT_BINS; j++)
 | |
|             dmxLogCont(dmxDebug, " %5lu", s->bins[j]);
 | |
|         dmxLogCont(dmxDebug, "\n");
 | |
| 
 | |
|                                 /* Reset/clear */
 | |
|         s->oldSyncCount = s->syncCount;
 | |
|         for (j = 0; j < DMX_STAT_BINS; j++) s->bins[j] = 0;
 | |
|     }
 | |
|     return DMX_STAT_INTERVAL;   /* Place on queue again */
 | |
| }
 | |
| 
 | |
| /** Try to initialize the statistic gathering and printing routines.
 | |
|  * Initialization only takes place if #dmxStatActivate has already been
 | |
|  * called.  We don't need the same generation protection that we used in
 | |
|  * dmxSyncInit because our timer is always on a queue -- hence, server
 | |
|  * generation will always free it. */
 | |
| void dmxStatInit(void)
 | |
| {
 | |
|     if (dmxStatInterval)
 | |
|         dmxStatTimer = TimerSet(NULL, 0,
 | |
|                                 dmxStatInterval, dmxStatCallback, NULL);
 | |
| }
 |