#!/usr/bin/ksh93 # # count_timer_syscalls.ksh- Count timer syscalls # # Tests whether clock_gettime() is implemented via traditional # syscalls or uses a shortcut (e.g. via shared memory/Linux VDSO) # # Written by Roland Mainz # builtin cat builtin wc #builtin rm set -o nounset function enumerate_clock_sources { typeset s typeset dummy aname avalue nameref ar="$1" s="$( clang -E -dM -m32 -include time.h - < '/dev/null' | \ egrep \ '#[[:space:]]*define[[:space:]]+CLOCK_.+[[:space:]]+[[:digit:]]+' )" while read dummy aname avalue ; do if [[ \ "${aname}" != ~(El)CLOCK_ || \ "${avalue}" != ~(Elr)[[:digit:]]+ ]] ; then continue fi integer ar["$aname"].value ar["$aname"].name="${aname}" ar["$aname"].value="${avalue}" done <<<"$s" return 0 } function generate_test_code { nameref ar="$1" integer num_testruns=$2 set -o errexit set -o nounset printf $"## Generating test code...\n" rm -f \ 'generated_count_timer_syscalls.c' \ 'generated_count_timer_syscalls' { cat < * */ #include #include #include #include #include #include #include #include #include #include #include #ifndef CLOCK_INVALID #define CLOCK_INVALID -1 #endif #define NSEC_PER_SEC (1000000000LL) #define NUM_TESTRUNS (${num_testruns}UL) static clockid_t get_clockid(int fd) { const int CLOCKFD = 3; return (((unsigned int) ~fd) << 3) | CLOCKFD; } static void test_loop_on_clock(clockid_t clkid, unsigned long num_cycles) { struct timespec ts; unsigned long cycles; for(cycles = 0UL ; cycles < num_cycles ; cycles++) { (void)clock_gettime(clkid, &ts); (void)ts; } } int main(int ac, char *av[]) { const char *clockname = av[1]; (void)puts("# start."); if ((ac != 2) || (!clockname)) { (void)fprintf(stderr, "%s: Missing clockname argument\n", av[0]); return EXIT_FAILURE; } if (!strcmp(clockname, "nic")) { int fd; clockid_t clkid; const char *devicename = "/dev/ptp0"; fd = open(devicename, O_RDWR); if (fd < 0) { (void)fprintf(stderr, "opening %s: %s\n", devicename, strerror(errno)); return(EXIT_FAILURE); } clkid = get_clockid(fd); if (CLOCK_INVALID == clkid) { (void)fprintf(stderr, "failed to read clock id\n"); return(EXIT_FAILURE); } (void)printf("## test run using nic clock\n"); test_loop_on_clock(clkid, NUM_TESTRUNS); (void)close(fd); EOF typeset i for i in "${!ar[@]}" ; do typeset name="${ar[$i].name}" printf '\t} else if (!strcmp(clockname, "%s")) {\n' "$name" printf '\t\t(void)printf("## test run using %s\\n");\n' "$name" printf '\n' printf '\t\ttest_loop_on_clock(%s, NUM_TESTRUNS);\n' "$name" done cat <"generated_count_timer_syscalls.c" return 0 } function compile_test_code { set -o errexit printf $"## Compiling test code...\n" clang -m32 -g -Wall -Wextra \ -o generated_count_timer_syscalls \ generated_count_timer_syscalls.c return 0 } function run_tests { nameref ar="$1" compound cmd typeset cmd.output integer cmd.exit_code typeset i printf $"## Running tests...\n" for i in "${!ar[@]}" ; do nameref n=ar["$i"] printf $"Probing %q...\n" "${n.name}" cmd.output="${ strace ./generated_count_timer_syscalls "${n.name}" 2>&1 ; cmd.exit_code="$?" ; }" integer n.num_syscalls_clock_gettime="${ egrep 'clock_gettime' <<<"${cmd.output}" | wc -l ;}" integer n.num_syscalls_total="${ wc -l <<<"${cmd.output}" ; }" integer n.exit_code=${cmd.exit_code} done } function print_results { nameref c=$1 set -o nounset printf $"## Raw test results:\n" print -v c printf $"## Summary of test results for %q:\n" "${c.uname}" nameref ar="c.clock_names" for i in "${!ar[@]}" ; do nameref n=ar["$i"] printf $"Clock name %-20s \t" ${n.name} if (( (n.exit_code != 0) || (n.num_syscalls_total < 30) )) ; then printf $"TEST FAILED\n" elif (( n.num_syscalls_clock_gettime >= c.num_testruns )) ; then printf $"uses traditional syscalls\n" elif (( n.num_syscalls_clock_gettime <= 1 )) ; then printf $"uses shortcut\n" else printf $"Data inconclusive\n" fi done return 0 } function main { compound c compound -A c.clock_names integer c.num_testruns=32100 typeset c.uname="$(uname -a)" typeset c.hostname="$(hostname)" if ! enumerate_clock_sources c.clock_names ; then print -u2 -f $"%s: Could not enumerate clock names.\n" "$1" return 1 fi if ! generate_test_code c.clock_names ${c.num_testruns} ; then print -u2 -f $"%s: Could not generate test code.\n" "$1" return 1 fi if ! compile_test_code ; then print -u2 -f $"%s: Could not compile test code.\n" "$1" return 1 fi run_tests c.clock_names print_results c print $"## Done." return 0 } main "$@" # EOF.