Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / dmx / dmxstat.c
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@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
80 usec(struct timeval *stop, struct timeval *start)
81 {
82 return (stop->tv_sec - start->tv_sec) * 1000000
83 + stop->tv_usec - start->tv_usec;
84 }
85
86 static unsigned long
87 avg(DMXStatAvg * data, unsigned long *max)
88 {
89 unsigned long sum;
90 int i;
91
92 *max = 0;
93 if (!data->count)
94 return 0;
95
96 for (i = 0, sum = 0; i < data->count; i++) {
97 if (data->value[i] > *max)
98 *max = data->value[i];
99 sum += data->value[i];
100 }
101 return sum / data->count;
102 }
103
104 /** Turn on XSync statistic gathering and printing. Print every \a
105 * interval seconds, with lines for the first \a displays. If \a
106 * interval is NULL, 1 will be used. If \a displays is NULL, 0 will be
107 * used (meaning a line for every display will be printed). Note that
108 * this function takes string arguments because it will usually be
109 * called from #ddxProcessArgument in \a dmxinit.c. */
110 void
111 dmxStatActivate(const char *interval, const char *displays)
112 {
113 dmxStatInterval = (interval ? atoi(interval) : 1) * 1000;
114 dmxStatDisplays = (displays ? atoi(displays) : 0);
115
116 if (dmxStatInterval < 1000)
117 dmxStatInterval = 1000;
118 if (dmxStatDisplays < 0)
119 dmxStatDisplays = 0;
120 }
121
122 /** Allocate a \a DMXStatInfo structure. */
123 DMXStatInfo *
124 dmxStatAlloc(void)
125 {
126 DMXStatInfo *pt = calloc(1, sizeof(*pt));
127
128 return pt;
129 }
130
131 /** Free the memory used by a \a DMXStatInfo structure. */
132 void
133 dmxStatFree(DMXStatInfo * pt)
134 {
135 free(pt);
136 }
137
138 static void
139 dmxStatValue(DMXStatAvg * data, unsigned long value)
140 {
141 if (data->count != DMX_STAT_LENGTH)
142 ++data->count;
143 if (data->pos >= DMX_STAT_LENGTH - 1)
144 data->pos = 0;
145 data->value[data->pos++] = value;
146 }
147
148 /** Note that a XSync() was just done on \a dmxScreen with the \a start
149 * and \a stop times (from gettimeofday()) and the number of
150 * pending-but-not-yet-processed XSync requests. This routine is called
151 * from #dmxDoSync in \a dmxsync.c */
152 void
153 dmxStatSync(DMXScreenInfo * dmxScreen,
154 struct timeval *stop, struct timeval *start, unsigned long pending)
155 {
156 DMXStatInfo *s = dmxScreen->stat;
157 unsigned long elapsed = usec(stop, start);
158 unsigned long thresh;
159 int i;
160
161 ++s->syncCount;
162 dmxStatValue(&s->usec, elapsed);
163 dmxStatValue(&s->pending, pending);
164
165 for (i = 0, thresh = DMX_STAT_BIN0; i < DMX_STAT_BINS - 1; i++) {
166 if (elapsed < thresh) {
167 ++s->bins[i];
168 break;
169 }
170 thresh *= DMX_STAT_BINMULT;
171 }
172 if (i == DMX_STAT_BINS - 1)
173 ++s->bins[i];
174 }
175
176 /* Actually do the work of printing out the human-readable message. */
177 static CARD32
178 dmxStatCallback(OsTimerPtr timer, CARD32 t, pointer arg)
179 {
180 int i, j;
181 static int header = 0;
182 int limit = dmxNumScreens;
183
184 if (!dmxNumScreens) {
185 header = 0;
186 return DMX_STAT_INTERVAL;
187 }
188
189 if (!header++ || !(header % 10)) {
190 dmxLog(dmxDebug,
191 " S SyncCount Sync/s avSync mxSync avPend mxPend | "
192 "<10ms <1s >1s\n");
193 }
194
195 if (dmxStatDisplays && dmxStatDisplays < limit)
196 limit = dmxStatDisplays;
197 for (i = 0; i < limit; i++) {
198 DMXScreenInfo *dmxScreen = &dmxScreens[i];
199 DMXStatInfo *s = dmxScreen->stat;
200 unsigned long aSync, mSync;
201 unsigned long aPend, mPend;
202
203 if (!s)
204 continue;
205
206 aSync = avg(&s->usec, &mSync);
207 aPend = avg(&s->pending, &mPend);
208 dmxLog(dmxDebug, "%2d %9lu %7lu %6lu %6lu %6lu %6lu |", i, /* S */
209 s->syncCount, /* SyncCount */
210 (s->syncCount - s->oldSyncCount) * 1000 / dmxStatInterval, /* Sync/s */
211 aSync, /* us/Sync */
212 mSync, /* max/Sync */
213 aPend, /* avgPend */
214 mPend); /* maxPend */
215 for (j = 0; j < DMX_STAT_BINS; j++)
216 dmxLogCont(dmxDebug, " %5lu", s->bins[j]);
217 dmxLogCont(dmxDebug, "\n");
218
219 /* Reset/clear */
220 s->oldSyncCount = s->syncCount;
221 for (j = 0; j < DMX_STAT_BINS; j++)
222 s->bins[j] = 0;
223 }
224 return DMX_STAT_INTERVAL; /* Place on queue again */
225 }
226
227 /** Try to initialize the statistic gathering and printing routines.
228 * Initialization only takes place if #dmxStatActivate has already been
229 * called. We don't need the same generation protection that we used in
230 * dmxSyncInit because our timer is always on a queue -- hence, server
231 * generation will always free it. */
232 void
233 dmxStatInit(void)
234 {
235 if (dmxStatInterval)
236 dmxStatTimer = TimerSet(NULL, 0,
237 dmxStatInterval, dmxStatCallback, NULL);
238 }