diff options
| -rwxr-xr-x | contrib/fsm-to-dot.py | 53 | 
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)  | 
