239 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			7.2 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);
 | |
| }
 |