Commit 1595e1c1 authored by Joanne Hugé's avatar Joanne Hugé

Update makefiles, remove latency-measure program, and upadte README

parent aea66d79
......@@ -5,7 +5,7 @@ of three OLinuXino LIME2 boards I have.
## cyclictest like programs
clockres, latency-measure and packet-exchange are C programs wrote using
clockres, and packet-exchange are C programs wrote using
the same structure as cyclictest, and borrows large portions of the cyclictest
code:
......@@ -18,9 +18,6 @@ can have it's priority set as an option, default is to 99. It writes results in
* clockres is used to determine the resolution of the clock (CLOCK_MONOTONIC), by doing successive
calls to clock_gettime
* latency-measure is used to determine the maximum wakeup latency of a real-time thread. It is
a simplified version of cyclictest I wrote to have a better understanding and more control.
* packet-exchange has a client and a server, which exchange UDP ethernet packets using real-time threads,
and can send them on a ETF qdisc by setting a txtime timestamp. software timestamps can be generated using
the SO_TIMESTAMPING option, and timestamps inside the userspace program, for measuring purposes. A RTT
......
ARM_CC = arm-linux-gnueabihf-gcc
ARM_PROG = clockres_arm
PROG = clockres
SRCDIR = ../src
......@@ -8,34 +6,26 @@ SRCS += tracer.c
OBJS = $(SRCS:%.c=%.o)
ifeq ($(DEBUG),)
CFLAGS = -O2
else
CFLAGS = -Og -g -Wall -Wextra
endif
CFLAGS += -MD -MP
CFLAGS += -I $(SRCDIR)
CFLAGS += -std=gnu99
CPPFLAGS = -D_GNU_SOURCE
LDFLAGS = -pthread
vpath %.c $(SRCDIR)
$(ARM_PROG): FORCE
make clean
make -e CC:=arm-linux-gnueabihf-gcc $(PROG)
mv $(PROG) $(ARM_PROG)
FORCE:
$(PROG): $(OBJS)
$(CC) $(LDFLAGS) $^ -o $@
-include $(subst .c,.d,$(SRCS))
run: $(PROG)
./$^
clean:
$(RM) $(OBJS) $(PROG) $(ARM_PROG) $(subst .c,.d,$(SRCS))
$(RM) $(OBJS) $(PROG) $(subst .c,.d,$(SRCS))
.PHONY: clean FORCE
.PHONY: clean
#define _GNU_SOURCE
#include <errno.h>
#include <error.h>
#include <inttypes.h>
......
#include "tracer.h"
#define _GNU_SOURCE
#include <errno.h>
#include <error.h>
#include <fcntl.h>
......
ARM_CC = arm-linux-gnueabihf-gcc
ARM_PROG = main_arm
PROG = main
SRCDIR = ../src
SRCS = main.c
OBJS = $(SRCS:%.c=%.o)
CFLAGS = -Og -g -Wall -Wextra
CFLAGS += -MD -MP
CFLAGS += -I $(SRCDIR)
CFLAGS += -std=gnu99
LDFLAGS = -pthread
vpath %.c $(SRCDIR)
$(ARM_PROG): FORCE
make clean
make -e CC:=arm-linux-gnueabihf-gcc $(PROG)
mv $(PROG) $(ARM_PROG)
FORCE:
$(PROG): $(OBJS)
$(CC) $(LDFLAGS) $^ -o $@
-include $(subst .c,.d,$(SRCS))
run: $(PROG)
./$^
clean:
$(RM) $(OBJS) $(PROG) $(subst .c,.d,$(SRCS))
.PHONY: clean FORCE
#include <errno.h>
#include <error.h>
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <stdint.h>
#include <inttypes.h>
#define CLOCK_ID CLOCK_MONOTONIC
#define NSECS_PER_SECOND INT64_C(1000000000)
#ifdef DEBUG_TRACE
struct timespec * debug_current_times;
struct timespec * debug_next_times;
int64_t * debug_diffs;
#endif
typedef struct thread_stat {
int nb_cycles;
int64_t max_diff;
int64_t min_diff;
} thread_stat_t;
typedef struct thread_param {
int64_t interval;
int priority;
int max_cycles;
thread_stat_t stat;
} thread_param_t;
typedef struct main_param {
int refresh_rate;
} main_param_t;
static inline int64_t diff_ns(struct timespec t1, struct timespec t2);
static inline int64_t max(int64_t a, int64_t b);
static inline int64_t min(int64_t a, int64_t b);
static inline struct timespec add_ns(struct timespec t, int64_t ns);
static inline struct timespec sub_ns(struct timespec t, int64_t ns);
static void sleep_poll(struct timespec * next, struct timespec * current) {
struct timespec pre_next;
pre_next = sub_ns(next, 3000000);
clock_nanosleep(CLOCK_ID, TIMER_ABSTIME, pre_next);
for(;;) {
clock_gettime(CLOCK_ID, current);
if( current->tv_sec > next->tv_sec || (current->tv_sec == next->tv_sec && current->tv_nsec >= next->tv_nsec) )
break;
}
}
static void sleep_nanosleep(struct timespec * next, struct timespec * current) {
clock_nanosleep(CLOCK_ID, TIMER_ABSTIME, next, NULL);
clock_gettime(CLOCK_ID, current);
}
// Real-time thread
static void *timerthread(void *p) {
struct timespec next;
struct timespec current;
struct sched_param priority;
thread_param_t * param = (thread_param_t *)p;
thread_stat_t * stat = &param->stat;
priority.sched_priority = param->priority;
int err = sched_setscheduler(0, SCHED_FIFO, &priority);
if (err)
error(EXIT_FAILURE, errno, "Couldn't set priority");
stat->max_diff = 0;
stat->min_diff = 1000000;
clock_gettime(CLOCK_ID, &next);
next = add_ns(next, param->interval);
for(stat->nb_cycles = 0;; stat->nb_cycles++) {
if(param->max_cycles)
if(stat->nb_cycles >= param->max_cycles)
break;
#ifdef DEBUG_TRACE
debug_next_times[stat->nb_cycles] = next;
#endif
sleep_poll(&next, &current);
#ifdef DEBUG_TRACE
debug_current_times[stat->nb_cycles] = current;
#endif
int64_t diff = diff_ns(current, next);
#ifdef DEBUG_TRACE
debug_diffs[stat->nb_cycles] = diff;
#endif
stat->max_diff = max(stat->max_diff, diff);
stat->min_diff = min(stat->min_diff, diff);
next = add_ns(current, param->interval);
}
printf("Done\n");
return NULL;
}
static void process_options(int argc, char *argv[], thread_param_t * param, main_param_t * main_param) {
for(;;) {
int c = getopt(argc, argv, "l:p:i:r:");
if(c == -1)
break;
switch(c) {
case 'p':
param->priority = atoi(optarg);
break;
case 'l':
param->max_cycles = atoi(optarg);
break;
case 'i':
param->interval = (atoi(optarg) * 1000);
break;
case 'r':
main_param->refresh_rate = atoi(optarg);
break;
default:
exit(EXIT_FAILURE);
break;
}
}
}
// Main thread, has non-real time priority
// Handles the IO and creates real time threads
int main(int argc, char *argv[]) {
pthread_t thread;
thread_param_t param;
main_param_t main_param;
int err;
// Default values
param.interval = 1000000;
param.max_cycles = 100;
param.priority = 80;
main_param.refresh_rate = 10000;
process_options(argc, argv, &param, &main_param);
usleep(10000);
#ifdef DEBUG_TRACE
debug_current_times = malloc(sizeof(struct timespec) * param.max_cycles);
debug_next_times = malloc(sizeof(struct timespec) * param.max_cycles);
debug_diffs = malloc(sizeof(int64_t) * param.max_cycles);
FILE * debug_log = fopen("latency-measure-log", "w+");
#endif
err = pthread_create(&thread, NULL, timerthread, (void *)&param);
if(err)
error(EXIT_FAILURE, errno, "Couldn't create thread");
for (;;) {
usleep(main_param.refresh_rate);
printf("Maximum latency: %" PRIi64 "ns (%d)", (param.stat.max_diff), param.stat.nb_cycles);
printf(", minimum latency: %" PRIi64 "ns (%d)\n", (param.stat.min_diff), param.stat.nb_cycles);
if( param.max_cycles == param.stat.nb_cycles )
break;
}
#ifdef DEBUG_TRACE
for(int i = 0; i < param.max_cycles; i++) {
fprintf(debug_log, "%ld %" PRIi64 " \n\n", debug_next_times[i].tv_sec, debug_next_times[i].tv_nsec);
fprintf(debug_log, "%ld %" PRIi64 " \n", debug_current_times[i].tv_sec, debug_current_times[i].tv_nsec);
fprintf(debug_log, " %" PRIi64 " \n", debug_diffs[i]);
}
fclose(debug_log);
#endif
exit(EXIT_SUCCESS);
}
static inline int64_t diff_ns(struct timespec t1, struct timespec t2) {
int64_t diff;
diff = NSECS_PER_SECOND * (t1.tv_sec - t2.tv_sec);
diff += ((int64_t)t1.tv_nsec) - ((int64_t)t2.tv_nsec);
if( diff < 0 )
diff = -diff;
return diff;
}
static inline int64_t max(int64_t a, int64_t b) {
return a > b ? a : b;
}
static inline int64_t min(int64_t a, int64_t b) {
return a < b ? a : b;
}
static inline struct timespec add_ns(struct timespec t, int64_t ns) {
struct timespec ret;
ret.tv_nsec = t.tv_nsec + ns;
ret.tv_sec = t.tv_sec;
if(ret.tv_nsec >= NSECS_PER_SECOND) {
ret.tv_sec++;
ret.tv_nsec -= NSECS_PER_SECOND;
}
return ret;
}
static inline struct timespec sub_ns(struct timespec t, int64_t ns) {
struct timespec ret;
ret.tv_nsec = t.tv_nsec - ns;
ret.tv_sec = t.tv_sec;
if(ret.tv_nsec < 0) {
ret.tv_sec--;
ret.tv_nsec += NSECS_PER_SECOND;
}
return ret;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment