358 lines
9.3 KiB
C
358 lines
9.3 KiB
C
/******************************************************************************
|
|
*
|
|
* 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
|
|
* gtod-latency.c
|
|
*
|
|
* DESCRIPTION
|
|
* Simple program to measure the time between several pairs of calls to
|
|
* gettimeofday(). If the average delta is greater than just a few
|
|
* microseconds on an unloaded system, then something is probably wrong.
|
|
*
|
|
* It is quite similar to the programs in the directory, but provides the
|
|
* additional capability to produce graphical output as a histogram or a
|
|
* scatter graph.*
|
|
*
|
|
* USAGE:
|
|
* Use run_auto.sh script in current directory to build and run test.
|
|
*
|
|
* AUTHOR
|
|
* Darren Hart <dvhltc@us.ibm.com>
|
|
*
|
|
* HISTORY
|
|
* 2006-Aug-17: Initial version by Darren Hart <dvhltc@us.ibm.com>
|
|
* 2006-Aug-23: Minor changes by John Kacur <jekacur@ca.ibm.com>
|
|
* 2006-Nov-20: Augmented to use libstats
|
|
* 2007-Jul-12: Latency tracing added by Josh Triplett <josh@kernel.org>
|
|
* 2007-Jul-13: Quantiles 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 <unistd.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sched.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <libstats.h>
|
|
#include <librttest.h>
|
|
#include <sys/mman.h>
|
|
|
|
#define ITERATIONS 10000000
|
|
#define MIN_ITERATION 10000
|
|
#define HIST_BUCKETS 20
|
|
|
|
#define SCATTER_FILENAME 0
|
|
#define HIST_FILENAME 1
|
|
|
|
#define SCATTER_TITLE 0
|
|
#define HIST_TITLE 1
|
|
|
|
#define SCATTER_LABELX 0
|
|
#define SCATTER_LABELY 1
|
|
#define HIST_LABELX 2
|
|
#define HIST_LABELY 3
|
|
|
|
char *titles[] = { "scatter plot",
|
|
"histogram"
|
|
};
|
|
|
|
char *filenames[] = { "scatter",
|
|
"hist"
|
|
};
|
|
|
|
char *labels[] = { "scatter plot x-axis",
|
|
"scatter plot y-axis",
|
|
"histogram x-axis",
|
|
"histogram y-axis"
|
|
};
|
|
|
|
static unsigned long long latency_threshold = 0;
|
|
static unsigned int iterations = ITERATIONS;
|
|
|
|
void stats_cmdline_help(void)
|
|
{
|
|
printf("Usage: ./gtod_latency {-[so|scatter-output] -[ho|hist-output]"
|
|
" -[st|scatter-title] -[ht|hist-title] -[sxl|scatter-xlabel]"
|
|
" -[syl|scatter-ylabel] -[hxl|hist-xlabel] -[hyl|hist-ylabel]"
|
|
" -[lt|latency-trace] -[i|iterations]}" " -[help] \n");
|
|
printf
|
|
("**command-line options are not supported yet for this testcase\n");
|
|
}
|
|
|
|
int stats_cmdline(int argc, char *argv[])
|
|
{
|
|
int i;
|
|
char *flag;
|
|
|
|
if (argc == 1)
|
|
return 0;
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
if (*argv[i] != '-') {
|
|
printf("missing flag indicator\n");
|
|
return -1;
|
|
}
|
|
|
|
flag = ++argv[i];
|
|
|
|
if (!strcmp(flag, "help") || !strcmp(flag, "h")) {
|
|
stats_cmdline_help();
|
|
exit(0);
|
|
}
|
|
|
|
if (!strcmp(flag, "so") || !strcmp(flag, "scatter-output")) {
|
|
if (i + 1 == argc) {
|
|
printf("flag has missing argument\n");
|
|
return -1;
|
|
}
|
|
filenames[SCATTER_FILENAME] = argv[++i];
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(flag, "ho") || !strcmp(flag, "hist-output")) {
|
|
if (i + 1 == argc) {
|
|
printf("flag has missing argument\n");
|
|
return -1;
|
|
}
|
|
filenames[HIST_FILENAME] = argv[++i];
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(flag, "st") || !strcmp(flag, "scatter-title")) {
|
|
if (i + 1 == argc) {
|
|
printf("flag has missing argument\n");
|
|
return -1;
|
|
}
|
|
titles[SCATTER_TITLE] = argv[++i];
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(flag, "ht") || !strcmp(flag, "hist-title")) {
|
|
if (i + 1 == argc) {
|
|
printf("flag has missing argument\n");
|
|
return -1;
|
|
}
|
|
titles[HIST_TITLE] = argv[++i];
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(flag, "sxl") || !strcmp(flag, "scatter-xlabel")) {
|
|
if (i + 1 == argc) {
|
|
printf("flag has missing argument\n");
|
|
return -1;
|
|
}
|
|
labels[SCATTER_LABELX] = argv[++i];
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(flag, "syl") || !strcmp(flag, "scatter-ylabel")) {
|
|
if (i + 1 == argc) {
|
|
printf("flag has missing argument\n");
|
|
return -1;
|
|
}
|
|
labels[SCATTER_LABELY] = argv[++i];
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(flag, "hxl") || !strcmp(flag, "hist-xlabel")) {
|
|
if (i + 1 == argc) {
|
|
printf("flag has missing argument\n");
|
|
return -1;
|
|
}
|
|
labels[HIST_LABELX] = argv[++i];
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(flag, "hyl") || !strcmp(flag, "hist-ylabel")) {
|
|
if (i + 1 == argc) {
|
|
printf("flag has missing argument\n");
|
|
return -1;
|
|
}
|
|
labels[HIST_LABELY] = argv[++i];
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(flag, "lt") || !strcmp(flag, "latency-trace")) {
|
|
if (i + 1 == argc) {
|
|
printf("flag has missing argument\n");
|
|
return -1;
|
|
}
|
|
latency_threshold = strtoull(argv[++i], NULL, 0);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(flag, "i") || !strcmp(flag, "iterations")) {
|
|
if (i + 1 == argc) {
|
|
printf("flag has missing argument\n");
|
|
return -1;
|
|
}
|
|
iterations = strtoull(argv[++i], NULL, 0);
|
|
continue;
|
|
}
|
|
|
|
printf("unknown flag given\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
long long timespec_subtract(struct timespec *a, struct timespec *b)
|
|
{
|
|
long long ns;
|
|
ns = (b->tv_sec - a->tv_sec) * 1000000000LL;
|
|
ns += (b->tv_nsec - a->tv_nsec);
|
|
return ns;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int i, j, k, err;
|
|
unsigned long long delta;
|
|
unsigned long long max, min;
|
|
struct sched_param param;
|
|
stats_container_t dat;
|
|
stats_container_t hist;
|
|
stats_quantiles_t quantiles;
|
|
stats_record_t rec;
|
|
struct timespec *start_data;
|
|
struct timespec *stop_data;
|
|
|
|
if (stats_cmdline(argc, argv) < 0) {
|
|
printf("usage: %s help\n", argv[0]);
|
|
exit(1);
|
|
}
|
|
|
|
if (iterations < MIN_ITERATION) {
|
|
iterations = MIN_ITERATION;
|
|
printf("user \"iterations\" value is too small (use: %d)\n",
|
|
iterations);
|
|
}
|
|
|
|
stats_container_init(&dat, iterations);
|
|
stats_container_init(&hist, HIST_BUCKETS);
|
|
stats_quantiles_init(&quantiles, (int)log10(iterations));
|
|
setup();
|
|
|
|
mlockall(MCL_CURRENT | MCL_FUTURE);
|
|
|
|
start_data = calloc(iterations, sizeof(struct timespec));
|
|
if (start_data == NULL) {
|
|
printf("Memory allocation Failed (too many Iteration: %d)\n",
|
|
iterations);
|
|
exit(1);
|
|
}
|
|
stop_data = calloc(iterations, sizeof(struct timespec));
|
|
if (stop_data == NULL) {
|
|
printf("Memory allocation Failed (too many Iteration: %d)\n",
|
|
iterations);
|
|
free(start_data);
|
|
exit(1);
|
|
}
|
|
|
|
/* switch to SCHED_FIFO 99 */
|
|
param.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
|
err = sched_setscheduler(0, SCHED_FIFO, ¶m);
|
|
|
|
/* Check that the user has the appropriate privileges */
|
|
if (err) {
|
|
if (errno == EPERM) {
|
|
fprintf(stderr,
|
|
"This program runs with a scheduling policy of SCHED_FIFO at priority %d\n",
|
|
param.sched_priority);
|
|
fprintf(stderr,
|
|
"You don't have the necessary privileges to create such a real-time process.\n");
|
|
} else {
|
|
fprintf(stderr, "Failed to set scheduler, errno %d\n",
|
|
errno);
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
printf("\n----------------------\n");
|
|
printf("Gettimeofday() Latency\n");
|
|
printf("----------------------\n");
|
|
printf("Iterations: %d\n\n", iterations);
|
|
|
|
/* collect iterations pairs of gtod calls */
|
|
max = min = 0;
|
|
if (latency_threshold) {
|
|
latency_trace_enable();
|
|
latency_trace_start();
|
|
}
|
|
/* This loop runs for a long time, hence can cause soft lockups.
|
|
Calling sleep periodically avoids this. */
|
|
for (i = 0; i < (iterations / 10000); i++) {
|
|
for (j = 0; j < 10000; j++) {
|
|
k = (i * 10000) + j;
|
|
clock_gettime(CLOCK_MONOTONIC, &start_data[k]);
|
|
clock_gettime(CLOCK_MONOTONIC, &stop_data[k]);
|
|
}
|
|
usleep(1000);
|
|
}
|
|
for (i = 0; i < iterations; i++) {
|
|
delta = timespec_subtract(&start_data[i], &stop_data[i]);
|
|
rec.x = i;
|
|
rec.y = delta;
|
|
stats_container_append(&dat, rec);
|
|
if (i == 0 || delta < min)
|
|
min = delta;
|
|
if (delta > max)
|
|
max = delta;
|
|
if (latency_threshold && delta > latency_threshold)
|
|
break;
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
stats_hist(&hist, &dat);
|
|
stats_container_save(filenames[SCATTER_FILENAME], titles[SCATTER_TITLE],
|
|
labels[SCATTER_LABELX], labels[SCATTER_LABELY],
|
|
&dat, "points");
|
|
stats_container_save(filenames[HIST_FILENAME], titles[HIST_TITLE],
|
|
labels[HIST_LABELX], labels[HIST_LABELY], &hist,
|
|
"steps");
|
|
|
|
/* report on deltas */
|
|
printf("Min: %llu ns\n", min);
|
|
printf("Max: %llu ns\n", max);
|
|
printf("Avg: %.4f ns\n", stats_avg(&dat));
|
|
printf("StdDev: %.4f ns\n", stats_stddev(&dat));
|
|
printf("Quantiles:\n");
|
|
stats_quantiles_calc(&dat, &quantiles);
|
|
stats_quantiles_print(&quantiles);
|
|
|
|
stats_container_free(&dat);
|
|
stats_container_free(&hist);
|
|
stats_quantiles_free(&quantiles);
|
|
|
|
return 0;
|
|
}
|