qemu-devel
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]