Hi Juri,
This series mainly intends to introduce calibration mechanism and simplify the grammar used in JSON files. [4, 5, 6] of this series are exceptions but also put in this series since the following grammar simplification patches are highly dependent on those features and removal of command line interface done in [4, 5, 6].
This series are also found in: ssh://git@git.linaro.org/people/picheng.chen/rt-app.git calibration_grammar_series
Vincent Guittot (9): make load frequency independent fix calibration issue on mt8173 describe resource names in plain string add run and sleep resource types add phase feature remove support of command line interface simplify grammar of JSON files make global object optional make resource object optional
README.in | 79 +------- src/rt-app.c | 451 +++++++++++++++++++++++++----------------- src/rt-app_args.c | 328 +------------------------------ src/rt-app_args.h | 3 - src/rt-app_parse_config.c | 492 +++++++++++++++++++++++++++------------------- src/rt-app_types.h | 63 +++--- src/rt-app_utils.c | 64 +++++- src/rt-app_utils.h | 3 + 8 files changed, 666 insertions(+), 817 deletions(-)
From: Vincent Guittot vincent.guittot@linaro.org
Use calibration variable to calculate loop count for load simulation. The CPU on which the calibration will run can be specified in "calibration" parameter in "global object" or an integer can be specified as ns per loop in the same parameter.
E.g. To calibrate on CPU2 set : "calibration" : "CPU2" To force 1234 ns per loop value set "calibration" : 1234
Signed-off-by: Christian Muller christian.muller@linaro.org Signed-off-by: Vincent Guittot vincent.guittot@linaro.org Signed-off-by: Pi-Cheng Chen pi-cheng.chen@linaro.org --- src/rt-app.c | 104 +++++++++++++++++++++++++++++++++++++++------- src/rt-app_parse_config.c | 30 ++++++++++++- src/rt-app_types.h | 2 + src/rt-app_utils.c | 17 ++++++++ src/rt-app_utils.h | 3 ++ 5 files changed, 140 insertions(+), 16 deletions(-)
diff --git a/src/rt-app.c b/src/rt-app.c index d9c3dca..518fe53 100644 --- a/src/rt-app.c +++ b/src/rt-app.c @@ -18,14 +18,18 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+#define _GNU_SOURCE #include <fcntl.h> #include "rt-app.h" #include "rt-app_utils.h" +#include <sched.h> +#include "pthread.h"
static int errno; static volatile int continue_running; static pthread_t *threads; static int nthreads; +static int p_load; rtapp_options_t opts;
static ftrace_data_t ft_data = { @@ -34,29 +38,84 @@ static ftrace_data_t ft_data = { .marker_fd = -1, };
-static inline busywait(struct timespec *to) +/* + * Function: to do some useless operation. + */ +void waste_cpu_cycles(int load_loops) { - struct timespec t_step; - while (1) { - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t_step); - if (!timespec_lower(&t_step, to)) - break; + double param, result; + double n, i; + + param = 0.95; + n = 4; + for (i = 0 ; i < load_loops ; i++) { + result = ldexp(param , (ldexp(param , ldexp(param , n)))); + result = ldexp(param , (ldexp(param , ldexp(param , n)))); + result = ldexp(param , (ldexp(param , ldexp(param , n)))); + result = ldexp(param , (ldexp(param , ldexp(param , n)))); + } + return; +} + +/* +* calibrate_cpu_cycles() +* collects the time that waste_cycles runs. +*/ +int calibrate_cpu_cycles(int clock) +{ + struct timespec start, stop; + int max_load_loop = 10000; + unsigned int diff; + int nsec_per_loop, avg_per_loop = 0; + int ret, cal_trial = 1000; + + while (cal_trial) { + cal_trial--; + + clock_gettime(clock, &start); + waste_cpu_cycles(max_load_loop); + clock_gettime(clock, &stop); + + diff = (int)timespec_sub_to_ns(&stop, &start); + nsec_per_loop = diff / max_load_loop; + avg_per_loop = (avg_per_loop + nsec_per_loop) >> 1; + + /* collect a critical mass of samples.*/ + if ((abs(nsec_per_loop - avg_per_loop) * 50) < avg_per_loop) + return avg_per_loop; + + /* + * use several loop duration in order to be sure to not + * fall into a specific platform loop duration + *(like the cpufreq period) + */ + /*randomize the number of loops and recheck 1000 times*/ + max_load_loop += 33333; + max_load_loop %= 1000000; } + return 0; +} + +static inline loadwait(struct timespec *exec_time) +{ + unsigned long exec, load_count; + + exec = timespec_to_usec(exec_time); + load_count = (exec * 1000)/p_load; + waste_cpu_cycles(load_count); }
void run(int ind, struct timespec *min, struct timespec *max, rtapp_tasks_resource_list_t *blockages, int nblockages) { int i; - struct timespec t_start, now, t_exec, t_totexec = *max; + struct timespec t_exec; rtapp_resource_access_list_t *lock, *last;
- /* Get the start time */ - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t_start); + t_exec = *min;
for (i = 0; i < nblockages; i++) { - /* Lock resources */ lock = blockages[i].acl; while (lock != NULL) { @@ -71,14 +130,13 @@ void run(int ind, struct timespec *min, struct timespec *max, }
/* Busy wait */ - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now); - t_exec = timespec_add(&now, &blockages[i].usage); log_debug("[%d] busywait for %lu", ind, timespec_to_usec(&blockages[i].usage)); if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] busywait for %d", ind, timespec_to_usec(&blockages[i].usage)); - busywait(&t_exec); + loadwait(&blockages[i].usage); + t_exec = timespec_sub(&t_exec, &blockages[i].usage);
/* Unlock resources */ lock = last; @@ -94,13 +152,12 @@ void run(int ind, struct timespec *min, struct timespec *max, }
/* Compute finish time for CPUTIME_ID clock */ - t_exec = timespec_add(&t_start, &t_totexec); log_debug("[%d] busywait for %lu", ind, timespec_to_usec(&t_exec)); if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] busywait for %lu", ind, timespec_to_usec(&t_exec)); - busywait(&t_exec); + loadwait(&t_exec); }
static void @@ -446,6 +503,7 @@ int main(int argc, char* argv[]) int i, res; thread_data_t *tdata; char tmp[PATH_LENGTH]; + static cpu_set_t orig_set;
parse_command_line(argc, argv, &opts);
@@ -484,6 +542,22 @@ int main(int argc, char* argv[])
continue_running = 1;
+ /*Needs to calibrate 'calib_cpu' core*/ + if (opts.calib_ns_per_loop == 0) { + cpu_set_t calib_set; + + CPU_ZERO(&calib_set); + CPU_SET(opts.calib_cpu, &calib_set); + sched_getaffinity(0, sizeof(cpu_set_t), &orig_set); + sched_setaffinity(0, sizeof(cpu_set_t), &calib_set); + p_load = calibrate_cpu_cycles(CLOCK_THREAD_CPUTIME_ID); + sched_setaffinity(0, sizeof(cpu_set_t), &orig_set); + log_notice("pLoad = %dns : calib_cpu %d", p_load, opts.calib_cpu); + } else { + p_load = opts.calib_ns_per_loop; + log_notice("pLoad = %dns", p_load); + } + /* Take the beginning time for everything */ clock_gettime(CLOCK_MONOTONIC, &t_start);
diff --git a/src/rt-app_parse_config.c b/src/rt-app_parse_config.c index 4f26c9a..36a68de 100644 --- a/src/rt-app_parse_config.c +++ b/src/rt-app_parse_config.c @@ -451,7 +451,10 @@ parse_tasks(struct json_object *tasks, rtapp_options_t *opts) static void parse_global(struct json_object *global, rtapp_options_t *opts) { - char *policy; + char *policy, *cal_str; + struct json_object *cal_obj; + int scan_cnt; + log_info(PFX "Parsing global section"); opts->spacing = get_int_value_from(global, "spacing", TRUE, 0); opts->duration = get_int_value_from(global, "duration", TRUE, -1); @@ -462,6 +465,31 @@ parse_global(struct json_object *global, rtapp_options_t *opts) log_critical(PFX "Invalid policy %s", policy); exit(EXIT_INV_CONFIG); } + + cal_obj = get_in_object(global, "calibration", TRUE); + if (cal_obj == NULL) { + /* no setting ? Calibrate CPU0 */ + opts->calib_cpu = 0; + opts->calib_ns_per_loop = 0; + log_error("missing calibration setting force CPU0"); + } else { + if (json_object_is_type(cal_obj, json_type_int)) { + /* integer (no " ") detected. */ + opts->calib_ns_per_loop = json_object_get_int(cal_obj); + log_debug("ns_per_loop %d", opts->calib_ns_per_loop); + } else { + /* Get CPU number */ + cal_str = get_string_value_from(global, "calibration", + TRUE, "CPU0"); + scan_cnt = sscanf(cal_str, "CPU%d", &opts->calib_cpu); + if (!scan_cnt) { + log_critical(PFX "Invalid calibration CPU%d", opts->calib_cpu); + exit(EXIT_INV_CONFIG); + } + log_debug("calibrating CPU%d", opts->calib_cpu); + } + } + opts->logdir = get_string_value_from(global, "logdir", TRUE, NULL); opts->lock_pages = get_bool_value_from(global, "lock_pages", TRUE, 1); opts->logbasename = get_string_value_from(global, "log_basename", diff --git a/src/rt-app_types.h b/src/rt-app_types.h index 6920e8d..8bec201 100644 --- a/src/rt-app_types.h +++ b/src/rt-app_types.h @@ -125,6 +125,8 @@ typedef struct _rtapp_options_t { char *logdir; char *logbasename; int gnuplot; + int calib_cpu; + int calib_ns_per_loop;
rtapp_resource_t *resources; int nresources; diff --git a/src/rt-app_utils.c b/src/rt-app_utils.c index 9719053..9012261 100644 --- a/src/rt-app_utils.c +++ b/src/rt-app_utils.c @@ -116,6 +116,23 @@ timespec_lower(struct timespec *what, struct timespec *than) return 0; }
+int64_t +timespec_sub_to_ns(struct timespec *t1, struct timespec *t2) +{ + int64_t diff; + + if (t1->tv_nsec < t2->tv_nsec) { + diff = 1E9 * (int64_t)((int) t1->tv_sec - + (int) t2->tv_sec - 1); + diff += ((int) t1->tv_nsec + 1E9 - (int) t2->tv_nsec); + } else { + diff = 1E9 * (int64_t)((int) t1->tv_sec - (int) t2->tv_sec); + diff += ((int) t1->tv_nsec - (int) t2->tv_nsec); + } + return diff; +} + + void log_timing(FILE *handler, timing_point_t *t) { diff --git a/src/rt-app_utils.h b/src/rt-app_utils.h index c7444dc..68f1ef0 100644 --- a/src/rt-app_utils.h +++ b/src/rt-app_utils.h @@ -109,6 +109,9 @@ timespec_sub(struct timespec *t1, struct timespec *t2); int timespec_lower(struct timespec *what, struct timespec *than);
+int64_t +timespec_sub_to_ns(struct timespec *t1, struct timespec *t2); + void log_timing(FILE *handler, timing_point_t *t);
From: Vincent Guittot vincent.guittot@linaro.org
Calibration sequence fails to get the right ns per loop value when the calibration is done on the A57 cluster of the mt8173evb. A new calibration has been added that adds a sleep period between each each calibration loop. This idle phase enable rt-app to get the to get the right ns per loop value for such kind of HW. The calibration sequence finally uses the lowest value of the calibration methods which match with the highest compute capacity
Reported-by: Koan-Sin Tan freedom.tan@linaro.org Signed-off-by: Vincent Guittot vincent.guittot@linaro.org Reviewed-by: Amit Kucheria amit.kucheria@linaro.org Signed-off-by: Pi-Cheng Chen pi-cheng.chen@linaro.org --- src/rt-app.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 5 deletions(-)
diff --git a/src/rt-app.c b/src/rt-app.c index 518fe53..bad3e21 100644 --- a/src/rt-app.c +++ b/src/rt-app.c @@ -58,12 +58,14 @@ void waste_cpu_cycles(int load_loops) }
/* -* calibrate_cpu_cycles() -* collects the time that waste_cycles runs. +* calibrate_cpu_cycles_1() +* 1st method to calibrate the ns per loop value +* We alternate idle period and run period in order to not trig some hw +* protection mechanism like thermal mitgation */ -int calibrate_cpu_cycles(int clock) +int calibrate_cpu_cycles_1(int clock) { - struct timespec start, stop; + struct timespec start, stop, sleep; int max_load_loop = 10000; unsigned int diff; int nsec_per_loop, avg_per_loop = 0; @@ -71,6 +73,10 @@ int calibrate_cpu_cycles(int clock)
while (cal_trial) { cal_trial--; + sleep.tv_sec = 1; + sleep.tv_nsec = 0; + + clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep, NULL);
clock_gettime(clock, &start); waste_cpu_cycles(max_load_loop); @@ -96,6 +102,69 @@ int calibrate_cpu_cycles(int clock) return 0; }
+/* +* calibrate_cpu_cycles_2() +* 2nd method to calibrate the ns per loop value +* We continously runs something to ensure that CPU is set to max freq by the +* governor +*/ +int calibrate_cpu_cycles_2(int clock) +{ + struct timespec start, stop, sleep; + int max_load_loop = 10000; + unsigned int diff; + int nsec_per_loop, avg_per_loop = 0; + int ret, cal_trial = 1000; + + while (cal_trial) { + cal_trial--; + + clock_gettime(clock, &start); + waste_cpu_cycles(max_load_loop); + clock_gettime(clock, &stop); + + diff = (int)timespec_sub_to_ns(&stop, &start); + nsec_per_loop = diff / max_load_loop; + avg_per_loop = (avg_per_loop + nsec_per_loop) >> 1; + + /* collect a critical mass of samples.*/ + if ((abs(nsec_per_loop - avg_per_loop) * 50) < avg_per_loop) + return avg_per_loop; + + /* + * use several loop duration in order to be sure to not + * fall into a specific platform loop duration + *(like the cpufreq period) + */ + /*randomize the number of loops and recheck 1000 times*/ + max_load_loop += 33333; + max_load_loop %= 1000000; + } + return 0; +} + +/* +* calibrate_cpu_cycles() +* Use several methods to calibrate the ns per loop and get the min value which +* correspond to the highest achievable compute capacity. +*/ +int calibrate_cpu_cycles(int clock) +{ + int calib1, calib2; + + /* Run 1st method */ + calib1 = calibrate_cpu_cycles_1(clock); + + /* Run 2nd method */ + calib2 = calibrate_cpu_cycles_2(clock); + + if (calib1 < calib2) + return calib1; + else + return calib2; + +} + static inline loadwait(struct timespec *exec_time) { unsigned long exec, load_count; @@ -544,13 +613,14 @@ int main(int argc, char* argv[])
/*Needs to calibrate 'calib_cpu' core*/ if (opts.calib_ns_per_loop == 0) { + log_notice("Calibrate ns per loop"); cpu_set_t calib_set;
CPU_ZERO(&calib_set); CPU_SET(opts.calib_cpu, &calib_set); sched_getaffinity(0, sizeof(cpu_set_t), &orig_set); sched_setaffinity(0, sizeof(cpu_set_t), &calib_set); - p_load = calibrate_cpu_cycles(CLOCK_THREAD_CPUTIME_ID); + p_load = calibrate_cpu_cycles(CLOCK_MONOTONIC); sched_setaffinity(0, sizeof(cpu_set_t), &orig_set); log_notice("pLoad = %dns : calib_cpu %d", p_load, opts.calib_cpu); } else {
From: Vincent Guittot vincent.guittot@linaro.org
Instead of using numbers as resource names, introduce a new way that allows to describe resource names in plain string in "resources" objects.
Signed-off-by: Vincent Guittot vincent.guittot@linaro.org Signed-off-by: Pi-Cheng Chen pi-cheng.chen@linaro.org --- src/rt-app.c | 4 +- src/rt-app_parse_config.c | 137 ++++++++++++++++++++++++++++++++-------------- src/rt-app_types.h | 12 +++- 3 files changed, 108 insertions(+), 45 deletions(-)
diff --git a/src/rt-app.c b/src/rt-app.c index bad3e21..0a4fe2e 100644 --- a/src/rt-app.c +++ b/src/rt-app.c @@ -193,7 +193,7 @@ void run(int ind, struct timespec *min, struct timespec *max, log_ftrace(ft_data.marker_fd, "[%d] locking %d", ind, lock->res->index); - pthread_mutex_lock(&lock->res->mtx); + pthread_mutex_lock(&(lock->res->res.mtx.obj)); last = lock; lock = lock->next; } @@ -215,7 +215,7 @@ void run(int ind, struct timespec *min, struct timespec *max, log_ftrace(ft_data.marker_fd, "[%d] unlocking %d", ind, lock->res->index); - pthread_mutex_unlock(&lock->res->mtx); + pthread_mutex_unlock(&(lock->res->res.mtx.obj)); lock = lock->prev; } } diff --git a/src/rt-app_parse_config.c b/src/rt-app_parse_config.c index 36a68de..9e08ffd 100644 --- a/src/rt-app_parse_config.c +++ b/src/rt-app_parse_config.c @@ -120,7 +120,6 @@ get_int_value_from(struct json_object *where, set_default_if_needed(key, value, have_def, def_value); assure_type_is(value, where, key, json_type_int); i_value = json_object_get_int(value); - json_object_put(value); log_info(PIN "key: %s, value: %d, type <int>", key, i_value); return i_value; } @@ -137,7 +136,6 @@ get_bool_value_from(struct json_object *where, set_default_if_needed(key, value, have_def, def_value); assure_type_is(value, where, key, json_type_boolean); b_value = json_object_get_boolean(value); - json_object_put(value); log_info(PIN "key: %s, value: %d, type <bool>", key, b_value); return b_value; } @@ -158,46 +156,85 @@ get_string_value_from(struct json_object *where, } assure_type_is(value, where, key, json_type_string); s_value = strdup(json_object_get_string(value)); - json_object_put(value); log_info(PIN "key: %s, value: %s, type <string>", key, s_value); return s_value; }
+static int init_mutex_resource(rtapp_resource_t *data, const rtapp_options_t *opts) +{ + log_info(PIN "Init: %s mutex", data->name); + + pthread_mutexattr_init(&data->res.mtx.attr); + if (opts->pi_enabled) { + pthread_mutexattr_setprotocol( + &data->res.mtx.attr, + PTHREAD_PRIO_INHERIT); + } + pthread_mutex_init(&data->res.mtx.obj, + &data->res.mtx.attr); +} + +static void +parse_resource_data(char *name, struct json_object *obj, int idx, + rtapp_resource_t *data, const rtapp_options_t *opts) +{ + char *type, *target; + char def_type[RTAPP_RESOURCE_DESCR_LENGTH]; + + log_info(PFX "Parsing resources %s [%d]", name, idx); + + /* common and defaults */ + data->index = idx; + data->name = strdup(name); + + init_mutex_resource(data, opts); +} + static void parse_resources(struct json_object *resources, rtapp_options_t *opts) { int i; - int res = json_object_get_int(resources); - log_info(PFX "Creating %d resources", res); - opts->resources = malloc(sizeof(rtapp_resource_t) * res); - for (i = 0; i < res; i++) { - pthread_mutexattr_init(&opts->resources[i].mtx_attr); - if (opts->pi_enabled) { - pthread_mutexattr_setprotocol( - &opts->resources[i].mtx_attr, - PTHREAD_PRIO_INHERIT); + struct lh_entry *entry; char *key; struct json_object *val; int idx; + + log_info(PFX "Parsing resource section"); + + if (json_object_is_type(resources, json_type_object)) { + opts->nresources = 0; + foreach(resources, entry, key, val, idx) { + opts->nresources++; + } + + log_info(PFX "Found %d Resources", opts->nresources); + opts->resources = malloc(sizeof(rtapp_resource_t) * opts->nresources); + + foreach (resources, entry, key, val, idx) { + parse_resource_data(key, val, idx, &opts->resources[idx], opts); } - pthread_mutex_init(&opts->resources[i].mtx, - &opts->resources[i].mtx_attr); - opts->resources[i].index = i; } - opts->nresources = res; +} + +static int get_resource_index(const char *name, rtapp_resource_t *resources) +{ + int i=0; + + while (strcmp(resources[i].name, name) != 0) + i++; + + return i; }
static void serialize_acl(rtapp_resource_access_list_t **acl, - int idx, + const char *name, struct json_object *task_resources, rtapp_resource_t *resources) { - int i, next_idx, found; + int i, idx, found; struct json_object *access, *res, *next_res; rtapp_resource_access_list_t *tmp; - char s_idx[5]; + const char * next_name;
- /* as keys are string in the json, we need a string for searching - * the resource */ - snprintf(s_idx, 5, "%d", idx); + idx = get_resource_index(name, resources);
if (!(*acl)) { *acl = malloc( sizeof(rtapp_resource_access_list_t)); @@ -225,10 +262,10 @@ serialize_acl(rtapp_resource_access_list_t **acl, } }
- res = get_in_object(task_resources, s_idx, TRUE); + res = get_in_object(task_resources, name, TRUE); if (!res) return; - assure_type_is(res, task_resources, s_idx, json_type_object); + assure_type_is(res, task_resources, name, json_type_object);
access = get_in_object(res, "access", TRUE); if (!access) @@ -237,14 +274,22 @@ serialize_acl(rtapp_resource_access_list_t **acl,
for (i=0; i<json_object_array_length(access); i++) { + char res_name[5]; + next_res = json_object_array_get_idx(access, i); - if (!json_object_is_type(next_res, json_type_int)){ - log_critical("Invalid resource index"); - exit(EXIT_INV_CONFIG); - } - next_idx = json_object_get_int(next_res); + if (!json_object_is_type(next_res, json_type_string)){ + if (!json_object_is_type(next_res, json_type_int)){ + log_critical("Invalid resource index"); + exit(EXIT_INV_CONFIG); + } else { + snprintf(res_name, 5, "%d", json_object_get_int(next_res)); + next_name = res_name; + } + log_critical("Legacy resource index"); + } else + next_name = json_object_get_string(next_res); /* recurse on the rest of resources */ - serialize_acl(&(*acl), next_idx, task_resources, resources); + serialize_acl(&(*acl), next_name, task_resources, resources); } }
@@ -252,10 +297,11 @@ static void parse_thread_resources(const rtapp_options_t *opts, struct json_object *locks, struct json_object *task_resources, thread_data_t *data) { - int i,j, cur_res_idx, usage_usec; + int i, j, usage_usec; struct json_object *res; int res_dur; - char res_name[4]; + char res_name[5]; + const char *cur_res_name;
rtapp_resource_access_list_t *tmp, *head, *last; char debug_msg[512], tmpmsg[512]; @@ -267,15 +313,21 @@ parse_thread_resources(const rtapp_options_t *opts, struct json_object *locks, for (i = 0; i< data->nblockages; i++) { res = json_object_array_get_idx(locks, i); - if (!json_object_is_type(res, json_type_int)){ - log_critical("Invalid resource index"); - exit(EXIT_INV_CONFIG); - } - cur_res_idx = json_object_get_int(res); + if (!json_object_is_type(res, json_type_string)) { + if (!json_object_is_type(res, json_type_int)) { + log_critical("Invalid resource index"); + exit(EXIT_INV_CONFIG); + } else { + snprintf(res_name, 5, "%d", json_object_get_int(res)); + cur_res_name = res_name; + } + log_critical("Legacy resource index"); + } else + cur_res_name = json_object_get_string(res);
data->blockages[i].usage = usec_to_timespec(0); data->blockages[i].acl = NULL; - serialize_acl(&data->blockages[i].acl, cur_res_idx, + serialize_acl(&data->blockages[i].acl, cur_res_name, task_resources, opts->resources);
/* since the "current" resource is returned as the first @@ -307,8 +359,7 @@ parse_thread_resources(const rtapp_options_t *opts, struct json_object *locks,
log_info(PIN "key: acl %s", debug_msg);
- snprintf(res_name, 4, "%d", cur_res_idx); - res = get_in_object(task_resources, res_name, TRUE); + res = get_in_object(task_resources, cur_res_name, TRUE); if (!res) { usage_usec = 0; data->blockages[i].usage = usec_to_timespec(0); @@ -318,7 +369,7 @@ parse_thread_resources(const rtapp_options_t *opts, struct json_object *locks, usage_usec = get_int_value_from(res, "duration", TRUE, 0); data->blockages[i].usage = usec_to_timespec(usage_usec); } - log_info(PIN "res %d, usage: %d acl: %s", cur_res_idx, + log_info(PIN "res %s, usage: %d acl: %s", cur_res_name, usage_usec, debug_msg); } } @@ -335,6 +386,7 @@ parse_thread_data(char *name, struct json_object *obj, int idx, int i, cpu_idx;
log_info(PFX "Parsing thread %s [%d]", name, idx); + /* common and defaults */ data->ind = idx; data->name = strdup(name); @@ -524,8 +576,11 @@ get_opts_from_json_object(struct json_object *root, rtapp_options_t *opts) log_info(PFX "resources: %s", json_object_to_json_string(resources));
parse_global(global, opts); + json_object_put(global); parse_resources(resources, opts); + json_object_put(resources); parse_tasks(tasks, opts); + json_object_put(tasks);
}
diff --git a/src/rt-app_types.h b/src/rt-app_types.h index 8bec201..a1a62fd 100644 --- a/src/rt-app_types.h +++ b/src/rt-app_types.h @@ -35,6 +35,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #endif /* AQUOSA */
#define RTAPP_POLICY_DESCR_LENGTH 16 +#define RTAPP_RESOURCE_DESCR_LENGTH 16 #define RTAPP_FTRACE_PATH_LENGTH 256 /* exit codes */
@@ -56,11 +57,18 @@ typedef enum policy_t #endif } policy_t;
+struct _rtapp_mutex { + pthread_mutex_t obj; + pthread_mutexattr_t attr; +} ; + /* Shared resources */ typedef struct _rtapp_resource_t { - pthread_mutex_t mtx; - pthread_mutexattr_t mtx_attr; + union { + struct _rtapp_mutex mtx; + } res; int index; + char *name; } rtapp_resource_t;
typedef struct _rtapp_resource_access_list_t {
From: Vincent Guittot vincent.guittot@linaro.org
In addition to mutex resource, add new run and sleep resource types.
Signed-off-by: Vincent Guittot vincent.guittot@linaro.org Signed-off-by: Pi-Cheng Chen pi-cheng.chen@linaro.org --- src/rt-app.c | 49 +++++++++++++++++++++++++++++++++++------------ src/rt-app_parse_config.c | 13 ++++++++++++- src/rt-app_types.h | 8 ++++++++ src/rt-app_utils.c | 34 ++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 13 deletions(-)
diff --git a/src/rt-app.c b/src/rt-app.c index 0a4fe2e..1c1c9fc 100644 --- a/src/rt-app.c +++ b/src/rt-app.c @@ -174,10 +174,33 @@ static inline loadwait(struct timespec *exec_time) waste_cpu_cycles(load_count); }
+int get_resource(rtapp_resource_access_list_t *lock, struct timespec *usage) +{ + int busywait = 1; + + switch(lock->res->type) { + case rtapp_mutex: + pthread_mutex_lock(&(lock->res->res.mtx.obj)); + break; + case rtapp_sleep: + nanosleep(usage, NULL); + busywait = 0; + break; + } + + return busywait; +} + +void put_resource(rtapp_resource_access_list_t *lock) +{ + if (lock->res->type == rtapp_mutex) + pthread_mutex_unlock(&(lock->res->res.mtx.obj)); +} + void run(int ind, struct timespec *min, struct timespec *max, rtapp_tasks_resource_list_t *blockages, int nblockages) { - int i; + int i, is_busywait = 1; struct timespec t_exec; rtapp_resource_access_list_t *lock, *last;
@@ -185,7 +208,7 @@ void run(int ind, struct timespec *min, struct timespec *max,
for (i = 0; i < nblockages; i++) { - /* Lock resources */ + /* Lock resources sequence including the busy wait */ lock = blockages[i].acl; while (lock != NULL) { log_debug("[%d] locking %d", ind, lock->res->index); @@ -193,19 +216,21 @@ void run(int ind, struct timespec *min, struct timespec *max, log_ftrace(ft_data.marker_fd, "[%d] locking %d", ind, lock->res->index); - pthread_mutex_lock(&(lock->res->res.mtx.obj)); + is_busywait = get_resource(lock, &blockages[i].usage); last = lock; lock = lock->next; }
- /* Busy wait */ - log_debug("[%d] busywait for %lu", ind, timespec_to_usec(&blockages[i].usage)); - if (opts.ftrace) - log_ftrace(ft_data.marker_fd, - "[%d] busywait for %d", - ind, timespec_to_usec(&blockages[i].usage)); - loadwait(&blockages[i].usage); - t_exec = timespec_sub(&t_exec, &blockages[i].usage); + if (is_busywait) { + /* Busy wait */ + log_debug("[%d] busywait for %lu", ind, timespec_to_usec(&blockages[i].usage)); + if (opts.ftrace) + log_ftrace(ft_data.marker_fd, + "[%d] busywait for %d", + ind, timespec_to_usec(&blockages[i].usage)); + loadwait(&blockages[i].usage); + t_exec = timespec_sub(&t_exec, &blockages[i].usage); + }
/* Unlock resources */ lock = last; @@ -215,7 +240,7 @@ void run(int ind, struct timespec *min, struct timespec *max, log_ftrace(ft_data.marker_fd, "[%d] unlocking %d", ind, lock->res->index); - pthread_mutex_unlock(&(lock->res->res.mtx.obj)); + put_resource(lock); lock = lock->prev; } } diff --git a/src/rt-app_parse_config.c b/src/rt-app_parse_config.c index 9e08ffd..dcc3010 100644 --- a/src/rt-app_parse_config.c +++ b/src/rt-app_parse_config.c @@ -187,7 +187,18 @@ parse_resource_data(char *name, struct json_object *obj, int idx, data->index = idx; data->name = strdup(name);
- init_mutex_resource(data, opts); + /* resource type */ + resource_to_string(0, def_type); + type = get_string_value_from(obj, "type", TRUE, def_type); + if (type) { + if (string_to_resource(type, &data->type) != 0) { + log_critical(PIN2 "Invalid type of resource %s", type); + exit(EXIT_INV_CONFIG); + } + } + + if (data->type == rtapp_mutex) + init_mutex_resource(data, opts); }
static void diff --git a/src/rt-app_types.h b/src/rt-app_types.h index a1a62fd..eb7cb95 100644 --- a/src/rt-app_types.h +++ b/src/rt-app_types.h @@ -57,6 +57,13 @@ typedef enum policy_t #endif } policy_t;
+typedef enum resource_t +{ + rtapp_mutex = 0, + rtapp_sleep, + rtapp_run +} resource_t; + struct _rtapp_mutex { pthread_mutex_t obj; pthread_mutexattr_t attr; @@ -68,6 +75,7 @@ typedef struct _rtapp_resource_t { struct _rtapp_mutex mtx; } res; int index; + resource_t type; char *name; } rtapp_resource_t;
diff --git a/src/rt-app_utils.c b/src/rt-app_utils.c index 9012261..1e13ee0 100644 --- a/src/rt-app_utils.c +++ b/src/rt-app_utils.c @@ -219,6 +219,40 @@ policy_to_string(policy_t policy, char *policy_name) return 0; }
+int +string_to_resource(const char *name, resource_t *resource) +{ + if (strcmp(name, "mutex") == 0) + *resource = rtapp_mutex; + else if (strcmp(name, "sleep") == 0) + *resource = rtapp_sleep; + else if (strcmp(name, "run") == 0) + *resource = rtapp_run; + else + return 1; + + return 0; +} + +int +resource_to_string(resource_t resource, char *resource_name) +{ + switch (resource) { + case rtapp_mutex: + strcpy(resource_name, "mutex"); + break; + case rtapp_sleep: + strcpy(resource_name, "sleep"); + break; + case rtapp_run: + strcpy(resource_name, "run"); + break; + default: + return 1; + } + + return 0; +}
void ftrace_write(int mark_fd, const char *fmt, ...) {
From: Vincent Guittot vincent.guittot@linaro.org
We can split the thread's behavior into several phases that will behave differently. This feature is useful to describe a behavior that is not fully periodic.
Signed-off-by: Vincent Guittot vincent.guittot@linaro.org Signed-off-by: Pi-Cheng Chen pi-cheng.chen@linaro.org --- src/rt-app.c | 112 +++++++++++++++++++++++++--------------- src/rt-app_args.c | 13 +++-- src/rt-app_parse_config.c | 127 ++++++++++++++++++++++++++++++---------------- src/rt-app_types.h | 15 ++++-- 4 files changed, 170 insertions(+), 97 deletions(-)
diff --git a/src/rt-app.c b/src/rt-app.c index 1c1c9fc..957f3fe 100644 --- a/src/rt-app.c +++ b/src/rt-app.c @@ -279,6 +279,7 @@ shutdown(int sig) void *thread_body(void *arg) { thread_data_t *data = (thread_data_t*) arg; + phase_data_t *pdata; struct sched_param param; struct timespec t_now, t_next; unsigned long t_start_usec; @@ -296,8 +297,7 @@ void *thread_body(void *arg) struct sched_attr attr; unsigned int flags = 0; #endif - int ret, i = 0; - int j; + int ret, i, j, loop;
/* set thread name */ ret = pthread_setname_np(pthread_self(), data->name); @@ -305,6 +305,9 @@ void *thread_body(void *arg) perror("pthread_setname_np thread name over 16 characters"); }
+ /* Get the 1st phase's data */ + pdata = &data->phases_data[0]; + /* set thread affinity */ if (data->cpuset != NULL) { @@ -337,13 +340,6 @@ void *thread_body(void *arg) exit(EXIT_FAILURE); }
- log_notice("[%d] starting thread with period: %lu, exec: %lu," - "deadline: %lu, priority: %d", - data->ind, - timespec_to_usec(&data->period), - timespec_to_usec(&data->min_et), - timespec_to_usec(&data->deadline), - data->sched_prio); break;
case other: @@ -351,13 +347,6 @@ void *thread_body(void *arg)
/* add priority setting */
- log_notice("[%d] starting thread with period: %lu, exec: %lu," - "deadline: %lu", - data->ind, - timespec_to_usec(&data->period), - timespec_to_usec(&data->min_et), - timespec_to_usec(&data->deadline)); - data->lock_pages = 0; /* forced off for SCHED_OTHER */ break;
@@ -398,10 +387,10 @@ void *thread_body(void *arg) attr.sched_flags = 0; attr.sched_policy = SCHED_DEADLINE; attr.sched_priority = 0; - attr.sched_runtime = timespec_to_nsec(&data->max_et) + - (timespec_to_nsec(&data->max_et) /100) * BUDGET_OVERP; - attr.sched_deadline = timespec_to_nsec(&data->deadline); - attr.sched_period = timespec_to_nsec(&data->period); + attr.sched_runtime = timespec_to_nsec(&pdata->max_et) + + (timespec_to_nsec(&pdata->max_et) /100) * BUDGET_OVERP; + attr.sched_deadline = timespec_to_nsec(&pdata->deadline); + attr.sched_period = timespec_to_nsec(&pdata->period); break; #endif
@@ -423,18 +412,37 @@ void *thread_body(void *arg) } }
+ log_notice("[%d] starting thread with period: %lu, exec: %lu," + "deadline: %lu", + data->ind, + timespec_to_usec(&pdata->period), + timespec_to_usec(&pdata->min_et), + timespec_to_usec(&pdata->deadline)); + /* if we know the duration we can calculate how many periods we will * do at most, and the log to memory, instead of logging to file. + * In case of several phase, we still use the logging file instead of + * memory. + * TODO: parse all the phase to compute the memory phase */ - timings = NULL; - if (data->duration > 0) { - my_duration_usec = (data->duration * 10e6) - - (data->wait_before_start * 1000); - nperiods = (int) ceil( my_duration_usec / - (double) timespec_to_usec(&data->period)); - timings = malloc ( nperiods * sizeof(timing_point_t)); + nperiods = 0; + if ((data->duration > 0) && (data->duration > data->wait_before_start) + && (data->nphases < 2)) { + my_duration_usec = (data->duration * 1000000) + - data->wait_before_start; + nperiods = (my_duration_usec + timespec_to_usec(&data->phases_data->period) - 1) + / timespec_to_usec(&data->phases_data->period) + 1; + } + + if ((pdata->loop > 0) && (pdata->loop < nperiods)) { + nperiods = data->loop + 1 ; }
+ if (nperiods) + timings = malloc(nperiods * sizeof(timing_point_t)); + else + timings = NULL; + fprintf(data->log_handler, "#idx\tperiod\tmin_et\tmax_et\trel_st\tstart" "\t\tend\t\tdeadline\tdur.\tslack" "\tBudget\tUsed Budget\n"); @@ -484,21 +492,24 @@ void *thread_body(void *arg)
clock_gettime(CLOCK_MONOTONIC, &t_now); t_next = t_now; - data->deadline = timespec_add(&t_now, &data->deadline); + pdata->deadline = timespec_add(&t_now, &pdata->deadline);
- while (continue_running && (i != data->loop)) { + i = j = loop = 0; + while (continue_running && (i != data->loop)) { struct timespec t_start, t_end, t_diff, t_slack;
if (opts.ftrace) - log_ftrace(ft_data.marker_fd, "[%d] begins loop %d", data->ind, i); + log_ftrace(ft_data.marker_fd, + "[%d] begins loop %d phase %d step %d", + data->ind, i, j, loop);
clock_gettime(CLOCK_MONOTONIC, &t_start); - run(data->ind, &data->min_et, &data->max_et, data->blockages, - data->nblockages); + run(data->ind, &pdata->min_et, &pdata->max_et, pdata->blockages, + pdata->nblockages); clock_gettime(CLOCK_MONOTONIC, &t_end);
t_diff = timespec_sub(&t_end, &t_start); - t_slack = timespec_sub(&data->deadline, &t_end); + t_slack = timespec_sub(&pdata->deadline, &t_end);
t_start_usec = timespec_to_usec(&t_start); if (timings) @@ -507,14 +518,14 @@ void *thread_body(void *arg) curr_timing = &tmp_timing;
curr_timing->ind = data->ind; - curr_timing->period = timespec_to_usec(&data->period); - curr_timing->min_et = timespec_to_usec(&data->min_et); - curr_timing->max_et = timespec_to_usec(&data->max_et); + curr_timing->period = timespec_to_usec(&pdata->period); + curr_timing->min_et = timespec_to_usec(&pdata->min_et); + curr_timing->max_et = timespec_to_usec(&pdata->max_et); curr_timing->rel_start_time = t_start_usec - timespec_to_usec(&data->main_app_start); curr_timing->abs_start_time = t_start_usec; curr_timing->end_time = timespec_to_usec(&t_end); - curr_timing->deadline = timespec_to_usec(&data->deadline); + curr_timing->deadline = timespec_to_usec(&pdata->deadline); curr_timing->duration = timespec_to_usec(&t_diff); curr_timing->slack = timespec_to_lusec(&t_slack);
@@ -536,12 +547,13 @@ void *thread_body(void *arg) if (!timings) log_timing(data->log_handler, curr_timing);
- t_next = timespec_add(&t_next, &data->period); - data->deadline = timespec_add(&data->deadline, &data->period); + t_next = timespec_add(&t_next, &pdata->period); + pdata->deadline = timespec_add(&pdata->deadline, &pdata->period);
if (opts.ftrace) - log_ftrace(ft_data.marker_fd, "[%d] end loop %d", - data->ind, i); + log_ftrace(ft_data.marker_fd, + "[%d] end loop %d phase %d step %d", + data->ind, i, j, loop);
if (curr_timing->slack < 0 && opts.die_on_dmiss) { log_critical("[%d] DEADLINE MISS !!!", data->ind); @@ -556,7 +568,23 @@ void *thread_body(void *arg) if (timespec_lower(&t_now, &t_next)) clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t_next, NULL);
- i++; + loop++; + if (loop == pdata->loop) { + loop = 0; + + clock_gettime(CLOCK_MONOTONIC, &t_now); + t_next = t_now; + pdata->deadline = timespec_add(&t_now, &pdata->deadline); + + j++; + if (j == data->nphases) { + j = 0; + + i++; + } + + pdata = &data->phases_data[j]; + } }
exit_miss: diff --git a/src/rt-app_args.c b/src/rt-app_args.c index fa2c646..70783c8 100644 --- a/src/rt-app_args.c +++ b/src/rt-app_args.c @@ -97,7 +97,7 @@ parse_thread_args(char *arg, int idx, thread_data_t *tdata, policy_t def_policy) if (period <= 0 ) usage("Cannot set negative period.", EXIT_INV_COMMANDLINE); - tdata->period = usec_to_timespec(period); + tdata->phases_data->period = usec_to_timespec(period); i++; break;
@@ -110,8 +110,8 @@ parse_thread_args(char *arg, int idx, thread_data_t *tdata, policy_t def_policy) if (exec <= 0 ) usage("Cannot set negative exec time", EXIT_INV_COMMANDLINE); - tdata->min_et = usec_to_timespec(exec); - tdata->max_et = usec_to_timespec(exec); + tdata->phases_data->min_et = usec_to_timespec(exec); + tdata->phases_data->max_et = usec_to_timespec(exec); i++; break;
@@ -168,7 +168,7 @@ parse_thread_args(char *arg, int idx, thread_data_t *tdata, policy_t def_policy) if (dline <= 0 ) usage("Cannot set negative deadline", EXIT_INV_COMMANDLINE); - tdata->deadline = usec_to_timespec(dline); + tdata->phases_data->deadline = usec_to_timespec(dline); i++; break; } @@ -181,7 +181,7 @@ parse_thread_args(char *arg, int idx, thread_data_t *tdata, policy_t def_policy) }
if (dline == 0) - tdata->deadline = tdata->period; + tdata->phases_data->deadline = tdata->phases_data->period;
/* set cpu affinity mask */ if (tdata->cpuset_str) @@ -223,6 +223,7 @@ parse_command_line_options(int argc, char **argv, rtapp_options_t *opts) opts->pi_enabled = 0; opts->policy = other; opts->threads_data = malloc(sizeof(thread_data_t)); + opts->threads_data->phases_data = malloc(sizeof(phase_data_t)); #ifdef AQUOSA opts->fragment = 1; #endif @@ -297,6 +298,8 @@ parse_command_line_options(int argc, char **argv, rtapp_options_t *opts) opts->threads_data, (opts->nthreads+1) * \ sizeof(thread_data_t)); + opts->threads_data[opts->nthreads+1].phases_data = + malloc(sizeof(phase_data_t)); } parse_thread_args(optarg, opts->nthreads, &opts->threads_data[opts->nthreads], diff --git a/src/rt-app_parse_config.c b/src/rt-app_parse_config.c index dcc3010..b399916 100644 --- a/src/rt-app_parse_config.c +++ b/src/rt-app_parse_config.c @@ -306,7 +306,7 @@ serialize_acl(rtapp_resource_access_list_t **acl,
static void parse_thread_resources(const rtapp_options_t *opts, struct json_object *locks, - struct json_object *task_resources, thread_data_t *data) + struct json_object *task_resources, phase_data_t *data) { int i, j, usage_usec; struct json_object *res; @@ -386,43 +386,17 @@ parse_thread_resources(const rtapp_options_t *opts, struct json_object *locks, }
static void -parse_thread_data(char *name, struct json_object *obj, int idx, - thread_data_t *data, const rtapp_options_t *opts) +parse_thread_phase_data(struct json_object *obj, int idx, + phase_data_t *data, const rtapp_options_t *opts) { long exec, period, dline; - char *policy; - char def_policy[RTAPP_POLICY_DESCR_LENGTH]; - struct array_list *cpuset; - struct json_object *cpuset_obj, *cpu, *resources, *locks; - int i, cpu_idx; - - log_info(PFX "Parsing thread %s [%d]", name, idx); - - /* common and defaults */ - data->ind = idx; - data->name = strdup(name); - data->lock_pages = opts->lock_pages; - data->sched_prio = DEFAULT_THREAD_PRIORITY; - data->cpuset = NULL; - data->cpuset_str = NULL; + struct json_object *resources, *locks;
/* loop */ data->loop = get_int_value_from(obj, "loop", TRUE, -1);
- /* period */ - period = get_int_value_from(obj, "period", FALSE, 0); - if (period <= 0) { - log_critical(PIN2 "Cannot set negative period"); - exit(EXIT_INV_CONFIG); - } - data->period = usec_to_timespec(period); - /* exec time */ exec = get_int_value_from(obj, "exec", FALSE, 0); - if (exec > period) { - log_critical(PIN2 "Exec must be greather than period"); - exit(EXIT_INV_CONFIG); - } if (exec < 0) { log_critical(PIN2 "Cannot set negative exec time"); exit(EXIT_INV_CONFIG); @@ -430,6 +404,19 @@ parse_thread_data(char *name, struct json_object *obj, int idx, data->min_et = usec_to_timespec(exec); data->max_et = usec_to_timespec(exec);
+ /* period */ + period = get_int_value_from(obj, "period", TRUE, exec); + if (period <= 0) { + log_critical(PIN2 "Cannot set negative period"); + exit(EXIT_INV_CONFIG); + } + if (exec > period) { + log_critical(PIN2 "Period must be greater or equal than period"); + exit(EXIT_INV_CONFIG); + } + + data->period = usec_to_timespec(period); + /* deadline */ dline = get_int_value_from(obj, "deadline", TRUE, period); if (dline < exec) { @@ -442,6 +429,48 @@ parse_thread_data(char *name, struct json_object *obj, int idx, } data->deadline = usec_to_timespec(dline);
+ /* resources */ + resources = get_in_object(obj, "resources", TRUE); + locks = get_in_object(obj, "lock_order", TRUE); + if (locks) { + assure_type_is(locks, obj, "lock_order", json_type_array); + log_info(PIN "key: lock_order %s", json_object_to_json_string(locks)); + if (resources) { + assure_type_is(resources, obj, "resources", + json_type_object); + log_info(PIN "key: resources %s", + json_object_to_json_string(resources)); + } + parse_thread_resources(opts, locks, resources, data); + } else { + data->nblockages = 0; + } +} + +static void +parse_thread_data(char *name, struct json_object *obj, int idx, + thread_data_t *data, const rtapp_options_t *opts) +{ + long exec, period, dline; + char *policy; + char def_policy[RTAPP_POLICY_DESCR_LENGTH]; + struct array_list *cpuset; + struct json_object *cpuset_obj, *phases_obj, *cpu, *resources, *locks; + int i, cpu_idx; + + log_info(PFX "Parsing thread %s [%d]", name, idx); + + /* common and defaults */ + data->ind = idx; + data->name = strdup(name); + data->lock_pages = opts->lock_pages; + data->sched_prio = DEFAULT_THREAD_PRIORITY; + data->cpuset = NULL; + data->cpuset_str = NULL; + + /* loop */ + data->loop = get_int_value_from(obj, "loop", TRUE, -1); + /* policy */ policy_to_string(opts->policy, def_policy); policy = get_string_value_from(obj, "policy", TRUE, def_policy); @@ -476,21 +505,31 @@ parse_thread_data(char *name, struct json_object *obj, int idx, } log_info(PIN "key: cpus %s", data->cpuset_str);
- /* resources */ - resources = get_in_object(obj, "resources", TRUE); - locks = get_in_object(obj, "lock_order", TRUE); - if (locks) { - assure_type_is(locks, obj, "lock_order", json_type_array); - log_info(PIN "key: lock_order %s", json_object_to_json_string(locks)); - if (resources) { - assure_type_is(resources, obj, "resources", - json_type_object); - log_info(PIN "key: resources %s", - json_object_to_json_string(resources)); + /* phases */ + phases_obj = get_in_object(obj, "phases", TRUE); + if (phases_obj) { + /* used in the foreach macro */ + struct lh_entry *entry; char *key; struct json_object *val; int idx; + + assure_type_is(phases_obj, obj, "phases", json_type_object); + + log_info(PIN "Parsing phases section"); + data->nphases = 0; + foreach(phases_obj, entry, key, val, idx) { + data->nphases++; } - parse_thread_resources(opts, locks, resources, data); + log_info(PIN "Found %d phases", data->nphases); + data->phases_data = malloc(sizeof(phase_data_t) * data->nphases); + foreach(phases_obj, entry, key, val, idx) { + log_info(PIN "Parsing phase %s", key); + parse_thread_phase_data(val, idx, &data->phases_data[idx], opts); + } + } else { + data->nphases = 1; + data->phases_data = malloc(sizeof(phase_data_t) * data->nphases); + data->loop = 1; + parse_thread_phase_data(obj, 0, data->phases_data, opts); } - }
static void @@ -591,8 +630,6 @@ get_opts_from_json_object(struct json_object *root, rtapp_options_t *opts) parse_resources(resources, opts); json_object_put(resources); parse_tasks(tasks, opts); - json_object_put(tasks); - }
void diff --git a/src/rt-app_types.h b/src/rt-app_types.h index eb7cb95..b8988f7 100644 --- a/src/rt-app_types.h +++ b/src/rt-app_types.h @@ -90,6 +90,14 @@ typedef struct _rtapp_tasks_resource_list_t { struct _rtapp_resource_access_list_t *acl; } rtapp_tasks_resource_list_t;
+typedef struct _phase_data_t { + struct timespec min_et, max_et; + struct timespec period, deadline; + int loop, sleep; + rtapp_tasks_resource_list_t *blockages; + int nblockages; +} phase_data_t; + typedef struct _thread_data_t { int ind; char *name; @@ -98,19 +106,16 @@ typedef struct _thread_data_t { cpu_set_t *cpuset; char *cpuset_str; unsigned long wait_before_start; - struct timespec min_et, max_et; - struct timespec period, deadline; struct timespec main_app_start; int loop; + int nphases; + phase_data_t *phases_data;
FILE *log_handler; policy_t sched_policy; char sched_policy_descr[RTAPP_POLICY_DESCR_LENGTH]; int sched_prio;
- rtapp_tasks_resource_list_t *blockages; - int nblockages; - #ifdef AQUOSA int fragment; int sid;
From: Vincent Guittot vincent.guittot@linaro.org
Command line interface is no more supported with new features and the coming new grammars.
Signed-off-by: Vincent Guittot vincent.guittot@linaro.org Signed-off-by: Pi-Cheng Chen pi-cheng.chen@linaro.org --- README.in | 79 +------------ src/rt-app_args.c | 331 +----------------------------------------------------- src/rt-app_args.h | 3 - 3 files changed, 4 insertions(+), 409 deletions(-)
diff --git a/README.in b/README.in index 1883455..0c15e93 100644 --- a/README.in +++ b/README.in @@ -5,8 +5,7 @@ README for rt-app @VERSION@ ==============
rt-app is a test application that starts multiple periodic threads in order to -simulate a real-time periodic load. It supports SCHED_OTHER, SCHED_FIFO, -SCHED_RR as well as the AQuoSA framework and SCHED_DEADLINE. +simulate a real-time periodic load.
Code is currently maintained on GitHub:
@@ -17,16 +16,8 @@ Code is currently maintained on GitHub: REQUIREMENTS ==============
-rt-app runs on GNU/Linux. It needs autoconf, automake, libtool and a recent -compiler (mainly: gcc) for basic features. - -For advanced usage, the following optional libraries are needed: -- qreslib for AQuoSA support (and, obviously a GENS_SCHED patched kernel) -- a SCHED_DEADLINE patched kernel to support SCHED_DEADLINE policy. -- json-c installed for reading config files. In case libjson is not available - for your target, you can download and compile json-c from - http://oss.metaparadigm.com/json-c/ - +rt-app runs on GNU/Linux. It needs autoconf, automake, libtool , json-c and +a recent compiler (mainly: gcc) for basic features.
============= COMPILATION @@ -69,67 +60,3 @@ $ rt-app <config_file> where config file is a full/relative path to a json file (look under doc/ for an example config file) or "-" (without quotes) to read JSON data from stdin.
-OR you can use commandline to define the taskset. -Keep in mind that on commandline it will never be possible to define resources -and how tasks access them. - -$ rt-app -usage: rt-app [options] -t <period>:<exec>[:cpu affinity[:policy[:deadline[:prio]]]] - --h, --help : show this help --f, --fifo : set default policy for threads to SCHED_FIFO --r, --rr : set default policy fior threads to SCHED_RR --s, --spacing : msec to wait beetween thread starts --l, --logdir : save logs to different directory --b, --baselog : basename for logs (implies -l . if not set) --G, --gnuplot : generate gnuplot script (needs -l) --D, --duration : time (in seconds) before stopping threads --K, --no-mlock : Do not lock pages in memory --q, --qos : create AQuoSA reservation --g, --frag : fragment for the reservation - -POLICY: f=SCHED_FIFO, r=SCHED_RR, o=SCHED_OTHER, q=AQuoSA -AFFINITY: comma-separated cpu index (starting from 0) i.e. 0,2,3 for first, -third and fourth CPU - -Note: when using AQuoSA scheduling, priority is used as percent increment for -budget over exec time - -Options: - -* --fifo,--rr and --qos sets the default scheduling policy for all threads. - --qos is available only when compiled with AQuoSA support enabled. - -* --logdir and --baselog control log output (rt-app creates one log for each - thread). If --logdir is not present, rt-app logs everything to stdout. - When logging to file and -D (see below) is present, the file is written - at the end of execution, during which all logging data is kept in - memory. - -* --gnuplot if it is present then rt-app outputs sample gnuplot scripts to - $logdir (which must be passed too - -* --spacing and --duration control the spacing between threads and their - duration of execution, i.e. each thread is started every $spacing ms - and the total runtime is $duration. - -* --frag (AQuoSA only): fragments the reservation period by $fragmentation, i.e. - reservation period is $frag times smaller than thread's period. - -* --no-mlock: do not lock threads' memory pages when using realtime scheduling - classes - -Each thread is specified by - - --thread <period>:<exec>[:$POLICY[:deadline[:prio]]]: - -period : thread period in microseconds -exec : thread WCET in microseconds -policy : one of f=SCHED_FIFO, r=SCHED_RR, o=SCHED_OTHER, q=AQuoSA (if enabled) -affinity : comma-separated cpu index (starting from 0) i.e. 0,2,3 for first, - third and fourth CPU -prio : thread priority in SCHED_FIFO/SCHED_RR/SCHED_OTHER, percentile - increment for AQuoSA reservation budget w.r.t. WCET. -deadline : deadline in microseconds (used ONLY for plotting!) - - diff --git a/src/rt-app_args.c b/src/rt-app_args.c index 70783c8..0a83fdd 100644 --- a/src/rt-app_args.c +++ b/src/rt-app_args.c @@ -23,340 +23,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. void usage (const char* msg, int ex_code) { -#ifdef JSON printf("usage:\n" - "rt-app <taskset.json>\nOR\n"); -#endif - printf("rt-app [options] -t <period>:<exec>[:policy" - "[:CPU affinity[:prio[:deadline]]]] -t ...\n\n"); - printf("-h, --help\t\t:\tshow this help\n"); - printf("-f, --fifo\t\t:\tset default policy for threads to SCHED_FIFO\n"); - printf("-r, --rr\t\t:\tset default policy fior threads to SCHED_RR\n"); - printf("-s, --spacing\t\t:\tmsec to wait beetween thread starts\n"); - printf("-l, --logdir\t\t:\tsave logs to different directory\n"); - printf("-b, --baselog\t\t:\tbasename for logs (implies -l . if not set)\n"); - printf("-G, --gnuplot\t\t:\tgenerate gnuplot script (needs -l)\n"); - printf("-D, --duration\t\t:\ttime (in seconds) before stopping threads\n"); - printf("-K, --no-mlock\t\t:\tDo not lock pages in memory\n"); - printf("-T, --ftrace\t\t:\tenable ftrace prints\n"); - printf("-P, --pi_enabled\t:\tenable priority inheritance on resources\n"); - printf("-M, --die_on_dmiss\t:\texit with an error if a task misses a deadline\n"); - -#ifdef AQUOSA - printf("-q, --qos\t:\tcreate AQuoSA reservation\n"); - printf("-g, --frag\t:\tfragment for the reservation\n\n"); -#else -#endif - printf("\nPOLICY: f=SCHED_FIFO, r=SCHED_RR, o=SCHED_OTHER"); -#ifdef DLSCHED - printf(", d=SCHED_DEADLINE"); -#endif -#ifdef AQUOSA - printf(", q=AQuoSA\n"); - printf("when using AQuoSA scheduling, priority is used as" - " percent increment \nfor budget over exec time\n"); -#else - printf("\n"); -#endif - printf("AFFINITY: comma-separated cpu index (starting from 0)\n"); - printf("\ti.e. 0,2,3 for first, third and fourth CPU\n"); + "rt-app <taskset.json>\n");
if (msg != NULL) printf("\n%s\n", msg); exit(ex_code); }
- -void -parse_thread_args(char *arg, int idx, thread_data_t *tdata, policy_t def_policy) -{ - char *str = strdup(arg); - char *token; - long period, exec, dline; - char tmp[256]; - int i = 0; - int cpu; - dline = 0; - - token = strtok(str, ":"); - tdata->name = malloc(sizeof(char) * 5); - tdata->ind = idx; - - /* default name for command line threads */ - snprintf(tdata->name, 1, "t%d", tdata->ind); - tdata->sched_prio = DEFAULT_THREAD_PRIORITY; - tdata->sched_policy = def_policy; - tdata->cpuset = NULL; - tdata->cpuset_str = NULL; - - while ( token != NULL) - { - switch(i) { - case 0: - period = strtol(token, NULL, 10); - if (period <= 0 ) - usage("Cannot set negative period.", - EXIT_INV_COMMANDLINE); - tdata->phases_data->period = usec_to_timespec(period); - i++; - break; - - case 1: - exec = strtol(token,NULL, 10); - //TODO: add support for max_et somehow - if (exec > period) - usage("Exec time cannot be greater than" - " period.", EXIT_INV_COMMANDLINE); - if (exec <= 0 ) - usage("Cannot set negative exec time", - EXIT_INV_COMMANDLINE); - tdata->phases_data->min_et = usec_to_timespec(exec); - tdata->phases_data->max_et = usec_to_timespec(exec); - i++; - break; - - case 2: -#ifdef AQUOSA - if (strcmp(token,"q") == 0) - tdata->sched_policy = aquosa; - else -#endif -#ifdef DLSCHED - if (strcmp(token,"d") == 0) - tdata->sched_policy = deadline; - else -#endif - if (strcmp(token,"f") == 0) - tdata->sched_policy = fifo; - else if (strcmp(token,"r") == 0) - tdata->sched_policy = rr ; - else if (strcmp(token,"o") == 0) - tdata->sched_policy = other; - else { - snprintf(tmp, 256, - "Invalid scheduling policy %s in %s", - token, arg); - usage(tmp, EXIT_INV_COMMANDLINE); - } - policy_to_string(tdata->sched_policy, - tdata->sched_policy_descr); - - i++; - break; - case 3: - if (strcmp(token, "-") == 0) - tdata->cpuset = NULL; - else { - tdata->cpuset = malloc (sizeof(cpu_set_t)); - tdata->cpuset_str = strdup(token); - } - i++; - break; - case 4: - tdata->sched_prio = strtol(token, NULL, 10); - // do not check, will fail in pthread_setschedparam - i++; - break; - case 5: - dline = strtol(token, NULL, 10); - if (dline < exec) - usage("Deadline cannot be less than " - "execution time", EXIT_INV_COMMANDLINE); - if (dline > period) - usage("Deadline cannot be greater than " - "period", EXIT_INV_COMMANDLINE); - if (dline <= 0 ) - usage("Cannot set negative deadline", - EXIT_INV_COMMANDLINE); - tdata->phases_data->deadline = usec_to_timespec(dline); - i++; - break; - } - token = strtok(NULL, ":"); - } - - if ( i < 2 ) { - printf("Period and exec time are mandatory\n"); - exit(EXIT_INV_COMMANDLINE); - } - - if (dline == 0) - tdata->phases_data->deadline = tdata->phases_data->period; - - /* set cpu affinity mask */ - if (tdata->cpuset_str) - { - snprintf(tmp, 256, "%s", tdata->cpuset_str); - token = strtok(tmp, ","); - while (token != NULL && i < 1000) { - cpu = strtol(token, NULL, 10); - CPU_SET(cpu, tdata->cpuset); - strtok(NULL, ","); - i++; - } - } else - tdata->cpuset_str = strdup("-"); - - free(str); -} - -static void -parse_command_line_options(int argc, char **argv, rtapp_options_t *opts) -{ - char tmp[PATH_LENGTH]; - signed char ch; - int longopt_idx; - int i; - - struct stat dirstat; - - /* set defaults */ - opts->spacing = 0; - opts->gnuplot = 0; - opts->lock_pages = 1; - opts->duration = -1; - opts->logbasename = strdup("rt-app"); - opts->logdir = NULL; - opts->nthreads = 0; - opts->ftrace = 0; - opts->die_on_dmiss = 0; - opts->pi_enabled = 0; - opts->policy = other; - opts->threads_data = malloc(sizeof(thread_data_t)); - opts->threads_data->phases_data = malloc(sizeof(phase_data_t)); -#ifdef AQUOSA - opts->fragment = 1; -#endif - - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"fifo", 0, 0, 'f'}, - {"rr", 0, 0, 'r'}, - {"thread", 1, 0, 't'}, - {"spacing", 1, 0, 's'}, - {"logdir", 1, 0, 'l'}, - {"baselog", 1, 0, 'b'}, - {"gnuplot", 1, 0, 'G'}, - {"duration", 1, 0, 'D'}, - {"ftrace", 0, 0, 'T'}, - {"pi_enabled", 0, 0, 'T'}, - {"die_on_dmiss", 0, 0, 'M'}, -#ifdef AQUOSA - {"qos", 0, 0, 'q'}, - {"frag",1, 0, 'g'}, -#endif - {0, 0, 0, 0} - }; - -#ifdef AQUOSA - while (( ch = getopt_long(argc,argv,"D:GKhfrb:s:l:qg:t:TM", - long_options, &longopt_idx)) != -1) -#else - while (( ch = getopt_long(argc,argv,"D:GKhfrb:s:l:t:TM", - long_options, &longopt_idx)) != -1) -#endif - { - switch (ch) - { - case 'h': - usage(NULL, EXIT_SUCCESS); - break; - case 'f': - if (opts->policy != other) - usage("Cannot set multiple policies", - EXIT_INV_COMMANDLINE); - opts->policy = fifo; - break; - case 'r': - if (opts->policy != other) - usage("Cannot set multiple policies", - EXIT_INV_COMMANDLINE); - opts->policy = rr; - break; - case 'b': - if (!opts->logdir) - opts->logdir = strdup("."); - opts->logbasename = strdup(optarg); - break; - case 's': - opts->spacing = strtol(optarg, NULL, 0); - if (opts->spacing < 0) - usage("Cannot set negative spacing", - EXIT_INV_COMMANDLINE); - break; - case 'l': - opts->logdir = strdup(optarg); - lstat(opts->logdir, &dirstat); - if (! S_ISDIR(dirstat.st_mode)) - usage("Cannot stat log directory", - EXIT_INV_COMMANDLINE); - break; - case 't': - if (opts->nthreads > 0) - { - opts->threads_data = realloc( - opts->threads_data, - (opts->nthreads+1) * \ - sizeof(thread_data_t)); - opts->threads_data[opts->nthreads+1].phases_data = - malloc(sizeof(phase_data_t)); - } - parse_thread_args(optarg, opts->nthreads, - &opts->threads_data[opts->nthreads], - opts->policy); - opts->nthreads++; - break; - case 'G': - opts->gnuplot = 1; - break; - case 'D': - opts->duration = strtol(optarg, NULL, 10); - if (opts->duration < 0) - usage("Cannot set negative duration", - EXIT_INV_COMMANDLINE); - break; - case 'K': - opts->lock_pages = 0; - break; - case 'T': - opts->ftrace = 1; - break; - case 'P': - opts->pi_enabled = 1; - break; - case 'M': - opts->die_on_dmiss = 1; - break; -#ifdef AQUOSA - case 'q': - if (opts->policy != other) - usage("Cannot set multiple policies", - EXIT_INV_COMMANDLINE); - opts->policy = aquosa; - break; - case 'g': - opts->fragment = strtol(optarg, NULL, 10); - if (opts->fragment < 1 || opts->fragment > 16) - usage("Fragment divisor must be between" - "1 and 16", EXIT_INV_COMMANDLINE); - break; -#endif - default: - log_error("Invalid option %c", ch); - usage(NULL, EXIT_INV_COMMANDLINE); - } - } - - if ( opts->nthreads < 1) - usage("You have to set parameters for at least one thread", - EXIT_INV_COMMANDLINE); - -} - void parse_command_line(int argc, char **argv, rtapp_options_t *opts) { -#ifdef JSON struct stat config_file_stat;
if (argc < 2) @@ -369,10 +46,4 @@ parse_command_line(int argc, char **argv, rtapp_options_t *opts) parse_config_stdin(opts); return; } -#endif - - parse_command_line_options(argc, argv, opts); - opts->resources = NULL; - opts->nresources = 0; } - diff --git a/src/rt-app_args.h b/src/rt-app_args.h index b7135c3..2f66a22 100644 --- a/src/rt-app_args.h +++ b/src/rt-app_args.h @@ -47,9 +47,6 @@ void usage (const char* msg, int ex_code);
void -parse_thread_args(char *arg, int idx, thread_data_t *tdata, policy_t def_policy); - -void parse_command_line(int argc, char **argv, rtapp_options_t *opts);
#endif // _RTAPP_ARGS_H_
From: Vincent Guittot vincent.guittot@linaro.org
Remove lock_order and resources objects in per-task objects. Instead, just describe the events sequence. Each event name must be unique and start with the known prefix: run : run a load sleep : sleep for a duration lock : lock a mutex unlock : unlock a mutex
As an example: "run0" : 275, "lock0" : "trig1_mutex", "run1" : 4725, "unlock0" : "trig1_mutex", "sleep1" : 19000,
Signed-off-by: Vincent Guittot vincent.guittot@linaro.org Signed-off-by: Pi-Cheng Chen pi-cheng.chen@linaro.org --- src/rt-app.c | 287 +++++++++++++++------------------------------- src/rt-app_parse_config.c | 284 ++++++++++++++------------------------------- src/rt-app_types.h | 35 +++--- src/rt-app_utils.c | 13 +-- 4 files changed, 195 insertions(+), 424 deletions(-)
diff --git a/src/rt-app.c b/src/rt-app.c index 957f3fe..e7bade0 100644 --- a/src/rt-app.c +++ b/src/rt-app.c @@ -165,93 +165,82 @@ int calibrate_cpu_cycles(int clock)
}
-static inline loadwait(struct timespec *exec_time) +static inline int loadwait(unsigned long exec) { - unsigned long exec, load_count; + unsigned long load_count;
- exec = timespec_to_usec(exec_time); load_count = (exec * 1000)/p_load; waste_cpu_cycles(load_count); + + return load_count; }
-int get_resource(rtapp_resource_access_list_t *lock, struct timespec *usage) +static int run_event(event_data_t *event, int dry_run, + unsigned long *perf, unsigned long *duration) { - int busywait = 1; - - switch(lock->res->type) { - case rtapp_mutex: - pthread_mutex_lock(&(lock->res->res.mtx.obj)); - break; - case rtapp_sleep: - nanosleep(usage, NULL); - busywait = 0; - break; + unsigned long lock = 0; + + switch(event->type) { + case rtapp_lock: + log_debug("lock %s ", event->res->name); + pthread_mutex_lock(&(event->res->res.mtx.obj)); + lock = 1; + break; + case rtapp_unlock: + log_debug("unlock %s ", event->res->name); + pthread_mutex_unlock(&(event->res->res.mtx.obj)); + lock = -1; + break; }
- return busywait; -} + if (dry_run) + return lock;
-void put_resource(rtapp_resource_access_list_t *lock) -{ - if (lock->res->type == rtapp_mutex) - pthread_mutex_unlock(&(lock->res->res.mtx.obj)); + switch(event->type) { + case rtapp_sleep: + { + struct timespec sleep; + sleep = usec_to_timespec(event->duration); + log_debug("sleep %d ", event->duration); + nanosleep(&sleep, NULL); + } + break; + case rtapp_run: + { + struct timespec t_start, t_end; + log_debug("run %d ", event->duration); + clock_gettime(CLOCK_MONOTONIC, &t_start); + *perf += loadwait(event->duration); + clock_gettime(CLOCK_MONOTONIC, &t_end); + t_end = timespec_sub(&t_end, &t_start); + *duration += timespec_to_usec(&t_end); + } + break; + } + + return lock; }
-void run(int ind, struct timespec *min, struct timespec *max, - rtapp_tasks_resource_list_t *blockages, int nblockages) +int run(int ind, event_data_t *events, + int nbevents, unsigned long *duration) { - int i, is_busywait = 1; - struct timespec t_exec; - rtapp_resource_access_list_t *lock, *last; + int i, lock = 0; + unsigned long perf = 0;
- t_exec = *min; - - for (i = 0; i < nblockages; i++) + for (i = 0; i < nbevents; i++) { - /* Lock resources sequence including the busy wait */ - lock = blockages[i].acl; - while (lock != NULL) { - log_debug("[%d] locking %d", ind, lock->res->index); - if (opts.ftrace) - log_ftrace(ft_data.marker_fd, - "[%d] locking %d", - ind, lock->res->index); - is_busywait = get_resource(lock, &blockages[i].usage); - last = lock; - lock = lock->next; - } - - if (is_busywait) { - /* Busy wait */ - log_debug("[%d] busywait for %lu", ind, timespec_to_usec(&blockages[i].usage)); - if (opts.ftrace) - log_ftrace(ft_data.marker_fd, - "[%d] busywait for %d", - ind, timespec_to_usec(&blockages[i].usage)); - loadwait(&blockages[i].usage); - t_exec = timespec_sub(&t_exec, &blockages[i].usage); - } + if (!continue_running && !lock) + return;
- /* Unlock resources */ - lock = last; - while (lock != NULL) { - log_debug("[%d] unlocking %d", ind, lock->res->index); - if (opts.ftrace) + log_debug("[%d] runs events %d type %d ", ind, i, events[i].type); + if (opts.ftrace) log_ftrace(ft_data.marker_fd, - "[%d] unlocking %d", - ind, lock->res->index); - put_resource(lock); - lock = lock->prev; - } + "[%d] locking %d", + ind, events[i].type); + lock += run_event(&events[i], !continue_running, &perf, duration); }
- /* Compute finish time for CPUTIME_ID clock */ - log_debug("[%d] busywait for %lu", ind, timespec_to_usec(&t_exec)); - if (opts.ftrace) - log_ftrace(ft_data.marker_fd, - "[%d] busywait for %lu", - ind, timespec_to_usec(&t_exec)); - loadwait(&t_exec); + return perf; }
static void @@ -281,10 +270,9 @@ void *thread_body(void *arg) thread_data_t *data = (thread_data_t*) arg; phase_data_t *pdata; struct sched_param param; - struct timespec t_now, t_next; + struct timespec t_start, t_end; unsigned long t_start_usec; - unsigned long my_duration_usec; - int nperiods; + unsigned long perf, duration; timing_point_t *timings; timing_point_t tmp_timing; timing_point_t *curr_timing; @@ -387,10 +375,6 @@ void *thread_body(void *arg) attr.sched_flags = 0; attr.sched_policy = SCHED_DEADLINE; attr.sched_priority = 0; - attr.sched_runtime = timespec_to_nsec(&pdata->max_et) + - (timespec_to_nsec(&pdata->max_et) /100) * BUDGET_OVERP; - attr.sched_deadline = timespec_to_nsec(&pdata->deadline); - attr.sched_period = timespec_to_nsec(&pdata->period); break; #endif
@@ -412,54 +396,11 @@ void *thread_body(void *arg) } }
- log_notice("[%d] starting thread with period: %lu, exec: %lu," - "deadline: %lu", - data->ind, - timespec_to_usec(&pdata->period), - timespec_to_usec(&pdata->min_et), - timespec_to_usec(&pdata->deadline)); - - /* if we know the duration we can calculate how many periods we will - * do at most, and the log to memory, instead of logging to file. - * In case of several phase, we still use the logging file instead of - * memory. - * TODO: parse all the phase to compute the memory phase - */ - nperiods = 0; - if ((data->duration > 0) && (data->duration > data->wait_before_start) - && (data->nphases < 2)) { - my_duration_usec = (data->duration * 1000000) - - data->wait_before_start; - nperiods = (my_duration_usec + timespec_to_usec(&data->phases_data->period) - 1) - / timespec_to_usec(&data->phases_data->period) + 1; - } + log_notice("[%d] starting thread ...\n", data->ind);
- if ((pdata->loop > 0) && (pdata->loop < nperiods)) { - nperiods = data->loop + 1 ; - } + timings = NULL;
- if (nperiods) - timings = malloc(nperiods * sizeof(timing_point_t)); - else - timings = NULL; - - fprintf(data->log_handler, "#idx\tperiod\tmin_et\tmax_et\trel_st\tstart" - "\t\tend\t\tdeadline\tdur.\tslack" - "\tBudget\tUsed Budget\n"); - - - if (data->wait_before_start > 0) { - log_notice("[%d] Waiting %ld usecs... ", data->ind, - data->wait_before_start); - clock_gettime(CLOCK_MONOTONIC, &t_now); - t_next = usec_to_timespec(data->wait_before_start); - t_next = timespec_add(&t_now, &t_next); - clock_nanosleep(CLOCK_MONOTONIC, - TIMER_ABSTIME, - &t_next, - NULL); - log_notice("[%d] Starting...", data->ind); - } + fprintf(data->log_handler, "#idx\tperf\trun\tperiod\tstart\t\tend\t\trel_st\n");
#ifdef DLSCHED /* TODO find a better way to handle that constraint */ @@ -468,14 +409,6 @@ void *thread_body(void *arg) * budget as little as possible for the first iteration. */ if (data->sched_policy == SCHED_DEADLINE) { - log_notice("[%d] starting thread with period: %llu, exec: %llu," - "deadline: %llu, priority: %d", - data->ind, - attr.sched_period / 1000, - attr.sched_runtime / 1000, - attr.sched_deadline / 1000, - attr.sched_priority); - ret = sched_setattr(tid, &attr, flags); if (ret != 0) { log_critical("[%d] sched_setattr " @@ -490,44 +423,38 @@ void *thread_body(void *arg) if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] starts", data->ind);
- clock_gettime(CLOCK_MONOTONIC, &t_now); - t_next = t_now; - pdata->deadline = timespec_add(&t_now, &pdata->deadline); - i = j = loop = 0; while (continue_running && (i != data->loop)) { - struct timespec t_start, t_end, t_diff, t_slack; + struct timespec t_diff;
if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] begins loop %d phase %d step %d", data->ind, i, j, loop);
+ log_debug("[%d] begins loop %d phase %d step %d", data->ind, i, j, loop);; + clock_gettime(CLOCK_MONOTONIC, &t_start); - run(data->ind, &pdata->min_et, &pdata->max_et, pdata->blockages, - pdata->nblockages); + perf = run(data->ind, pdata->events, pdata->nbevents, &duration); clock_gettime(CLOCK_MONOTONIC, &t_end);
- t_diff = timespec_sub(&t_end, &t_start); - t_slack = timespec_sub(&pdata->deadline, &t_end); - - t_start_usec = timespec_to_usec(&t_start); if (timings) curr_timing = &timings[i]; else curr_timing = &tmp_timing;
+ t_diff = timespec_sub(&t_end, &t_start); + + t_start_usec = timespec_to_usec(&t_start); + curr_timing->ind = data->ind; - curr_timing->period = timespec_to_usec(&pdata->period); - curr_timing->min_et = timespec_to_usec(&pdata->min_et); - curr_timing->max_et = timespec_to_usec(&pdata->max_et); curr_timing->rel_start_time = t_start_usec - timespec_to_usec(&data->main_app_start); - curr_timing->abs_start_time = t_start_usec; + curr_timing->start_time = t_start_usec; curr_timing->end_time = timespec_to_usec(&t_end); - curr_timing->deadline = timespec_to_usec(&pdata->deadline); - curr_timing->duration = timespec_to_usec(&t_diff); - curr_timing->slack = timespec_to_lusec(&t_slack); + curr_timing->period = timespec_to_usec(&t_diff); + curr_timing->duration = duration; + curr_timing->perf = perf;
#ifdef AQUOSA if (data->sched_policy == aquosa) { @@ -547,35 +474,15 @@ void *thread_body(void *arg) if (!timings) log_timing(data->log_handler, curr_timing);
- t_next = timespec_add(&t_next, &pdata->period); - pdata->deadline = timespec_add(&pdata->deadline, &pdata->period); - if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] end loop %d phase %d step %d", data->ind, i, j, loop);
- if (curr_timing->slack < 0 && opts.die_on_dmiss) { - log_critical("[%d] DEADLINE MISS !!!", data->ind); - if (opts.ftrace) - log_ftrace(ft_data.marker_fd, - "[%d] DEADLINE MISS!!", data->ind); - shutdown(SIGTERM); - goto exit_miss; - } - - clock_gettime(CLOCK_MONOTONIC, &t_now); - if (timespec_lower(&t_now, &t_next)) - clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t_next, NULL); - loop++; if (loop == pdata->loop) { loop = 0;
- clock_gettime(CLOCK_MONOTONIC, &t_now); - t_next = t_now; - pdata->deadline = timespec_add(&t_now, &pdata->deadline); - j++; if (j == data->nphases) { j = 0; @@ -587,7 +494,6 @@ void *thread_body(void *arg) } }
-exit_miss: param.sched_priority = 0; ret = pthread_setschedparam(pthread_self(), SCHED_OTHER, @@ -620,7 +526,7 @@ exit_miss:
int main(int argc, char* argv[]) { - struct timespec t_curr, t_next, t_start; + struct timespec t_start; FILE *gnuplot_script = NULL; int i, res; thread_data_t *tdata; @@ -687,15 +593,6 @@ int main(int argc, char* argv[]) /* start threads */ for (i = 0; i < nthreads; i++) { tdata = &opts.threads_data[i]; - if (opts.spacing > 0 ) { - /* start the thread, then it will sleep accordingly - * to its position. We don't sleep here anymore as - * this would mean that - * duration = spacing * nthreads + duration */ - tdata->wait_before_start = opts.spacing * (i+1); - } else { - tdata->wait_before_start = 0; - }
tdata->duration = opts.duration; tdata->main_app_start = t_start; @@ -727,27 +624,24 @@ int main(int argc, char* argv[])
/* print gnuplot files */ if (opts.logdir && opts.gnuplot) { - snprintf(tmp, PATH_LENGTH, "%s/%s-duration.plot", + snprintf(tmp, PATH_LENGTH, "%s/%s-period.plot", opts.logdir, opts.logbasename); gnuplot_script = fopen(tmp, "w+"); - snprintf(tmp, PATH_LENGTH, "%s-duration.eps", + snprintf(tmp, PATH_LENGTH, "%s-period.eps", opts.logbasename); fprintf(gnuplot_script, "set terminal postscript enhanced color\n" "set output '%s'\n" "set grid\n" "set key outside right\n" - "set title "Measured exec time per period"\n" - "set xlabel "Cycle start time [usec]"\n" - "set ylabel "Exec Time [usec]"\n" + "set title "Measured time per period"\n" + "set xlabel "Loop start time [usec]"\n" + "set ylabel "Period Time [usec]"\n" "plot ", tmp);
- for (i=0; i<nthreads; i++) { - snprintf(tmp, PATH_LENGTH, "%s/%s-duration.plot", - opts.logdir, opts.logbasename); - + for (i = 0; i < nthreads; i++) { fprintf(gnuplot_script, - ""%s-%s.log" u ($5/1000):9 w l" + ""%s-%s.log" u ($5/1000):4 w l" " title "thread [%s] (%s)"", opts.logbasename, opts.threads_data[i].name, opts.threads_data[i].name, @@ -762,10 +656,11 @@ int main(int argc, char* argv[]) fprintf(gnuplot_script, "set terminal wxt\nreplot\n"); fclose(gnuplot_script);
- snprintf(tmp, PATH_LENGTH, "%s/%s-slack.plot", + /* gnuplot of the run time */ + snprintf(tmp, PATH_LENGTH, "%s/%s-run.plot", opts.logdir, opts.logbasename); gnuplot_script = fopen(tmp, "w+"); - snprintf(tmp, PATH_LENGTH, "%s-slack.eps", + snprintf(tmp, PATH_LENGTH, "%s-run.eps", opts.logbasename);
fprintf(gnuplot_script, @@ -773,21 +668,21 @@ int main(int argc, char* argv[]) "set output '%s'\n" "set grid\n" "set key outside right\n" - "set title "Slack (negative = tardiness)"\n" - "set xlabel "Cycle start time [msec]"\n" - "set ylabel "Slack/Tardiness [usec]"\n" + "set title "Measured run time per loop"\n" + "set xlabel "Loop start time [usec]"\n" + "set ylabel "Run Time [usec]"\n" "plot ", tmp);
- for (i=0; i < nthreads; i++) { + for (i = 0; i < nthreads; i++) { fprintf(gnuplot_script, - ""%s-%s.log" u ($5/1000):10 w l" + ""%s-%s.log" u ($5/1000):3 w l" " title "thread [%s] (%s)"", opts.logbasename, opts.threads_data[i].name, opts.threads_data[i].name, opts.threads_data[i].sched_policy_descr);
if ( i == nthreads-1) - fprintf(gnuplot_script, ", 0 notitle\n"); + fprintf(gnuplot_script, "\n"); else fprintf(gnuplot_script, ", ");
diff --git a/src/rt-app_parse_config.c b/src/rt-app_parse_config.c index b399916..0f8998e 100644 --- a/src/rt-app_parse_config.c +++ b/src/rt-app_parse_config.c @@ -235,220 +235,107 @@ static int get_resource_index(const char *name, rtapp_resource_t *resources) }
static void -serialize_acl(rtapp_resource_access_list_t **acl, - const char *name, - struct json_object *task_resources, - rtapp_resource_t *resources) +parse_thread_event_data(char *name, struct json_object *obj, + event_data_t *data, const rtapp_options_t *opts) { - int i, idx, found; - struct json_object *access, *res, *next_res; - rtapp_resource_access_list_t *tmp; - const char * next_name; - - idx = get_resource_index(name, resources); - - if (!(*acl)) { - *acl = malloc( sizeof(rtapp_resource_access_list_t)); - (*acl)->res = &resources[idx]; -// (*acl)->index = idx; - (*acl)->next = NULL; - (*acl)->prev = NULL; - tmp = *acl; - } else { - found = 0; - tmp = *acl; - while (tmp->next != NULL) { - if (tmp->res->index == idx) - found = 1; - tmp = tmp->next; - } - if (found == 0) { - /* add the resource to the acl only if it is not already - * present in the list */ - tmp->next = malloc ( sizeof (rtapp_resource_access_list_t)); - // tmp->next->index = idx; - tmp->next->next = NULL; - tmp->next->prev = tmp; - tmp->next->res = &resources[idx]; - } - } + int i;
- res = get_in_object(task_resources, name, TRUE); - if (!res) - return; - assure_type_is(res, task_resources, name, json_type_object); + if (!strncmp(name, "run", strlen("run")) || + !strncmp(name, "sleep", strlen("sleep"))) { + if (!json_object_is_type(obj, json_type_int)) + goto unknown_event;
- access = get_in_object(res, "access", TRUE); - if (!access) + data->duration = json_object_get_int(obj); + + if (!strncmp(name, "sleep", strlen("sleep"))) + data->type = rtapp_sleep; + else + data->type = rtapp_run; + + log_info(PIN2 "type %d duration %d", data->type, data->duration); return; - assure_type_is(access, res, "access", json_type_array); + }
- for (i=0; i<json_object_array_length(access); i++) - { - char res_name[5]; + if (!strncmp(name, "lock", strlen("lock")) || + !strncmp(name, "unlock", strlen("unlock"))) { + if (!json_object_is_type(obj, json_type_string)) + goto unknown_event;
- next_res = json_object_array_get_idx(access, i); - if (!json_object_is_type(next_res, json_type_string)){ - if (!json_object_is_type(next_res, json_type_int)){ - log_critical("Invalid resource index"); - exit(EXIT_INV_CONFIG); - } else { - snprintf(res_name, 5, "%d", json_object_get_int(next_res)); - next_name = res_name; - } - log_critical("Legacy resource index"); - } else - next_name = json_object_get_string(next_res); - /* recurse on the rest of resources */ - serialize_acl(&(*acl), next_name, task_resources, resources); + i = get_resource_index(json_object_get_string(obj), + opts->resources); + + if (i >= opts->nresources) + goto unknown_event; + + data->res = &opts->resources[i]; + + if (!strncmp(name, "lock", strlen("lock"))) + data->type = rtapp_lock; + else + data->type = rtapp_unlock; + + log_info(PIN2 "type %d target %s", data->type, data->res->name); + return; } + +unknown_event: + data->duration = 0; + data->type = rtapp_run; + log_info(PIN2 "unknown type %d duration %d", data->type, data->duration); }
-static void -parse_thread_resources(const rtapp_options_t *opts, struct json_object *locks, - struct json_object *task_resources, phase_data_t *data) +static int +obj_is_event(char *name) { - int i, j, usage_usec; - struct json_object *res; - int res_dur; - char res_name[5]; - const char *cur_res_name; - - rtapp_resource_access_list_t *tmp, *head, *last; - char debug_msg[512], tmpmsg[512]; - - data->nblockages = json_object_array_length(locks); - data->blockages = malloc(sizeof(rtapp_tasks_resource_list_t) * - data->nblockages); - - for (i = 0; i< data->nblockages; i++) - { - res = json_object_array_get_idx(locks, i); - if (!json_object_is_type(res, json_type_string)) { - if (!json_object_is_type(res, json_type_int)) { - log_critical("Invalid resource index"); - exit(EXIT_INV_CONFIG); - } else { - snprintf(res_name, 5, "%d", json_object_get_int(res)); - cur_res_name = res_name; - } - log_critical("Legacy resource index"); - } else - cur_res_name = json_object_get_string(res); - - data->blockages[i].usage = usec_to_timespec(0); - data->blockages[i].acl = NULL; - serialize_acl(&data->blockages[i].acl, cur_res_name, - task_resources, opts->resources); - - /* since the "current" resource is returned as the first - * element in the list, we move it to the back */ - tmp = data->blockages[i].acl; - head = data->blockages[i].acl; - do { - last = tmp; - tmp = tmp->next; - } while (tmp != NULL); - - /* move first element to list end */ - if (last != head) { - data->blockages[i].acl = head->next; - data->blockages[i].acl->prev = NULL; - last->next = head; - head->next = NULL; - head->prev = last; - } - - tmp = data->blockages[i].acl; - debug_msg[0] = '\0'; - do { - snprintf(tmpmsg, 512, "%s %d", debug_msg, tmp->res->index); - strncpy(debug_msg, tmpmsg, 512); - last = tmp; - tmp = tmp->next; - } while (tmp != NULL); - - log_info(PIN "key: acl %s", debug_msg); - - res = get_in_object(task_resources, cur_res_name, TRUE); - if (!res) { - usage_usec = 0; - data->blockages[i].usage = usec_to_timespec(0); - } else { - assure_type_is(res, task_resources, res_name, - json_type_object); - usage_usec = get_int_value_from(res, "duration", TRUE, 0); - data->blockages[i].usage = usec_to_timespec(usage_usec); - } - log_info(PIN "res %s, usage: %d acl: %s", cur_res_name, - usage_usec, debug_msg); - } + if (!strncmp(name, "lock", strlen("lock"))) + return 1; + if (!strncmp(name, "unlock", strlen("unlock"))) + return 1; + if (!strncmp(name, "sleep", strlen("sleep"))) + return 1; + if (!strncmp(name, "run", strlen("run"))) + return 1; + + return 0; }
static void -parse_thread_phase_data(struct json_object *obj, int idx, - phase_data_t *data, const rtapp_options_t *opts) +parse_thread_phase_data(struct json_object *obj, phase_data_t *data, + const rtapp_options_t *opts) { - long exec, period, dline; - struct json_object *resources, *locks; + /* used in the foreach macro */ + struct lh_entry *entry; char *key; struct json_object *val; int idx; + int i;
/* loop */ data->loop = get_int_value_from(obj, "loop", TRUE, -1);
- /* exec time */ - exec = get_int_value_from(obj, "exec", FALSE, 0); - if (exec < 0) { - log_critical(PIN2 "Cannot set negative exec time"); - exit(EXIT_INV_CONFIG); + /* Count number of events */ + data->nbevents = 0; + foreach(obj, entry, key, val, idx) { + if (obj_is_event(key)) + data->nbevents++; } - data->min_et = usec_to_timespec(exec); - data->max_et = usec_to_timespec(exec); + log_info(PIN "Found %d events", data->nbevents);
- /* period */ - period = get_int_value_from(obj, "period", TRUE, exec); - if (period <= 0) { - log_critical(PIN2 "Cannot set negative period"); - exit(EXIT_INV_CONFIG); - } - if (exec > period) { - log_critical(PIN2 "Period must be greater or equal than period"); - exit(EXIT_INV_CONFIG); - } + if (data->nbevents == 0) + return;
- data->period = usec_to_timespec(period); + data->events = malloc(data->nbevents * sizeof(event_data_t));
- /* deadline */ - dline = get_int_value_from(obj, "deadline", TRUE, period); - if (dline < exec) { - log_critical(PIN2 "Deadline cannot be less than exec time"); - exit(EXIT_INV_CONFIG); - } - if (dline > period) { - log_critical(PIN2 "Deadline cannot be greater than period"); - exit(EXIT_INV_CONFIG); - } - data->deadline = usec_to_timespec(dline); - - /* resources */ - resources = get_in_object(obj, "resources", TRUE); - locks = get_in_object(obj, "lock_order", TRUE); - if (locks) { - assure_type_is(locks, obj, "lock_order", json_type_array); - log_info(PIN "key: lock_order %s", json_object_to_json_string(locks)); - if (resources) { - assure_type_is(resources, obj, "resources", - json_type_object); - log_info(PIN "key: resources %s", - json_object_to_json_string(resources)); + /* Parse events */ + i = 0; + foreach(obj, entry, key, val, idx) { + if (obj_is_event(key)) { + log_info(PIN "Parsing event %s", key); + parse_thread_event_data(key, val, &data->events[i], opts); + i++; } - parse_thread_resources(opts, locks, resources, data); - } else { - data->nblockages = 0; } }
static void -parse_thread_data(char *name, struct json_object *obj, int idx, +parse_thread_data(char *name, struct json_object *obj, int index, thread_data_t *data, const rtapp_options_t *opts) { long exec, period, dline; @@ -458,19 +345,16 @@ parse_thread_data(char *name, struct json_object *obj, int idx, struct json_object *cpuset_obj, *phases_obj, *cpu, *resources, *locks; int i, cpu_idx;
- log_info(PFX "Parsing thread %s [%d]", name, idx); + log_info(PFX "Parsing thread %s [%d]", name, index);
/* common and defaults */ - data->ind = idx; + data->ind = index; data->name = strdup(name); data->lock_pages = opts->lock_pages; data->sched_prio = DEFAULT_THREAD_PRIORITY; data->cpuset = NULL; data->cpuset_str = NULL;
- /* loop */ - data->loop = get_int_value_from(obj, "loop", TRUE, -1); - /* policy */ policy_to_string(opts->policy, def_policy); policy = get_string_value_from(obj, "policy", TRUE, def_policy); @@ -486,7 +370,7 @@ parse_thread_data(char *name, struct json_object *obj, int idx, data->sched_prio = get_int_value_from(obj, "priority", TRUE, DEFAULT_THREAD_PRIORITY);
- /* cpu set */ + /* cpuset */ cpuset_obj = get_in_object(obj, "cpus", TRUE); if (cpuset_obj) { assure_type_is(cpuset_obj, obj, "cpus", json_type_array); @@ -494,7 +378,7 @@ parse_thread_data(char *name, struct json_object *obj, int idx, data->cpuset = malloc(sizeof(cpu_set_t)); cpuset = json_object_get_array(cpuset_obj); CPU_ZERO(data->cpuset); - for (i=0; i < json_object_array_length(cpuset_obj); i++) { + for (i = 0; i < json_object_array_length(cpuset_obj); i++) { cpu = json_object_array_get_idx(cpuset_obj, i); cpu_idx = json_object_get_int(cpu); CPU_SET(cpu_idx, data->cpuset); @@ -505,7 +389,7 @@ parse_thread_data(char *name, struct json_object *obj, int idx, } log_info(PIN "key: cpus %s", data->cpuset_str);
- /* phases */ + /* Get phases */ phases_obj = get_in_object(obj, "phases", TRUE); if (phases_obj) { /* used in the foreach macro */ @@ -518,17 +402,22 @@ parse_thread_data(char *name, struct json_object *obj, int idx, foreach(phases_obj, entry, key, val, idx) { data->nphases++; } + log_info(PIN "Found %d phases", data->nphases); data->phases_data = malloc(sizeof(phase_data_t) * data->nphases); foreach(phases_obj, entry, key, val, idx) { log_info(PIN "Parsing phase %s", key); - parse_thread_phase_data(val, idx, &data->phases_data[idx], opts); + parse_thread_phase_data(val, &data->phases_data[idx], opts); } + + /* Get loop number */ + data->loop = get_int_value_from(obj, "loop", TRUE, -1); + } else { data->nphases = 1; data->phases_data = malloc(sizeof(phase_data_t) * data->nphases); + parse_thread_phase_data(obj, &data->phases_data[0], opts); data->loop = 1; - parse_thread_phase_data(obj, 0, data->phases_data, opts); } }
@@ -558,7 +447,6 @@ parse_global(struct json_object *global, rtapp_options_t *opts) int scan_cnt;
log_info(PFX "Parsing global section"); - opts->spacing = get_int_value_from(global, "spacing", TRUE, 0); opts->duration = get_int_value_from(global, "duration", TRUE, -1); opts->gnuplot = get_bool_value_from(global, "gnuplot", TRUE, 0); policy = get_string_value_from(global, "default_policy", diff --git a/src/rt-app_types.h b/src/rt-app_types.h index b8988f7..aeea37d 100644 --- a/src/rt-app_types.h +++ b/src/rt-app_types.h @@ -61,7 +61,9 @@ typedef enum resource_t { rtapp_mutex = 0, rtapp_sleep, - rtapp_run + rtapp_run, + rtapp_lock, + rtapp_unlock } resource_t;
struct _rtapp_mutex { @@ -79,23 +81,17 @@ typedef struct _rtapp_resource_t { char *name; } rtapp_resource_t;
-typedef struct _rtapp_resource_access_list_t { +typedef struct _event_data_t { + resource_t type; rtapp_resource_t *res; - struct _rtapp_resource_access_list_t *next; - struct _rtapp_resource_access_list_t *prev; -} rtapp_resource_access_list_t; - -typedef struct _rtapp_tasks_resource_list_t { - struct timespec usage; - struct _rtapp_resource_access_list_t *acl; -} rtapp_tasks_resource_list_t; + int duration; + int count; +} event_data_t;
typedef struct _phase_data_t { - struct timespec min_et, max_et; - struct timespec period, deadline; - int loop, sleep; - rtapp_tasks_resource_list_t *blockages; - int nblockages; + int loop; + event_data_t *events; + int nbevents; } phase_data_t;
typedef struct _thread_data_t { @@ -105,7 +101,6 @@ typedef struct _thread_data_t { int duration; cpu_set_t *cpuset; char *cpuset_str; - unsigned long wait_before_start; struct timespec main_app_start; int loop; int nphases; @@ -141,7 +136,6 @@ typedef struct _rtapp_options_t {
policy_t policy; int duration; - unsigned long spacing;
char *logdir; char *logbasename; @@ -164,14 +158,11 @@ typedef struct _rtapp_options_t { typedef struct _timing_point_t { int ind; unsigned long period; - unsigned long min_et; - unsigned long max_et; unsigned long rel_start_time; - unsigned long abs_start_time; + unsigned long start_time; unsigned long end_time; - unsigned long deadline; unsigned long duration; - long slack; + unsigned long perf; #ifdef AQUOSA qres_time_t budget; qres_time_t used_budget; diff --git a/src/rt-app_utils.c b/src/rt-app_utils.c index 1e13ee0..46de2b6 100644 --- a/src/rt-app_utils.c +++ b/src/rt-app_utils.c @@ -137,17 +137,14 @@ void log_timing(FILE *handler, timing_point_t *t) { fprintf(handler, - "%d\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu\t%ld", + "%d\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu", t->ind, + t->perf, + t->duration, t->period, - t->min_et, - t->max_et, - t->rel_start_time, - t->abs_start_time, + t->start_time, t->end_time, - t->deadline, - t->duration, - t->slack + t->rel_start_time ); #ifdef AQUOSA fprintf(handler,
From: Vincent Guittot vincent.guittot@linaro.org
When global object is absent, give a set of default values so that global object could be optional in JSON file.
Signed-off-by: Vincent Guittot vincent.guittot@linaro.org Signed-off-by: Pi-Cheng Chen pi-cheng.chen@linaro.org --- src/rt-app_parse_config.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/src/rt-app_parse_config.c b/src/rt-app_parse_config.c index 0f8998e..1f4c2db 100644 --- a/src/rt-app_parse_config.c +++ b/src/rt-app_parse_config.c @@ -447,6 +447,21 @@ parse_global(struct json_object *global, rtapp_options_t *opts) int scan_cnt;
log_info(PFX "Parsing global section"); + + if (!global) { + opts->duration = -1; + opts->gnuplot = 0; + opts->policy = other; + opts->calib_cpu = 0; + opts->calib_ns_per_loop = 0; + opts->logdir = strdup("./"); + opts->lock_pages = 1; + opts->logbasename = strdup("rt-app"); + opts->ftrace = 0; + opts->pi_enabled = 0; + return; + } + opts->duration = get_int_value_from(global, "duration", TRUE, -1); opts->gnuplot = get_bool_value_from(global, "gnuplot", TRUE, 0); policy = get_string_value_from(global, "default_policy", @@ -480,7 +495,7 @@ parse_global(struct json_object *global, rtapp_options_t *opts) } }
- opts->logdir = get_string_value_from(global, "logdir", TRUE, NULL); + opts->logdir = get_string_value_from(global, "logdir", TRUE, ./); opts->lock_pages = get_bool_value_from(global, "lock_pages", TRUE, 1); opts->logbasename = get_string_value_from(global, "log_basename", TRUE, "rt-app"); @@ -504,7 +519,7 @@ get_opts_from_json_object(struct json_object *root, rtapp_options_t *opts) log_info(PFX "Successfully parsed input JSON"); log_info(PFX "root : %s", json_object_to_json_string(root));
- global = get_in_object(root, "global", FALSE); + global = get_in_object(root, "global", TRUE); log_info(PFX "global : %s", json_object_to_json_string(global));
tasks = get_in_object(root, "tasks", FALSE);
From: Vincent Guittot vincent.guittot@linaro.org
The large ressources object is now optional, the reosources are declaring the 1st time they are used in a thread.
Signed-off-by: Vincent Guittot vincent.guittot@linaro.org Signed-off-by: Pi-Cheng Chen pi-cheng.chen@linaro.org --- src/rt-app.c | 19 ++++---- src/rt-app_parse_config.c | 110 ++++++++++++++++++++++++++++++++++------------ src/rt-app_types.h | 7 ++- 3 files changed, 98 insertions(+), 38 deletions(-)
diff --git a/src/rt-app.c b/src/rt-app.c index e7bade0..575dea6 100644 --- a/src/rt-app.c +++ b/src/rt-app.c @@ -176,19 +176,20 @@ static inline int loadwait(unsigned long exec) }
static int run_event(event_data_t *event, int dry_run, - unsigned long *perf, unsigned long *duration) + unsigned long *perf, unsigned long *duration, rtapp_resource_t *resources) { + rtapp_resource_t *rdata = &(resources[event->res]); unsigned long lock = 0;
switch(event->type) { case rtapp_lock: - log_debug("lock %s ", event->res->name); - pthread_mutex_lock(&(event->res->res.mtx.obj)); + log_debug("lock %s ", rdata->name); + pthread_mutex_lock(&(rdata->res.mtx.obj)); lock = 1; break; case rtapp_unlock: - log_debug("unlock %s ", event->res->name); - pthread_mutex_unlock(&(event->res->res.mtx.obj)); + log_debug("unlock %s ", rdata->name); + pthread_mutex_unlock(&(rdata->res.mtx.obj)); lock = -1; break; } @@ -222,7 +223,8 @@ static int run_event(event_data_t *event, int dry_run, }
int run(int ind, event_data_t *events, - int nbevents, unsigned long *duration) + int nbevents, unsigned long *duration, + rtapp_resource_t *resources) { int i, lock = 0; unsigned long perf = 0; @@ -237,7 +239,7 @@ int run(int ind, event_data_t *events, log_ftrace(ft_data.marker_fd, "[%d] locking %d", ind, events[i].type); - lock += run_event(&events[i], !continue_running, &perf, duration); + lock += run_event(&events[i], !continue_running, &perf, duration, resources); }
return perf; @@ -249,6 +251,7 @@ shutdown(int sig) int i; // notify threads, join them, then exit continue_running = 0; + for (i = 0; i < nthreads; i++) { pthread_join(threads[i], NULL); @@ -435,7 +438,7 @@ void *thread_body(void *arg) log_debug("[%d] begins loop %d phase %d step %d", data->ind, i, j, loop);;
clock_gettime(CLOCK_MONOTONIC, &t_start); - perf = run(data->ind, pdata->events, pdata->nbevents, &duration); + perf = run(data->ind, pdata->events, pdata->nbevents, &duration, *(data->resources)); clock_gettime(CLOCK_MONOTONIC, &t_end);
if (timings) diff --git a/src/rt-app_parse_config.c b/src/rt-app_parse_config.c index 1f4c2db..6834b65 100644 --- a/src/rt-app_parse_config.c +++ b/src/rt-app_parse_config.c @@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #define PFL " "PFX #define PIN PFX" " #define PIN2 PIN" " +#define PIN3 PIN2" " #define JSON_FILE_BUF_SIZE 4096
/* redefine foreach as in <json/json_object.h> but to be ANSI @@ -162,7 +163,7 @@ get_string_value_from(struct json_object *where,
static int init_mutex_resource(rtapp_resource_t *data, const rtapp_options_t *opts) { - log_info(PIN "Init: %s mutex", data->name); + log_info(PIN3 "Init: %s mutex", data->name);
pthread_mutexattr_init(&data->res.mtx.attr); if (opts->pi_enabled) { @@ -175,6 +176,24 @@ static int init_mutex_resource(rtapp_resource_t *data, const rtapp_options_t *op }
static void +init_resource_data(const char *name, int type, int idx, const rtapp_options_t *opts) +{ + rtapp_resource_t *data = &(opts->resources[idx]); + char *target; + + /* common and defaults */ + data->index = idx; + data->name = strdup(name); + data->type = type; + + switch (data->type) { + case rtapp_mutex: + init_mutex_resource(data, opts); + break; + } +} + +static void parse_resource_data(char *name, struct json_object *obj, int idx, rtapp_resource_t *data, const rtapp_options_t *opts) { @@ -183,10 +202,6 @@ parse_resource_data(char *name, struct json_object *obj, int idx,
log_info(PFX "Parsing resources %s [%d]", name, idx);
- /* common and defaults */ - data->index = idx; - data->name = strdup(name); - /* resource type */ resource_to_string(0, def_type); type = get_string_value_from(obj, "type", TRUE, def_type); @@ -197,8 +212,24 @@ parse_resource_data(char *name, struct json_object *obj, int idx, } }
- if (data->type == rtapp_mutex) - init_mutex_resource(data, opts); + init_resource_data(name, data->type, idx, opts); +} + +static int +add_resource_data(const char *name, int type, rtapp_options_t *opts) +{ + int idx; + + idx = opts->nresources; + + log_info(PIN2 "Add new resource %s [%d] type %d", name, idx, type); + + opts->nresources++; + opts->resources = realloc(opts->resources, sizeof(rtapp_resource_t) * opts->nresources); + + init_resource_data(name, type, idx, opts); + + return idx; }
static void @@ -209,6 +240,11 @@ parse_resources(struct json_object *resources, rtapp_options_t *opts)
log_info(PFX "Parsing resource section");
+ if (!resources) { + log_info(PFX "No resource section Found"); + return; + } + if (json_object_is_type(resources, json_type_object)) { opts->nresources = 0; foreach(resources, entry, key, val, idx) { @@ -224,24 +260,32 @@ parse_resources(struct json_object *resources, rtapp_options_t *opts) } }
-static int get_resource_index(const char *name, rtapp_resource_t *resources) +static int get_resource_index(const char *name, int type, rtapp_options_t *opts) { - int i=0; + rtapp_resource_t *resources = opts->resources; + int nresources = opts->nresources; + int i = 0;
- while (strcmp(resources[i].name, name) != 0) + while ((i < nresources) && ((strcmp(resources[i].name, name) != 0) || (resources[i].type != type))) i++;
+ if (i >= nresources) + i = add_resource_data(name, type, opts); + return i; }
static void parse_thread_event_data(char *name, struct json_object *obj, - event_data_t *data, const rtapp_options_t *opts) + event_data_t *data, rtapp_options_t *opts) { + rtapp_resource_t *rdata, *ddata; + const char *ref; int i;
if (!strncmp(name, "run", strlen("run")) || !strncmp(name, "sleep", strlen("sleep"))) { + if (!json_object_is_type(obj, json_type_int)) goto unknown_event;
@@ -258,50 +302,55 @@ parse_thread_event_data(char *name, struct json_object *obj,
if (!strncmp(name, "lock", strlen("lock")) || !strncmp(name, "unlock", strlen("unlock"))) { + if (!json_object_is_type(obj, json_type_string)) goto unknown_event;
- i = get_resource_index(json_object_get_string(obj), - opts->resources); + ref = json_object_get_string(obj); + i = get_resource_index(ref, rtapp_mutex, opts);
- if (i >= opts->nresources) - goto unknown_event; - - data->res = &opts->resources[i]; + data->res = i;
if (!strncmp(name, "lock", strlen("lock"))) data->type = rtapp_lock; else data->type = rtapp_unlock;
- log_info(PIN2 "type %d target %s", data->type, data->res->name); + rdata = &(opts->resources[data->res]); + ddata = &(opts->resources[data->dep]); + + log_info(PIN2 "type %d target %s [%d]", data->type, rdata->name, rdata->index); return; }
+unknown_resource: + log_error(PIN2 "Resource %s not found in the resource section !!!", ref); + log_error(PIN2 "Please check the resource name or the resource section"); + unknown_event: data->duration = 0; data->type = rtapp_run; - log_info(PIN2 "unknown type %d duration %d", data->type, data->duration); + log_error(PIN2 "Unknown or mismatch %s event type !!!", name); }
static int obj_is_event(char *name) { if (!strncmp(name, "lock", strlen("lock"))) - return 1; + return rtapp_mutex; if (!strncmp(name, "unlock", strlen("unlock"))) - return 1; + return rtapp_lock; if (!strncmp(name, "sleep", strlen("sleep"))) - return 1; + return rtapp_sleep; if (!strncmp(name, "run", strlen("run"))) - return 1; + return rtapp_run;
return 0; }
static void parse_thread_phase_data(struct json_object *obj, phase_data_t *data, - const rtapp_options_t *opts) + rtapp_options_t *opts) { /* used in the foreach macro */ struct lh_entry *entry; char *key; struct json_object *val; int idx; @@ -336,7 +385,7 @@ parse_thread_phase_data(struct json_object *obj, phase_data_t *data,
static void parse_thread_data(char *name, struct json_object *obj, int index, - thread_data_t *data, const rtapp_options_t *opts) + thread_data_t *data, rtapp_options_t *opts) { long exec, period, dline; char *policy; @@ -348,6 +397,7 @@ parse_thread_data(char *name, struct json_object *obj, int index, log_info(PFX "Parsing thread %s [%d]", name, index);
/* common and defaults */ + data->resources = &opts->resources; data->ind = index; data->name = strdup(name); data->lock_pages = opts->lock_pages; @@ -449,6 +499,7 @@ parse_global(struct json_object *global, rtapp_options_t *opts) log_info(PFX "Parsing global section");
if (!global) { + log_info(PFX " No global section Found: Use default value"); opts->duration = -1; opts->gnuplot = 0; opts->policy = other; @@ -520,16 +571,19 @@ get_opts_from_json_object(struct json_object *root, rtapp_options_t *opts) log_info(PFX "root : %s", json_object_to_json_string(root));
global = get_in_object(root, "global", TRUE); - log_info(PFX "global : %s", json_object_to_json_string(global)); + if (global) + log_info(PFX "global : %s", json_object_to_json_string(global));
tasks = get_in_object(root, "tasks", FALSE); log_info(PFX "tasks : %s", json_object_to_json_string(tasks));
- resources = get_in_object(root, "resources", FALSE); - log_info(PFX "resources: %s", json_object_to_json_string(resources)); + resources = get_in_object(root, "resources", TRUE); + if (resources) + log_info(PFX "resources: %s", json_object_to_json_string(resources));
parse_global(global, opts); json_object_put(global); + log_info(PFX "Parsing resources"); parse_resources(resources, opts); json_object_put(resources); parse_tasks(tasks, opts); diff --git a/src/rt-app_types.h b/src/rt-app_types.h index aeea37d..3ddd7fd 100644 --- a/src/rt-app_types.h +++ b/src/rt-app_types.h @@ -59,7 +59,8 @@ typedef enum policy_t
typedef enum resource_t { - rtapp_mutex = 0, + rtapp_unknown = 0, + rtapp_mutex, rtapp_sleep, rtapp_run, rtapp_lock, @@ -83,7 +84,8 @@ typedef struct _rtapp_resource_t {
typedef struct _event_data_t { resource_t type; - rtapp_resource_t *res; + int res; + int dep; int duration; int count; } event_data_t; @@ -99,6 +101,7 @@ typedef struct _thread_data_t { char *name; int lock_pages; int duration; + rtapp_resource_t **resources; cpu_set_t *cpuset; char *cpuset_str; struct timespec main_app_start;
Hi Juri,
Would you have some time to review this series and put some comments to make it progress?
Thanks, Pi-Cheng
On Wed, Sep 9, 2015 at 6:08 PM, Pi-Cheng Chen pi-cheng.chen@linaro.org wrote:
Hi Juri,
This series mainly intends to introduce calibration mechanism and simplify the grammar used in JSON files. [4, 5, 6] of this series are exceptions but also put in this series since the following grammar simplification patches are highly dependent on those features and removal of command line interface done in [4, 5, 6].
This series are also found in: ssh://git@git.linaro.org/people/picheng.chen/rt-app.git calibration_grammar_series
Vincent Guittot (9): make load frequency independent fix calibration issue on mt8173 describe resource names in plain string add run and sleep resource types add phase feature remove support of command line interface simplify grammar of JSON files make global object optional make resource object optional
README.in | 79 +------- src/rt-app.c | 451 +++++++++++++++++++++++++----------------- src/rt-app_args.c | 328 +------------------------------ src/rt-app_args.h | 3 - src/rt-app_parse_config.c | 492 +++++++++++++++++++++++++++------------------- src/rt-app_types.h | 63 +++--- src/rt-app_utils.c | 64 +++++- src/rt-app_utils.h | 3 + 8 files changed, 666 insertions(+), 817 deletions(-)
-- 1.9.1
Hi Pi-Cheng,
first of all I have to apologize for not having looked at this since now. I'm really sorry about this.
On 24/02/16 09:45, Pi-Cheng Chen wrote:
Hi Juri,
Would you have some time to review this series and put some comments to make it progress?
I'm actually finding some time these days to progress this, and I should be able to get back to you shortly.
Best,
- Juri