File Coverage

lib/Event/Stats.xs
Criterion Covered Total %
statement 155 197 78.6
branch 62 130 47.6
condition n/a
subroutine n/a
pod n/a
total 217 327 66.3


line stmt bran cond sub pod time code
1             /*-*-c-*-*/
2             #define MIN_PERL_DEFINE 1
3              
4             #include
5             #include
6             #include
7              
8             #include "EventAPI.h"
9              
10             static int EnforceMaxCBTime=0;
11              
12             /*********************************************************/
13             typedef struct snap snap;
14             struct snap {
15             double elapse;
16             int live;
17             int max_tm;
18             struct timeval start;
19             };
20              
21 7373           static void snap_on(snap *sn)
22             {
23             assert(sn);
24             /* if (sn->live) warn("snap reinit"); /**/
25 7373           gettimeofday(&sn->start, 0);
26 7373           sn->live = 1;
27 7373           }
28              
29 7320           static void snap_off(snap *sn)
30             {
31             struct timeval done_tm;
32             assert(sn);
33 7320 100         if (!sn->live) return;
34 7319           gettimeofday(&done_tm, 0);
35 7319           sn->live = 0;
36 7319           sn->elapse += (done_tm.tv_sec - sn->start.tv_sec +
37 7319           (done_tm.tv_usec - sn->start.tv_usec)/1000000.0);
38             }
39              
40             /*********************************************************/
41             #define PE_STAT_SECONDS 3 /* 3 sec per interval */
42             #define PE_STAT_I1 20
43             #define PE_STAT_I2 20
44              
45             typedef struct pe_run pe_run;
46             struct pe_run {
47             double elapse;
48             int ran, die;
49             };
50              
51             typedef struct pe_stat pe_stat;
52             struct pe_stat {
53             int xsec, xmin; /* first index of circular buffers */
54             pe_run sec[PE_STAT_I1];
55             pe_run min[PE_STAT_I2];
56             };
57              
58             static pe_stat totalStats;
59             static pe_stat idleStats;
60             static struct timeval total_tm;
61             static pe_timer *RollTimer=0;
62              
63 20           static void pe_stat_init(pe_stat *st)
64             {
65             int xx;
66 20           st->xsec = 0;
67 420 100         for (xx=0; xx < PE_STAT_I1; xx++) {
68 400           st->sec[xx].elapse = 0;
69 400           st->sec[xx].ran = 0;
70 400           st->sec[xx].die = 0;
71             }
72 20           st->xmin = 0;
73 420 100         for (xx=0; xx < PE_STAT_I2; xx++) {
74 400           st->min[xx].elapse = 0;
75 400           st->min[xx].ran = 0;
76 400           st->min[xx].die = 0;
77             }
78 20           }
79              
80             #if 0
81             static void pe_stat_dump(pe_stat *st) /*BROKEN*/
82             {
83             int xx;
84             fprintf(stderr,"stat(0x%x)\nsec> ", st);
85             for (xx=0; xx < PE_STAT_I1; xx++) {
86             pe_run *run = &st->sec[(st->xsec + xx) % PE_STAT_I1];
87             fprintf(stderr, "%5.2f/%d ", run->elapse, run->ran);
88             }
89             fprintf(stderr,"\nmin5> ");
90             for (xx=0; xx < PE_STAT_I2; xx++) {
91             pe_run *run = &st->min[(st->xmin + xx) % PE_STAT_I2];
92             fprintf(stderr, "%5.2f/%d ", run->elapse, run->ran);
93             }
94             fprintf(stderr,"\n");
95             }
96             #endif
97              
98 7320           static void pe_stat_record(pe_stat *st, double elapse)
99             {
100 7320           pe_run *run = &st->sec[st->xsec];
101             /* warn("recording %f\n", elapse); pe_stat_dump(st); /**/
102 7320           run->elapse += elapse;
103 7320           run->ran += 1;
104             /* pe_stat_dump(st); /**/
105 7320           }
106              
107 18           static void pe_stat_query(pe_stat *st, int sec, int *ran, int *die, double *elapse)
108             {
109 18           *ran = 0;
110 18           *die = 0;
111 18           *elapse = 0;
112 18 50         if (sec <= 1)
113 0           return;
114 18 50         if (sec <= PE_STAT_SECONDS * PE_STAT_I1) {
115             int xx;
116 108 100         for (xx=0; xx <= (sec-1) / PE_STAT_SECONDS; xx++) {
117 90           pe_run *run = &st->sec[(st->xsec + xx + 1) % PE_STAT_I1];
118             assert(xx <= PE_STAT_I1);
119 90           *ran += run->ran;
120 90           *die += run->die;
121 90           *elapse += run->elapse;
122             }
123 18           return;
124             }
125 0 0         if (sec <= PE_STAT_SECONDS * PE_STAT_I1 * PE_STAT_I2) {
126             int xx;
127 0 0         for (xx=0; xx <= (sec-1) / (PE_STAT_SECONDS*PE_STAT_I1); xx++) {
128 0           pe_run *run = &st->min[(st->xmin + xx + 1) % PE_STAT_I2];
129             assert(xx <= PE_STAT_I2);
130 0           *ran += run->ran;
131 0           *die += run->die;
132 0           *elapse += run->elapse;
133             }
134 0           return;
135             }
136 0           warn("Stats available only for the last %d seconds (vs. %d)",
137             PE_STAT_SECONDS * PE_STAT_I1 * PE_STAT_I2, sec);
138             }
139              
140 16           static void pe_stat_roll(pe_stat *st)
141             {
142 16           st->xsec = (st->xsec + PE_STAT_I1 - 1) % PE_STAT_I1;
143 16 50         if (st->xsec == 0) {
144             int xx;
145 0           st->xmin = (st->xmin + PE_STAT_I2 - 1) % PE_STAT_I2;
146 0           st->min[st->xmin].ran = 0;
147 0           st->min[st->xmin].die = 0;
148 0           st->min[st->xmin].elapse = 0;
149 0 0         for (xx=0; xx < PE_STAT_I1; xx++) {
150 0           st->min[st->xmin].ran += st->sec[xx].ran;
151 0           st->min[st->xmin].die += st->sec[xx].die;
152 0           st->min[st->xmin].elapse += st->sec[xx].elapse;
153             }
154             }
155 16           st->sec[st->xsec].ran = 0;
156 16           st->sec[st->xsec].die = 0;
157 16           st->sec[st->xsec].elapse = 0;
158 16           }
159              
160 3           static void pe_stat_roll_cb()
161             {
162             pe_watcher *ev;
163             struct timeval done_tm;
164             /* warn("roll"); /**/
165 3           gettimeofday(&done_tm, 0);
166 3           pe_stat_record(&totalStats, (done_tm.tv_sec-total_tm.tv_sec +
167 3           (done_tm.tv_usec-total_tm.tv_usec)/1000000.0));
168 3           gettimeofday(&total_tm, 0);
169              
170 3           ev = GEventAPI->AllWatchers->next->self;
171 13 100         while (ev) {
172 10 100         if (!ev->stats) {
173 3           New(0, ev->stats, 1, pe_stat);
174 3           pe_stat_init(ev->stats);
175             }
176 10           pe_stat_roll(ev->stats);
177 10           ev = ev->all.next->self;
178             }
179 3           pe_stat_roll(&idleStats);
180 3           pe_stat_roll(&totalStats);
181 3           }
182              
183             static snap SysTm; /* for idleStats */
184             static int RefTimes=0;
185             static snap *RefTime=0;
186              
187 7319           static void *pe_enter(int frame, int max_tm)
188             {
189             snap *sn;
190             /* warn("enter %d", frame); /**/
191 7319 100         if (frame == -1) {
192 7240           SysTm.elapse = 0;
193 7240           snap_on(&SysTm);
194 7240           return &SysTm;
195             }
196 79 100         if (frame >= RefTimes) {
197 4           int cnt = frame + 10;
198 4 50         if (!RefTime) {
199 4 50         Newz(0, RefTime, cnt, snap);
200             } else {
201             int xx;
202 0 0         Renew(RefTime, cnt, snap);
203 0 0         for (xx=RefTimes; xx < cnt; xx++)
204 0           RefTime[xx].live=0;
205             }
206 4           RefTimes = cnt;
207             }
208 79           sn = RefTime + frame;
209 79           sn->elapse = 0;
210 79           sn->max_tm = max_tm;
211 79           snap_on(RefTime + frame);
212 79 100         if (EnforceMaxCBTime && max_tm)
    50          
213 52           alarm(max_tm);
214 79           return sn;
215             }
216              
217 3           static void pe_suspend(void *vp)
218             {
219 3 100         if (EnforceMaxCBTime)
220 2           alarm(0);
221 3           snap_off((snap*)vp);
222 3           }
223              
224 54           static void pe_resume(void *vp)
225             {
226 54           snap *sn = (snap*) vp;
227 54 100         if (EnforceMaxCBTime && sn->max_tm)
    50          
228 51           alarm(sn->max_tm - ((int)sn->elapse));
229 54           snap_on(sn);
230 54           }
231              
232 7317           static void pe_commit(void *vp, pe_watcher *wa)
233             {
234 7317           snap *sn = (snap*) vp;
235             /* warn("commit %x %x", vp, wa); /**/
236 7317 100         if (EnforceMaxCBTime)
237 3391           alarm(0);
238 7317 100         if (wa && !wa->stats) {
    100          
239 8           New(0, wa->stats, 1, pe_stat);
240 8           pe_stat_init(wa->stats);
241             }
242 7317           snap_off(sn);
243 7317 100         pe_stat_record(wa? wa->stats : &idleStats, sn->elapse);
244 7317           }
245              
246 2           static void pe_abort(void *vp, pe_watcher *wa)
247             {
248             pe_run *run;
249             pe_stat *st;
250 2 100         if (EnforceMaxCBTime)
251 1           alarm(0);
252 2           ((snap*)vp)->live=0;
253             assert(wa);
254 2 100         if (!wa->stats) {
255 1           New(0, wa->stats, 1, pe_stat);
256 1           pe_stat_init(wa->stats);
257             }
258 2           st = (pe_stat*) wa->stats;
259 2           run = &st->sec[st->xsec];
260 2           run->die += 1;
261 2           }
262              
263 12           static void pe_dtor(void *stats)
264 12           { safefree(stats); }
265              
266             static pe_event_stats_vtbl Myvtbl =
267             { 0, pe_enter, pe_suspend, pe_resume, pe_commit, pe_abort, pe_dtor };
268              
269             static int Stats;
270 4           static void use_stats(int yes)
271             {
272 4           int prev = Stats;
273 4           Stats += yes;
274 4 50         if (Stats < 0)
275 0           Stats=0;
276 4 50         if (!(!prev ^ !Stats))
277 0           return;
278 4 50         if (Stats) {
279             pe_watcher *ev;
280             /* warn("reinit stats"); /**/
281 4           ev = GEventAPI->AllWatchers->next->self;
282 12 100         while (ev) {
283 8 50         if (ev->stats)
284 0           pe_stat_init(ev->stats);
285 8           ev = ev->all.next->self;
286             }
287 4           pe_stat_init(&idleStats);
288 4           pe_stat_init(&totalStats);
289            
290 4 50         if (!RollTimer)
291 4           RollTimer = GEventAPI->new_timer(0,0);
292 4           RollTimer->interval = newSVnv(PE_STAT_SECONDS);
293 4           ev = (pe_watcher*) RollTimer;
294 4           WaREPEAT_on(ev);
295 4           sv_setpv(ev->desc, "Event::Stats");
296 4           ev->prio = PE_PRIO_NORMAL - 1;
297 4           ev->callback = (void*) pe_stat_roll_cb;
298 4           gettimeofday(&total_tm, 0);
299             /* pretend we are repeating so 'at' can be uninitialized */
300 4           GEventAPI->start(ev, 1);
301 4           GEventAPI->collect_stats(1);
302             } else {
303 0           GEventAPI->stop((pe_watcher*) RollTimer, 1);
304 0           GEventAPI->collect_stats(0);
305             }
306             }
307              
308             MODULE = Event::Stats PACKAGE = Event::Stats
309              
310             PROTOTYPES: DISABLE
311              
312             BOOT:
313             {
314 4           HV *stash = gv_stashpv("Event::Stats", 1);
315 4           newCONSTSUB(stash, "MINTIME", newSViv(PE_STAT_SECONDS));
316 4           newCONSTSUB(stash, "MAXTIME",
317             newSViv(PE_STAT_SECONDS * PE_STAT_I1 * PE_STAT_I2));
318 4 50         I_EVENT_API(HvNAME(stash));
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
319 4           GEventAPI->install_stats(&Myvtbl);
320             }
321              
322             void
323             _enforcing_max_callback_time()
324             PPCODE:
325             {
326 0 0         XPUSHs(boolSV(EnforceMaxCBTime));
    0          
327             }
328              
329             void
330             _enforce_max_callback_time(yes)
331             bool yes
332             PPCODE:
333             {
334 1 50         XPUSHs(boolSV(EnforceMaxCBTime));
    50          
335 1 50         if (!EnforceMaxCBTime ^ !yes)
336 1 50         use_stats(yes? 1:-1);
337 1           EnforceMaxCBTime = yes;
338 1 50         if (!yes) alarm(0);
339             }
340              
341             int
342             round_seconds(sec)
343             int sec;
344             CODE:
345 0 0         if (sec <= 0)
346 0           RETVAL = PE_STAT_SECONDS;
347 0 0         else if (sec < PE_STAT_SECONDS * PE_STAT_I1)
348 0           RETVAL = ((int)(sec + PE_STAT_SECONDS-1)/ PE_STAT_SECONDS) *
349             PE_STAT_SECONDS;
350 0 0         else if (sec < PE_STAT_SECONDS * PE_STAT_I1 * PE_STAT_I2)
351 0           RETVAL = ((int)(sec + PE_STAT_SECONDS * PE_STAT_I1 - 1) /
352             (PE_STAT_SECONDS * PE_STAT_I1)) *
353             PE_STAT_SECONDS * PE_STAT_I1;
354             else
355 0           RETVAL = PE_STAT_SECONDS * PE_STAT_I1 * PE_STAT_I2;
356             OUTPUT:
357             RETVAL
358              
359             void
360             idle_time(sec)
361             int sec
362             PREINIT:
363             int ran, die;
364             double elapse;
365             PPCODE:
366 0 0         if (!Stats) croak("Event::Stats are not enabled");
367 0           pe_stat_query(&idleStats, sec, &ran, &die, &elapse);
368 0 0         XPUSHs(sv_2mortal(newSViv(ran)));
369 0 0         XPUSHs(sv_2mortal(newSViv(die)));
370 0 0         XPUSHs(sv_2mortal(newSVnv(elapse)));
371              
372             void
373             total_time(sec)
374             int sec
375             PREINIT:
376             int ran,die;
377             double elapse;
378             PPCODE:
379 0 0         if (!Stats) croak("Event::Stats are not enabled");
380 0           pe_stat_query(&totalStats, sec, &ran, &die, &elapse);
381 0 0         XPUSHs(sv_2mortal(newSVnv(elapse)));
382              
383             int
384             collect(yes)
385             int yes
386             CODE:
387             {
388 3           use_stats(yes);
389 3           RETVAL = Stats;
390             }
391             OUTPUT:
392             RETVAL
393              
394             MODULE = Event::Stats PACKAGE = Event::Watcher
395              
396             void
397             stats(obj, sec)
398             SV *obj
399             int sec
400             PREINIT:
401             int ran, die;
402             double elapse;
403             pe_watcher *THIS;
404             PPCODE:
405 21 50         if (!Stats)
406 0           croak("Event::Stats are not enabled");
407 21           THIS = (pe_watcher*) GEventAPI->sv_2watcher(obj);
408 21 100         if (THIS->stats)
409 18           pe_stat_query(THIS->stats, sec, &ran, &die, &elapse);
410             else
411 3           ran = die = elapse = 0;
412 21 50         XPUSHs(sv_2mortal(newSViv(ran)));
413 21 50         XPUSHs(sv_2mortal(newSViv(die)));
414 21 50         XPUSHs(sv_2mortal(newSVnv(elapse)));
415