All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC rt-tests v1 0/4] Generate machine-readable output
@ 2020-12-18 19:35 Daniel Wagner
  2020-12-18 19:35 ` [RFC rt-tests v1 1/4] rt-tests: Rename error.h to rt-error.h Daniel Wagner
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Daniel Wagner @ 2020-12-18 19:35 UTC (permalink / raw)
  To: Clark Williams, John Kacur; +Cc: linux-rt-users, Daniel Wagner

The current output of cyclictest is optimized for humans to read. This
is all good when working directly with the tools. But for CI
integration it's a bit of pain. Furthermore, not all rt-tests use the
same output format.

By using some easy to parse existing machine-readable format we can use
standard libraries to parse the data. For example in jitterdebug there
is a short Python program[1] to visualize either the histogram[2] or
all samples[3].

The implementation for JSON output for this is very simple. The last
patch adds a version of jitterdebugs's JSON output, which looks like

{
  "file_version": 1,
  "version:": "cyclictest V 1.90",
  "num_threads": 2,
  "resolution_in_ns": 0,
  "cmdline:": "./cyclictest --affinity=1-2 --duration=1s --output=dump.json -h 1000 -p 80",
  "sysinfo": {
    "sysname": "Linux",
    "nodename": "beryllium",
    "release": "5.9.14-1-default",
    "version": "#1 SMP Sat Dec 12 06:57:32 UTC 2020 (c648a46)",
    "machine": "x86_64"
  },
  "thread": {
    "0": {
      "histogram": {
        "0": 16,
        "1": 853,
        "2": 80,
        "3": 50,
        "4": 1
      },
      "cycles": 1000,
      "min": 0,
      "max": 4,
      "avg": 1.17,
      "cpu": 1,
      "node": 0
    },
    "1": {
      "histogram": {
        "0": 14,
        "1": 833,
        "2": 93,
        "3": 56,
        "4": 4
      },
      "cycles": 1000,
      "min": 0,
      "max": 4,
      "avg": 1.20,
      "cpu": 2,
      "node": 0
    }
  }
}

It's just a rough version. I didn't try to make it generic for the
other rt-tests or make it as plugin as John was suggesting. I'd think
we could make this feature as compile option, if you want to keep the
program small. Obviously, we could also make the terminal output a
compile option, to keep it small.

Anyway, what do you think about it?

Thanks,
Daniel

[1] https://github.com/igaw/jitterdebugger/blob/master/jitterplot
[2] https://www.monom.org/data/jitterdebug/jitterplot-hist.png
[3] https://www.monom.org/data/jitterdebug/jitterplot-samples.png

Daniel Wagner (4):
  rt-tests: Rename error.h to rt-error.h
  cyclictest: Move thread data to struct thread_param
  signaltest: Move thread data to struct thread_param
  cyclictest: Add JSON output feature

 src/cyclictest/cyclictest.c         | 182 +++++++++++++++++++++++++---
 src/include/pip_stress.h            |   5 +-
 src/include/{error.h => rt-error.h} |   0
 src/lib/error.c                     |   2 +-
 src/lib/rt-numa.c                   |   2 +-
 src/lib/rt-utils.c                  |   3 +-
 src/oslat/oslat.c                   |   2 +-
 src/pi_tests/pi_stress.c            |   3 +-
 src/pmqtest/pmqtest.c               |   6 +-
 src/ptsematest/ptsematest.c         |   6 +-
 src/signaltest/signaltest.c         |  46 +++----
 src/sigwaittest/sigwaittest.c       |   2 +-
 src/svsematest/svsematest.c         |   3 +-
 13 files changed, 204 insertions(+), 58 deletions(-)
 rename src/include/{error.h => rt-error.h} (100%)

-- 
2.29.2


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [RFC rt-tests v1 1/4] rt-tests: Rename error.h to rt-error.h
  2020-12-18 19:35 [RFC rt-tests v1 0/4] Generate machine-readable output Daniel Wagner
@ 2020-12-18 19:35 ` Daniel Wagner
  2020-12-18 19:35 ` [RFC rt-tests v1 2/4] cyclictest: Move thread data to struct thread_param Daniel Wagner
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Daniel Wagner @ 2020-12-18 19:35 UTC (permalink / raw)
  To: Clark Williams, John Kacur; +Cc: linux-rt-users, Daniel Wagner

Avoid confusion with the system header called error.h.

Signed-off-by: Daniel Wagner <dwagner@suse.de>
---
 src/cyclictest/cyclictest.c         | 2 +-
 src/include/pip_stress.h            | 5 +++--
 src/include/{error.h => rt-error.h} | 0
 src/lib/error.c                     | 2 +-
 src/lib/rt-numa.c                   | 2 +-
 src/lib/rt-utils.c                  | 3 ++-
 src/oslat/oslat.c                   | 2 +-
 src/pi_tests/pi_stress.c            | 3 +--
 src/pmqtest/pmqtest.c               | 6 +++---
 src/ptsematest/ptsematest.c         | 6 +++---
 src/signaltest/signaltest.c         | 2 +-
 src/sigwaittest/sigwaittest.c       | 2 +-
 src/svsematest/svsematest.c         | 3 ++-
 13 files changed, 20 insertions(+), 18 deletions(-)
 rename src/include/{error.h => rt-error.h} (100%)

diff --git a/src/cyclictest/cyclictest.c b/src/cyclictest/cyclictest.c
index 5c738005f778..9225e52aa982 100644
--- a/src/cyclictest/cyclictest.c
+++ b/src/cyclictest/cyclictest.c
@@ -36,7 +36,7 @@
 
 #include "rt-utils.h"
 #include "rt-numa.h"
-#include "error.h"
+#include "rt-error.h"
 
 #include <bionic.h>
 
diff --git a/src/include/pip_stress.h b/src/include/pip_stress.h
index ee8b545ab117..f03aaf397897 100644
--- a/src/include/pip_stress.h
+++ b/src/include/pip_stress.h
@@ -14,8 +14,9 @@
 #include <sys/wait.h>
 #include <signal.h>
 #include <sched.h>
-#include <rt-utils.h>
-#include "error.h"
+
+#include "rt-utils.h"
+#include "rt-error.h"
 
 void low(pid_t pid);	/* low priority process */
 void medium(void);	/* medium priority process */
diff --git a/src/include/error.h b/src/include/rt-error.h
similarity index 100%
rename from src/include/error.h
rename to src/include/rt-error.h
diff --git a/src/lib/error.c b/src/lib/error.c
index 4434a842da17..616f70b044e0 100644
--- a/src/lib/error.c
+++ b/src/lib/error.c
@@ -5,7 +5,7 @@
  * error routines, similar to those found in
  * Advanced Programming in the UNIX Environment 2nd ed.
  */
-#include "error.h"
+#include "rt-error.h"
 
 /* Print an error message, plus a message for err and exit with error err */
 void err_exit(int err, char *fmt, ...)
diff --git a/src/lib/rt-numa.c b/src/lib/rt-numa.c
index 04f2e9adb4b1..4a0865715141 100644
--- a/src/lib/rt-numa.c
+++ b/src/lib/rt-numa.c
@@ -10,7 +10,7 @@
 #include <sched.h>
 #include <pthread.h>
 
-#include "error.h"
+#include "rt-error.h"
 #include "rt-numa.h"
 
 int get_available_cpus(struct bitmask *cpumask)
diff --git a/src/lib/rt-utils.c b/src/lib/rt-utils.c
index 2d68d62cd875..321a11b1172d 100644
--- a/src/lib/rt-utils.c
+++ b/src/lib/rt-utils.c
@@ -20,9 +20,10 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #include <sys/syscall.h> /* For SYS_gettid definitions */
+
 #include "rt-utils.h"
 #include "rt-sched.h"
-#include "error.h"
+#include "rt-error.h"
 
 #define  TRACEBUFSIZ  1024
 
diff --git a/src/oslat/oslat.c b/src/oslat/oslat.c
index 0811079d9f04..9e6f70600830 100644
--- a/src/oslat/oslat.c
+++ b/src/oslat/oslat.c
@@ -43,7 +43,7 @@
 
 #include "rt-utils.h"
 #include "rt-numa.h"
-#include "error.h"
+#include "rt-error.h"
 
 #ifdef __GNUC__
 # define atomic_inc(ptr)   __sync_add_and_fetch((ptr), 1)
diff --git a/src/pi_tests/pi_stress.c b/src/pi_tests/pi_stress.c
index 538d12c975aa..49f89b7b0136 100644
--- a/src/pi_tests/pi_stress.c
+++ b/src/pi_tests/pi_stress.c
@@ -47,8 +47,7 @@
 
 #include "rt-sched.h"
 #include "rt-utils.h"
-
-#include "error.h"
+#include "rt-error.h"
 
 /* test timeout */
 #define TIMEOUT 2
diff --git a/src/pmqtest/pmqtest.c b/src/pmqtest/pmqtest.c
index 5f7a24d55db6..349b47741003 100644
--- a/src/pmqtest/pmqtest.c
+++ b/src/pmqtest/pmqtest.c
@@ -21,11 +21,11 @@
 #include <linux/unistd.h>
 #include <utmpx.h>
 #include <mqueue.h>
+#include <pthread.h>
+
 #include "rt-utils.h"
 #include "rt-get_cpu.h"
-#include "error.h"
-
-#include <pthread.h>
+#include "rt-error.h"
 
 #define SYNCMQ_NAME "/syncmsg%d"
 #define TESTMQ_NAME "/testmsg%d"
diff --git a/src/ptsematest/ptsematest.c b/src/ptsematest/ptsematest.c
index 2e392299fdc4..e41b8af127da 100644
--- a/src/ptsematest/ptsematest.c
+++ b/src/ptsematest/ptsematest.c
@@ -19,11 +19,11 @@
 #include <sys/mman.h>
 #include <linux/unistd.h>
 #include <utmpx.h>
+#include <pthread.h>
+
 #include "rt-utils.h"
 #include "rt-get_cpu.h"
-#include "error.h"
-
-#include <pthread.h>
+#include "rt-error.h"
 
 enum {
 	AFFINITY_UNSPECIFIED,
diff --git a/src/signaltest/signaltest.c b/src/signaltest/signaltest.c
index 5427db7f8d85..c34bc994d886 100644
--- a/src/signaltest/signaltest.c
+++ b/src/signaltest/signaltest.c
@@ -31,7 +31,7 @@
 #include <sys/time.h>
 #include <sys/mman.h>
 
-#include "error.h"
+#include "rt-error.h"
 #include "rt-utils.h"
 #include "rt-numa.h"
 
diff --git a/src/sigwaittest/sigwaittest.c b/src/sigwaittest/sigwaittest.c
index 7e287bd2424f..f10c24914d4a 100644
--- a/src/sigwaittest/sigwaittest.c
+++ b/src/sigwaittest/sigwaittest.c
@@ -42,7 +42,7 @@
 
 #include "rt-utils.h"
 #include "rt-get_cpu.h"
-#include "error.h"
+#include "rt-error.h"
 
 enum {
 	AFFINITY_UNSPECIFIED,
diff --git a/src/svsematest/svsematest.c b/src/svsematest/svsematest.c
index 7388efb3f488..7a298e0dea8c 100644
--- a/src/svsematest/svsematest.c
+++ b/src/svsematest/svsematest.c
@@ -28,9 +28,10 @@
 #include <sys/sem.h>
 #include <sys/time.h>
 #include <sys/mman.h>
+
 #include "rt-utils.h"
 #include "rt-get_cpu.h"
-#include "error.h"
+#include "rt-error.h"
 
 #define SEM_WAIT_FOR_RECEIVER 0
 #define SEM_WAIT_FOR_SENDER 1
-- 
2.29.2


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [RFC rt-tests v1 2/4] cyclictest: Move thread data to struct thread_param
  2020-12-18 19:35 [RFC rt-tests v1 0/4] Generate machine-readable output Daniel Wagner
  2020-12-18 19:35 ` [RFC rt-tests v1 1/4] rt-tests: Rename error.h to rt-error.h Daniel Wagner
