[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 5/7] scenario-engine: add a time based event schedul
From: |
Victor CLEMENT |
Subject: |
[Qemu-devel] [PATCH 5/7] scenario-engine: add a time based event scheduler |
Date: |
Fri, 11 Sep 2015 14:50:28 +0200 |
The scheduler reads an event description file containing timestamps and
parameters then schedules those events. A user defined function is
called when those events expire. It is used for precise time
simulations.
Signed-off-by: Victor CLEMENT <address@hidden>
---
Makefile.objs | 1 +
include/scenario/scheduler.h | 26 ++++++
scenario/scheduler.c | 186 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 213 insertions(+)
create mode 100644 include/scenario/scheduler.h
create mode 100644 scenario/scheduler.c
diff --git a/Makefile.objs b/Makefile.objs
index 3ebb694..112794d 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -108,6 +108,7 @@ target-obj-y += trace/
######################################################################
# scenario
+util-obj-$(CONFIG_SCENARIO) += scenario/scheduler.o
util-obj-$(CONFIG_SCENARIO) += scenario/utils.o
######################################################################
diff --git a/include/scenario/scheduler.h b/include/scenario/scheduler.h
new file mode 100644
index 0000000..03a0e92
--- /dev/null
+++ b/include/scenario/scheduler.h
@@ -0,0 +1,26 @@
+#include "qemu/timer.h"
+#ifndef SCENARIO_H
+#define SCENARIO_H
+
+/*
+ * Prototype of callback function to implement in a scenario using scheduler
+ *
+ * opaque: an opaque pointer you choose when calling sched_start
+ * ts: the timestamp triggering this callback
+ * param: the parameter string associated to this timestamp
+ */
+typedef void scenario_scheduler_cb_t(void *opaque, uint64_t ts, char *param);
+
+/*
+ * Call this function to start the scheduler
+ *
+ * cb: the callback function to call when a scheduled event occurs
+ * opaque: the opaque pointer to give to the callback
+ * file: a file with timestamps and optional parameters associated
+ */
+int scenario_scheduler_start(scenario_scheduler_cb_t *cb,
+ void *opaque,
+ const char *file);
+
+#endif
+
diff --git a/scenario/scheduler.c b/scenario/scheduler.c
new file mode 100644
index 0000000..342a73a
--- /dev/null
+++ b/scenario/scheduler.c
@@ -0,0 +1,186 @@
+#include "scenario/scheduler.h"
+
+// #define DEBUG_SCHEDULER
+
+#ifdef DEBUG_SCHEDULER
+#define DPRINTF(fmt, ...) \
+ do { printf("scheduler: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define EPRINTF(fmt, ...) \
+ do { printf("scheduler: " fmt, ## __VA_ARGS__); } while (0)
+
+#define MAX_TS 1000
+#define MAX_LINE 1000
+
+struct simu_event_t {
+ uint64_t ts;
+ char param[MAX_LINE+1];
+};
+
+/*
+ * This type of struct will be passed to timers.
+ *
+ * It contains the callback, its opaque pointer, and every information to
+ * schedule the next timer.
+ */
+struct scheduler_struct {
+ scenario_scheduler_cb_t *cb;
+ void *opaque;
+ QEMUTimer *timer;
+ unsigned int next_ts;
+ struct simu_event_t event_list[MAX_TS];
+ uint64_t start_ts;
+};
+
+/*
+ * Function to parse "vcd" like files
+ * Those file will contain a list of timestamp, on per line beginning with '#'.
+ * Each timestamp could be followed by one parameter line beginning with '%',
+ * the rest of this line will be passed to the scenario callback.
+ *
+ * vcd_fname: the filename
+ * event_list: a pointer to an allocated array of simu_event_t
+ * max_length: the maximal number of events in the file
+ * (aka the event_list length)
+ *
+ * returns the number of events read or -1 if the simulation file is not found
+ */
+static int parse_vcd(const char *vcd_fname,
+ struct simu_event_t *event_list,
+ unsigned int max_length)
+{
+ FILE *vcd_file;
+ char line[MAX_LINE];
+ uint32_t line_len;
+ unsigned int i;
+ int n;
+ uint64_t timestamp = 0;
+ uint64_t last_timestamp = 0;
+ bool was_timestamp = false;
+
+ vcd_file = fopen(vcd_fname, "r");
+ if (vcd_file) {
+ DPRINTF("Event file opened\n");
+
+ i = 1;
+ n = -1;
+ while (!feof(vcd_file) && (i < max_length)) {
+ if (fgets(line, MAX_LINE+1, vcd_file)) {
+ line_len = strlen(line);
+ if (line_len < MAX_LINE) {
+ if ((line_len == 0) || (line[0] == '\n')) {
+ DPRINTF("Line %u is empty\n", i);
+ } else if (line[0] == '#') {
+ timestamp = strtoul(line+1, NULL, 0);
+ if (timestamp) {
+ if (timestamp <= last_timestamp) {
+ EPRINTF("Erroneous timestamp dropped (%lu)\n",
+ timestamp);
+ } else {
+ DPRINTF("Timestamp found: %lu\n", timestamp);
+ n++;
+ event_list[n].ts = timestamp;
+ event_list[n].param[0] = '\0';
+ last_timestamp = timestamp;
+ was_timestamp = true;
+ }
+ } else {
+ EPRINTF("Bad timestamp dropped at line %u: %s",
+ i, line);
+ }
+ } else if (line[0] == '%') {
+ if (was_timestamp) {
+ DPRINTF("Parameter found: %s", line+1);
+ pstrcpy(event_list[n].param, line_len-1, line+1);
+ was_timestamp = false;
+ } else {
+ EPRINTF("Bad parameter dropped at line %u: %s",
+ i, line);
+ }
+ } else if (line[0] == ';') {
+ DPRINTF("Skip comment: %s", line+1);
+ } else {
+ EPRINTF("Unknown line beginning char\n");
+ }
+ } else {
+ EPRINTF("Line #%u is too long\n", i);
+ while (!feof(vcd_file) && fgetc(vcd_file) != '\n') {
+ }
+ }
+ } else if (!feof(vcd_file)) {
+ EPRINTF("Error, reading line #%u: ", i);
+ perror("");
+ }
+ i++;
+ }
+ event_list[n+1].ts = 0;
+ DPRINTF("EOF\n");
+
+ } else {
+ event_list[0].ts = 0;
+ return -1;
+ }
+ fclose(vcd_file);
+ return ++n;
+}
+
+/*
+ * This callback is called when a scheduled timer expires
+ * It will call the "real" callback then schedule the next "non zero" event
+ */
+static void scheduler_cb(void *opaque)
+{
+ struct scheduler_struct *sched_opaque = (struct scheduler_struct *)opaque;
+
+ DPRINTF("Event @%luns\n",
+ sched_opaque->event_list[(sched_opaque->next_ts)-1].ts);
+ sched_opaque->cb(sched_opaque->opaque,
+ sched_opaque->event_list[(sched_opaque->next_ts)-1].ts,
+ sched_opaque->event_list[(sched_opaque->next_ts)-1].param);
+
+ if (sched_opaque->event_list[sched_opaque->next_ts].ts != 0) {
+ timer_mod(sched_opaque->timer,
+ sched_opaque->start_ts + \
+ sched_opaque->event_list[(sched_opaque->next_ts)++].ts);
+ }
+}
+
+int scenario_scheduler_start(scenario_scheduler_cb_t *cb,
+ void *opaque,
+ const char *file)
+{
+ static struct scheduler_struct sched_opaque;
+ static bool started;
+ int parsed;
+
+ if (!started) {
+ DPRINTF("Starting\n");
+ started = true;
+ parsed = parse_vcd(file, sched_opaque.event_list, MAX_TS);
+ if (parsed > 0) {
+ sched_opaque.cb = cb;
+ sched_opaque.opaque = opaque;
+ sched_opaque.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ scheduler_cb, &sched_opaque);
+ sched_opaque.start_ts = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ sched_opaque.next_ts = 0;
+ timer_mod(sched_opaque.timer,
+ sched_opaque.start_ts + \
+ sched_opaque.event_list[(sched_opaque.next_ts)++].ts);
+ DPRINTF("Started\n");
+
+ return 1;
+ } else if (parsed == 0) {
+ EPRINTF("error while reading file: no events found\n");
+ return 0;
+ } else {
+ EPRINTF("error while reading file: \"%s\" not found\n", file);
+ return -1;
+ }
+ } else {
+ return 2;
+ }
+}
--
2.5.1
- [Qemu-devel] [PATCH 0/7] Qemu scenario engine, Victor CLEMENT, 2015/09/11
- [Qemu-devel] [PATCH 1/7] configure: add --enable-scenario-engine option, Victor CLEMENT, 2015/09/11
- [Qemu-devel] [PATCH 4/7] scenario-engine: add utilities, Victor CLEMENT, 2015/09/11
- [Qemu-devel] [PATCH 2/7] gpio-pl061: add a scenario engine interaction API, Victor CLEMENT, 2015/09/11
- [Qemu-devel] [PATCH 3/7] chardev: add a scenario engine backend, Victor CLEMENT, 2015/09/11
- [Qemu-devel] [PATCH 7/7] scenario engine: add a Qemu option to start it, Victor CLEMENT, 2015/09/11
- [Qemu-devel] [PATCH 6/7] scenario engine: provide a scenario file template, Victor CLEMENT, 2015/09/11
- [Qemu-devel] [PATCH 5/7] scenario-engine: add a time based event scheduler,
Victor CLEMENT <=
- Re: [Qemu-devel] [PATCH 0/7] Qemu scenario engine, Andreas Färber, 2015/09/11