Testing tweak to improve repeatability.
[exim.git] / src / exim_monitor / em_strip.c
1 /* $Cambridge: exim/src/exim_monitor/em_strip.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
2
3 /*************************************************
4 *                   Exim Monitor                 *
5 *************************************************/
6
7 /* Copyright (c) University of Cambridge 1995 - 2004 */
8 /* See the file NOTICE for conditions of use and distribution. */
9
10
11 #include "em_hdr.h"
12
13 /* This module contains functions for handling stripcharts */
14
15
16 /*************************************************
17 *               Static variables                 *
18 *************************************************/
19
20 static int     queue_first_time = 1;         /* flag for resetting time */
21 static int     size_first_time = 1;          /* and another */
22
23 static int     stripchart_count = 0;         /* count stripcharts created */
24 static int    *stripchart_delay;             /* vector of delay counts */
25 static Widget *stripchart_label;             /* vector of label widgets */
26 static int    *stripchart_last_total;        /* vector of prevous values */
27 static int    *stripchart_max;               /* vector of maxima */
28 static int    *stripchart_middelay;          /* vector of */
29 static int    *stripchart_midmax;            /* vector of */
30 static uschar  **stripchart_name;              /* vector of name strings */
31 static Widget  stripchart_prev_chart = NULL; /* previously created chart */
32 static Widget  stripchart_prev_label = NULL; /* previously created label */
33
34
35
36 /*************************************************
37 *               Initialize                       *
38 *************************************************/
39
40 void stripchart_init(void)
41 {
42 stripchart_delay =      (int *)store_malloc(stripchart_number * sizeof(int));
43 stripchart_label =   (Widget *)store_malloc(stripchart_number * sizeof(Widget));
44 stripchart_last_total = (int *)store_malloc(stripchart_number * sizeof(int));
45 stripchart_max =        (int *)store_malloc(stripchart_number * sizeof(int));
46 stripchart_middelay =   (int *)store_malloc(stripchart_number * sizeof(int));
47 stripchart_midmax =     (int *)store_malloc(stripchart_number * sizeof(int));
48 stripchart_name =     (uschar **)store_malloc(stripchart_number * sizeof(uschar *));
49 stripchart_total =      (int *)store_malloc(stripchart_number * sizeof(int));
50 }
51
52
53
54 /*************************************************
55 *           Stripchart callback function         *
56 *************************************************/
57
58 /* The client data is the index of the stripchart. We have to play
59 a little game in order to ensure that the double value is correctly
60 passed back via the value pointer without the compiler doing an
61 unwanted cast. */
62
63 static void stripchartAction(Widget w, XtPointer client_data, XtPointer value)
64 {
65 double *ptr = (double *)value;
66 static int thresholds[] =
67   {10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 0};
68 int num = (int)client_data;
69 int oldmax = 0;
70 int newmax = 0;
71 int newvalue = 0;
72 int i = 0;
73
74 /* For the queue stripchart, the value is the current vector value.
75 We reset the initial delay of 1 second to the normal value. */
76
77 if (num == 0)
78   {
79   newvalue = stripchart_total[0];
80   if (queue_first_time)
81     {
82     xs_SetValues(w, 1, "update", stripchart_update);
83     queue_first_time = 0;
84     }
85   }
86
87 /* For the size monitoring stripchart, the value is the percentage
88 fullness of the partition. A similar fudge to the above is implemented
89 for the first time. Not all OS have statvfs(); for those that don't this
90 code is omitted. In fact it should never be obeyed, as we don't allow
91 size_stripchart to get set in that case. For some OS the old function
92 and struct name statfs is used; that is handled by a macro. */
93
94 else if (size_stripchart != NULL && num == 1)
95   {
96 #ifdef HAVE_STATFS
97   struct statvfs statbuf;
98   if (statvfs(CS size_stripchart, &statbuf) == 0)
99     {
100     int used = statbuf.f_blocks - statbuf.f_bfree;
101     int max = used + statbuf.f_bavail;
102     double fraction = ((double)used) / ((double)max);
103     newvalue = (int)((fraction + 0.005) * 100.0);
104     }
105 #endif
106   if (size_first_time)
107     {
108     xs_SetValues(w, 1, "update", stripchart_update);
109     size_first_time = 0;
110     }
111   }
112
113 /* For the configured stripcharts, the value to be set is
114 the difference from last time; save the current total for
115 next time. */
116
117 else
118   {
119   newvalue = stripchart_total[num] - stripchart_last_total[num];
120   stripchart_last_total[num] = stripchart_total[num];
121   }
122
123 /* Adjust the scale of the stripchart according to the value;
124 we delay enlarging the scale for a while after the values
125 reduce. Keep the maximum value while delaying, and reset
126 down to that. For the size stripchart, the threshold is always
127 forced to be at least 100. */
128
129 while (thresholds[i] > 0)
130   {
131   int thresh = (size_stripchart != NULL && num == 1)? 100 : thresholds[i++];
132   if (newvalue < (double)thresh)
133     {
134     /* If the current maximum is less than required, or if it is
135     greater and we have delayed long enough, adjust the scale. */
136
137     if (stripchart_max[num] < thresh ||
138        (stripchart_max[num] > thresh && stripchart_delay[num]++ > 20))
139       {
140       uschar buffer[128];
141       newmax = (thresh > stripchart_midmax[num])?
142         thresh : stripchart_midmax[num];
143       if (newmax == 10) sprintf(CS buffer, "%s", stripchart_name[num]);
144         else sprintf(CS buffer, "%s x%d", stripchart_name[num], newmax/10);
145       if (size_stripchart != NULL && num == 1) Ustrcat(buffer, "%");
146       xs_SetValues(stripchart_label[num], 1, "label", buffer);
147       oldmax = stripchart_max[num];
148       stripchart_max[num] = newmax;
149       stripchart_midmax[num] = 0;
150       stripchart_delay[num] -= stripchart_middelay[num];
151       }
152
153     /* Otherwise, if the current maximum is greater than required,
154     keep the highest value encountered during the delay, and its
155     position so we can adjust the delay when re-scaling. */
156
157     else if (stripchart_max[num] > thresh)
158       {
159       if (thresh > stripchart_midmax[num])
160         {
161         stripchart_midmax[num] = thresh;
162         stripchart_middelay[num] = stripchart_delay[num];
163         }
164       }
165
166     /* If the maximum is exactly what we need, reset the delay. */
167
168     if (stripchart_max[num] == thresh) stripchart_delay[num] = 0;
169     break;
170     }
171   }
172
173 /* The vanilla Athena stripchart widget does not support change of
174 scale - it just draws scale lines closer and closer together, which
175 doesn't work when the number gets very large. However, we can cause
176 it to change scale quite simply by recomputing all the values and
177 then calling its repaint routine. I had to nobble the repaint routine
178 too, to stop it changing scale to anything other than 10. There's
179 probably a better way to do this, like adding some new resource, but
180 I'm not a widget programmer and want to get on with the rest of
181 eximon... */
182
183 if (oldmax > 0)
184   {
185   int i;
186   StripChartWidget ww = (StripChartWidget)w;
187   ww->strip_chart.max_value = 0;
188   for (i = 0; i < (int)ww->strip_chart.interval; i++)
189     {
190     ww->strip_chart.valuedata[i] =
191       (ww->strip_chart.valuedata[i] * oldmax)/newmax;
192     if (ww->strip_chart.valuedata[i] > ww->strip_chart.max_value)
193       ww->strip_chart.max_value = ww->strip_chart.valuedata[i];
194     }
195   XClearWindow( XtDisplay(w), XtWindow(w));
196   ww->strip_chart.interval = repaint_window(ww, 0, (int)w->core.width);
197   }
198
199 /* Pass back the new value at the new scale */
200
201 *ptr = ((double)newvalue * 10.0)/(double)(stripchart_max[num]);
202 }
203
204
205
206 /*************************************************
207 *            Create one stripchart               *
208 *************************************************/
209
210 /* This function creates two widgets, one being the title and the other being
211 the stripchart. The client_data values for each stripchart are index into the
212 stripchart_values vector; each new stripchart just gets the next number. There
213 is a fudge for the very first stripchart, which is the queue length display,
214 and for the second if it is a partition size display; its update time is
215 initially set to 1 second so that it gives an immediate display of the queue.
216 The first time its callback function is obeyed, the update time gets reset. */
217
218 void create_stripchart(Widget parent, uschar *title)
219 {
220 Widget chart;
221
222 Widget label = XtCreateManagedWidget("label",
223   labelWidgetClass, parent, NULL, 0);
224
225 xs_SetValues(label, 10,
226   "label",          title,
227   "width",          stripchart_width + 2,
228   "borderWidth",    0,
229   "internalHeight", 0,
230   "internalWidth",  0,
231   "left",           XawChainLeft,
232   "right",          XawChainLeft,
233   "top",            XawChainTop,
234   "bottom",         XawChainTop,
235   XtNfromHoriz,     stripchart_prev_label);
236
237 chart = XtCreateManagedWidget("stripchart",
238   mystripChartWidgetClass, parent, NULL, 0);
239
240 xs_SetValues(chart, 11,
241   "jumpScroll", 1,
242   "update",     (stripchart_count < stripchart_varstart)? 1:stripchart_update,
243   "minScale",   10,
244   "width",      stripchart_width,
245   "height",     stripchart_height,
246   "left",       XawChainLeft,
247   "right",      XawChainLeft,
248   "top",        XawChainTop,
249   "bottom",     XawChainTop,
250   XtNfromHoriz, stripchart_prev_chart,
251   XtNfromVert,  label);
252
253 XtAddCallback(chart, "getValue", stripchartAction,
254   (XtPointer)stripchart_count);
255
256 stripchart_last_total[stripchart_count] = 0;
257 stripchart_max[stripchart_count] = 10;
258 stripchart_midmax[stripchart_count] = 0;
259 stripchart_name[stripchart_count] = title;
260 stripchart_prev_label = stripchart_label[stripchart_count] = label;
261 stripchart_prev_chart = chart;
262 stripchart_total[stripchart_count] = 0;
263 stripchart_count++;
264 }
265
266 /* End of em_strip.c */