Home | History | Annotate | Line # | Download | only in dmx
      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