Module: xenomai-head Branch: master Commit: d9f6585b00ccfe952cc1e7aabea77427ff8065b2 URL: http://git.xenomai.org/?p=xenomai-head.git;a=commit;h=d9f6585b00ccfe952cc1e7aabea77427ff8065b2
Author: Gilles Chanteperdrix <[email protected]> Date: Sun Jan 2 01:32:55 2011 +0100 xeno-test: simplify --- configure.in | 5 +- doc/man/Makefile.am | 2 +- doc/man/xeno-test.man.in | 136 ------ scripts/Makefile.am | 4 +- scripts/xeno-test.in | 458 -------------------- src/testsuite/Makefile.am | 11 +- src/testsuite/xeno-test/Makefile.am | 9 + src/testsuite/xeno-test/dohell | 96 +++++ src/testsuite/xeno-test/xeno-test-run-wrapper | 20 + src/testsuite/xeno-test/xeno-test-run.c | 567 +++++++++++++++++++++++++ src/testsuite/xeno-test/xeno-test.in | 17 + 11 files changed, 723 insertions(+), 602 deletions(-) diff --git a/configure.in b/configure.in index 38f9a53..35809f6 100644 --- a/configure.in +++ b/configure.in @@ -936,7 +936,6 @@ AC_CONFIG_FILES([ \ scripts/Makefile \ scripts/xeno-config \ scripts/xeno-load \ - scripts/xeno-test \ scripts/xeno \ src/Makefile \ src/rtdk/Makefile \ @@ -961,6 +960,8 @@ AC_CONFIG_FILES([ \ src/testsuite/klatency/Makefile \ src/testsuite/unit/Makefile \ src/testsuite/sigtest/Makefile \ + src/testsuite/xeno-test/Makefile \ + src/testsuite/xeno-test/xeno-test \ src/utils/Makefile \ src/utils/can/Makefile \ src/utils/analogy/Makefile \ @@ -1011,9 +1012,7 @@ if test \! x$XENO_MAYBE_DOCDIR = x ; then doc/man/runinfo.man \ doc/man/xeno.man \ doc/man/xeno-config.man \ - doc/man/xeno-info.man \ doc/man/xeno-load.man \ - doc/man/xeno-test.man \ doc/doxygen/Makefile \ doc/doxygen/Doxyfile-common \ doc/doxygen/Doxyfile \ diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index 493c861..3bbe198 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -1,4 +1,4 @@ -man1_MANS = xeno-config.man xeno-info.man xeno-load.man xeno-test.man \ +man1_MANS = xeno-config.man \ clocktest.man cyclictest.man irqbench.man irqloop.man \ klatency.man latency.man rtcanconfig.man \ rtcanrecv.man rtcansend.man switchbench.man \ diff --git a/doc/man/xeno-test.man.in b/doc/man/xeno-test.man.in deleted file mode 100644 index 91befea..0000000 --- a/doc/man/xeno-test.man.in +++ /dev/null @@ -1,136 +0,0 @@ -'\" t -.\" ** The above line should force tbl to be a preprocessor ** -.\" Man page for xeno-test -.\" -.\" Copyright (C) 2005, 2006 Romain Lenglet <[email protected]> -.\" -.\" You may distribute under the terms of the GNU General Public -.\" License as specified in the file COPYING that comes with the -.\" Xenomai distribution. -.\" -.pc -.TH XENO-TEST 1 "2006-04-29" "@PACKAGE_VERSION@" "Xenomai" -.SH NAME -xeno\-test \- Tests and measures the performance of a Xenomai installation -.SH SYNOPSIS -\fBxeno\-test\fP [\fB\-v\fP] [\fB\-w\fP \fIworkloads\fP] [\fB\-d\fP \fIdevice\fP] [\fB\-W\fP \fIcommand\fP] [\fB\-p\fP \fIcommand\fP] [\fB\-L\fP] [\fB\-N\fP \fIprefix\fP] [\fB\-m\fP | \fB\-M\fP \fIemail\fP | \fB\-U\fP \fIurl\fP] [\fB\-s\fP] [\fB\-l\fP \fIsamples\fP] [\fB\-h\fP [\fB\-H\fP \fIcategories\fP] [\fB\-B\fP \fIgranularity\fP]] [\fB\-T\fP \fIseconds\fP [\fB\-q\fP]] [\fB\-\-\fP] [\fIargs\fP] ... -.SH DESCRIPTION -\fBxeno\-test\fP measures the performance of Xenomai, by executing Xenomai's \fBlatency\fP tests while generating a high workload on the system. -The default command that is executed to simulate workload (if no alternate command is specified with a \fB-W\fP \fIcommand\fP option) is: -.RS -.sp -.B dd if=/dev/zero of=/dev/null -.sp -.RE -The executed \fBlatency\fP tests periodically (every second) sample and print out the minimum, average, and maximum latency. -However, by default, if no option is specified to \fBxeno\-test\fP, those tests are executed with the \fB\-q\fP option (see below), which disables the printing of samples. -If the \fB\-q\fP option is not specified, samples are printed in groups separated by headers. -In addition, \fBlatency\fP print statistics for all samples before terminating, and can also optionally print statistics for every group of samples (see the \fB\-s\fP option), and distribution histograms (see the \fB\-h\fP option). - -.SH OPTIONS -If an invalid option is specified, \fBxeno\-test\fP prints out an usage help message and exits. - -You are strongly encouraged to use the \fB\-m\fP option, to anonymously help the Xenomai team collecting statistics about Xenomai's performance on the widest range of systems. - -The following options are specific to \fBxeno\-test\fP: -.TP -\fB\-v\fP -Produces more verbose tests results. -.TP -\fB\-w\fP \fIworkloads\fP -The number of workload commands to execute simultaneously. By default, this number is \fB1\fP. -.TP -\fB\-d\fP \fIdevice\fP -If the default workload command is to be executed, sets the input device to be read by \fBdd\fP to \fIdevice\fP instead of \fB/dev/zero\fP. -For instance, specifying the device of a real hard-drive (e.g. \fB/dev/hda1\fP) is useful for generating interrupts with I/O. -The specified \fIdevice\fP must be mounted, and cannot be an NFS mount. -.TP -\fB\-W\fP \fIcommand\fP -Executes the specified \fIcommand\fP to generate workload, instead of the default \fBdd if=/dev/zero of=/dev/null\fP command. -If the command requires arguments, the command and its arguments must be quoted and passed as a single \fIcommand\fP argument. -In addition to such static arguments, the \fIargs\fP optional arguments passed to \fBxeno\-test\fP are appended to \fIcommand\fP to execute it. -.TP -\fB\-p\fP \fIcommand\fP -Makes \fBxeno\-test\fP execute the specified \fIcommand\fP before and after all the \fBlatency\fP executions. -.TP -\fB\-L\fP -Activates logging of the tests results. -The log file is created in the \fB/tmp/\fP directory, and is named \fBtest\-\fP\fIkernel_release\fP\fB\-\fP\fItimestamp\fP, where \fIkernel_release\fP is the release number of the running Linux kernel (as determined by executing \fBuname \-r\fP), and \fItimestamp\fP is a textual representation of the current date and time (as determined by executing \fBdate\fP) used to reduce the risk of file name collisions. -The log file name can be customized by using the \fB\-N\fP option instead of, or in conjunction with this option. -.TP -\fB\-N\fP \fIprefix\fP -Activates logging of the tests results. -If the \fB\-L\fP option is also specified, prepends \fIprefix\fP to the log file name, hence the log file name is \fIprefix\fP\fBtest\-\fP\fIkernel_release\fP\fB\-\fP\fItimestamp\fP. -If the \fB\-L\fP option is not specified, the log file name is \fIprefix\fP\fB\-\fP\fItimestamp\fP. -This option is useful to create the log file in a different directory than \fB/tmp/\fP (default prefix when using the \fB\-L\fP option), by specifying a \fIprefix\fP which starts with an absolute directory path or a directory path relative to the working directory. -.TP -\fB\-m\fP -Sends the tests results to the Xenomai team's email address (\[email protected]\fp), to help collecting statistics about Xenomai's performance. The email is sent using the system's \fBmail\fP command. The email's sender address is \[email protected]\fp, and the email's subject is \fB"xeno-test results"\fP. -.TP -\fB\-M\fP \fIemail\fP -Similar to the \fB\-m\fP option, but sends the tests results to the specified email address instead of the default one. -.TP -\fB\-U\fP \fIurl\fP -Uploads the tests results to the specified URL. -If there is no file part in the specified \fIurl\fP, and the \fB\-L\fP or \fB\-N\fP option is also specified, the log file name is appended to it to form the URL used for upload. -The tests results are transmitted as the contents of an HTTP request using the PUT method. -The upload is performed using the \fBcurl\fP command. -This option fails silently (i.e. the tests results are not sent) if \fBcurl\fP is not available. -.PP -The following options are directly passed to the \fBlatency\fP test command executed by \fBxeno\-test\fP. -If no such options are specified, the \fB\-s \-T 120 \-q\fP options are implicitly passed by default by \fBxeno\-test\fP. -Any user-specified set of options overrides this default set of options. -.TP -\fB\-s\fP -Displays statistics (minimum, average, and maximum latencies) for every group of samples. -The number of samples in each group is determined by the \fB\-l\fP \fIsamples\fP option (default is \fB21\fP samples). -.TP -\fB\-l\fP \fIsamples\fP -The number of samples in every group of samples. -This number is the number of sample lines displayed between every header line, and is the number of samples used to calculate intermediate statistics if the \fB\-s\fP option is specified. -By default, this number is \fB21\fP. -.TP -\fB\-h\fP -Displays histograms of all sampled data, at the end of tests. -The number of categories - or value intervals - of each histogram is determined by the \fB\-H\fP \fIcategories\fP option (default is \fB100\fP categories). -The granularity - or bucket size -, used to separate latency samples into categories, is determined by the \fB\-B\fP \fIgranularity\fP option (default is \fB1000\fP ns). -This option implies \fB\-s\fP. -.TP -\fB\-H\fP \fIcategories\fP -The number of categories - or value intervals - of histograms to display if the \fB\-h\fP option is specified. -By default, this number is \fB100\fP. -.TP -\fB\-B\fP \fIgranularity\fP -The granularity - or bucket size -, in nanoseconds, used to discriminate between the categories of latency samples in histograms (to be printed if the \fB\-h\fP option is specified). -By default, this number is \fB1000\fP ns. -.TP -\fB\-T\fP \fIseconds\fP -The period, in seconds, during which \fBlatency\fP tests are executed. -If that option is not specified, the tests execute infinitely until the user types \fBCONTROL-C\fP (i.e. sends a SIGINT signal to them). -.TP -\fB\-q\fP -Disables the printing of samples and sample group statistics (hence this overrides the \fB\-s\fP option), and only global statistics and histograms are being printed. -The default behaviour, if this option is not specified, is to display every sample (measured minimum, average, and maximum latency) and optionally sample group statistics (if \fB\-s\fP is specified). -.TP -.B \-\- -Indicates the end of options. -This must be specified before \fIargs\fP arguments if the first argument starts with \fB\-\fP, so that it is not considered as an option. -.PP -.SH "RETURN CODES" -.TP -.B 0 -The tests executed successfully. -.TP -.B 1 -An invalid option was specified. -.SH KNOWN BUGS -\fBlatency\fP allows to specify the \fB\-H\fP \fIcategories\fP and \fB\-B\fP \fIgranularity\fP options without the \fB\-h\fP option, in which case they have no effect. - -It is possible to specify the period between samples (default is \fB1 second\fP) using the \fBlatency\fP command's \fB\-p\fP option, but this option cannot be passed through \fBxeno-test\fP (this conflicts with \fBxeno-test\fP's own \fB\-p\fP option). - -The workload generation task (e.g., the default \fBdd if=/dev/zero of=/dev/null\fP command) may terminate before the tests are finished, which may produce inaccurate tests results. It may be necessary to specify an alternate command which lasts longer, using the \fB-W\fP \fIcommand\fP option. - -Workload processes may not be properly killed when \fBxeno-test\fP terminate. -.SH "SEE ALSO" -.BR xeno\-load (1), -.BR uname (1) diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 74d1f66..2dac697 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -1,4 +1,4 @@ -bin_SCRIPTS=xeno-config xeno-load xeno-test xeno-info xeno wrap-link.sh +bin_SCRIPTS=xeno-config xeno-load xeno wrap-link.sh install-exec-local: @if test -r $(srcdir)/postinstall.sh ; then \ @@ -10,11 +10,9 @@ EXTRA_DIST = \ bootstrap \ prepare-kernel.sh \ prepare-patch.sh \ - xeno-info \ wrap-link.sh \ Kconfig.frag \ Modules.frag \ defconfig.frag \ help_from_kconfig.pl \ $(wildcard postinstall.sh) - diff --git a/scripts/xeno-test.in b/scripts/xeno-test.in deleted file mode 100644 index e5b71b0..0000000 --- a/scripts/xeno-test.in +++ /dev/null @@ -1,458 +0,0 @@ -#! /bin/sh - -# Adapted to be run also under the BusyBox. -# If you want to test it this way, do: sh xeno-test -# BusyBox >= 1.1.3 with a make defconfig should provide all needed applets. - -prefix="@prefix@" -pkgdatadir="@exec_prefix@/share/xenomai" - -myusage() { - cat >&1 <<EOF -xeno-test [options] - runs latency test in all 3 test-modes - -w <number> spawn N workloads (dd if=/dev/zero of=/dev/null) default=1 - -d <device> used as alternate src in workload (dd if=$device ..) - The device must be mounted, and (unfortunately) cannot - be an NFS mount a real device (ex /dev/hda) will - generate interrupts - -W <script> script is an alternate workload. If you need to pass args - to your program, use quotes. The program must clean - up its children when it gets a SIGTERM - -P <cmd> cmd is run before and after rt-tests - (forex: 'ntpdate -b <host>' or 'ntpq -p') - -L writes logs to /tmp/test-`uname -r`-<timestamp> - -N <name> like -L, but writes to name-<timestamp> (in PWD) - name can be full or relative pathname - -v verbose - -M <email> sends output to given addr - -m sends output to [email protected] - -U <url> uploads output to given URL - -D <datefmt> alternate options to date, for timestamp (dflt: - - # following options are passed thru to latency - -s print statistics of sampled data (default on) - -h print histogram of sampled data (default on, implies -s) - -q quiet, dont print 1 sec sampled data (default on, off if !-T) - -T <sec test> (default: 120 sec) - -l <data/header lines> (default 21) - -H <bucketcount> (default 100) - -B <bucketsize ns> (default 1000 ns) - -p <sample_period_us> (default 100 us) -EOF - # NB: many defaults are coded in latency - exit 1 -} - -echo "xeno-test: started $*" -#set -e # ctrl-C's should end everything, not just subshells. - # commenting it out may help to debug stuff. - -set -o notify # see dd's finish immediately.(or not!) - -withBusybox=0 -if sh --help 2>&1| grep -q BusyBox; then - withBusybox=1; -# else running a real /bin/sh (bash) shell -fi -echo withBusybox is $withBusybox -pidFile=/var/lock/`basename $0.$$`.pids - -checkUtilities() { - # Check for needed helper utilities - neededApplets="awk basename cut date dd dirname egrep grep head - kill md5sum mount sleep test top uname zcat" - foundAll=1 - for _j in $neededApplets - do - if test -z "`which $_j`"; then - echo "Please build busybox with support for applet $_j" - foundAll=0 - fi - done - if ! type getopts 2>&1 >/dev/null; then - echo "Please build busybox's ash with support for getopts" - foundAll=0 - fi - if test $foundAll -eq 0 ; then - needApplets="" - foundAll="" - exit 3 - fi - needApplets="" - foundAll="" -} - -checkHelpers() { - foundAll=1 - if test -z "`which script`" -a -n "$logging"; then - echo "You will not be able to log (-L option) as script is missing" - foundAll=0 - fi - if test -z "`which mail`" -a "$sendit" = 'm' ; then - echo "You will not be able send mail (-m/-M options) as mail is missing" - foundAll=0 - fi - if test -z "`which curl`" -a "$sendit" = 'm' ; then - echo "You will not be able to upload (-U option) as curl is missing" - foundAll=0 - fi - if test $foundAll -eq 0 ; then - foundAll="" - exit 3 - fi - foundAll="" -} - -loudly() { - [ "$1" = "" ] && return - # run task after announcing it - echo; date; - echo running: $* - eval $* & # eval helps w complex cmds, like zegrep -E - wait $! -} - -# defaults for cpu workload -device=/dev/zero -dd_jobs= - -# used in generate-loads -mkload() { dd if=$device of=/dev/null $* & } - -generate_loads() { - jobsct=$1; - # if test -z "$1"; then echo Skipping generate_loads; return; fi - shift 1 - if test 0 -eq $withBusybox ; then - reaper() { echo something died a $! or $* ; sleep 1; } - trap reaper CHLD - trap cleanup_load EXIT # under all exit conditions - fi - while test $jobsct -ge 1; do - jobsct=$(($jobsct-1)); - mkload - dd_jobs="$dd_jobs $!" ; - done - - echo dd workload started, pids $dd_jobs stored in $pidFile - echo $dd_jobs > $pidFile - jobsct="" -} - -cleanup_load() { - # kill the workload - if test -z "$dd_jobs" -a -r $pidFile; then - dd_jobs=`cat $pidFile` - rm -f $pidFile - fi - - if test -z "$dd_jobs" ; then - echo "cleanup_load: no dd_jobs found" - else - for _j in $dd_jobs - do - ps | grep $_j - kill $_j - done - fi - unset dd_jobs; -} - -whatconf="XENO|IPIPE|PREEMPT|CONFIG_ACPI|CONFIG_PM|CPU_FREQ|CONFIG_DEBUG_SPINLOCK|CONFIG_FRAME_POINTER" - - -boxinfo() { # static info, show once - loudly `dirname $0`/xeno-config --verbose - loudly `dirname $0`/xeno-info - - loudly cat /proc/cpuinfo # bogomips changes under CPU_FREQ - loudly md5sum /proc/cpuinfo '# cpuinfo fingerprint' - - # how much of the config do we want ? - filter=" grep -E '$whatconf'" - [ "$verbose" = 1 ] && filter= - if test -f /proc/config.gz; then # get the config - loudly zcat /proc/config.gz | $filter - elif test -f /lib/modules/`uname -r`/build/.config - then - loudly cat /lib/modules/`uname -r`/build/.config | $filter - fi - - [ -d /proc/adeos ] && for f in /proc/adeos/*; do loudly cat $f; done - [ -d /proc/ipipe ] && for f in /proc/ipipe/*; do loudly cat $f; done - filter="" -} - -boxstatus() { # get dynamic status - loudly cat /proc/interrupts - loudly cat /proc/loadavg - loudly cat /proc/meminfo - - if [ -d /proc/xenomai ]; then - for f in /proc/xenomai/*; do [ -f $f ] && loudly cat $f; done - for f in /proc/xenomai/*/*; do [ -f $f ] && loudly cat $f; done - fi - [ -n "$prepost" ] && loudly $prepost - if test 0 -eq $withBusybox ; then - loudly top -bn1c | head -n $(( 12 + $workload )) - else - loudly top | head -n $(( 12 + $workload )) - fi -} - - -run_w_load() { - local latpass - local sbpass - - while :; do - case $1 in - *=*) - eval "$1" - shift;; - *) - break;; - esac - done - - latpass="$*"; - [ "$latpass" = '' ] && latpass='-sh -T 120' - [ "$sbpass" = '' ] && sbpass='-h' - - boxinfo - loudly generate_loads $workload - boxstatus - ( - cd $pkgdatadir/testsuite/latency - loudly ./run -- $XENOT_LATENCY $latpass -t0 '# latency' - loudly ./run -- $XENOT_LATENCY $latpass -t1 '# latency' - loudly ./run -- $XENOT_LATENCY $latpass -t2 '# latency' - ) - ( cd $pkgdatadir/testsuite/switchtest - loudly ./run -- -T 120 $XENOT_SWITCHTEST '# switchtest' - ) - ( cd $pkgdatadir/testsuite/switchbench - loudly ./run -- $XENOT_SWITCHBENCH $sbpass '# switchbench' - ) - ( cd $pkgdatadir/testsuite/cyclic - loudly ./run -- -p 10 -n -l 1000 $XENOT_CYCLIC '# cyclictest' - ) - - if [ "$XENOT_IRQBENCH" != "" ] ; then - ( - cd $pkgdatadir/testsuite/irqbench - loudly ./run -- -P 10 $XENOT_IRQBENCH -t0 '# irqbench user' - loudly ./run -- -P 10 $XENOT_IRQBENCH -t1 '# irqbench kernel' - loudly ./run -- -P 10 $XENOT_IRQBENCH -t2 '# irqbench irq-handler' - loudly ./run -- -P 10 $XENOT_IRQBENCH -t3 '# irqbench hard-irq-handler' - ) - fi - - boxstatus - cleanup_load -} - -##################### -# MAIN -checkUtilities - -if [ -f /proc/config.gz ] ; then - - # check/warn on problem configs - eval `zcat /proc/config.gz | grep CONFIG_CPU_FREQ` ; - if [ ! -z "$CONFIG_CPU_FREQ" ] ; then - echo "warning: CONFIG_CPU_FREQ=$CONFIG_CPU_FREQ may be problematic" - fi -fi - -workload=1 # default = 1 job - -# *pass get all legit options, except -N, -L -latpass= # pass thru to latency -sbpass= # pass thru to switchbench -loadpass= # pass thru to subshell, not to actual tests - -logging= # no logging by default -logfile=test-`uname -r` # defaults to test-`uname -r`-<datestampe> -logprefix=/tmp/ # someplace usually there -prepost= # command to run pre, and post test (ex ntpq -p) - -email='[email protected]' # may reject til you subscribe -sentby='[email protected]' # tbd -url= -sendit= # send it by m-mail, u-url -verbose= -dateargs='+%y%m%d.%H%M%S' - -sendit() { - file=$1 - if test "$sendit" = 'm' ; then - echo "mailing $file to $email" - if test -n "$file" ; then - mail -s 'xeno-test results' $email -- -F $sentby < $file - else - cat - | mail -s 'xeno-test results' $email -- -F $sentby - fi - elif test "$sendit" = 'u' ; then - echo "uploading $file to $url" - which curl && curl -T $file $url - # -x $proxy >/tmp/.submit_result - # which wget && curl -T $file $url \ - else - echo "sendit unsupported option '$sendit'" - fi - file="" -} - -handle_options() { # called for XENOTEST_OPTS, ARGV - case $FOO in - - # latency & switchbench passthrus - h) - latpass="$latpass -$FOO" - sbpass="$sbpass -$FOO" ;; - p) - latpass="$latpass -$FOO $OPTARG" - sbpass="$sbpass -$FOO $OPTARG" ;; - - # latency only passthrus - s|q) - latpass="$latpass -$FOO" ;; - T|l|H|B) - latpass="$latpass -$FOO $OPTARG" ;; - - # workload related - d) - device=$OPTARG - echo creating workload using dd if=$device - if !(mount | grep -q ^$device) ; then - echo d option must be a block device, ie one of: - mount | cut -d\ -f1 | egrep -ve 'sysfs|proc|depts' - exit 1; - fi - loadpass="$loadpass -d $device" - ;; - w) - workload=$OPTARG - loadpass="$loadpass -w $OPTARG" ;; - W) - altwork=$OPTARG - loadpass="$loadpass -W '$OPTARG'" ;; - P) - prepost=$OPTARG - loadpass="$loadpass -P '$OPTARG'" ;; - - # output disposition - L) - logging=1 - logfile=test-`uname -r` ;; - N) - logging=1 - logprefix=$OPTARG ;; - M) - email=$OPTARG - sendit='m' ;; - m) - sendit='m' ;; - U) - url=$OPTARG - sendit='u' ;; - v) - verbose=1 ;; - D) - dateargs=$OPTARG ;; - ?) - myusage ;; - esac -} - -# process options from envar, then from cmd-line -while getopts 'd:shqvT:l:H:B:uLN:w:W:p:mM:U:P:D:' FOO $XENOTEST_OPTS ; do - handle_options; -done -while getopts 'd:shqvT:l:H:B:uLN:w:W:p:mM:U:P:D:' FOO ; do - handle_options; -done - -checkHelpers - -# all args have been handled, and split into 2 passthrus -shift $(($OPTIND - 1)); - -if test -f pidFile && ! test -w $pidFile ; then - echo "pidfile $pidFile not writable "; - exit 4 -fi - -echo "xeno-test: running tests" - -wfile=$logprefix$logfile-`date $dateargs` -if test "$logging" != "" ; then - # restart inside a script invocation, passing appropriate args - script -c "`dirname $0`/xeno-test $loadpass $latpass $*" $wfile - res="$?" - if test "$res" = "0" ; then - sendit $wfile - fi -else - if test "$altwork" != "" ; then - mkload() { exec $altwork; } - fi - run_w_load sbpass="$sbpass" $latpass $* 2>&1 | tee $wfile - res="$?" - if test "$sendit" != '' && test $res -eq 0 ; then - sendit $wfile - fi -fi - -echo "xeno-test: finished" -exit 0 - - -################################################# -# -#DONE: -# -#1. added -W <program invocation> -# -#The program should generate a load that is appropriately demanding -#upon cpu, interrupts, devices, etc. -# -#It should also work when invoked more than once, and scale the loads -#reasonably linearly (since the -w will count by N). -# -#Also, if it spawns subtasks, it should end them all when it gets SIGTERM. -# -# -#2. added timestamp to the output filename to avoid overwriting -# previous results. -# -#3. added -p 'command', which runs command before, between, and after -# the latency tests. -# -# -#TODO: -# -#1. get workload child reaper to work when child is killed from -#separate window, or when it finishes. Forex, 'dd if=/dev/hda ...' -#will eventually finish, and should be restarted to keep the load up. -#Figure out why killall didnt work properly. -# -#2. Much more testing. Heres a weak start. You might be better off -#using and improving test/test-xeno-test.rb. -# -##!/bin/bash -#PATH=.:$PATH -#xeno-test -L -#xeno-test -N foo -T 18 -l 6 -s -#xeno-test -L -N foo1- -#xeno-test -m -#xeno-test -T 1 -M $u...@localhost -#xeno-test -N foo0 -w0 -l 5 -T 30 -h -#xeno-test -L -N foo4- -w4 -#xeno-test -L -N foo4W- -w4 -W 'dd if=/dev/hda1 of=/dev/null' -# -#3. Repeat the same tests under the BusyBox. -# -#4. Check for border cases like missing awk, curl, mail, script, /proc/config.gz diff --git a/src/testsuite/Makefile.am b/src/testsuite/Makefile.am index 70ab3ce..761a58d 100644 --- a/src/testsuite/Makefile.am +++ b/src/testsuite/Makefile.am @@ -1 +1,10 @@ -SUBDIRS = latency cyclic switchtest irqbench clocktest klatency unit sigtest +SUBDIRS = \ + latency \ + cyclic \ + switchtest \ + irqbench \ + clocktest \ + klatency \ + unit \ + sigtest \ + xeno-test diff --git a/src/testsuite/xeno-test/Makefile.am b/src/testsuite/xeno-test/Makefile.am new file mode 100644 index 0000000..f3e0e5a --- /dev/null +++ b/src/testsuite/xeno-test/Makefile.am @@ -0,0 +1,9 @@ +testdir = @XENO_TEST_DIR@ + +test_SCRIPTS = xeno-test-run-wrapper dohell +test_PROGRAMS = xeno-test-run +bin_SCRIPTS = xeno-test + +xeno_test_run_CPPFLAGS = -DTESTDIR=\"$(testdir)\" + +EXTRA_DIST = $(test_SCRIPTS) diff --git a/src/testsuite/xeno-test/dohell b/src/testsuite/xeno-test/dohell new file mode 100644 index 0000000..cdece39 --- /dev/null +++ b/src/testsuite/xeno-test/dohell @@ -0,0 +1,96 @@ +#! /bin/sh + +usage() { + cat <<EOF +$0 [ -b path ] [ -s server ] [ -p port ] [ -m mntpoint ] [ -l path | seconds ] + +Generate load, using: +- hackbench if the path to the hackbench binary is specified with -b; +- nc to send TCP data to "server" port "port" if -s is specified (if -p +is not specified, the port 9, aka discard is used); +- dd to write data under "mntpoint" if -m is specified. +- during the runtime of the LTP test if the path to the LTP installation +directory is specifed with -l +- or during "seconds" seconds +EOF +} + +port=9 +while [ $# -gt 0 ]; do + case $1 in + -h|--help) usage + exit 0;; + -b) shift; hackbench="$1"; shift + ;; + -s) shift; server="$1"; shift + ;; + -p) shift; port="$1"; shift + ;; + -m) shift; mntpoint="$1"; shift + ;; + -l) shift; ltpdir="$1"; shift + ;; + -*) usage + exit 1;; + *) break;; + esac +done + +if [ -z "$ltpdir" -a $# -ne 1 ]; then + usage + exit 1 +fi + +pids="" + +if [ -n "$server" ]; then + if type nc > /dev/null 2>&1; then + nc=nc + elif type netcat > /dev/null 2>&1; then + nc=netcat + else + echo netcat or nc not found + exit 1 + fi + + seq 1 399999 > /tmp/netcat.data + ( while :; do cat /tmp/netcat.data; sleep 15; done | $nc $server $port ) & + pids="$!" +fi + +if [ -n "$mntpoint" ]; then + while :; do dd if=/dev/zero of=$mntpoint/bigfile bs=1024000 count=100; done & + pids="$pids $!" +fi + +if [ -n "$hackbench" ]; then + while :; do $hackbench 1; done & + pids="$pids $!" +fi + +while :; do cat /proc/interrupts; done > /dev/null 2>&1 & +pids="$pids $!" + +while :; do ps w; done > /dev/null 2>&1 & +pids="$pids $!" + +dd if=/dev/zero of=/dev/null & +pids="$pids $!" + +while :; do ls -lR / > /dev/null 2>&1; done & +pids="$pids $!" + +test -e /proc/sys/kernel/hung_task_timeout_secs && \ +echo 0 > /proc/sys/kernel/hung_task_timeout_secs + +if [ -n "$ltpdir" ]; then + cd "$ltpdir" && ./runalltests.sh + cd "$ltpdir" && ./runalltests.sh +else + sleep $1 +fi + +kill $pids > /dev/null 2>&1 +sleep 5 +killall -KILL -q cat $nc dd hackbench ls ps +killall -KILL -q `basename $0` sleep diff --git a/src/testsuite/xeno-test/xeno-test-run-wrapper b/src/testsuite/xeno-test/xeno-test-run-wrapper new file mode 100644 index 0000000..d12d6a5 --- /dev/null +++ b/src/testsuite/xeno-test/xeno-test-run-wrapper @@ -0,0 +1,20 @@ +#! /bin/sh + +check_alive() +{ + echo check_alive ${1+"$@"} >&1023 +} + +start_load() +{ + echo start_load >&1023 +} + +wait_load() +{ + read rc <&1022 +} + +script="$1"; shift +set -- ${1+"$@"} +source $script diff --git a/src/testsuite/xeno-test/xeno-test-run.c b/src/testsuite/xeno-test/xeno-test-run.c new file mode 100644 index 0000000..e1db488 --- /dev/null +++ b/src/testsuite/xeno-test/xeno-test-run.c @@ -0,0 +1,567 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/fcntl.h> +#include <unistd.h> +#include <signal.h> +#include <sys/wait.h> +#include <sys/select.h> + +#define CHILD_SCRIPT 0 +#define CHILD_CHECKED 1 +#define CHILD_LOAD 2 +#define CHILD_ANY -1 + +struct child { + unsigned type: 2; + unsigned dead: 1; + pid_t pid; + struct child *next; + int in, out; + time_t timeout; + int exit_status; + void (*handle)(struct child *, fd_set *); +}; + +#define fail_fprintf(f, fmt, args...) \ + fprintf(f, "%s failed: " fmt, scriptname , ##args) + +#define fail_perror(str) \ + fail_fprintf(stderr, "%s: %s\n", str, strerror(errno)) + +static const char *scriptname; +static volatile int sigexit; +static sigset_t sigchld_mask; +static struct child *first_child; +static char *loadcmd = "exec dd if=/dev/zero of=/dev/null"; +static fd_set inputs; +static struct child script, load; + +void handle_checked_child(struct child *child, fd_set *fds); +void handle_script_child(struct child *child, fd_set *fds); +void handle_load_child(struct child *child, fd_set *fds); + +int child_initv(struct child *child, int type, char *argv[]) +{ + int pipe_in[2]; + int pipe_out[2]; + int err, i; + pid_t pid; + + if (pipe(pipe_out) < 0) + return -errno; + + /* Set the CLOEXEC flag so that we do not leak file + descriptors in our children. */ + fcntl(pipe_out[0], F_SETFD, + fcntl(pipe_out[0], F_GETFD) | FD_CLOEXEC); + fcntl(pipe_out[1], F_SETFD, + fcntl(pipe_out[0], F_GETFD) | FD_CLOEXEC); + + if (type == CHILD_SCRIPT) { + if (pipe(pipe_in) < 0) + goto err_close_pipe_out; + + /* Set the CLOEXEC flag so that we do not leak file + descriptors in our children. */ + fcntl(pipe_in[0], F_SETFD, + fcntl(pipe_out[0], F_GETFD) | FD_CLOEXEC); + fcntl(pipe_in[1], F_SETFD, + fcntl(pipe_out[0], F_GETFD) | FD_CLOEXEC); + + } + + sigprocmask(SIG_BLOCK, &sigchld_mask, NULL); + pid = vfork(); + if (pid < 0) { + sigprocmask(SIG_UNBLOCK, &sigchld_mask, NULL); + err = -errno; + goto out_close_pipe; + } + + if (pid == 0) { + sigprocmask(SIG_UNBLOCK, &sigchld_mask, NULL); + + switch(type) { + case CHILD_CHECKED: + case CHILD_LOAD: + if (dup2(pipe_out[1], STDOUT_FILENO) < 0) { + fail_perror("dup2(pipe_out)"); + _exit(EXIT_FAILURE); + } + if (dup2(pipe_out[1], STDERR_FILENO) < 0) { + fail_perror("dup2(pipe_err)"); + _exit(EXIT_FAILURE); + } + break; + + case CHILD_SCRIPT: + if (dup2(pipe_in[0], 1022) < 0) { + fail_perror("dup2(pipe_in)"); + _exit(EXIT_FAILURE); + } + if (dup2(pipe_out[1], 1023) < 0) { + fail_perror("dup2(pipe_out)"); + _exit(EXIT_FAILURE); + } + break; + } + + err = execvp(argv[0], argv); + if (err < 0) { + fail_fprintf(stderr, "execvp(%s): %m", argv[0]); + _exit(EXIT_FAILURE); + } + } + child->type = type; + child->dead = 0; + child->pid = pid; + + child->next = first_child; + first_child = child; + sigprocmask(SIG_UNBLOCK, &sigchld_mask, NULL); + + fprintf(stderr, "Started child %d:", pid); + for (i = 0; argv[i]; i++) + fprintf(stderr, " %s", argv[i]); + fputc('\n', stderr); + + close(pipe_out[1]); + fcntl(pipe_out[0], F_SETFL, + fcntl(pipe_out[0], F_GETFL) | O_NONBLOCK); + child->out = pipe_out[0]; + FD_SET(child->out, &inputs); + + if (type == CHILD_SCRIPT) { + close(pipe_in[0]); + child->in = pipe_in[1]; + } + + time(&child->timeout); + child->timeout += 300; + + switch(type) { + case CHILD_CHECKED: + child->handle = handle_checked_child; + break; + case CHILD_SCRIPT: + child->handle = handle_script_child; + break; + case CHILD_LOAD: + child->handle = handle_load_child; + break; + } + + return 0; + + out_close_pipe: + if (type == CHILD_SCRIPT) { + close(pipe_in[0]); + close(pipe_in[1]); + } + err_close_pipe_out: + close(pipe_out[0]); + close(pipe_out[1]); + return err; +} + +int child_init(struct child *child, int type, char *cmdline) +{ + char *argv[] = { + getenv("SHELL") ?: "sh" , "-c", cmdline, NULL + }; + return child_initv(child, type, argv); +} + +void child_cleanup(struct child *child) +{ + struct child *prev; + + if (child == first_child) + first_child = child->next; + else + for (prev = first_child; prev; prev = prev->next) + if (prev->next == child) { + prev->next = child->next; + break; + } + + FD_CLR(child->out, &inputs); + close(child->out); + if (child->type == CHILD_SCRIPT) + close(child->in); +} + +struct child *child_search(pid_t pid) +{ + struct child *child; + + for (child = first_child; child; child = child->next) + if (child->pid == pid) + break; + + return child; +} + +int children_done_p(int type) +{ + struct child *child; + + for (child = first_child; child; child = child->next) + if ((type == CHILD_ANY || type == child->type) && !child->dead) + return 0; + + return 1; +} + +int children_term(int type) +{ + struct child *child; + struct timespec ts; + + for (child = first_child; child; child = child->next) + if (type == CHILD_ANY || child->type == type) + kill(child->pid, SIGTERM); + + ts.tv_sec = 5; + ts.tv_nsec = 0; + do { + if (children_done_p(type)) + return 1; + } while (nanosleep(&ts, &ts) == -1 && errno == EINTR); + + for (child = first_child; child; child = child->next) + if ((type == CHILD_ANY || child->type == type) + && !child->dead) { + fail_fprintf(stderr, "child %d would not die, sending " + "SIGKILL\n", child->pid); + kill(child->pid, SIGKILL); + } + + return 0; +} + +void sigchld_handler(int sig) +{ + struct child *child; + int status; + pid_t pid; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + child = child_search(pid); + if (!child) { + fail_fprintf(stderr, "dead child %d not found!\n", pid); + exit(EXIT_FAILURE); + } + + child->exit_status = status; + child->dead = 1; + } +} + +void cleanup(void) +{ + if (!children_term(CHILD_ANY)) + _exit(EXIT_FAILURE); +} + +void termsig(int sig) +{ + sigexit = sig; + cleanup(); +} + +void copy(int from, int to) +{ + char buffer[4096]; + ssize_t sz; + + do { + ssize_t written, wsz; + sz = read(from, buffer, sizeof(buffer)); + if (sz == -1) { + if (errno == EAGAIN) + break; + fail_perror("read"); + exit(EXIT_FAILURE); + } + + for (written = 0; written < sz; + written += (wsz > 0 ? wsz : 0)) { + wsz = write(to, buffer + written, sz - written); + if (wsz == -1) { + fail_perror("write"); + exit(EXIT_FAILURE); + } + } + } while (sz > 0); +} + +void handle_checked_child(struct child *child, fd_set *fds) +{ + time_t now = time(NULL); + + if (FD_ISSET(child->out, fds)) { + copy(child->out, STDOUT_FILENO); + child->timeout = now + 300; + } + + if (child->dead) { + int status = child->exit_status; + + /* A checked child died, this may be abnormal if no + termination signal was sent. */ + if (WIFEXITED(status)) { + if (sigexit) + goto cleanup; + fail_fprintf(stderr, + "child %d exited with status %d\n", + child->pid, WEXITSTATUS(status)); + } + + if (WIFSIGNALED(status)) { + if (sigexit && WTERMSIG(status) == SIGTERM) { + cleanup: + child_cleanup(child); + free(child); + return; + } + fail_fprintf(stderr, "child %d exited with signal %d\n", + child->pid, WTERMSIG(status)); + if (WCOREDUMP(status)) + fprintf(stderr, "(core dumped)\n"); + } + + exit(EXIT_FAILURE); + return; + } + + if (now > child->timeout) { + fail_fprintf(stderr, "child %d produced no output for 5 minutes.\n", + child->pid); + exit(EXIT_FAILURE); + } + +} + +void handle_script_child(struct child *child, fd_set *fds) +{ + static char buffer[4096]; + static unsigned pos; + char *l, *eol; + ssize_t sz; + int rc; + + if (child->dead) + exit(child->exit_status); + + if (!FD_ISSET(child->out, fds)) + return; + + sz = read(child->out, buffer + pos, sizeof(buffer) - pos); + for (l = buffer; (eol = strchr(l, '\n')); l = eol + 1) { + char buf[16]; + *eol = '\0'; + + if (!memcmp(l, "check_alive ", 12)) { + struct child *new_child; + + new_child = malloc(sizeof(*new_child)); + if (!new_child) { + fail_fprintf(stderr, "allocation failed\n"); + exit(EXIT_FAILURE); + } + + rc = child_init(new_child, CHILD_CHECKED, l + 12); + if (rc) { + fail_perror("child_init"); + exit(EXIT_FAILURE); + } + } else if (!memcmp(l, "start_load", 10)) { + if (!load.dead) { + fail_fprintf(stderr, "start_load run while load" + " script is already running.\n"); + exit(EXIT_FAILURE); + } + + rc = child_init(&load, CHILD_LOAD, loadcmd); + if (rc) { + fail_perror("child_init"); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "Invalid command %s\n", l); + exit(EXIT_FAILURE); + } + } + if (l != buffer) { + pos = strlen(l); + memmove(buffer, l, pos + 1); + } +} + +void handle_load_child(struct child *child, fd_set *fds) +{ + struct child *next; + + if (FD_ISSET(child->out, fds)) + copy(child->out, STDOUT_FILENO); + + if (child->dead) { + child_cleanup(child); + if (sigexit) + return; + + sigexit = SIGTERM; + + fprintf(stderr, "Load script terminated, " + "terminating checked scripts\n"); + + if (!children_term(CHILD_CHECKED)) + exit(EXIT_FAILURE); + + for (child = first_child; child; child = child->next) + if (child->type == CHILD_CHECKED) { + child_cleanup(child); + free(child); + } + + sigexit = 0; + + write(script.in, "0\n", 2); + return; + } +} + +void usage(const char *progname) +{ + fprintf(stderr, "%s [-l \"load command\"] script arguments...\n" + "Run \"script\" with \"arguments\" in a shell supplemented with" + " a few commands\nsuitable for running Xenomai tests.\n" + "\"load command\" is a command line to be run in order to" + " generate load\nwhile running tests.\n", progname); +} + +void setpath(void) +{ + char *path; + size_t path_len; + + path_len = strlen(getenv("PATH") ?: "") + strlen(TESTDIR) + 2; + path = malloc(path_len); + if (!path) { + perror("malloc"); + exit(EXIT_FAILURE); + } + if (getenv("PATH")) + snprintf(path, path_len, TESTDIR ":%s", getenv("PATH")); + else + snprintf(path, path_len, TESTDIR); + + setenv("PATH", path, 1); +} + +int main(int argc, char *argv[]) +{ + struct sigaction action; + int rc, maxfd; + + if (argc < 2) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + if (!strcmp(argv[argc - 1], "-h") + || !strcmp(argv[argc - 1], "--help")) { + usage(argv[0]); + exit(EXIT_SUCCESS); + } + + if (argc >= 3) { + if (!strcmp(argv[2], "-l")) { + if (argc == 3) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + loadcmd = argv[3]; + + argv[3] = argv[1]; + argv += 2; + } + } + scriptname = argv[1]; + + setpath(); + + action.sa_handler = termsig; + sigemptyset(&action.sa_mask); + action.sa_flags = SA_RESTART; + if (sigaction(SIGTERM, &action, NULL) < 0) { + fail_perror("sigaction(SIGTERM)"); + exit(EXIT_FAILURE); + } + if (sigaction(SIGINT, &action, NULL) < 0) { + fail_perror("sigaction(SIGTERM)"); + exit(EXIT_FAILURE); + } + + action.sa_flags |= SA_NOCLDSTOP; + action.sa_handler = sigchld_handler; + if (sigaction(SIGCHLD, &action, NULL) < 0) { + fail_perror("sigaction(SIGCHLD)"); + exit(EXIT_FAILURE); + } + atexit(&cleanup); + + load.dead = 1; + FD_ZERO(&inputs); + argv[0] = TESTDIR "/xeno-test-run-wrapper"; + rc = child_initv(&script, CHILD_SCRIPT, argv); + if (rc < 0) { + fail_fprintf(stderr, "script creation failed: %s\n", strerror(-rc)); + exit(EXIT_FAILURE); + } + maxfd = script.out; + + for (;;) { + struct child *child, *next; + struct timeval tv; + fd_set in; + int rc; + + tv.tv_sec = 0; + tv.tv_usec = 100000; + + in = inputs; + rc = select(maxfd + 1, &in, NULL, NULL, &tv); + + if (rc == -1) { + if (errno == EINTR) + continue; + fail_perror("select"); + exit(EXIT_FAILURE); + } + + maxfd = 0; + for (child = first_child; child; child = next) { + next = child->next; + + if (child->out > maxfd) + maxfd = child->out; + + child->handle(child, &in); + } + + if (children_done_p(CHILD_ANY)) { + if (sigexit) { + signal(sigexit, SIG_DFL); + raise(sigexit); + } + exit(EXIT_SUCCESS); + } + } +} diff --git a/src/testsuite/xeno-test/xeno-test.in b/src/testsuite/xeno-test/xeno-test.in new file mode 100644 index 0000000..4636856 --- /dev/null +++ b/src/testsuite/xeno-test/xeno-test.in @@ -0,0 +1,17 @@ +#! @XENO_TEST_DIR@/xeno-test-run + +set -e + +arith +check-vdso +clocktest -T 10 +cond-torture-native +cond-torture-posix +mutex-torture-native +mutex-torture-posix + +start_load + +check_alive latency ${1+"$@"} + +wait_load _______________________________________________ Xenomai-git mailing list [email protected] https://mail.gna.org/listinfo/xenomai-git
