diff --git a/doc/tutorial.txt b/doc/tutorial.txt index 07dfa56e..9cf343b0 100644 --- a/doc/tutorial.txt +++ b/doc/tutorial.txt @@ -285,16 +285,16 @@ scheduler point of view. *** NUMA memory binding * nodes_membind: Array of Integer. Define the NUMA binding of the thread. -Default value is all NUMA nodes of the system. +Default value is all NUMA nodes of the system. An example : "nodes_membind" : [0, 2, 3] -"nodes_membind" Can be specified at task level or phase level. +"nodes_membind" Can be specified at task level or phase level. As an example, below creates two threads. -One thread will run with memory binding to nodes 2 and 3. -The second task will run first phase with memory binding to nodes 0 and 1, +One thread will run with memory binding to nodes 2 and 3. +The second task will run first phase with memory binding to nodes 0 and 1, second phase with memory binding to node 2. -Please note, that we follow an "event based policy", which means that -rt-app changes memory binding when there is a "nodes_membind" event +Please note, that we follow an "event based policy", which means that +rt-app changes memory binding when there is a "nodes_membind" event and don't do anything otherwise. "tasks" : { @@ -339,8 +339,32 @@ unsigned int max_nbr_tgs = 32' at compile time. *** events *** -events are simple action that will be performed by the thread or on the -thread. They have to be listed by execution order. +events are simple action that will be performed by the phase or on the +thread. + +Two formats are supported: +* Either specified directly in the thread or phase body, in execution order: + + "run": 1000, + "timer": {"ref": "helloworld", "period": 16000} + +* As objects inside an array stored under the "events" key: + + "events": [ + {"run": 1000}, + {"timer": {"ref": "helloworld", "period": 16000}}, + {"run": 9000}, + {"sleep": 1000} + ] + + Each object in the array must contain a single event key. This format allows + specifying the same event more than once (like "run" in the example), and is + friendlier to languages with map types that are not preserving insertion + order. + +Both formats cannot be mixed together. + +Available events: * run : Integer. Emulate the execution of a load. The duration is defined in usec but the run event will effectively run a number of time a loop that waste @@ -650,7 +674,7 @@ The generated events are of these main categories: - rtapp_loop: event=start thread_loop=0 phase=0 phase_loop=0 - rtapp_loop: event=end thread_loop=0 phase=0 phase_loop=0 - + Reporting the start and end of workload's phases and loops. * Workload's events, for example: @@ -756,5 +780,3 @@ below) 20000 ++--+----+---+----+---+---+----+---+----+--++ 494000 17560556057560556057560656065606756065606756075607 Loop start time [msec] - - diff --git a/src/rt-app_parse_config.c b/src/rt-app_parse_config.c index e33b059d..b9e0a50d 100644 --- a/src/rt-app_parse_config.c +++ b/src/rt-app_parse_config.c @@ -983,37 +983,94 @@ parse_task_phase_data(struct json_object *obj, { /* used in the foreach macro */ struct lh_entry *entry; char *key; struct json_object *val; int idx; - int i; + struct json_object *array_val; + size_t i; + struct json_object *events_array = NULL; + bool is_first_entry; log_info(PFX "Parsing phase"); + data->nbevents = 0; + data->events = NULL; + /* loop */ data->loop = get_int_value_from(obj, "loop", TRUE, 1); - /* Count number of events */ - data->nbevents = 0; foreach(obj, entry, key, val, idx) { - if (obj_is_event(key)) + if(!strncmp("events", key, 6)) { + if(!json_object_is_type(val, json_type_array)) { + log_critical(PIN "\"events\" key must be an array"); + exit(EXIT_INV_CONFIG); + } + events_array = val; + break; + } + } + + foreach(obj, entry, key, val, idx) { + if (obj_is_event(key)) { + /* Check that we are not trying to mix old and new styles */ + if (events_array) { + log_critical(PIN "Event key %s cannot be used in conjunction with \"events\" key", key); + exit(EXIT_INV_CONFIG); + /* Count number of events */ + } else { data->nbevents++; + } + } } + if (events_array) + /* Assumes that all item in the array will be valid. If an item + * does not yield an entry in data->events, we must ensure it won't + * be used by exiting + */ + data->nbevents = json_object_array_length(events_array); + if (data->nbevents == 0) { log_critical(PIN "No events found. Task must have events or it's useless"); exit(EXIT_INV_CONFIG); - } log_info(PIN "Found %d events", data->nbevents); - data->events = malloc(data->nbevents * sizeof(event_data_t)); /* Parse events */ - i = 0; - foreach(obj, entry, key, val, idx) { - if (obj_is_event(key)) { - log_info(PIN "Parsing event %s", key); - parse_task_event_data(key, val, &data->events[i], tdata, opts); - i++; + if (events_array) { + for (i=0; i < data->nbevents; i++) { + array_val = json_object_array_get_idx(events_array, i); + is_first_entry = true; + foreach(array_val, entry, key, val, idx) { + if (!is_first_entry) { + log_critical(PIN "Encountered a second key %s in an \"events\" item", key); + exit(EXIT_INV_CONFIG); + } else { + is_first_entry = false; + } + + if (obj_is_event(key)) { + log_info(PIN "Parsing event %s", key); + parse_task_event_data(key, val, &data->events[i], tdata, opts); + } else { + /* We must exit in this branch, + * otherwise we will have a non-initialized + * entry in data->events that would be an + * undefined behavior + */ + log_critical(PIN "Encountered non-event key %s in \"events\" array", key); + exit(EXIT_INV_CONFIG); + } + } + + } + } else { + i = 0; + foreach(obj, entry, key, val, idx) { + if (obj_is_event(key)) { + log_info(PIN "Parsing event %s", key); + parse_task_event_data(key, val, &data->events[i], tdata, opts); + i++; + } } } parse_cpuset_data(obj, &data->cpu_data);