2 * Copyright 2002, 2003 Red Hat Inc., Durham, North Carolina.
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:
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.
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
30 * Rickard E. (Rik) Faith <faith@redhat.com>
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 #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. */
45 #ifdef HAVE_DMX_CONFIG_H
46 #include <dmx-config.h>
52 #include <X11/Xos.h> /* For sys/time.h */
54 /** Used to compute a running average of value. */
55 typedef struct _DMXStatAvg
{
58 unsigned long value
[DMX_STAT_LENGTH
];
61 /** Statistical information about XSync calls. */
63 unsigned long syncCount
;
64 unsigned long oldSyncCount
;
69 unsigned long bins
[DMX_STAT_BINS
];
72 /* Interval in mS between statistic message log entries. */
74 static int dmxStatDisplays
;
75 static OsTimerPtr dmxStatTimer
;
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
)
81 return (stop
->tv_sec
- start
->tv_sec
) * 1000000
82 + stop
->tv_usec
- start
->tv_usec
;
85 static unsigned long avg(DMXStatAvg
*data
, unsigned long *max
)
91 if (!data
->count
) return 0;
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
];
97 return sum
/ data
->count
;
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 #dmxinit.c. */
106 void dmxStatActivate(const char *interval
, const char *displays
)
108 dmxStatInterval
= (interval
? atoi(interval
) : 1) * 1000;
109 dmxStatDisplays
= (displays
? atoi(displays
) : 0);
111 if (dmxStatInterval
< 1000) dmxStatInterval
= 1000;
112 if (dmxStatDisplays
< 0) dmxStatDisplays
= 0;
115 /** Allocate a \a DMXStatInfo structure. */
116 DMXStatInfo
*dmxStatAlloc(void)
118 DMXStatInfo
*pt
= malloc(sizeof(*pt
));
119 memset(pt
, 0, sizeof(*pt
));
123 /** Free the memory used by a \a DMXStatInfo structure. */
124 void dmxStatFree(DMXStatInfo
*pt
)
129 static void dmxStatValue(DMXStatAvg
*data
, unsigned long value
)
131 if (data
->count
!= DMX_STAT_LENGTH
) ++data
->count
;
132 if (data
->pos
>= DMX_STAT_LENGTH
-1) data
->pos
= 0;
133 data
->value
[data
->pos
++] = value
;
136 /** Note that a XSync() was just done on \a dmxScreen with the \a start
137 * and \a stop times (from gettimeofday()) and the number of
138 * pending-but-not-yet-processed XSync requests. This routine is called
139 * from #dmxDoSync in #dmxsync.c */
140 void dmxStatSync(DMXScreenInfo
*dmxScreen
,
141 struct timeval
*stop
, struct timeval
*start
,
142 unsigned long pending
)
144 DMXStatInfo
*s
= dmxScreen
->stat
;
145 unsigned long elapsed
= usec(stop
, start
);
146 unsigned long thresh
;
150 dmxStatValue(&s
->usec
, elapsed
);
151 dmxStatValue(&s
->pending
, pending
);
153 for (i
= 0, thresh
= DMX_STAT_BIN0
; i
< DMX_STAT_BINS
-1; i
++) {
154 if (elapsed
< thresh
) {
158 thresh
*= DMX_STAT_BINMULT
;
160 if (i
== DMX_STAT_BINS
-1) ++s
->bins
[i
];
163 /* Actually do the work of printing out the human-readable message. */
164 static CARD32
dmxStatCallback(OsTimerPtr timer
, CARD32 t
, pointer arg
)
167 static int header
= 0;
168 int limit
= dmxNumScreens
;
170 if (!dmxNumScreens
) {
172 return DMX_STAT_INTERVAL
;
175 if (!header
++ || !(header
% 10)) {
177 " S SyncCount Sync/s avSync mxSync avPend mxPend | "
181 if (dmxStatDisplays
&& dmxStatDisplays
< limit
) limit
= dmxStatDisplays
;
182 for (i
= 0; i
< limit
; i
++) {
183 DMXScreenInfo
*dmxScreen
= &dmxScreens
[i
];
184 DMXStatInfo
*s
= dmxScreen
->stat
;
185 unsigned long aSync
, mSync
;
186 unsigned long aPend
, mPend
;
190 aSync
= avg(&s
->usec
, &mSync
);
191 aPend
= avg(&s
->pending
, &mPend
);
192 dmxLog(dmxDebug
, "%2d %9lu %7lu %6lu %6lu %6lu %6lu |",
194 s
->syncCount
, /* SyncCount */
196 - s
->oldSyncCount
) * 1000 / dmxStatInterval
, /* Sync/s */
198 mSync
, /* max/Sync */
200 mPend
); /* maxPend */
201 for (j
= 0; j
< DMX_STAT_BINS
; j
++)
202 dmxLogCont(dmxDebug
, " %5lu", s
->bins
[j
]);
203 dmxLogCont(dmxDebug
, "\n");
206 s
->oldSyncCount
= s
->syncCount
;
207 for (j
= 0; j
< DMX_STAT_BINS
; j
++) s
->bins
[j
] = 0;
209 return DMX_STAT_INTERVAL
; /* Place on queue again */
212 /** Try to initialize the statistic gathering and printing routines.
213 * Initialization only takes place if #dmxStatActivate has already been
214 * called. We don't need the same generation protection that we used in
215 * dmxSyncInit because our timer is always on a queue -- hence, server
216 * generation will always free it. */
217 void dmxStatInit(void)
220 dmxStatTimer
= TimerSet(NULL
, 0,
221 dmxStatInterval
, dmxStatCallback
, NULL
);