summaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorNeels Hofmeyr <neels@hofmeyr.de>2017-10-23 04:03:19 +0200
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2017-10-24 15:30:58 +0000
commita568af25d2a8382265b44c9b2b3a72ca504d2443 (patch)
tree00f9ea83078e6de6434a9bdefdeaa06ab75b69eb /contrib
parent37f465e275bea35d36b0763bfaef759792a9031f (diff)
contrib/fsm-to-dot.py: some tweaks that help with osmo-bsc's new FSMs
Combine the C source file name and the string name into the fsm's internal name token, and use it in most places instead of the plain struct name: osmo-bsc's new FSMs have identical struct names in each static c context. Output in a file name that includes all of these more detailed name tokens. Also parse '(1 << EVENT)' as event names. Note that besides this patch, there are also some tweaks to the osmo-bsc patch that improve the fsm-to-dot experience... - call fsm-to-dot for single files to avoid name conflicts, or rename each struct as a unique name. - Add comments for the event name a callback is intended for, so that not all transitions are interpreted as TEARDOWN (because it is invoked in common error handling, which causes the script to interpret it as the causing event). (or change the event-checking if into a switch that names the valid event and has a default case for all others.) Change-Id: Ib60df7fd19efc99ba9fe797f14c0e3239c4bea20
Diffstat (limited to 'contrib')
-rwxr-xr-xcontrib/fsm-to-dot.py53
1 files changed, 40 insertions, 13 deletions
diff --git a/contrib/fsm-to-dot.py b/contrib/fsm-to-dot.py
index 3549d1e5..06d2df10 100755
--- a/contrib/fsm-to-dot.py
+++ b/contrib/fsm-to-dot.py
@@ -5,7 +5,7 @@ fsm-to-dot: convert FSM definitons to graph images
Usage:
./fsm-to-dot.py ~/openbsc/openbsc/src/libvlr/*.c
- for f in *.dot ; do dot -Tpng $f > $f.png; done
+ for f in *.dot ; do dot -Tpng "$f" > "$f.png"; done
# dot comes from 'apt-get install graphviz'
Looks for osmo_fsm finite state machine definitions and madly parses .c files
@@ -13,7 +13,7 @@ to draw graphs of them. This uses wild regexes that rely on coding style etc..
No proper C parsing is done here (pycparser sucked, unfortunately).
'''
-import sys, re
+import sys, re, os
def err(msg):
sys.stderr.write(msg + '\n')
@@ -63,9 +63,14 @@ class listdict(object):
return ld
re_state_start = re.compile(r'\[([A-Z_][A-Z_0-9]*)\]')
-re_event = re.compile(r'S\(([A-Z_][A-Z_0-9]*)\)')
+re_event_alternatives = [
+ re.compile(r'\(1 *<< *([A-Z_][A-Z_0-9]*)\)'),
+ re.compile(r'S\(([A-Z_][A-Z_0-9]*)\)'),
+ ]
re_action = re.compile(r'.action *= *([a-z_][a-z_0-9]*)')
+re_insane_dot_name_chars = re.compile('[^a-zA-Z_]')
+
def state_starts(line):
m = re_state_start.search(line)
if m:
@@ -79,7 +84,10 @@ def out_state_starts(line):
return line.find('out_state_mask') >= 0
def states_or_events(line):
- return re_event.findall(line)
+ results = []
+ for one_re in re_event_alternatives:
+ results.extend(one_re.findall(line))
+ return results
def parse_action(line):
a = re_action.findall(line)
@@ -224,13 +232,15 @@ class State:
return 'State(name=%r,short_name=%r,out=%d)' % (state.name, state.short_name, len(state.out_edges))
class Fsm:
- def __init__(fsm, struct_name, states_struct_name, from_file=None):
+ def __init__(fsm, struct_name, string_name, states_struct_name, from_file=None):
fsm.states = []
fsm.struct_name = struct_name
+ fsm.string_name = string_name
fsm.states_struct_name = states_struct_name
fsm.from_file = from_file
fsm.action_funcs = set()
fsm.event_names = set()
+ fsm.dot_name = fsm.all_names_sanitized()
def parse_states(fsm, src):
state = None
@@ -462,8 +472,8 @@ class Fsm:
edge_action = caller
if calling_state.action == edge_action:
edge_action = None
- calling_fsm.add_special_state(calling_fsm.states, fsm.struct_name,
- calling_state, kind=KIND_FSM, edge_action=edge_action, label=label)
+ calling_fsm.add_special_state(calling_fsm.states, fsm.dot_name,
+ calling_state, kind=KIND_FSM, edge_action=edge_action, label=' '.join(fsm.all_names()))
label = None
if calling_state.kind == KIND_STATE:
@@ -471,13 +481,13 @@ class Fsm:
edge_action = caller
if state.action == edge_action:
edge_action = None
- fsm.add_special_state(fsm.states, calling_fsm.struct_name, None,
+ fsm.add_special_state(fsm.states, calling_fsm.dot_name, None,
state, kind=KIND_FSM, edge_action=edge_action,
label=label)
# meta overview
- meta_called_fsm = fsm_meta.have_state(fsm.struct_name, KIND_FSM)
- meta_calling_fsm = fsm_meta.have_state(calling_fsm.struct_name, KIND_FSM)
+ meta_called_fsm = fsm_meta.have_state(fsm.dot_name, KIND_FSM)
+ meta_calling_fsm = fsm_meta.have_state(calling_fsm.dot_name, KIND_FSM)
meta_calling_fsm.add_out_edge(Edge(meta_called_fsm))
@@ -519,8 +529,23 @@ class Fsm:
return '\n'.join(out)
+ def all_names(fsm):
+ n = []
+ if fsm.from_file:
+ n.append(os.path.basename(fsm.from_file.path))
+ if fsm.struct_name:
+ n.append(fsm.struct_name)
+ if fsm.string_name:
+ n.append(fsm.string_name)
+ return n
+
+ def all_names_sanitized(fsm, sep='_'):
+ n = sep.join(fsm.all_names())
+ n = re_insane_dot_name_chars.sub('_', n)
+ return n
+
def write_dot_file(fsm):
- dot_path = '%s.dot' % fsm.struct_name
+ dot_path = '%s.dot' % ('_'.join(fsm.all_names()))
f = open(dot_path, 'w')
f.write(fsm.to_dot())
f.close()
@@ -528,6 +553,7 @@ class Fsm:
re_fsm = re.compile(r'struct osmo_fsm ([a-z_][a-z_0-9]*) =')
+re_fsm_string_name = re.compile(r'\bname = "([^"]*)"')
re_fsm_states_struct_name = re.compile(r'\bstates = ([a-z_][a-z_0-9]*)\W*,')
re_fsm_states = re.compile(r'struct osmo_fsm_state ([a-z_][a-z_0-9]*)\[\] =')
re_func = re.compile(r'(\b[a-z_][a-z_0-9]*\b)\([^)]*\)\W*^{', re.MULTILINE)
@@ -575,8 +601,9 @@ class CFile():
for m in re_fsm.finditer(c_file.src):
struct_name = m.group(1)
struct_def = c_file.extract_block('{', '}', m.start())
+ string_name = (re_fsm_string_name.findall(struct_def) or [None])[0]
states_struct_name = re_fsm_states_struct_name.findall(struct_def)[0]
- fsm = Fsm(struct_name, states_struct_name, c_file)
+ fsm = Fsm(struct_name, string_name, states_struct_name, c_file)
fsms.append(fsm)
return fsms
@@ -694,7 +721,7 @@ for fsm in fsms:
fsm.find_event_edges(c_files)
fsm.add_fsm_alloc(c_files)
-fsm_meta = Fsm("meta", "meta")
+fsm_meta = Fsm("meta", None, "meta")
for fsm in fsms:
fsm.add_cross_fsm_links(fsms, c_files, fsm_meta)