14 #endif /* !__UCLIBC__ */
16 #include <native/task.h>
17 #include <native/timer.h>
18 #include <native/sem.h>
19 #include <rtdm/rttesting.h>
21 RT_TASK latency_task
, display_task
;
25 #define ONE_BILLION 1000000000
26 #define TEN_MILLION 10000000
29 long minjitter
, maxjitter
, avgjitter
;
30 long gminjitter
= TEN_MILLION
, gmaxjitter
= -TEN_MILLION
, goverrun
= 0;
31 long long gavgjitter
= 0;
33 long long period_ns
= 0;
34 int test_duration
= 0; /* sec of testing, via -T <sec>, 0 is inf */
35 int data_lines
= 21; /* data lines per header line, -l <lines> to change */
36 int quiet
= 0; /* suppress printing of RTH, RTD lines when -T given */
40 int priority
= T_HIPRIO
;
41 int stop_upon_switch
= 0;
42 sig_atomic_t sampling_relaxed
= 0;
46 #define TIMER_HANDLER 2
48 int test_mode
= USER_TASK
;
49 const char *test_mode_names
[] = {
50 "periodic user-mode task",
51 "in-kernel periodic task",
52 "in-kernel timer handler"
55 time_t test_start
, test_end
; /* report test duration */
56 int test_loops
= 0; /* outer loop count */
58 #define MEASURE_PERIOD ONE_BILLION
59 #define SAMPLE_COUNT (MEASURE_PERIOD / period_ns)
61 /* Warmup time : in order to avoid spurious cache effects on low-end machines. */
63 #define HISTOGRAM_CELLS 300
64 int histogram_size
= HISTOGRAM_CELLS
;
65 long *histogram_avg
= NULL
, *histogram_max
= NULL
, *histogram_min
= NULL
;
67 char *do_gnuplot
= NULL
;
68 int do_histogram
= 0, do_stats
= 0, finished
= 0;
69 int bucketsize
= 1000; /* default = 1000ns, -B <size> to override */
71 #define need_histo() (do_histogram || do_stats || do_gnuplot)
73 static inline void add_histogram(long *histogram
, long addval
)
75 /* bucketsize steps */
77 rt_timer_tsc2ns(addval
>= 0 ? addval
: -addval
) / bucketsize
;
78 histogram
[inabs
< histogram_size
? inabs
: histogram_size
- 1]++;
81 void latency(void *cookie
)
83 int err
, count
, nsamples
, warmup
= 1;
84 RTIME expected_tsc
, period_tsc
, start_ticks
, fault_threshold
;
85 RT_TIMER_INFO timer_info
;
86 unsigned old_relaxed
= 0;
88 err
= rt_timer_inquire(&timer_info
);
91 fprintf(stderr
, "latency: rt_timer_inquire, code %d\n", err
);
95 fault_threshold
= rt_timer_ns2tsc(CONFIG_XENO_DEFAULT_PERIOD
);
96 nsamples
= ONE_BILLION
/ period_ns
;
97 period_tsc
= rt_timer_ns2tsc(period_ns
);
98 /* start time: one millisecond from now. */
99 start_ticks
= timer_info
.date
+ rt_timer_ns2ticks(1000000);
100 expected_tsc
= timer_info
.tsc
+ rt_timer_ns2tsc(1000000);
103 rt_task_set_periodic(NULL
, start_ticks
,
104 rt_timer_ns2ticks(period_ns
));
107 fprintf(stderr
, "latency: failed to set periodic, code %d\n",
113 long minj
= TEN_MILLION
, maxj
= -TEN_MILLION
, dt
;
118 for (count
= sumj
= 0; count
< nsamples
; count
++) {
119 unsigned new_relaxed
;
122 expected_tsc
+= period_tsc
;
123 err
= rt_task_wait_period(&ov
);
125 dt
= (long)(rt_timer_tsc() - expected_tsc
);
126 new_relaxed
= sampling_relaxed
;
128 if (new_relaxed
!= old_relaxed
129 && dt
> fault_threshold
)
131 new_relaxed
- old_relaxed
;
134 old_relaxed
= new_relaxed
;
140 if (err
!= -ETIMEDOUT
) {
142 "latency: wait period failed, code %d\n",
144 exit(EXIT_FAILURE
); /* Timer stopped. */
148 expected_tsc
+= period_tsc
* ov
;
151 if (freeze_max
&& (dt
> gmaxjitter
)
152 && !(finished
|| warmup
)) {
153 xntrace_user_freeze(rt_timer_tsc2ns(dt
), 0);
157 if (!(finished
|| warmup
) && need_histo())
158 add_histogram(histogram_avg
, dt
);
162 if (!finished
&& need_histo()) {
163 add_histogram(histogram_max
, maxj
);
164 add_histogram(histogram_min
, minj
);
168 if (minj
< gminjitter
)
172 if (maxj
> gmaxjitter
)
175 avgjitter
= sumj
/ nsamples
;
176 gavgjitter
+= avgjitter
;
178 rt_sem_v(&display_sem
);
181 if (warmup
&& test_loops
== WARMUP_TIME
) {
188 void display(void *cookie
)
194 if (test_mode
== USER_TASK
) {
195 snprintf(sem_name
, sizeof(sem_name
), "dispsem-%d", getpid());
196 err
= rt_sem_create(&display_sem
, sem_name
, 0, S_FIFO
);
200 "latency: cannot create semaphore: %s\n",
206 struct rttst_tmbench_config config
;
208 if (test_mode
== KERNEL_TASK
)
209 config
.mode
= RTTST_TMBENCH_TASK
;
211 config
.mode
= RTTST_TMBENCH_HANDLER
;
213 config
.period
= period_ns
;
214 config
.priority
= priority
;
215 config
.warmup_loops
= WARMUP_TIME
;
216 config
.histogram_size
= need_histo() ? histogram_size
: 0;
217 config
.histogram_bucketsize
= bucketsize
;
218 config
.freeze_max
= freeze_max
;
221 rt_dev_ioctl(benchdev
, RTTST_RTIOC_TMBENCH_START
, &config
);
225 "latency: failed to start in-kernel timer benchmark, code %d\n",
234 printf("warming up...\n");
237 fprintf(stderr
, "running quietly for %d seconds\n",
241 long minj
, gminj
, maxj
, gmaxj
, avgj
;
243 if (test_mode
== USER_TASK
) {
244 err
= rt_sem_p(&display_sem
, TM_INFINITE
);
249 "latency: failed to pend on semaphore, code %d\n",
255 /* convert jitters to nanoseconds. */
256 minj
= rt_timer_tsc2ns(minjitter
);
257 gminj
= rt_timer_tsc2ns(gminjitter
);
258 avgj
= rt_timer_tsc2ns(avgjitter
);
259 maxj
= rt_timer_tsc2ns(maxjitter
);
260 gmaxj
= rt_timer_tsc2ns(gmaxjitter
);
263 struct rttst_interm_bench_res result
;
266 rt_dev_ioctl(benchdev
, RTTST_RTIOC_INTERM_BENCH_RES
,
272 "latency: failed to call RTTST_RTIOC_INTERM_BENCH_RES, code %d\n",
278 minj
= result
.last
.min
;
279 gminj
= result
.overall
.min
;
280 avgj
= result
.last
.avg
;
281 maxj
= result
.last
.max
;
282 gmaxj
= result
.overall
.max
;
283 goverrun
= result
.overall
.overruns
;
287 if (data_lines
&& (n
++ % data_lines
) == 0) {
290 dt
= now
- start
- WARMUP_TIME
;
292 ("RTT| %.2ld:%.2ld:%.2ld (%s, %Ld us period, "
293 "priority %d)\n", dt
/ 3600,
294 (dt
/ 60) % 60, dt
% 60,
295 test_mode_names
[test_mode
],
296 period_ns
/ 1000, priority
);
297 printf("RTH|%11s|%11s|%11s|%8s|%6s|%11s|%11s\n",
298 "----lat min", "----lat avg",
299 "----lat max", "-overrun", "---msw",
300 "---lat best", "--lat worst");
302 printf("RTD|%11.3f|%11.3f|%11.3f|%8ld|%6u|%11.3f|%11.3f\n",
308 (double)gminj
/ 1000, (double)gmaxj
/ 1000);
313 double dump_histogram(long *histogram
, char *kind
)
315 int n
, total_hits
= 0;
316 double avg
= 0; /* used to sum hits 1st */
319 printf("---|--param|----range-|--samples\n");
321 for (n
= 0; n
< histogram_size
; n
++) {
322 long hits
= histogram
[n
];
328 printf("HSD| %s| %3d -%3d | %8ld\n",
329 kind
, n
, n
+ 1, hits
);
333 avg
/= total_hits
; /* compute avg, reuse variable */
338 void dump_histo_gnuplot(long *histogram
)
340 unsigned start
, stop
;
344 f
= fopen(do_gnuplot
, "w");
348 for (n
= 0; n
< histogram_size
&& histogram
[n
] == 0L; n
++)
352 for (n
= histogram_size
- 1; n
>= 0 && histogram
[n
] == 0L; n
--)
356 fprintf(f
, "%g 1\n", start
* bucketsize
/ 1000.0);
357 for (n
= start
; n
<= stop
; n
++)
358 fprintf(f
, "%g %ld\n",
359 (n
+ 0.5) * bucketsize
/ 1000.0, histogram
[n
] + 1);
360 fprintf(f
, "%g 1\n", (stop
+ 1) * bucketsize
/ 1000.0);
365 void dump_stats(long *histogram
, char *kind
, double avg
)
367 int n
, total_hits
= 0;
370 for (n
= 0; n
< histogram_size
; n
++) {
371 long hits
= histogram
[n
];
375 variance
+= hits
* (n
- avg
) * (n
- avg
);
379 /* compute std-deviation (unbiased form) */
380 if (total_hits
> 1) {
381 variance
/= total_hits
- 1;
382 variance
= sqrt(variance
);
386 printf("HSS| %s| %9d| %10.3f| %10.3f\n",
387 kind
, total_hits
, avg
, variance
);
390 void dump_hist_stats(void)
392 double minavg
, maxavg
, avgavg
;
394 /* max is last, where its visible w/o scrolling */
395 minavg
= dump_histogram(histogram_min
, "min");
396 avgavg
= dump_histogram(histogram_avg
, "avg");
397 maxavg
= dump_histogram(histogram_max
, "max");
399 printf("HSH|--param|--samples-|--average--|---stddev--\n");
401 dump_stats(histogram_min
, "min", minavg
);
402 dump_stats(histogram_avg
, "avg", avgavg
);
403 dump_stats(histogram_max
, "max", maxavg
);
406 dump_histo_gnuplot(histogram_avg
);
411 time_t actual_duration
;
412 long gmaxj
, gminj
, gavgj
;
414 if (test_mode
== USER_TASK
) {
415 rt_sem_delete(&display_sem
);
417 gavgjitter
/= (test_loops
> 1 ? test_loops
: 2) - 1;
419 gminj
= rt_timer_tsc2ns(gminjitter
);
420 gmaxj
= rt_timer_tsc2ns(gmaxjitter
);
421 gavgj
= rt_timer_tsc2ns(gavgjitter
);
423 struct rttst_overall_bench_res overall
;
425 overall
.histogram_min
= histogram_min
;
426 overall
.histogram_max
= histogram_max
;
427 overall
.histogram_avg
= histogram_avg
;
429 rt_dev_ioctl(benchdev
, RTTST_RTIOC_TMBENCH_STOP
, &overall
);
431 gminj
= overall
.result
.min
;
432 gmaxj
= overall
.result
.max
;
433 gavgj
= overall
.result
.avg
;
434 goverrun
= overall
.result
.overruns
;
438 rt_dev_close(benchdev
);
444 actual_duration
= test_end
- test_start
- WARMUP_TIME
;
446 test_duration
= actual_duration
;
449 ("---|-----------|-----------|-----------|--------|------|-------------------------\n"
450 "RTS|%11.3f|%11.3f|%11.3f|%8ld|%6u| %.2ld:%.2ld:%.2ld/%.2d:%.2d:%.2d\n",
451 (double)gminj
/ 1000, (double)gavgj
/ 1000, (double)gmaxj
/ 1000,
452 goverrun
, max_relaxed
, actual_duration
/ 3600, (actual_duration
/ 60) % 60,
453 actual_duration
% 60, test_duration
/ 3600,
454 (test_duration
/ 60) % 60, test_duration
% 60);
457 "Warning! some latency maxima may have been due to involuntary mode switches.\n"
458 "Please contact xenomai@xenomai.org\n");
470 void faulthand(int sig
)
472 xntrace_user_freeze(0, 1);
473 signal(sig
, SIG_DFL
);
477 static const char *reason_str
[] = {
478 [SIGDEBUG_UNDEFINED
] = "latency: received SIGXCPU for unknown reason",
479 [SIGDEBUG_MIGRATE_SIGNAL
] = "received signal",
480 [SIGDEBUG_MIGRATE_SYSCALL
] = "invoked syscall",
481 [SIGDEBUG_MIGRATE_FAULT
] = "triggered fault",
482 [SIGDEBUG_MIGRATE_PRIOINV
] = "affected by priority inversion",
483 [SIGDEBUG_NOMLOCK
] = "Xenomai: process memory not locked "
484 "(missing mlockall?)",
485 [SIGDEBUG_WATCHDOG
] = "Xenomai: watchdog triggered "
486 "(period too short?)",
489 static char buffer
[256];
491 void sigdebug(int sig
, siginfo_t
*si
, void *context
)
494 const char fmt
[] = "Mode switch (reason: %s), aborting. Backtrace:\n";
495 static void *bt
[200];
496 #else /* __UCLIBC__ */
497 const char fmt
[] = "Mode switch (reason: %s), aborting."
498 " Backtrace unavailable with uclibc.\n";
499 #endif /* __UCLIBC__ */
500 unsigned int reason
= si
->si_value
.sival_int
;
503 if (reason
> SIGDEBUG_WATCHDOG
)
504 reason
= SIGDEBUG_UNDEFINED
;
507 case SIGDEBUG_UNDEFINED
:
508 case SIGDEBUG_NOMLOCK
:
509 case SIGDEBUG_WATCHDOG
:
510 n
= snprintf(buffer
, sizeof(buffer
),
511 "%s\n", reason_str
[reason
]);
512 write(STDERR_FILENO
, buffer
, n
);
516 if (!stop_upon_switch
) {
521 n
= snprintf(buffer
, sizeof(buffer
), fmt
, reason_str
[reason
]);
522 n
= write(STDERR_FILENO
, buffer
, n
);
524 n
= backtrace(bt
, sizeof(bt
)/sizeof(bt
[0]));
525 backtrace_symbols_fd(bt
, n
, STDERR_FILENO
);
526 #endif /* !__UCLIBC__ */
528 signal(sig
, SIG_DFL
);
532 int main(int argc
, char **argv
)
534 int cpu
= 0, c
, err
, sig
;
539 while ((c
= getopt(argc
, argv
, "g:hp:l:T:qH:B:sD:t:fc:P:b")) != EOF
)
542 do_gnuplot
= strdup(optarg
);
557 histogram_size
= atoi(optarg
);
562 bucketsize
= atoi(optarg
);
567 period_ns
= atoi(optarg
) * 1000LL;
572 data_lines
= atoi(optarg
);
577 test_duration
= atoi(optarg
);
578 alarm(test_duration
+ WARMUP_TIME
);
588 benchdev_no
= atoi(optarg
);
593 test_mode
= atoi(optarg
);
602 cpu
= T_CPU(atoi(optarg
));
606 priority
= atoi(optarg
);
610 stop_upon_switch
= 1;
616 "usage: latency [options]\n"
617 " [-h] # print histograms of min, avg, max latencies\n"
618 " [-g <file>] # dump histogram to <file> in gnuplot format\n"
619 " [-s] # print statistics of min, avg, max latencies\n"
620 " [-H <histogram-size>] # default = 200, increase if your last bucket is full\n"
621 " [-B <bucket-size>] # default = 1000ns, decrease for more resolution\n"
622 " [-p <period_us>] # sampling period\n"
623 " [-l <data-lines per header>] # default=21, 0 to suppress headers\n"
624 " [-T <test_duration_seconds>] # default=0, so ^C to end\n"
625 " [-q] # suppresses RTD, RTH lines if -T is used\n"
626 " [-D <testing_device_no>] # number of testing device, default=0\n"
627 " [-t <test_mode>] # 0=user task (default), 1=kernel task, 2=timer IRQ\n"
628 " [-f] # freeze trace for each new max latency\n"
629 " [-c <cpu>] # pin measuring task down to given CPU\n"
630 " [-P <priority>] # task priority (test mode 0 and 1 only)\n"
631 " [-b] # break upon mode switch\n"
636 if (!test_duration
&& quiet
) {
638 "latency: -q only works if -T has been given.\n");
642 if ((test_mode
< USER_TASK
) || (test_mode
> TIMER_HANDLER
)) {
643 fprintf(stderr
, "latency: invalid test mode.\n");
649 histogram_avg
= calloc(histogram_size
, sizeof(long));
650 histogram_max
= calloc(histogram_size
, sizeof(long));
651 histogram_min
= calloc(histogram_size
, sizeof(long));
653 if (!(histogram_avg
&& histogram_max
&& histogram_min
))
657 period_ns
= CONFIG_XENO_DEFAULT_PERIOD
; /* ns */
659 if (priority
<= T_LOPRIO
)
660 priority
= T_LOPRIO
+ 1;
661 else if (priority
> T_HIPRIO
)
665 sigaddset(&mask
, SIGINT
);
666 sigaddset(&mask
, SIGTERM
);
667 sigaddset(&mask
, SIGHUP
);
668 sigaddset(&mask
, SIGALRM
);
669 pthread_sigmask(SIG_BLOCK
, &mask
, NULL
);
671 sigemptyset(&sa
.sa_mask
);
672 sa
.sa_sigaction
= sigdebug
;
673 sa
.sa_flags
= SA_SIGINFO
;
674 sigaction(SIGDEBUG
, &sa
, NULL
);
677 /* If something goes wrong, we want to freeze the current
678 trace path to help debugging. */
679 signal(SIGSEGV
, faulthand
);
680 signal(SIGBUS
, faulthand
);
685 printf("== Sampling period: %Ld us\n"
687 "== All results in microseconds\n",
688 period_ns
/ 1000, test_mode_names
[test_mode
]);
690 mlockall(MCL_CURRENT
| MCL_FUTURE
);
692 if (test_mode
!= USER_TASK
) {
693 char devname
[RTDM_MAX_DEVNAME_LEN
];
695 snprintf(devname
, RTDM_MAX_DEVNAME_LEN
, "rttest-timerbench%d",
697 benchdev
= rt_dev_open(devname
, O_RDWR
);
701 "latency: failed to open benchmark device, code %d\n"
702 "(modprobe xeno_timerbench?)\n", benchdev
);
707 rt_timer_set_mode(TM_ONESHOT
); /* Force aperiodic timing. */
709 snprintf(task_name
, sizeof(task_name
), "display-%d", getpid());
710 err
= rt_task_create(&display_task
, task_name
, 0, 0, T_FPU
);
714 "latency: failed to create display task, code %d\n",
719 err
= rt_task_start(&display_task
, &display
, NULL
);
723 "latency: failed to start display task, code %d\n",
728 if (test_mode
== USER_TASK
) {
729 snprintf(task_name
, sizeof(task_name
), "sampling-%d", getpid());
731 rt_task_create(&latency_task
, task_name
, 0, priority
,
732 T_FPU
| cpu
| T_WARNSW
);
736 "latency: failed to create latency task, code %d\n",
741 err
= rt_task_start(&latency_task
, &latency
, NULL
);
745 "latency: failed to start latency task, code %d\n",
751 sigwait(&mask
, &sig
);