223 lines
7.5 KiB
C
223 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 #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 #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 = malloc(sizeof(*pt));
|
||
|
memset(pt, 0, sizeof(*pt));
|
||
|
return pt;
|
||
|
}
|
||
|
|
||
|
/** Free the memory used by a \a DMXStatInfo structure. */
|
||
|
void dmxStatFree(DMXStatInfo *pt)
|
||
|
{
|
||
|
if (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 #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);
|
||
|
}
|