@ 2020-12-18 19:35 ` Daniel Wagner
  2020-12-18 19:35 ` [RFC rt-tests v1 3/4] signaltest: " Daniel Wagner
  2020-12-18 19:35 ` [RFC rt-tests v1 4/4] cyclictest: Add JSON output feature Daniel Wagner
  3 siblings, 0 replies; 5+ messages in thread
From: Daniel Wagner @ 2020-12-18 19:35 UTC (permalink / raw)
  To: Clark Williams, John Kacur; +Cc: linux-rt-users, Daniel Wagner

Group thread realated data such as thread ID to struct thread_param.

Signed-off-by: Daniel Wagner <dwagner@suse.de>
---
 src/cyclictest/cyclictest.c | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/src/cyclictest/cyclictest.c b/src/cyclictest/cyclictest.c
index 9225e52aa982..c0e7600b4740 100644
--- a/src/cyclictest/cyclictest.c
+++ b/src/cyclictest/cyclictest.c
@@ -113,6 +113,9 @@ static char *policyname(int policy);
 
 /* Struct to transfer parameters to the thread */
 struct thread_param {
+	pthread_t thread;
+	int threadstarted;
+	int tid;
 	int prio;
 	int policy;
 	int mode;
@@ -141,9 +144,6 @@ struct thread_stat {
 	long *smis;
 	long *hist_array;
 	long *outliers;
-	pthread_t thread;
-	int threadstarted;
-	int tid;
 	long reduce;
 	long redmax;
 	long cycleofmax;
@@ -530,7 +530,7 @@ static void *timerthread(void *param)
 	interval.tv_sec = par->interval / USEC_PER_SEC;
 	interval.tv_nsec = (par->interval % USEC_PER_SEC) * 1000;
 
-	stat->tid = gettid();
+	par->tid = gettid();
 
 	sigemptyset(&sigset);
 	sigaddset(&sigset, par->signal);
@@ -539,7 +539,7 @@ static void *timerthread(void *param)
 	if (par->mode == MODE_CYCLIC) {
 		sigev.sigev_notify = SIGEV_THREAD_ID | SIGEV_SIGNAL;
 		sigev.sigev_signo = par->signal;
-		sigev.sigev_notify_thread_id = stat->tid;
+		sigev.sigev_notify_thread_id = par->tid;
 		timer_create(par->clock, &sigev, &timer);
 		tspec.it_interval = interval;
 	}
@@ -613,7 +613,7 @@ static void *timerthread(void *param)
 		setitimer(ITIMER_REAL, &itimer, NULL);
 	}
 
-	stat->threadstarted++;
+	par->threadstarted++;
 
 	while (!shutdown) {
 
@@ -719,7 +719,7 @@ static void *timerthread(void *param)
 			shutdown++;
 			pthread_mutex_lock(&break_thread_id_lock);
 			if (break_thread_id == 0) {
-				break_thread_id = stat->tid;
+				break_thread_id = par->tid;
 				tracemark("hit latency threshold (%llu > %d)",
 					  (unsigned long long) diff, tracelimit);
 				break_thread_value = diff;
@@ -795,7 +795,7 @@ static void *timerthread(void *param)
 	/* switch to normal */
 	schedp.sched_priority = 0;
 	sched_setscheduler(0, SCHED_OTHER, &schedp);
-	stat->threadstarted = -1;
+	par->threadstarted = -1;
 
 	return NULL;
 }
@@ -1298,7 +1298,7 @@ static void print_tids(struct thread_param *par[], int nthreads)
 
 	printf("# Thread Ids:");
 	for (i = 0; i < nthreads; i++)
-		printf(" %05d", par[i]->stats->tid);
+		printf(" %05d", par[i]->tid);
 	printf("\n");
 }
 
@@ -1412,7 +1412,7 @@ static void print_stat(FILE *fp, struct thread_param *par, int index, int verbos
 				fmt = "T:%2d (%5d) P:%2d I:%ld C:%7lu "
 				        "Min:%7ld Act:%5ld Avg:%5ld Max:%8ld";
 
-			fprintf(fp, fmt, index, stat->tid, par->prio,
+			fprintf(fp, fmt, index, par->tid, par->prio,
 				par->interval, stat->cycles, stat->min,
 				stat->act, stat->cycles ?
 				(long)(stat->avg/stat->cycles) : 0, stat->max);
@@ -1468,7 +1468,7 @@ static void rstat_print_stat(struct thread_param *par, int index, int verbose, i
 				fmt = "T:%2d (%5d) P:%2d I:%ld C:%7lu "
 				        "Min:%7ld Act:%5ld Avg:%5ld Max:%8ld";
 
-			dprintf(fd, fmt, index, stat->tid, par->prio,
+			dprintf(fd, fmt, index, par->tid, par->prio,
 				par->interval, stat->cycles, stat->min,
 				stat->act, stat->cycles ?
 				(long)(stat->avg/stat->cycles) : 0, stat->max);
@@ -1969,9 +1969,9 @@ int main(int argc, char **argv)
 		stat->min = 1000000;
 		stat->max = 0;
 		stat->avg = 0.0;
-		stat->threadstarted = 1;
 		stat->smi_count = 0;
-		status = pthread_create(&stat->thread, &attr, timerthread, par);
+		par->threadstarted = 1;
+		status = pthread_create(&par->thread, &attr, timerthread, par);
 		if (status)
 			fatal("failed to create thread %d: %s\n", i, strerror(status));
 
@@ -2041,10 +2041,10 @@ int main(int argc, char **argv)
 	if (quiet)
 		quiet = 2;
 	for (i = 0; i < num_threads; i++) {
-		if (statistics[i]->threadstarted > 0)
-			pthread_kill(statistics[i]->thread, SIGTERM);
-		if (statistics[i]->threadstarted) {
-			pthread_join(statistics[i]->thread, NULL);
+		if (parameters[i]->threadstarted > 0)
+			pthread_kill(parameters[i]->thread, SIGTERM);
+		if (parameters[i]->threadstarted) {
+			pthread_join(parameters[i]->thread, NULL);
 			if (quiet && !histogram)
 				print_stat(stdout, parameters[i], i, 0, 0);
 		}
-- 
2.29.2


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [RFC rt-tests v1 3/4] signaltest: Move thread data to struct thread_param
  2020-12-18 19:35 [RFC rt-tests v1 0/4] Generate machine-readable output Daniel Wagner
  2020-12-18 19:35 ` [RFC rt-tests v1 1/4] rt-tests: Rename error.h to rt-error.h Daniel Wagner
  2020-12-18 19:35 ` [RFC rt-tests v1 2/4] cyclictest: Move thread data to struct thread_param Daniel Wagner
@ 2020-12-18 19:35 ` Daniel Wagner
  2020-12-18 19:35 ` [RFC rt-tests v1 4/4] cyclictest: Add JSON output feature Daniel Wagner
  3 siblings, 0 replies; 5+ messages in thread
From: Daniel Wagner @ 2020-12-18 19:35 UTC (permalink / raw)
  To: Clark Williams, John Kacur; +Cc: linux-rt-users, Daniel Wagner

Group thread realated data such as thread ID to struct thread_param.

Signed-off-by: Daniel Wagner <dwagner@suse.de>
---
 src/signaltest/signaltest.c | 44 ++++++++++++++++++-------------------
 1 file changed, 22 insertions(+), 22 deletions(-)

diff --git a/src/signaltest/signaltest.c b/src/signaltest/signaltest.c
index c34bc994d886..dd5633d5fc51 100644
--- a/src/signaltest/signaltest.c
+++ b/src/signaltest/signaltest.c
@@ -40,6 +40,10 @@
 
 /* Struct to transfer parameters to the thread */
 struct thread_param {
+	pthread_t thread;
+	pthread_t tothread;
+	int threadstarted;
+	int tid;
 	int id;
 	int prio;
 	int signal;
@@ -47,6 +51,7 @@ struct thread_param {
 	struct thread_stat *stats;
 	int bufmsk;
 	int cpu;
+	int interrupted;
 };
 
 /* Struct for statistics */
@@ -58,11 +63,6 @@ struct thread_stat {
 	long act;
 	double avg;
 	long *values;
-	pthread_t thread;
-	pthread_t tothread;
-	int threadstarted;
-	int tid;
-	int interrupted;
 };
 
 static int shutdown;
@@ -86,7 +86,7 @@ void *signalthread(void *param)
 	pthread_t thread;
 	cpu_set_t mask;
 
-	stat->tid = gettid();
+	par->tid = gettid();
 
 	if (par->cpu != -1) {
 		CPU_ZERO(&mask);
@@ -105,7 +105,7 @@ void *signalthread(void *param)
 	schedp.sched_priority = par->prio;
 	sched_setscheduler(0, policy, &schedp);
 
-	stat->threadstarted++;
+	par->threadstarted++;
 
 	clock_gettime(CLOCK_MONOTONIC, &before);
 
@@ -128,7 +128,7 @@ void *signalthread(void *param)
 
 		/* Get current time */
 		clock_gettime(CLOCK_MONOTONIC, &now);
-		pthread_kill(stat->tothread, SIGUSR1);
+		pthread_kill(par->tothread, SIGUSR1);
 
 		/* Skip the first cycle */
 		if (first) {
@@ -148,7 +148,7 @@ void *signalthread(void *param)
 
 		if (!stopped && tracelimit && !par->id  && (diff > tracelimit)) {
 			stat->act = diff;
-			stat->interrupted = 1;
+			par->interrupted = 1;
 			stopped++;
 			shutdown++;
 		}
@@ -167,7 +167,7 @@ void *signalthread(void *param)
 	schedp.sched_priority = 0;
 	sched_setscheduler(0, SCHED_OTHER, &schedp);
 
-	stat->threadstarted = -1;
+	par->threadstarted = -1;
 
 	return NULL;
 }
@@ -298,7 +298,7 @@ static void print_stat(struct thread_param *par, int index, int verbose)
 		if (quiet != 1) {
 			printf("T:%2d (%5d) P:%2d C:%7lu "
 			       "Min:%7ld Act:%5ld Avg:%5ld Max:%8ld\n",
-			       index, stat->tid, par->prio,
+			       index, par->tid, par->prio,
 			       stat->cycles, stat->min, stat->act,
 			       stat->cycles ?
 			       (long)(stat->avg/stat->cycles) : 0, stat->max);
@@ -389,8 +389,8 @@ int main(int argc, char **argv)
 		stat[i].min = 1000000;
 		stat[i].max = -1000000;
 		stat[i].avg = 0.0;
-		stat[i].threadstarted = 1;
-		status = pthread_create(&stat[i].thread, NULL, signalthread,
+		par[i].threadstarted = 1;
+		status = pthread_create(&par[i].thread, NULL, signalthread,
 					&par[i]);
 		if (status)
 			fatal("failed to create thread %d: %s\n", i,
@@ -401,18 +401,18 @@ int main(int argc, char **argv)
 		int allstarted = 1;
 
 		for (i = 0; i < num_threads; i++) {
-			if (stat[i].threadstarted != 2)
+			if (par[i].threadstarted != 2)
 				allstarted = 0;
 		}
 		if (!allstarted)
 			continue;
 
 		for (i = 0; i < num_threads - 1; i++)
-			stat[i].tothread = stat[i+1].thread;
-		stat[i].tothread = stat[0].thread;
+			par[i].tothread = par[i+1].thread;
+		par[i].tothread = par[0].thread;
 		break;
 	}
-	pthread_kill(stat[0].thread, signum);
+	pthread_kill(par[0].thread, signum);
 
 	while (!shutdown) {
 		char lavg[256];
@@ -443,12 +443,12 @@ int main(int argc, char **argv)
 	if (quiet)
 		quiet = 2;
 	for (i = 0; i < num_threads; i++) {
-		if (stat[i].threadstarted > 0)
-			pthread_kill(stat[i].thread, SIGUSR1);
-		if (stat[i].interrupted)
+		if (par[i].threadstarted > 0)
+			pthread_kill(par[i].thread, SIGUSR1);
+		if (par[i].interrupted)
 			printf("Thread %d exceeded trace limit.\n", i);
-		if (stat[i].threadstarted) {
-			pthread_join(stat[i].thread, NULL);
+		if (par[i].threadstarted) {
+			pthread_join(par[i].thread, NULL);
 			print_stat(&par[i], i, 0);
 		}
 		if (stat[i].values)
-- 
2.29.2


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [RFC rt-tests v1 4/4] cyclictest: Add JSON output feature
  2020-12-18 19:35 [RFC rt-tests v1 0/4] Generate machine-readable output Daniel Wagner
                   ` (2 preceding siblings ...)
  2020-12-18 19:35 ` [RFC rt-tests v1 3/4] signaltest: " Daniel Wagner
@ 2020-12-18 19:35 ` Daniel Wagner
  3 siblings, 0 replies; 5+ messages in thread
