/****************************************************************************** * * Copyright © International Business Machines Corp., 2006-2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * NAME * sched_latency.c * * DESCRIPTION * Measure the latency involved with periodic scheduling. * Steps: * - A thread is created at a priority of 89. * - It periodically sleeps for a specified duration(period). * - The delay is measured as * * delay = (now - start - i*period) converted to microseconds * * where, * now = CLOCK_MONOTONIC gettime in ns, start = CLOCK_MONOTONIC gettime * at the start of the test, i = iteration number, period = period chosen * * USAGE: * Use run_auto.sh script in current directory to build and run test. * * AUTHOR * Darren Hart <dvhltc@us.ibm.com> * * HISTORY * 2006-May-10: Initial version by Darren Hart <dvhltc@us.ibm.com> * 2007-Jul-11: Quantiles added by Josh Triplett <josh@kernel.org> * 2007-Jul-12: Latency tracing added by Josh Triplett <josh@kernel.org> * * This line has to be added to avoid a stupid CVS problem *****************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <librttest.h> #include <libstats.h> #define PRIO 89 //#define PERIOD 17*NS_PER_MS //#define ITERATIONS 100 #define MIN_ITERATIONS 100 #define DEFAULT_ITERATIONS 10000 #define DEF_PERIOD 5*NS_PER_MS #define DEF_LOAD_MS 1 #define PASS_US 100 #define HIST_BUCKETS 100 #define OVERHEAD 50000 // allow for 50 us of periodic overhead (context switch, etc.) nsec_t start; nsec_t end; static int ret = 0; static int iterations = 0; static unsigned long long latency_threshold = 0; static nsec_t period = DEF_PERIOD; static unsigned int load_ms = DEF_LOAD_MS; stats_container_t dat; stats_container_t hist; stats_quantiles_t quantiles; stats_record_t rec; void usage(void) { rt_help(); printf("sched_latency specific options:\n"); printf(" -dLOAD periodic load in ms (default 1)\n"); printf(" -lTHRESHOLD trace latency, with given threshold in us\n"); printf(" -tPERIOD period in ms (default 5)\n"); printf(" -iITERATIONS number of iterations (default %d)\n", DEFAULT_ITERATIONS); } int parse_args(int c, char *v) { int handled = 1; switch (c) { case 'h': usage(); exit(0); case 'd': load_ms = atoi(v); break; case 'i': iterations = atoi(v); break; case 'l': latency_threshold = strtoull(v, NULL, 0); break; case 't': period = strtoull(v, NULL, 0) * NS_PER_MS; break; default: handled = 0; break; } return handled; } void *periodic_thread(void *arg) { int i; nsec_t delay, avg_delay = 0, start_delay, min_delay = -1ULL, max_delay = 0; int failures = 0; nsec_t next = 0, now = 0, sched_delta = 0, delta = 0, prev = 0, iter_start; /* wait for the specified start time */ rt_nanosleep_until(start); now = rt_gettime(); start_delay = (now - start) / NS_PER_US; iter_start = next = now; debug(DBG_INFO, "ITERATION DELAY(US) MAX_DELAY(US) FAILURES\n"); debug(DBG_INFO, "--------- --------- ------------- --------\n"); if (latency_threshold) { latency_trace_enable(); latency_trace_start(); } for (i = 0; i < iterations; i++) { /* wait for the period to start */ next += period; prev = now; now = rt_gettime(); if (next < now) { printf("\nPERIOD MISSED!\n"); printf(" scheduled delta: %8llu us\n", sched_delta / 1000); printf(" actual delta: %8llu us\n", delta / 1000); printf(" latency: %8llu us\n", (delta - sched_delta) / 1000); printf("---------------------------------------\n"); printf(" previous start: %8llu us\n", (prev - iter_start) / 1000); printf(" now: %8llu us\n", (now - iter_start) / 1000); printf(" scheduled start: %8llu us\n", (next - iter_start) / 1000); printf("next scheduled start is in the past!\n"); ret = 1; break; } sched_delta = next - now; /* how long we should sleep */ delta = 0; do { nsec_t new_now; rt_nanosleep(next - now); new_now = rt_gettime(); delta += new_now - now; /* how long we did sleep */ now = new_now; } while (now < next); /* start of period */ delay = (now - iter_start - (nsec_t) (i + 1) * period) / NS_PER_US; rec.x = i; rec.y = delay; stats_container_append(&dat, rec); if (delay < min_delay) min_delay = delay; if (delay > max_delay) max_delay = delay; if (delay > pass_criteria) { failures++; ret = 1; } avg_delay += delay; if (latency_threshold && delay > latency_threshold) break; /* continuous status ticker */ debug(DBG_INFO, "%9i %9llu %13llu %8i\r", i, delay, max_delay, failures); fflush(stdout); busy_work_ms(load_ms); } if (latency_threshold) { latency_trace_stop(); if (i != iterations) { printf ("Latency threshold (%lluus) exceeded at iteration %d\n", latency_threshold, i); latency_trace_print(); stats_container_resize(&dat, i + 1); } } /* save samples before the quantile calculation messes things up! */ stats_hist(&hist, &dat); stats_container_save("samples", "Periodic Scheduling Latency Scatter Plot", "Iteration", "Latency (us)", &dat, "points"); stats_container_save("hist", "Periodic Scheduling Latency Histogram", "Latency (us)", "Samples", &hist, "steps"); avg_delay /= i; printf("\n\n"); printf("Start: %4llu us: %s\n", start_delay, start_delay < pass_criteria ? "PASS" : "FAIL"); printf("Min: %4llu us: %s\n", min_delay, min_delay < pass_criteria ? "PASS" : "FAIL"); printf("Max: %4llu us: %s\n", max_delay, max_delay < pass_criteria ? "PASS" : "FAIL"); printf("Avg: %4llu us: %s\n", avg_delay, avg_delay < pass_criteria ? "PASS" : "FAIL"); printf("StdDev: %.4f us\n", stats_stddev(&dat)); printf("Quantiles:\n"); stats_quantiles_calc(&dat, &quantiles); stats_quantiles_print(&quantiles); printf("Failed Iterations: %d\n", failures); return NULL; } int main(int argc, char *argv[]) { int per_id; setup(); pass_criteria = PASS_US; rt_init("d:l:ht:i:", parse_args, argc, argv); printf("-------------------------------\n"); printf("Scheduling Latency\n"); printf("-------------------------------\n\n"); if (load_ms * NS_PER_MS >= period - OVERHEAD) { printf("ERROR: load must be < period - %d us\n", OVERHEAD / NS_PER_US); exit(1); } if (iterations == 0) iterations = DEFAULT_ITERATIONS; if (iterations < MIN_ITERATIONS) { printf ("Too few iterations (%d), use min iteration instead (%d)\n", iterations, MIN_ITERATIONS); iterations = MIN_ITERATIONS; } printf("Running %d iterations with a period of %llu ms\n", iterations, period / NS_PER_MS); printf("Periodic load duration: %d ms\n", load_ms); printf("Expected running time: %d s\n", (int)(iterations * ((float)period / NS_PER_SEC))); if (stats_container_init(&dat, iterations)) exit(1); if (stats_container_init(&hist, HIST_BUCKETS)) { stats_container_free(&dat); exit(1); } /* use the highest value for the quantiles */ if (stats_quantiles_init(&quantiles, (int)log10(iterations))) { stats_container_free(&hist); stats_container_free(&dat); exit(1); } /* wait one quarter second to execute */ start = rt_gettime() + 250 * NS_PER_MS; per_id = create_fifo_thread(periodic_thread, NULL, PRIO); join_thread(per_id); join_threads(); printf("\nCriteria: latencies < %d us\n", (int)pass_criteria); printf("Result: %s\n", ret ? "FAIL" : "PASS"); stats_container_free(&dat); stats_container_free(&hist); stats_quantiles_free(&quantiles); return ret; }