Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

verification/rvgen: Add support for linear temporal logic

Add support for generating RV monitors from linear temporal logic, similar
to the generation of deterministic automaton monitors.

Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Gabriele Monaco <gmonaco@redhat.com>
Link: https://lore.kernel.org/f3c63b363ff9c5af3302ba2b5d92a26a98700eaf.1751634289.git.namcao@linutronix.de
Signed-off-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>

authored by

Nam Cao and committed by
Steven Rostedt (Google)
97ffa4ce cce86e03

+915 -1
+3
tools/verification/rvgen/.gitignore
··· 1 + __pycache__/ 2 + parser.out 3 + parsetab.py
+2
tools/verification/rvgen/Makefile
··· 21 21 $(INSTALL) rvgen/dot2k.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/dot2k.py 22 22 $(INSTALL) rvgen/container.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/container.py 23 23 $(INSTALL) rvgen/generator.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/generator.py 24 + $(INSTALL) rvgen/ltl2ba.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/ltl2ba.py 25 + $(INSTALL) rvgen/ltl2k.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/ltl2k.py 24 26 $(INSTALL) __main__.py -D -m 755 $(DESTDIR)$(bindir)/rvgen 25 27 cp -rp rvgen/templates $(DESTDIR)$(PYLIB)/rvgen/
+2 -1
tools/verification/rvgen/__main__.py
··· 12 12 from rvgen.dot2k import dot2k 13 13 from rvgen.generator import Monitor 14 14 from rvgen.container import Container 15 + from rvgen.ltl2k import ltl2k 15 16 import argparse 16 17 import sys 17 18 ··· 45 44 if params.monitor_class == "da": 46 45 monitor = dot2k(params.spec, params.monitor_type, vars(params)) 47 46 elif params.monitor_class == "ltl": 48 - raise NotImplementedError 47 + monitor = ltl2k(params.spec, params.monitor_type, vars(params)) 49 48 else: 50 49 print("Unknown monitor class:", params.monitor_class) 51 50 sys.exit(1)
+540
tools/verification/rvgen/rvgen/ltl2ba.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0-only 3 + # 4 + # Implementation based on 5 + # Gerth, R., Peled, D., Vardi, M.Y., Wolper, P. (1996). 6 + # Simple On-the-fly Automatic Verification of Linear Temporal Logic. 7 + # https://doi.org/10.1007/978-0-387-34892-6_1 8 + # With extra optimizations 9 + 10 + from ply.lex import lex 11 + from ply.yacc import yacc 12 + 13 + # Grammar: 14 + # ltl ::= opd | ( ltl ) | ltl binop ltl | unop ltl 15 + # 16 + # Operands (opd): 17 + # true, false, user-defined names 18 + # 19 + # Unary Operators (unop): 20 + # always 21 + # eventually 22 + # not 23 + # 24 + # Binary Operators (binop): 25 + # until 26 + # and 27 + # or 28 + # imply 29 + # equivalent 30 + 31 + tokens = ( 32 + 'AND', 33 + 'OR', 34 + 'IMPLY', 35 + 'UNTIL', 36 + 'ALWAYS', 37 + 'EVENTUALLY', 38 + 'VARIABLE', 39 + 'LITERAL', 40 + 'NOT', 41 + 'LPAREN', 42 + 'RPAREN', 43 + 'ASSIGN', 44 + ) 45 + 46 + t_AND = r'and' 47 + t_OR = r'or' 48 + t_IMPLY = r'imply' 49 + t_UNTIL = r'until' 50 + t_ALWAYS = r'always' 51 + t_EVENTUALLY = r'eventually' 52 + t_VARIABLE = r'[A-Z_0-9]+' 53 + t_LITERAL = r'true|false' 54 + t_NOT = r'not' 55 + t_LPAREN = r'\(' 56 + t_RPAREN = r'\)' 57 + t_ASSIGN = r'=' 58 + t_ignore_COMMENT = r'\#.*' 59 + t_ignore = ' \t\n' 60 + 61 + def t_error(t): 62 + raise ValueError(f"Illegal character '{t.value[0]}'") 63 + 64 + lexer = lex() 65 + 66 + class GraphNode: 67 + uid = 0 68 + 69 + def __init__(self, incoming: set['GraphNode'], new, old, _next): 70 + self.init = False 71 + self.outgoing = set() 72 + self.labels = set() 73 + self.incoming = incoming.copy() 74 + self.new = new.copy() 75 + self.old = old.copy() 76 + self.next = _next.copy() 77 + self.id = GraphNode.uid 78 + GraphNode.uid += 1 79 + 80 + def expand(self, node_set): 81 + if not self.new: 82 + for nd in node_set: 83 + if nd.old == self.old and nd.next == self.next: 84 + nd.incoming |= self.incoming 85 + return node_set 86 + 87 + new_current_node = GraphNode({self}, self.next, set(), set()) 88 + return new_current_node.expand({self} | node_set) 89 + n = self.new.pop() 90 + return n.expand(self, node_set) 91 + 92 + def __lt__(self, other): 93 + return self.id < other.id 94 + 95 + class ASTNode: 96 + uid = 1 97 + 98 + def __init__(self, op): 99 + self.op = op 100 + self.id = ASTNode.uid 101 + ASTNode.uid += 1 102 + 103 + def __hash__(self): 104 + return hash(self.op) 105 + 106 + def __eq__(self, other): 107 + return self is other 108 + 109 + def __iter__(self): 110 + yield self 111 + yield from self.op 112 + 113 + def negate(self): 114 + self.op = self.op.negate() 115 + return self 116 + 117 + def expand(self, node, node_set): 118 + return self.op.expand(self, node, node_set) 119 + 120 + def __str__(self): 121 + if isinstance(self.op, Literal): 122 + return str(self.op.value) 123 + if isinstance(self.op, Variable): 124 + return self.op.name.lower() 125 + return "val" + str(self.id) 126 + 127 + def normalize(self): 128 + # Get rid of: 129 + # - ALWAYS 130 + # - EVENTUALLY 131 + # - IMPLY 132 + # And move all the NOT to be inside 133 + self.op = self.op.normalize() 134 + return self 135 + 136 + class BinaryOp: 137 + op_str = "not_supported" 138 + 139 + def __init__(self, left: ASTNode, right: ASTNode): 140 + self.left = left 141 + self.right = right 142 + 143 + def __hash__(self): 144 + return hash((self.left, self.right)) 145 + 146 + def __iter__(self): 147 + yield from self.left 148 + yield from self.right 149 + 150 + def normalize(self): 151 + raise NotImplementedError 152 + 153 + def negate(self): 154 + raise NotImplementedError 155 + 156 + def _is_temporal(self): 157 + raise NotImplementedError 158 + 159 + def is_temporal(self): 160 + if self.left.op.is_temporal(): 161 + return True 162 + if self.right.op.is_temporal(): 163 + return True 164 + return self._is_temporal() 165 + 166 + @staticmethod 167 + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: 168 + raise NotImplementedError 169 + 170 + class AndOp(BinaryOp): 171 + op_str = '&&' 172 + 173 + def normalize(self): 174 + return self 175 + 176 + def negate(self): 177 + return OrOp(self.left.negate(), self.right.negate()) 178 + 179 + def _is_temporal(self): 180 + return False 181 + 182 + @staticmethod 183 + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: 184 + if not n.op.is_temporal(): 185 + node.old.add(n) 186 + return node.expand(node_set) 187 + 188 + tmp = GraphNode(node.incoming, 189 + node.new | ({n.op.left, n.op.right} - node.old), 190 + node.old | {n}, 191 + node.next) 192 + return tmp.expand(node_set) 193 + 194 + class OrOp(BinaryOp): 195 + op_str = '||' 196 + 197 + def normalize(self): 198 + return self 199 + 200 + def negate(self): 201 + return AndOp(self.left.negate(), self.right.negate()) 202 + 203 + def _is_temporal(self): 204 + return False 205 + 206 + @staticmethod 207 + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: 208 + if not n.op.is_temporal(): 209 + node.old |= {n} 210 + return node.expand(node_set) 211 + 212 + node1 = GraphNode(node.incoming, 213 + node.new | ({n.op.left} - node.old), 214 + node.old | {n}, 215 + node.next) 216 + node2 = GraphNode(node.incoming, 217 + node.new | ({n.op.right} - node.old), 218 + node.old | {n}, 219 + node.next) 220 + return node2.expand(node1.expand(node_set)) 221 + 222 + class UntilOp(BinaryOp): 223 + def normalize(self): 224 + return self 225 + 226 + def negate(self): 227 + return VOp(self.left.negate(), self.right.negate()) 228 + 229 + def _is_temporal(self): 230 + return True 231 + 232 + @staticmethod 233 + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: 234 + node1 = GraphNode(node.incoming, 235 + node.new | ({n.op.left} - node.old), 236 + node.old | {n}, 237 + node.next | {n}) 238 + node2 = GraphNode(node.incoming, 239 + node.new | ({n.op.right} - node.old), 240 + node.old | {n}, 241 + node.next) 242 + return node2.expand(node1.expand(node_set)) 243 + 244 + class VOp(BinaryOp): 245 + def normalize(self): 246 + return self 247 + 248 + def negate(self): 249 + return UntilOp(self.left.negate(), self.right.negate()) 250 + 251 + def _is_temporal(self): 252 + return True 253 + 254 + @staticmethod 255 + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: 256 + node1 = GraphNode(node.incoming, 257 + node.new | ({n.op.right} - node.old), 258 + node.old | {n}, 259 + node.next | {n}) 260 + node2 = GraphNode(node.incoming, 261 + node.new | ({n.op.left, n.op.right} - node.old), 262 + node.old | {n}, 263 + node.next) 264 + return node2.expand(node1.expand(node_set)) 265 + 266 + class ImplyOp(BinaryOp): 267 + def normalize(self): 268 + # P -> Q === !P | Q 269 + return OrOp(self.left.negate(), self.right) 270 + 271 + def _is_temporal(self): 272 + return False 273 + 274 + def negate(self): 275 + # !(P -> Q) === !(!P | Q) === P & !Q 276 + return AndOp(self.left, self.right.negate()) 277 + 278 + class UnaryOp: 279 + def __init__(self, child: ASTNode): 280 + self.child = child 281 + 282 + def __iter__(self): 283 + yield from self.child 284 + 285 + def __hash__(self): 286 + return hash(self.child) 287 + 288 + def normalize(self): 289 + raise NotImplementedError 290 + 291 + def _is_temporal(self): 292 + raise NotImplementedError 293 + 294 + def is_temporal(self): 295 + if self.child.op.is_temporal(): 296 + return True 297 + return self._is_temporal() 298 + 299 + def negate(self): 300 + raise NotImplementedError 301 + 302 + class EventuallyOp(UnaryOp): 303 + def __str__(self): 304 + return "eventually " + str(self.child) 305 + 306 + def normalize(self): 307 + # <>F == true U F 308 + return UntilOp(ASTNode(Literal(True)), self.child) 309 + 310 + def _is_temporal(self): 311 + return True 312 + 313 + def negate(self): 314 + # !<>F == [](!F) 315 + return AlwaysOp(self.child.negate()).normalize() 316 + 317 + class AlwaysOp(UnaryOp): 318 + def normalize(self): 319 + # []F === !(true U !F) == false V F 320 + new = ASTNode(Literal(False)) 321 + return VOp(new, self.child) 322 + 323 + def _is_temporal(self): 324 + return True 325 + 326 + def negate(self): 327 + # ![]F == <>(!F) 328 + return EventuallyOp(self.child.negate()).normalize() 329 + 330 + class NotOp(UnaryOp): 331 + def __str__(self): 332 + return "!" + str(self.child) 333 + 334 + def normalize(self): 335 + return self.child.op.negate() 336 + 337 + def negate(self): 338 + return self.child.op 339 + 340 + def _is_temporal(self): 341 + return False 342 + 343 + @staticmethod 344 + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: 345 + for f in node.old: 346 + if n.op.child is f: 347 + return node_set 348 + node.old |= {n} 349 + return node.expand(node_set) 350 + 351 + class Variable: 352 + def __init__(self, name: str): 353 + self.name = name 354 + 355 + def __hash__(self): 356 + return hash(self.name) 357 + 358 + def __iter__(self): 359 + yield from () 360 + 361 + def negate(self): 362 + new = ASTNode(self) 363 + return NotOp(new) 364 + 365 + def normalize(self): 366 + return self 367 + 368 + def is_temporal(self): 369 + return False 370 + 371 + @staticmethod 372 + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: 373 + for f in node.old: 374 + if isinstance(f, NotOp) and f.op.child is n: 375 + return node_set 376 + node.old |= {n} 377 + return node.expand(node_set) 378 + 379 + class Literal: 380 + def __init__(self, value: bool): 381 + self.value = value 382 + 383 + def __iter__(self): 384 + yield from () 385 + 386 + def __hash__(self): 387 + return hash(self.value) 388 + 389 + def __str__(self): 390 + if self.value: 391 + return "true" 392 + return "false" 393 + 394 + def negate(self): 395 + self.value = not self.value 396 + return self 397 + 398 + def normalize(self): 399 + return self 400 + 401 + def is_temporal(self): 402 + return False 403 + 404 + @staticmethod 405 + def expand(n: ASTNode, node: GraphNode, node_set) -> set[GraphNode]: 406 + if not n.op.value: 407 + return node_set 408 + node.old |= {n} 409 + return node.expand(node_set) 410 + 411 + def p_spec(p): 412 + ''' 413 + spec : assign 414 + | assign spec 415 + ''' 416 + if len(p) == 3: 417 + p[2].append(p[1]) 418 + p[0] = p[2] 419 + else: 420 + p[0] = [p[1]] 421 + 422 + def p_assign(p): 423 + ''' 424 + assign : VARIABLE ASSIGN ltl 425 + ''' 426 + p[0] = (p[1], p[3]) 427 + 428 + def p_ltl(p): 429 + ''' 430 + ltl : opd 431 + | binop 432 + | unop 433 + ''' 434 + p[0] = p[1] 435 + 436 + def p_opd(p): 437 + ''' 438 + opd : VARIABLE 439 + | LITERAL 440 + | LPAREN ltl RPAREN 441 + ''' 442 + if p[1] == "true": 443 + p[0] = ASTNode(Literal(True)) 444 + elif p[1] == "false": 445 + p[0] = ASTNode(Literal(False)) 446 + elif p[1] == '(': 447 + p[0] = p[2] 448 + else: 449 + p[0] = ASTNode(Variable(p[1])) 450 + 451 + def p_unop(p): 452 + ''' 453 + unop : ALWAYS ltl 454 + | EVENTUALLY ltl 455 + | NOT ltl 456 + ''' 457 + if p[1] == "always": 458 + op = AlwaysOp(p[2]) 459 + elif p[1] == "eventually": 460 + op = EventuallyOp(p[2]) 461 + elif p[1] == "not": 462 + op = NotOp(p[2]) 463 + else: 464 + raise ValueError(f"Invalid unary operator {p[1]}") 465 + 466 + p[0] = ASTNode(op) 467 + 468 + def p_binop(p): 469 + ''' 470 + binop : opd UNTIL ltl 471 + | opd AND ltl 472 + | opd OR ltl 473 + | opd IMPLY ltl 474 + ''' 475 + if p[2] == "and": 476 + op = AndOp(p[1], p[3]) 477 + elif p[2] == "until": 478 + op = UntilOp(p[1], p[3]) 479 + elif p[2] == "or": 480 + op = OrOp(p[1], p[3]) 481 + elif p[2] == "imply": 482 + op = ImplyOp(p[1], p[3]) 483 + else: 484 + raise ValueError(f"Invalid binary operator {p[2]}") 485 + 486 + p[0] = ASTNode(op) 487 + 488 + parser = yacc() 489 + 490 + def parse_ltl(s: str) -> ASTNode: 491 + spec = parser.parse(s) 492 + 493 + rule = None 494 + subexpr = {} 495 + 496 + for assign in spec: 497 + if assign[0] == "RULE": 498 + rule = assign[1] 499 + else: 500 + subexpr[assign[0]] = assign[1] 501 + 502 + if rule is None: 503 + raise ValueError("Please define your specification in the \"RULE = <LTL spec>\" format") 504 + 505 + for node in rule: 506 + if not isinstance(node.op, Variable): 507 + continue 508 + replace = subexpr.get(node.op.name) 509 + if replace is not None: 510 + node.op = replace.op 511 + 512 + return rule 513 + 514 + def create_graph(s: str): 515 + atoms = set() 516 + 517 + ltl = parse_ltl(s) 518 + for c in ltl: 519 + c.normalize() 520 + if isinstance(c.op, Variable): 521 + atoms.add(c.op.name) 522 + 523 + init = GraphNode(set(), set(), set(), set()) 524 + head = GraphNode({init}, {ltl}, set(), set()) 525 + graph = sorted(head.expand(set())) 526 + 527 + for i, node in enumerate(graph): 528 + # The id assignment during graph generation has gaps. Reassign them 529 + node.id = i 530 + 531 + for incoming in node.incoming: 532 + if incoming is init: 533 + node.init = True 534 + else: 535 + incoming.outgoing.add(node) 536 + for o in node.old: 537 + if not o.op.is_temporal(): 538 + node.labels.add(str(o)) 539 + 540 + return sorted(atoms), graph, ltl
+252
tools/verification/rvgen/rvgen/ltl2k.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0-only 3 + 4 + from pathlib import Path 5 + from . import generator 6 + from . import ltl2ba 7 + 8 + COLUMN_LIMIT = 100 9 + 10 + def line_len(line: str) -> int: 11 + tabs = line.count('\t') 12 + return tabs * 7 + len(line) 13 + 14 + def break_long_line(line: str, indent='') -> list[str]: 15 + result = [] 16 + while line_len(line) > COLUMN_LIMIT: 17 + i = line[:COLUMN_LIMIT - line_len(line)].rfind(' ') 18 + result.append(line[:i]) 19 + line = indent + line[i + 1:] 20 + if line: 21 + result.append(line) 22 + return result 23 + 24 + def build_condition_string(node: ltl2ba.GraphNode): 25 + if not node.labels: 26 + return "(true)" 27 + 28 + result = "(" 29 + 30 + first = True 31 + for label in sorted(node.labels): 32 + if not first: 33 + result += " && " 34 + result += label 35 + first = False 36 + 37 + result += ")" 38 + 39 + return result 40 + 41 + def abbreviate_atoms(atoms: list[str]) -> list[str]: 42 + def shorten(s: str) -> str: 43 + skip = ["is", "by", "or", "and"] 44 + return '_'.join([x[:2] for x in s.lower().split('_') if x not in skip]) 45 + 46 + abbrs = [] 47 + for atom in atoms: 48 + for i in range(len(atom), -1, -1): 49 + if sum(a.startswith(atom[:i]) for a in atoms) > 1: 50 + break 51 + share = atom[:i] 52 + unique = atom[i:] 53 + abbrs.append((shorten(share) + shorten(unique))) 54 + return abbrs 55 + 56 + class ltl2k(generator.Monitor): 57 + template_dir = "ltl2k" 58 + 59 + def __init__(self, file_path, MonitorType, extra_params={}): 60 + if MonitorType != "per_task": 61 + raise NotImplementedError("Only per_task monitor is supported for LTL") 62 + super().__init__(extra_params) 63 + with open(file_path) as f: 64 + self.atoms, self.ba, self.ltl = ltl2ba.create_graph(f.read()) 65 + self.atoms_abbr = abbreviate_atoms(self.atoms) 66 + self.name = extra_params.get("model_name") 67 + if not self.name: 68 + self.name = Path(file_path).stem 69 + 70 + def _fill_states(self) -> str: 71 + buf = [ 72 + "enum ltl_buchi_state {", 73 + ] 74 + 75 + for node in self.ba: 76 + buf.append("\tS%i," % node.id) 77 + buf.append("\tRV_NUM_BA_STATES") 78 + buf.append("};") 79 + buf.append("static_assert(RV_NUM_BA_STATES <= RV_MAX_BA_STATES);") 80 + return buf 81 + 82 + def _fill_atoms(self): 83 + buf = ["enum ltl_atom {"] 84 + for a in sorted(self.atoms): 85 + buf.append("\tLTL_%s," % a) 86 + buf.append("\tLTL_NUM_ATOM") 87 + buf.append("};") 88 + buf.append("static_assert(LTL_NUM_ATOM <= RV_MAX_LTL_ATOM);") 89 + return buf 90 + 91 + def _fill_atoms_to_string(self): 92 + buf = [ 93 + "static const char *ltl_atom_str(enum ltl_atom atom)", 94 + "{", 95 + "\tstatic const char *const names[] = {" 96 + ] 97 + 98 + for name in self.atoms_abbr: 99 + buf.append("\t\t\"%s\"," % name) 100 + 101 + buf.extend([ 102 + "\t};", 103 + "", 104 + "\treturn names[atom];", 105 + "}" 106 + ]) 107 + return buf 108 + 109 + def _fill_atom_values(self): 110 + buf = [] 111 + for node in self.ltl: 112 + if node.op.is_temporal(): 113 + continue 114 + 115 + if isinstance(node.op, ltl2ba.Variable): 116 + buf.append("\tbool %s = test_bit(LTL_%s, mon->atoms);" % (node, node.op.name)) 117 + elif isinstance(node.op, ltl2ba.AndOp): 118 + buf.append("\tbool %s = %s && %s;" % (node, node.op.left, node.op.right)) 119 + elif isinstance(node.op, ltl2ba.OrOp): 120 + buf.append("\tbool %s = %s || %s;" % (node, node.op.left, node.op.right)) 121 + elif isinstance(node.op, ltl2ba.NotOp): 122 + buf.append("\tbool %s = !%s;" % (node, node.op.child)) 123 + buf.reverse() 124 + 125 + buf2 = [] 126 + for line in buf: 127 + buf2.extend(break_long_line(line, "\t ")) 128 + return buf2 129 + 130 + def _fill_transitions(self): 131 + buf = [ 132 + "static void", 133 + "ltl_possible_next_states(struct ltl_monitor *mon, unsigned int state, unsigned long *next)", 134 + "{" 135 + ] 136 + buf.extend(self._fill_atom_values()) 137 + buf.extend([ 138 + "", 139 + "\tswitch (state) {" 140 + ]) 141 + 142 + for node in self.ba: 143 + buf.append("\tcase S%i:" % node.id) 144 + 145 + for o in sorted(node.outgoing): 146 + line = "\t\tif " 147 + indent = "\t\t " 148 + 149 + line += build_condition_string(o) 150 + lines = break_long_line(line, indent) 151 + buf.extend(lines) 152 + 153 + buf.append("\t\t\t__set_bit(S%i, next);" % o.id) 154 + buf.append("\t\tbreak;") 155 + buf.extend([ 156 + "\t}", 157 + "}" 158 + ]) 159 + 160 + return buf 161 + 162 + def _fill_start(self): 163 + buf = [ 164 + "static void ltl_start(struct task_struct *task, struct ltl_monitor *mon)", 165 + "{" 166 + ] 167 + buf.extend(self._fill_atom_values()) 168 + buf.append("") 169 + 170 + for node in self.ba: 171 + if not node.init: 172 + continue 173 + 174 + line = "\tif " 175 + indent = "\t " 176 + 177 + line += build_condition_string(node) 178 + lines = break_long_line(line, indent) 179 + buf.extend(lines) 180 + 181 + buf.append("\t\t__set_bit(S%i, mon->states);" % node.id) 182 + buf.append("}") 183 + return buf 184 + 185 + def fill_tracepoint_handlers_skel(self): 186 + buff = [] 187 + buff.append("static void handle_example_event(void *data, /* XXX: fill header */)") 188 + buff.append("{") 189 + buff.append("\tltl_atom_update(task, LTL_%s, true/false);" % self.atoms[0]) 190 + buff.append("}") 191 + buff.append("") 192 + return '\n'.join(buff) 193 + 194 + def fill_tracepoint_attach_probe(self): 195 + return "\trv_attach_trace_probe(\"%s\", /* XXX: tracepoint */, handle_example_event);" \ 196 + % self.name 197 + 198 + def fill_tracepoint_detach_helper(self): 199 + return "\trv_detach_trace_probe(\"%s\", /* XXX: tracepoint */, handle_sample_event);" \ 200 + % self.name 201 + 202 + def fill_atoms_init(self): 203 + buff = [] 204 + for a in self.atoms: 205 + buff.append("\tltl_atom_set(mon, LTL_%s, true/false);" % a) 206 + return '\n'.join(buff) 207 + 208 + def fill_model_h(self): 209 + buf = [ 210 + "/* SPDX-License-Identifier: GPL-2.0 */", 211 + "", 212 + "/*", 213 + " * C implementation of Buchi automaton, automatically generated by", 214 + " * tools/verification/rvgen from the linear temporal logic specification.", 215 + " * For further information, see kernel documentation:", 216 + " * Documentation/trace/rv/linear_temporal_logic.rst", 217 + " */", 218 + "", 219 + "#include <linux/rv.h>", 220 + "", 221 + "#define MONITOR_NAME " + self.name, 222 + "" 223 + ] 224 + 225 + buf.extend(self._fill_atoms()) 226 + buf.append('') 227 + 228 + buf.extend(self._fill_atoms_to_string()) 229 + buf.append('') 230 + 231 + buf.extend(self._fill_states()) 232 + buf.append('') 233 + 234 + buf.extend(self._fill_start()) 235 + buf.append('') 236 + 237 + buf.extend(self._fill_transitions()) 238 + buf.append('') 239 + 240 + return '\n'.join(buf) 241 + 242 + def fill_monitor_class_type(self): 243 + return "LTL_MON_EVENTS_ID" 244 + 245 + def fill_monitor_class(self): 246 + return "ltl_monitor_id" 247 + 248 + def fill_main_c(self): 249 + main_c = super().fill_main_c() 250 + main_c = main_c.replace("%%ATOMS_INIT%%", self.fill_atoms_init()) 251 + 252 + return main_c
+102
tools/verification/rvgen/rvgen/templates/ltl2k/main.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/ftrace.h> 3 + #include <linux/tracepoint.h> 4 + #include <linux/kernel.h> 5 + #include <linux/module.h> 6 + #include <linux/init.h> 7 + #include <linux/rv.h> 8 + #include <rv/instrumentation.h> 9 + 10 + #define MODULE_NAME "%%MODEL_NAME%%" 11 + 12 + /* 13 + * XXX: include required tracepoint headers, e.g., 14 + * #include <trace/events/sched.h> 15 + */ 16 + #include <rv_trace.h> 17 + %%INCLUDE_PARENT%% 18 + 19 + /* 20 + * This is the self-generated part of the monitor. Generally, there is no need 21 + * to touch this section. 22 + */ 23 + #include "%%MODEL_NAME%%.h" 24 + #include <rv/ltl_monitor.h> 25 + 26 + static void ltl_atoms_fetch(struct task_struct *task, struct ltl_monitor *mon) 27 + { 28 + /* 29 + * This is called everytime the Buchi automaton is triggered. 30 + * 31 + * This function could be used to fetch the atomic propositions which 32 + * are expensive to trace. It is possible only if the atomic proposition 33 + * does not need to be updated at precise time. 34 + * 35 + * It is recommended to use tracepoints and ltl_atom_update() instead. 36 + */ 37 + } 38 + 39 + static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation) 40 + { 41 + /* 42 + * This should initialize as many atomic propositions as possible. 43 + * 44 + * @task_creation indicates whether the task is being created. This is 45 + * false if the task is already running before the monitor is enabled. 46 + */ 47 + %%ATOMS_INIT%% 48 + } 49 + 50 + /* 51 + * This is the instrumentation part of the monitor. 52 + * 53 + * This is the section where manual work is required. Here the kernel events 54 + * are translated into model's event. 55 + */ 56 + %%TRACEPOINT_HANDLERS_SKEL%% 57 + static int enable_%%MODEL_NAME%%(void) 58 + { 59 + int retval; 60 + 61 + retval = ltl_monitor_init(); 62 + if (retval) 63 + return retval; 64 + 65 + %%TRACEPOINT_ATTACH%% 66 + 67 + return 0; 68 + } 69 + 70 + static void disable_%%MODEL_NAME%%(void) 71 + { 72 + %%TRACEPOINT_DETACH%% 73 + 74 + ltl_monitor_destroy(); 75 + } 76 + 77 + /* 78 + * This is the monitor register section. 79 + */ 80 + static struct rv_monitor rv_%%MODEL_NAME%% = { 81 + .name = "%%MODEL_NAME%%", 82 + .description = "%%DESCRIPTION%%", 83 + .enable = enable_%%MODEL_NAME%%, 84 + .disable = disable_%%MODEL_NAME%%, 85 + }; 86 + 87 + static int __init register_%%MODEL_NAME%%(void) 88 + { 89 + return rv_register_monitor(&rv_%%MODEL_NAME%%, %%PARENT%%); 90 + } 91 + 92 + static void __exit unregister_%%MODEL_NAME%%(void) 93 + { 94 + rv_unregister_monitor(&rv_%%MODEL_NAME%%); 95 + } 96 + 97 + module_init(register_%%MODEL_NAME%%); 98 + module_exit(unregister_%%MODEL_NAME%%); 99 + 100 + MODULE_LICENSE("GPL"); 101 + MODULE_AUTHOR(/* TODO */); 102 + MODULE_DESCRIPTION("%%MODEL_NAME%%: %%DESCRIPTION%%");
+14
tools/verification/rvgen/rvgen/templates/ltl2k/trace.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + /* 4 + * Snippet to be included in rv_trace.h 5 + */ 6 + 7 + #ifdef CONFIG_RV_MON_%%MODEL_NAME_UP%% 8 + DEFINE_EVENT(event_%%MONITOR_CLASS%%, event_%%MODEL_NAME%%, 9 + TP_PROTO(struct task_struct *task, char *states, char *atoms, char *next), 10 + TP_ARGS(task, states, atoms, next)); 11 + DEFINE_EVENT(error_%%MONITOR_CLASS%%, error_%%MODEL_NAME%%, 12 + TP_PROTO(struct task_struct *task), 13 + TP_ARGS(task)); 14 + #endif /* CONFIG_RV_MON_%%MODEL_NAME_UP%% */