xref: /OK3568_Linux_fs/external/xserver/hw/dmx/dmxstat.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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
usec(struct timeval * stop,struct timeval * start)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
avg(DMXStatAvg * data,unsigned long * max)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
dmxStatActivate(const char * interval,const char * displays)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 *
dmxStatAlloc(void)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
dmxStatFree(DMXStatInfo * pt)133 dmxStatFree(DMXStatInfo * pt)
134 {
135     free(pt);
136 }
137 
138 static void
dmxStatValue(DMXStatAvg * data,unsigned long value)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
dmxStatSync(DMXScreenInfo * dmxScreen,struct timeval * stop,struct timeval * start,unsigned long pending)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
dmxStatCallback(OsTimerPtr timer,CARD32 t,void * arg)178 dmxStatCallback(OsTimerPtr timer, CARD32 t, void *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
dmxStatInit(void)233 dmxStatInit(void)
234 {
235     if (dmxStatInterval)
236         dmxStatTimer = TimerSet(NULL, 0,
237                                 dmxStatInterval, dmxStatCallback, NULL);
238 }
239