From: Daniel Wagner @ 2020-12-18 19:35 UTC (permalink / raw)
  To: Clark Williams, John Kacur; +Cc: linux-rt-users, Daniel Wagner

Add --output command line opttion which will store the results into a
JSON file.

Signed-off-by: Daniel Wagner <dwagner@suse.de>
---
 src/cyclictest/cyclictest.c | 146 +++++++++++++++++++++++++++++++++++-
 1 file changed, 145 insertions(+), 1 deletion(-)

diff --git a/src/cyclictest/cyclictest.c b/src/cyclictest/cyclictest.c
index c0e7600b4740..a93e9ee2426d 100644
--- a/src/cyclictest/cyclictest.c
+++ b/src/cyclictest/cyclictest.c
@@ -11,6 +11,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <inttypes.h>
 #include <stdarg.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -217,6 +218,7 @@ static struct timespec globalt;
 
 static char fifopath[MAX_PATH];
 static char histfile[MAX_PATH];
+static char outfile[MAX_PATH];
 
 static struct thread_param **parameters;
 static struct thread_stat **statistics;
@@ -950,7 +952,7 @@ enum option_values {
 	OPT_TRIGGER_NODES, OPT_UNBUFFERED, OPT_NUMA, OPT_VERBOSE,
 	OPT_DBGCYCLIC, OPT_POLICY, OPT_HELP, OPT_NUMOPTS,
 	OPT_ALIGNED, OPT_SECALIGNED, OPT_LAPTOP, OPT_SMI,
-	OPT_TRACEMARK, OPT_POSIX_TIMERS,
+	OPT_TRACEMARK, OPT_POSIX_TIMERS, OPT_OUTPUT
 };
 
 /* Process commandline options */
