xref: /openbmc/linux/tools/verification/dot2/automata.py (revision 278002edb19bce2c628fafb0af936e77000f3a5b)
1e3c9fc78SDaniel Bristot de Oliveira#!/usr/bin/env python3
2e3c9fc78SDaniel Bristot de Oliveira# SPDX-License-Identifier: GPL-2.0-only
3e3c9fc78SDaniel Bristot de Oliveira#
4e3c9fc78SDaniel Bristot de Oliveira# Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org>
5e3c9fc78SDaniel Bristot de Oliveira#
6e3c9fc78SDaniel Bristot de Oliveira# Automata object: parse an automata in dot file digraph format into a python object
74041b9bbSDaniel Bristot de Oliveira#
84041b9bbSDaniel Bristot de Oliveira# For further information, see:
94041b9bbSDaniel Bristot de Oliveira#   Documentation/trace/rv/deterministic_automata.rst
10e3c9fc78SDaniel Bristot de Oliveira
11e3c9fc78SDaniel Bristot de Oliveiraimport ntpath
12e3c9fc78SDaniel Bristot de Oliveira
13e3c9fc78SDaniel Bristot de Oliveiraclass Automata:
14e3c9fc78SDaniel Bristot de Oliveira    """Automata class: Reads a dot file and part it as an automata.
15e3c9fc78SDaniel Bristot de Oliveira
16e3c9fc78SDaniel Bristot de Oliveira    Attributes:
17e3c9fc78SDaniel Bristot de Oliveira        dot_file: A dot file with an state_automaton definition.
18e3c9fc78SDaniel Bristot de Oliveira    """
19e3c9fc78SDaniel Bristot de Oliveira
20e3c9fc78SDaniel Bristot de Oliveira    invalid_state_str = "INVALID_STATE"
21e3c9fc78SDaniel Bristot de Oliveira
22e3c9fc78SDaniel Bristot de Oliveira    def __init__(self, file_path):
23e3c9fc78SDaniel Bristot de Oliveira        self.__dot_path = file_path
24e3c9fc78SDaniel Bristot de Oliveira        self.name = self.__get_model_name()
25e3c9fc78SDaniel Bristot de Oliveira        self.__dot_lines = self.__open_dot()
26e3c9fc78SDaniel Bristot de Oliveira        self.states, self.initial_state, self.final_states = self.__get_state_variables()
27e3c9fc78SDaniel Bristot de Oliveira        self.events = self.__get_event_variables()
28e3c9fc78SDaniel Bristot de Oliveira        self.function = self.__create_matrix()
29e3c9fc78SDaniel Bristot de Oliveira
30e3c9fc78SDaniel Bristot de Oliveira    def __get_model_name(self):
31e3c9fc78SDaniel Bristot de Oliveira        basename = ntpath.basename(self.__dot_path)
32*56233417SGabriele Monaco        if not basename.endswith(".dot") and not basename.endswith(".gv"):
33e3c9fc78SDaniel Bristot de Oliveira            print("not a dot file")
34e3c9fc78SDaniel Bristot de Oliveira            raise Exception("not a dot file: %s" % self.__dot_path)
35e3c9fc78SDaniel Bristot de Oliveira
36*56233417SGabriele Monaco        model_name = ntpath.splitext(basename)[0]
37e3c9fc78SDaniel Bristot de Oliveira        if model_name.__len__() == 0:
38e3c9fc78SDaniel Bristot de Oliveira            raise Exception("not a dot file: %s" % self.__dot_path)
39e3c9fc78SDaniel Bristot de Oliveira
40e3c9fc78SDaniel Bristot de Oliveira        return model_name
41e3c9fc78SDaniel Bristot de Oliveira
42e3c9fc78SDaniel Bristot de Oliveira    def __open_dot(self):
43e3c9fc78SDaniel Bristot de Oliveira        cursor = 0
44e3c9fc78SDaniel Bristot de Oliveira        dot_lines = []
45e3c9fc78SDaniel Bristot de Oliveira        try:
46e3c9fc78SDaniel Bristot de Oliveira            dot_file = open(self.__dot_path)
47e3c9fc78SDaniel Bristot de Oliveira        except:
48e3c9fc78SDaniel Bristot de Oliveira            raise Exception("Cannot open the file: %s" % self.__dot_path)
49e3c9fc78SDaniel Bristot de Oliveira
50e3c9fc78SDaniel Bristot de Oliveira        dot_lines = dot_file.read().splitlines()
51e3c9fc78SDaniel Bristot de Oliveira        dot_file.close()
52e3c9fc78SDaniel Bristot de Oliveira
53e3c9fc78SDaniel Bristot de Oliveira        # checking the first line:
54e3c9fc78SDaniel Bristot de Oliveira        line = dot_lines[cursor].split()
55e3c9fc78SDaniel Bristot de Oliveira
56e3c9fc78SDaniel Bristot de Oliveira        if (line[0] != "digraph") and (line[1] != "state_automaton"):
57e3c9fc78SDaniel Bristot de Oliveira            raise Exception("Not a valid .dot format: %s" % self.__dot_path)
58e3c9fc78SDaniel Bristot de Oliveira        else:
59e3c9fc78SDaniel Bristot de Oliveira            cursor += 1
60e3c9fc78SDaniel Bristot de Oliveira        return dot_lines
61e3c9fc78SDaniel Bristot de Oliveira
62e3c9fc78SDaniel Bristot de Oliveira    def __get_cursor_begin_states(self):
63e3c9fc78SDaniel Bristot de Oliveira        cursor = 0
64e3c9fc78SDaniel Bristot de Oliveira        while self.__dot_lines[cursor].split()[0] != "{node":
65e3c9fc78SDaniel Bristot de Oliveira            cursor += 1
66e3c9fc78SDaniel Bristot de Oliveira        return cursor
67e3c9fc78SDaniel Bristot de Oliveira
68e3c9fc78SDaniel Bristot de Oliveira    def __get_cursor_begin_events(self):
69e3c9fc78SDaniel Bristot de Oliveira        cursor = 0
70e3c9fc78SDaniel Bristot de Oliveira        while self.__dot_lines[cursor].split()[0] != "{node":
71e3c9fc78SDaniel Bristot de Oliveira            cursor += 1
72e3c9fc78SDaniel Bristot de Oliveira        while self.__dot_lines[cursor].split()[0] == "{node":
73e3c9fc78SDaniel Bristot de Oliveira            cursor += 1
74e3c9fc78SDaniel Bristot de Oliveira        # skip initial state transition
75e3c9fc78SDaniel Bristot de Oliveira        cursor += 1
76e3c9fc78SDaniel Bristot de Oliveira        return cursor
77e3c9fc78SDaniel Bristot de Oliveira
78e3c9fc78SDaniel Bristot de Oliveira    def __get_state_variables(self):
79e3c9fc78SDaniel Bristot de Oliveira        # wait for node declaration
80e3c9fc78SDaniel Bristot de Oliveira        states = []
81e3c9fc78SDaniel Bristot de Oliveira        final_states = []
82e3c9fc78SDaniel Bristot de Oliveira
83e3c9fc78SDaniel Bristot de Oliveira        has_final_states = False
84e3c9fc78SDaniel Bristot de Oliveira        cursor = self.__get_cursor_begin_states()
85e3c9fc78SDaniel Bristot de Oliveira
86e3c9fc78SDaniel Bristot de Oliveira        # process nodes
87e3c9fc78SDaniel Bristot de Oliveira        while self.__dot_lines[cursor].split()[0] == "{node":
88e3c9fc78SDaniel Bristot de Oliveira            line = self.__dot_lines[cursor].split()
89e3c9fc78SDaniel Bristot de Oliveira            raw_state = line[-1]
90e3c9fc78SDaniel Bristot de Oliveira
91e3c9fc78SDaniel Bristot de Oliveira            #  "enabled_fired"}; -> enabled_fired
92e3c9fc78SDaniel Bristot de Oliveira            state = raw_state.replace('"', '').replace('};', '').replace(',','_')
93e3c9fc78SDaniel Bristot de Oliveira            if state[0:7] == "__init_":
94e3c9fc78SDaniel Bristot de Oliveira                initial_state = state[7:]
95e3c9fc78SDaniel Bristot de Oliveira            else:
96e3c9fc78SDaniel Bristot de Oliveira                states.append(state)
97*56233417SGabriele Monaco                if "doublecircle" in self.__dot_lines[cursor]:
98e3c9fc78SDaniel Bristot de Oliveira                    final_states.append(state)
99e3c9fc78SDaniel Bristot de Oliveira                    has_final_states = True
100e3c9fc78SDaniel Bristot de Oliveira
101*56233417SGabriele Monaco                if "ellipse" in self.__dot_lines[cursor]:
102e3c9fc78SDaniel Bristot de Oliveira                    final_states.append(state)
103e3c9fc78SDaniel Bristot de Oliveira                    has_final_states = True
104e3c9fc78SDaniel Bristot de Oliveira
105e3c9fc78SDaniel Bristot de Oliveira            cursor += 1
106e3c9fc78SDaniel Bristot de Oliveira
107e3c9fc78SDaniel Bristot de Oliveira        states = sorted(set(states))
108e3c9fc78SDaniel Bristot de Oliveira        states.remove(initial_state)
109e3c9fc78SDaniel Bristot de Oliveira
110e3c9fc78SDaniel Bristot de Oliveira        # Insert the initial state at the bein og the states
111e3c9fc78SDaniel Bristot de Oliveira        states.insert(0, initial_state)
112e3c9fc78SDaniel Bristot de Oliveira
113*56233417SGabriele Monaco        if not has_final_states:
114e3c9fc78SDaniel Bristot de Oliveira            final_states.append(initial_state)
115e3c9fc78SDaniel Bristot de Oliveira
116e3c9fc78SDaniel Bristot de Oliveira        return states, initial_state, final_states
117e3c9fc78SDaniel Bristot de Oliveira
118e3c9fc78SDaniel Bristot de Oliveira    def __get_event_variables(self):
119e3c9fc78SDaniel Bristot de Oliveira        # here we are at the begin of transitions, take a note, we will return later.
120e3c9fc78SDaniel Bristot de Oliveira        cursor = self.__get_cursor_begin_events()
121e3c9fc78SDaniel Bristot de Oliveira
122e3c9fc78SDaniel Bristot de Oliveira        events = []
123*56233417SGabriele Monaco        while self.__dot_lines[cursor].lstrip()[0] == '"':
124e3c9fc78SDaniel Bristot de Oliveira            # transitions have the format:
125e3c9fc78SDaniel Bristot de Oliveira            # "all_fired" -> "both_fired" [ label = "disable_irq" ];
126e3c9fc78SDaniel Bristot de Oliveira            #  ------------ event is here ------------^^^^^
127e3c9fc78SDaniel Bristot de Oliveira            if self.__dot_lines[cursor].split()[1] == "->":
128e3c9fc78SDaniel Bristot de Oliveira                line = self.__dot_lines[cursor].split()
129e3c9fc78SDaniel Bristot de Oliveira                event = line[-2].replace('"','')
130e3c9fc78SDaniel Bristot de Oliveira
131e3c9fc78SDaniel Bristot de Oliveira                # when a transition has more than one lables, they are like this
132e3c9fc78SDaniel Bristot de Oliveira                # "local_irq_enable\nhw_local_irq_enable_n"
133e3c9fc78SDaniel Bristot de Oliveira                # so split them.
134e3c9fc78SDaniel Bristot de Oliveira
135e3c9fc78SDaniel Bristot de Oliveira                event = event.replace("\\n", " ")
136e3c9fc78SDaniel Bristot de Oliveira                for i in event.split():
137e3c9fc78SDaniel Bristot de Oliveira                    events.append(i)
138e3c9fc78SDaniel Bristot de Oliveira            cursor += 1
139e3c9fc78SDaniel Bristot de Oliveira
140e3c9fc78SDaniel Bristot de Oliveira        return sorted(set(events))
141e3c9fc78SDaniel Bristot de Oliveira
142e3c9fc78SDaniel Bristot de Oliveira    def __create_matrix(self):
143e3c9fc78SDaniel Bristot de Oliveira        # transform the array into a dictionary
144e3c9fc78SDaniel Bristot de Oliveira        events = self.events
145e3c9fc78SDaniel Bristot de Oliveira        states = self.states
146e3c9fc78SDaniel Bristot de Oliveira        events_dict = {}
147e3c9fc78SDaniel Bristot de Oliveira        states_dict = {}
148e3c9fc78SDaniel Bristot de Oliveira        nr_event = 0
149e3c9fc78SDaniel Bristot de Oliveira        for event in events:
150e3c9fc78SDaniel Bristot de Oliveira            events_dict[event] = nr_event
151e3c9fc78SDaniel Bristot de Oliveira            nr_event += 1
152e3c9fc78SDaniel Bristot de Oliveira
153e3c9fc78SDaniel Bristot de Oliveira        nr_state = 0
154e3c9fc78SDaniel Bristot de Oliveira        for state in states:
155e3c9fc78SDaniel Bristot de Oliveira            states_dict[state] = nr_state
156e3c9fc78SDaniel Bristot de Oliveira            nr_state += 1
157e3c9fc78SDaniel Bristot de Oliveira
158e3c9fc78SDaniel Bristot de Oliveira        # declare the matrix....
159e3c9fc78SDaniel Bristot de Oliveira        matrix = [[ self.invalid_state_str for x in range(nr_event)] for y in range(nr_state)]
160e3c9fc78SDaniel Bristot de Oliveira
161e3c9fc78SDaniel Bristot de Oliveira        # and we are back! Let's fill the matrix
162e3c9fc78SDaniel Bristot de Oliveira        cursor = self.__get_cursor_begin_events()
163e3c9fc78SDaniel Bristot de Oliveira
164*56233417SGabriele Monaco        while self.__dot_lines[cursor].lstrip()[0] == '"':
165e3c9fc78SDaniel Bristot de Oliveira            if self.__dot_lines[cursor].split()[1] == "->":
166e3c9fc78SDaniel Bristot de Oliveira                line = self.__dot_lines[cursor].split()
167e3c9fc78SDaniel Bristot de Oliveira                origin_state = line[0].replace('"','').replace(',','_')
168e3c9fc78SDaniel Bristot de Oliveira                dest_state = line[2].replace('"','').replace(',','_')
169e3c9fc78SDaniel Bristot de Oliveira                possible_events = line[-2].replace('"','').replace("\\n", " ")
170e3c9fc78SDaniel Bristot de Oliveira                for event in possible_events.split():
171e3c9fc78SDaniel Bristot de Oliveira                    matrix[states_dict[origin_state]][events_dict[event]] = dest_state
172e3c9fc78SDaniel Bristot de Oliveira            cursor += 1
173e3c9fc78SDaniel Bristot de Oliveira
174e3c9fc78SDaniel Bristot de Oliveira        return matrix
175