1 /* 2 * Copyright 2002, 2003 Red Hat Inc., Durham, North Carolina. 3 * 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining 7 * a copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation on the rights to use, copy, modify, merge, 10 * publish, distribute, sublicense, and/or sell copies of the Software, 11 * and to permit persons to whom the Software is furnished to do so, 12 * subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial 16 * portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS 22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 * SOFTWARE. 26 */ 27 28 /* 29 * Authors: 30 * Rickard E. (Rik) Faith <faith (at) redhat.com> 31 * 32 */ 33 34 /** \file 35 * 36 * The DMX server code is written to call #dmxSync() whenever an XSync() 37 * might be necessary. However, since XSync() requires a two way 38 * communication with the other X server, eliminating unnecessary 39 * XSync() calls is a key performance optimization. Support for this 40 * optimization is provided in \a dmxsync.c. This file provides routines 41 * that evaluate this optimization by counting the number of XSync() 42 * calls and monitoring their latency. This functionality can be turned 43 * on using the -stat command-line parameter. */ 44 45 #ifdef HAVE_DMX_CONFIG_H 46 #include <dmx-config.h> 47 #endif 48 49 #include "dmx.h" 50 #include "dmxstat.h" 51 #include "dmxlog.h" 52 #include <X11/Xos.h> /* For sys/time.h */ 53 54 /** Used to compute a running average of value. */ 55 typedef struct _DMXStatAvg { 56 int pos; 57 int count; 58 unsigned long value[DMX_STAT_LENGTH]; 59 } DMXStatAvg; 60 61 /** Statistical information about XSync calls. */ 62 struct _DMXStatInfo { 63 unsigned long syncCount; 64 unsigned long oldSyncCount; 65 66 DMXStatAvg usec; 67 DMXStatAvg pending; 68 69 unsigned long bins[DMX_STAT_BINS]; 70 }; 71 72 /* Interval in mS between statistic message log entries. */ 73 int dmxStatInterval; 74 static int dmxStatDisplays; 75 static OsTimerPtr dmxStatTimer; 76 77 /** Return the number of microseconds as an unsigned long. 78 * Unfortunately, this is only useful for intervals < about 4 sec. */ 79 static unsigned long usec(struct timeval *stop, struct timeval *start) 80 { 81 return (stop->tv_sec - start->tv_sec) * 1000000 82 + stop->tv_usec - start->tv_usec; 83 } 84 85 static unsigned long avg(DMXStatAvg *data, unsigned long *max) 86 { 87 unsigned long sum; 88 int i; 89 90 *max = 0; 91 if (!data->count) return 0; 92 93 for (i = 0, sum = 0; i < data->count; i++) { 94 if (data->value[i] > *max) *max = data->value[i]; 95 sum += data->value[i]; 96 } 97 return sum / data->count; 98 } 99 100 /** Turn on XSync statistic gathering and printing. Print every \a 101 * interval seconds, with lines for the first \a displays. If \a 102 * interval is NULL, 1 will be used. If \a displays is NULL, 0 will be 103 * used (meaning a line for every display will be printed). Note that 104 * this function takes string arguments because it will usually be 105 * called from #ddxProcessArgument in \a dmxinit.c. */ 106 void dmxStatActivate(const char *interval, const char *displays) 107 { 108 dmxStatInterval = (interval ? atoi(interval) : 1) * 1000; 109 dmxStatDisplays = (displays ? atoi(displays) : 0); 110 111 if (dmxStatInterval < 1000) dmxStatInterval = 1000; 112 if (dmxStatDisplays < 0) dmxStatDisplays = 0; 113 } 114 115 /** Allocate a \a DMXStatInfo structure. */ 116 DMXStatInfo *dmxStatAlloc(void) 117 { 118 DMXStatInfo *pt = calloc(1, sizeof(*pt)); 119 return pt; 120 } 121 122 /** Free the memory used by a \a DMXStatInfo structure. */ 123 void dmxStatFree(DMXStatInfo *pt) 124 { 125 free(pt); 126 } 127 128 static void dmxStatValue(DMXStatAvg *data, unsigned long value) 129 { 130 if (data->count != DMX_STAT_LENGTH) ++data->count; 131 if (data->pos >= DMX_STAT_LENGTH-1) data->pos = 0; 132 data->value[data->pos++] = value; 133 } 134 135 /** Note that a XSync() was just done on \a dmxScreen with the \a start 136 * and \a stop times (from gettimeofday()) and the number of 137 * pending-but-not-yet-processed XSync requests. This routine is called 138 * from #dmxDoSync in \a dmxsync.c */ 139 void dmxStatSync(DMXScreenInfo *dmxScreen, 140 struct timeval *stop, struct timeval *start, 141 unsigned long pending) 142 { 143 DMXStatInfo *s = dmxScreen->stat; 144 unsigned long elapsed = usec(stop, start); 145 unsigned long thresh; 146 int i; 147 148 ++s->syncCount; 149 dmxStatValue(&s->usec, elapsed); 150 dmxStatValue(&s->pending, pending); 151 152 for (i = 0, thresh = DMX_STAT_BIN0; i < DMX_STAT_BINS-1; i++) { 153 if (elapsed < thresh) { 154 ++s->bins[i]; 155 break; 156 } 157 thresh *= DMX_STAT_BINMULT; 158 } 159 if (i == DMX_STAT_BINS-1) ++s->bins[i]; 160 } 161 162 /* Actually do the work of printing out the human-readable message. */ 163 static CARD32 dmxStatCallback(OsTimerPtr timer, CARD32 t, pointer arg) 164 { 165 int i, j; 166 static int header = 0; 167 int limit = dmxNumScreens; 168 169 if (!dmxNumScreens) { 170 header = 0; 171 return DMX_STAT_INTERVAL; 172 } 173 174 if (!header++ || !(header % 10)) { 175 dmxLog(dmxDebug, 176 " S SyncCount Sync/s avSync mxSync avPend mxPend | " 177 "<10ms <1s >1s\n"); 178 } 179 180 if (dmxStatDisplays && dmxStatDisplays < limit) limit = dmxStatDisplays; 181 for (i = 0; i < limit; i++) { 182 DMXScreenInfo *dmxScreen = &dmxScreens[i]; 183 DMXStatInfo *s = dmxScreen->stat; 184 unsigned long aSync, mSync; 185 unsigned long aPend, mPend; 186 187 if (!s) continue; 188 189 aSync = avg(&s->usec, &mSync); 190 aPend = avg(&s->pending, &mPend); 191 dmxLog(dmxDebug, "%2d %9lu %7lu %6lu %6lu %6lu %6lu |", 192 i, /* S */ 193 s->syncCount, /* SyncCount */ 194 (s->syncCount 195 - s->oldSyncCount) * 1000 / dmxStatInterval, /* Sync/s */ 196 aSync, /* us/Sync */ 197 mSync, /* max/Sync */ 198 aPend, /* avgPend */ 199 mPend); /* maxPend */ 200 for (j = 0; j < DMX_STAT_BINS; j++) 201 dmxLogCont(dmxDebug, " %5lu", s->bins[j]); 202 dmxLogCont(dmxDebug, "\n"); 203 204 /* Reset/clear */ 205 s->oldSyncCount = s->syncCount; 206 for (j = 0; j < DMX_STAT_BINS; j++) s->bins[j] = 0; 207 } 208 return DMX_STAT_INTERVAL; /* Place on queue again */ 209 } 210 211 /** Try to initialize the statistic gathering and printing routines. 212 * Initialization only takes place if #dmxStatActivate has already been 213 * called. We don't need the same generation protection that we used in 214 * dmxSyncInit because our timer is always on a queue -- hence, server 215 * generation will always free it. */ 216 void dmxStatInit(void) 217 { 218 if (dmxStatInterval) 219 dmxStatTimer = TimerSet(NULL, 0, 220 dmxStatInterval, dmxStatCallback, NULL); 221 } 222