@@ -1003,6 +1005,7 @@ static void process_options(int argc, char *argv[])
 			{"policy",           required_argument, NULL, OPT_POLICY },
 			{"help",             no_argument,       NULL, OPT_HELP },
 			{"posix_timers",     no_argument,	NULL, OPT_POSIX_TIMERS },
+			{"output",           required_argument, NULL, OPT_OUTPUT },
 			{NULL, 0, NULL, 0 },
 		};
 		int c = getopt_long(argc, argv, "a::A::b:c:d:D:h:H:i:l:MNo:p:mqrRsSt::uvD:x",
@@ -1059,6 +1062,7 @@ static void process_options(int argc, char *argv[])
 		case OPT_HISTFILE:
 			use_histfile = 1;
 			strncpy(histfile, optarg, strnlen(optarg, MAX_PATH-1));
+			histfile[MAX_PATH-1] = '\0';
 			break;
 		case 'i':
 		case OPT_INTERVAL:
@@ -1078,6 +1082,10 @@ static void process_options(int argc, char *argv[])
 		case 'o':
 		case OPT_OSCOPE:
 			oscope_reduction = atoi(optarg); break;
+		case OPT_OUTPUT:
+			strncpy(outfile, optarg, strnlen(optarg, MAX_PATH-1));
+			outfile[MAX_PATH-1] = '\0';
+			break;
 		case 'p':
 		case OPT_PRIORITY:
 			priority = atoi(optarg);
@@ -1695,6 +1703,124 @@ static void rstat_setup(void)
 	return;
 }
 
