[LTP] [PATCH 3/6] api/state_machine: Add validating state machines
Richard Palethorpe
rpalethorpe@suse.com
Tue Sep 27 18:14:05 CEST 2022
Allows creating state machines where the state transitions are
validated. Also one can assert which states a line of code expects to
be executed in.
This is useful for verifying implicit or explicit state machines used
to process I/O events or data.
When a state violation is found a trace of previous state transitions
is printed.
Signed-off-by: Richard Palethorpe <rpalethorpe@suse.com>
---
include/tst_state_machine.h | 50 +++++++++++++++++++
lib/tst_state_machine.c | 98 +++++++++++++++++++++++++++++++++++++
2 files changed, 148 insertions(+)
create mode 100644 include/tst_state_machine.h
create mode 100644 lib/tst_state_machine.c
diff --git a/include/tst_state_machine.h b/include/tst_state_machine.h
new file mode 100644
index 000000000..2e86535c6
--- /dev/null
+++ b/include/tst_state_machine.h
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 SUSE LLC <rpalethorpe@suse.com>
+ */
+
+#include "inttypes.h"
+
+#ifndef TST_STATE_MACHINE_H
+#define TST_STATE_MACHINE_H
+
+#define TST_STATE_ANY (~(uint64_t)0)
+
+struct tst_state_matrix {
+ char *names[64];
+ uint64_t states[64];
+};
+
+struct tst_state_trace {
+ const char *file;
+ int line;
+ unsigned from;
+ unsigned to;
+};
+
+struct tst_state_mach {
+ const struct tst_state_matrix *mat;
+
+ unsigned top;
+ struct tst_state_trace ring[8];
+};
+
+#define TST_STATE_SET(mach, to) \
+ tst_state_set(__FILE__, __LINE__, mach, to)
+
+void tst_state_set(const char *const file, const int lineno,
+ struct tst_state_mach *mach, unsigned to);
+
+#define TST_STATE_EXP(mach, mask) \
+ tst_state_exp(__FILE__, __LINE__, mach, mask)
+
+void tst_state_exp(const char *const file, const int lineno,
+ struct tst_state_mach *mach, uint64_t mask);
+
+#define TST_STATE_GET(mach, mask) \
+ tst_state_get(__FILE__, __LINE__, mach, mask)
+
+unsigned tst_state_get(const char *const file, const int lineno,
+ struct tst_state_mach *mach, uint64_t mask);
+
+#endif
diff --git a/lib/tst_state_machine.c b/lib/tst_state_machine.c
new file mode 100644
index 000000000..cb8ed79c4
--- /dev/null
+++ b/lib/tst_state_machine.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 SUSE LLC <rpalethorpe@suse.com>
+ */
+
+#define _GNU_SOURCE
+#define TST_NO_DEFAULT_MAIN
+
+#include <unistd.h>
+#include "stdio.h"
+
+#include "tst_test.h"
+#include "tst_state_machine.h"
+
+static const char *state_trace(struct tst_state_mach *mach)
+{
+ static char buf[4096];
+ char *const *const names = mach->mat->names;
+ size_t off = 1;
+ unsigned c = 0, i;
+
+ buf[0] = '\n';
+
+ for (i = mach->top; c < 8; c++) {
+ const struct tst_state_trace *t = mach->ring + i;
+
+ if (!t->file)
+ break;
+
+ if (off >= sizeof(buf))
+ break;
+
+ off += snprintf(buf + off,
+ sizeof(buf) - off - 1,
+ "\t%s:%d %s (%u) -> %s (%u)\n",
+ t->file, t->line,
+ names[t->from], t->from,
+ names[t->to], t->to);
+
+ if (!i)
+ i = 7;
+ else
+ i--;
+ }
+
+ return buf;
+}
+
+static void state_trace_set(const char *const file, const int lineno,
+ struct tst_state_trace *trace, unsigned from, unsigned to)
+{
+ trace->file = file;
+ trace->line = lineno;
+ trace->from = from;
+ trace->to = to;
+}
+
+void tst_state_set(const char *const file, const int lineno,
+ struct tst_state_mach *mach, unsigned to)
+{
+ char *const *const names = mach->mat->names;
+ const unsigned cur = mach->ring[mach->top].to;
+
+ if (cur > 63)
+ tst_brk_(file, lineno, TBROK, "Attempting to transition from an invalid state: %u: %s", cur, state_trace(mach));
+
+ if (to > 63)
+ tst_brk_(file, lineno, TBROK, "Attempting to transition to invalid state: %u: %s", to, state_trace(mach));
+
+ if (!(mach->mat->states[cur] & (1 << to)))
+ tst_brk_(file, lineno, TBROK, "Invalid transition: %s (%u) -> %s (%u): %s", names[cur], cur, names[to], to, state_trace(mach));
+
+ if (++(mach->top) == 8)
+ mach->top = 0;
+
+ state_trace_set(file, lineno, &mach->ring[mach->top], cur, to);
+}
+
+unsigned tst_state_get(const char *const file, const int lineno,
+ struct tst_state_mach *mach, uint64_t mask)
+{
+ char *const *const names = mach->mat->names;
+ const unsigned cur = mach->ring[mach->top].to;
+
+ if (mask & (1 << cur))
+ return cur;
+
+ tst_brk_(file, lineno, TBROK, "Should not reach here while in state: %s (%u): %s",
+ names[cur], cur, state_trace(mach));
+
+ return cur;
+}
+
+void tst_state_exp(const char *const file, const int lineno,
+ struct tst_state_mach *mach, uint64_t mask)
+{
+ tst_state_get(file, lineno, mach, mask);
+}
--
2.36.1
More information about the ltp
mailing list