+struct system_info {
+	char *sysname;
+	char *nodename;
+	char *release;
+	char *version;
+	char *machine;
+};
+
+struct system_info *collect_system_info(void)
+{
+	struct system_info *si;
+	struct utsname buf;
+	int err;
+
+	si = malloc(sizeof(struct system_info));
+	if (!si)
+		err_exit(ENOMEM, "malloc()");
+
+	err = uname(&buf);
+	if (err)
+		err_exit(errno, "Could not retrieve system information");
+
+	si->sysname = strdup(buf.sysname);
+	si->nodename = strdup(buf.nodename);
+	si->release = strdup(buf.release);
+	si->version = strdup(buf.version);
+	si->machine = strdup(buf.machine);
+
+	if (!si->sysname ||
+            !si->nodename ||
+	    !si->release ||
+	    !si->version ||
+	    !si->machine)
+		err_exit(ENOMEM, "Could not copy system information");
+
+	return si;
+}
+
+static char *get_cmdline(int argc, char *argv[])
+{
+	char *cmdline;
+	int len, i;
+
+	len = 0;
+	for (i = 0; i < argc; i++)
+		len += strlen(argv[i]) + 1;
+
+	cmdline = malloc(len);
+	if (!cmdline)
+		err_exit(ENOMEM, "Could not copy cmdline");
+
+	cmdline[0] = '\0';
+	for (i = 0; i < argc;) {
+		cmdline = strcat(cmdline, argv[i]);
+		i++;
+		if (i < argc)
+			cmdline = strcat(cmdline, " ");
+	}
+
+	return cmdline;
+}
+
+void free_system_info(struct system_info *sysinfo)
+{
+	free(sysinfo->sysname);
+	free(sysinfo->nodename);
+	free(sysinfo->release);
+	free(sysinfo->version);
+	free(sysinfo->machine);
+	free(sysinfo);
+}
+
+static void dump_stats(FILE *f, struct system_info *sysinfo,
+		char *cmdline, struct thread_param **par)
+{
+	unsigned int i, j, comma;
+	struct thread_stat *s;
+
+	fprintf(f, "{\n");
+	fprintf(f, "  \"file_version\": 1,\n");
+	fprintf(f, "  \"version:\": \"cyclictest V %1.2f\",\n", VERSION);
+	fprintf(f, "  \"num_threads\": %d,\n", num_threads);
+	fprintf(f, "  \"resolution_in_ns\": %u,\n", use_nsecs);
+	fprintf(f, "  \"cmdline:\": \"%s\",\n", cmdline);
+	fprintf(f, "  \"sysinfo\": {\n");
+	fprintf(f, "    \"sysname\": \"%s\",\n", sysinfo->sysname);
+	fprintf(f, "    \"nodename\": \"%s\",\n", sysinfo->nodename);
+	fprintf(f, "    \"release\": \"%s\",\n", sysinfo->release);
+	fprintf(f, "    \"version\": \"%s\",\n", sysinfo->version);
+	fprintf(f, "    \"machine\": \"%s\"\n", sysinfo->machine);
+	fprintf(f, "  },\n");
+	fprintf(f, "  \"thread\": {\n");
+	for (i = 0; i < num_threads; i++) {
+		fprintf(f, "    \"%u\": {\n", i);
+
+		fprintf(f, "      \"histogram\": {");
+		s = par[i]->stats;
+		for (j = 0, comma = 0; j < histogram; j++) {
+			if (s->hist_array[j] == 0)
+				continue;
+			fprintf(f, "%s", comma ? ",\n" : "\n");
+			fprintf(f, "        \"%u\": %" PRIu64,j, s->hist_array[j]);
+			comma = 1;
+		}
+		if (comma)
+			fprintf(f, "\n");
+		fprintf(f, "      },\n");
+		fprintf(f, "      \"cycles\": %" PRIu64 ",\n", s->cycles);
+		fprintf(f, "      \"min\": %" PRIu64 ",\n", s->min);
+		fprintf(f, "      \"max\": %" PRIu64 ",\n", s->max);
+		fprintf(f, "      \"avg\": %.2f,\n", s->avg/s->cycles);
+		fprintf(f, "      \"cpu\": %d,\n", par[i]->cpu);
+		fprintf(f, "      \"node\": %d\n", par[i]->node);
+		fprintf(f, "    }%s\n", i == num_threads - 1 ? "" : ",");
+	}
+	fprintf(f, "  }\n");
+	fprintf(f, "}\n");
+}
 
 int main(int argc, char **argv)
 {
@@ -2038,6 +2164,24 @@ int main(int argc, char **argv)
 	if (!verbose && !quiet && refresh_on_max)
 		printf("\033[%dB", num_threads + 2);
 
+	if (strlen(outfile) != 0) {
+		FILE *f = fopen(outfile, "w");
+		struct system_info *sysinfo;
+		char *cmdline;
+
+		if (!f)
+			err_exit(errno, "Failed to open output file '%s'", outfile);
+
+		sysinfo = collect_system_info();
+		cmdline = get_cmdline(argc, argv);
+
+		dump_stats(f, sysinfo, cmdline, parameters);
+
+		free(cmdline);
+		free_system_info(sysinfo);
+		fclose(f);
+	}
+
 	if (quiet)
 		quiet = 2;
 	for (i = 0; i < num_threads; i++) {
-- 
2.29.2


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-12-18 19:36 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-18 19:35 [RFC rt-tests v1 0/4] Generate machine-readable output Daniel Wagner
2020-12-18 19:35 ` [RFC rt-tests v1 1/4] rt-tests: Rename error.h to rt-error.h Daniel Wagner
2020-12-18 19:35 ` [RFC rt-tests v1 2/4] cyclictest: Move thread data to struct thread_param Daniel Wagner
2020-12-18 19:35 ` [RFC rt-tests v1 3/4] signaltest: " Daniel Wagner
2020-12-18 19:35 ` [RFC rt-tests v1 4/4] cyclictest: Add JSON output feature Daniel Wagner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.