blob: 83c95b69d809de5cc4d46e767d1e9d1b65c69395 [file] [log] [blame]
Neels Hofmeyr0898a002016-11-16 14:36:29 +01001#!/usr/bin/env python
2
3__doc__ = '''
4fsm-to-dot: convert FSM definitons to graph images
5
6Usage:
7 ./fsm-to-dot.py ~/openbsc/openbsc/src/libvlr/*.c
Neels Hofmeyra568af22017-10-23 04:03:19 +02008 for f in *.dot ; do dot -Tpng "$f" > "$f.png"; done
Neels Hofmeyr0898a002016-11-16 14:36:29 +01009 # dot comes from 'apt-get install graphviz'
10
11Looks for osmo_fsm finite state machine definitions and madly parses .c files
12to draw graphs of them. This uses wild regexes that rely on coding style etc..
13No proper C parsing is done here (pycparser sucked, unfortunately).
14'''
15
Neels Hofmeyra568af22017-10-23 04:03:19 +020016import sys, re, os
Neels Hofmeyr0898a002016-11-16 14:36:29 +010017
Neels Hofmeyr167f8082018-03-25 01:01:32 +010018if '-h' in sys.argv or '--help' in sys.argv:
19 print(__doc__)
20 exit(0)
21
Neels Hofmeyr0898a002016-11-16 14:36:29 +010022def err(msg):
23 sys.stderr.write(msg + '\n')
24
25class listdict(object):
26 def __getattr__(ld, name):
27 if name == 'add':
28 return ld.__getattribute__(name)
29 return ld.__dict__.__getattribute__(name)
30
31 def _have(ld, name):
32 l = ld.__dict__.get(name)
33 if not l:
34 l = []
35 ld.__dict__[name] = l
36 return l
37
38 def add(ld, name, item):
39 l = ld._have(name)
40 l.append(item)
41 return ld
42
43 def add_dict(ld, d):
44 for k,v in d.items():
45 ld.add(k, v)
46
47 def __setitem__(ld, name, val):
48 return ld.__dict__.__setitem__(name, val)
49
50 def __getitem__(ld, name):
51 return ld.__dict__.__getitem__(name)
52
53 def __str__(ld):
54 return ld.__dict__.__str__()
55
56 def __repr__(ld):
57 return ld.__dict__.__repr__()
58
59 def update(ld, other_ld):
60 for name, items in other_ld.items():
61 ld.extend(name, items)
62 return ld
63
64 def extend(ld, name, vals):
65 l = ld._have(name)
66 l.extend(vals)
67 return ld
68
69re_state_start = re.compile(r'\[([A-Z_][A-Z_0-9]*)\]')
Neels Hofmeyra568af22017-10-23 04:03:19 +020070re_event_alternatives = [
71 re.compile(r'\(1 *<< *([A-Z_][A-Z_0-9]*)\)'),
72 re.compile(r'S\(([A-Z_][A-Z_0-9]*)\)'),
73 ]
Neels Hofmeyr0898a002016-11-16 14:36:29 +010074re_action = re.compile(r'.action *= *([a-z_][a-z_0-9]*)')
75
Neels Hofmeyra568af22017-10-23 04:03:19 +020076re_insane_dot_name_chars = re.compile('[^a-zA-Z_]')
77
Neels Hofmeyr0898a002016-11-16 14:36:29 +010078def state_starts(line):
79 m = re_state_start.search(line)
80 if m:
81 return m.group(1)
82 return None
83
84def in_event_starts(line):
85 return line.find('in_event_mask') >= 0
86
87def out_state_starts(line):
88 return line.find('out_state_mask') >= 0
89
90def states_or_events(line):
Neels Hofmeyra568af22017-10-23 04:03:19 +020091 results = []
92 for one_re in re_event_alternatives:
93 results.extend(one_re.findall(line))
94 return results
Neels Hofmeyr0898a002016-11-16 14:36:29 +010095
96def parse_action(line):
97 a = re_action.findall(line)
98 if a:
99 return a[0]
100 return None
101
102def _common_prefix(a, b):
103 for l in reversed(range(1,len(a))):
104 aa = a[:l+1]
105 if b.startswith(aa):
106 return aa
107 return ''
108
109def common_prefix(strs):
110 if not strs:
111 return ''
112 p = None
113 for s in strs:
114 if p is None:
115 p = s
116 continue
117 p = _common_prefix(p, s)
118 if not p:
119 return ''
120 return p
121
122KIND_STATE = 'KIND_STATE'
123KIND_FUNC = 'KIND_FUNC'
124KIND_FSM = 'KIND_FSM'
125BOX_SHAPES = {
126 KIND_STATE : None,
127 KIND_FUNC : 'box',
128 KIND_FSM : 'box3d',
129}
130
131class Event:
132 def __init__(event, name):
133 event.name = name
134 event.short_name = name
135
136 def __cmp__(event, other):
137 return cmp(event.name, other.name)
138
139class Edge:
Neels Hofmeyr536534a2018-03-25 04:15:37 +0200140 def __init__(edge, to_state, event_name=None, style=None, action=None, color=None, arrow_head=None):
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100141 edge.to_state = to_state
142 edge.style = style
Neels Hofmeyrfcf79922018-03-25 01:03:26 +0100143 edge.color = color
Neels Hofmeyr536534a2018-03-25 04:15:37 +0200144 edge.arrow_head = arrow_head
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100145 edge.events = []
146 edge.actions = []
147 edge.add_event_name(event_name)
148 edge.add_action(action)
149
150 def add_event_name(edge, event_name):
151 if not event_name:
152 return
153 edge.add_event(Event(event_name))
154
155 def add_event(edge, event):
156 if not event:
157 return
158 if event in edge.events:
159 return
160 edge.events.append(event)
161
162 def add_events(edge, events):
163 for event in events:
164 edge.add_event(event)
165
166 def add_action(edge, action):
167 if not action or action in edge.actions:
168 return
169 edge.actions.append(action)
170
171 def add_actions(edge, actions):
172 for action in actions:
173 edge.add_action(action)
174
175 def event_names(edge):
176 return sorted([event.name for event in edge.events])
177
178 def event_labels(edge):
179 return sorted([event.short_name for event in edge.events])
180
181 def action_labels(edge):
182 return sorted([action + '()' for action in edge.actions])
183
184 def has_event_name(edge, event_name):
185 return event_name in edge.event_names()
186
187class State:
188 name = None
189 short_name = None
190 action = None
191 label = None
192 in_event_names = None
193 out_state_names = None
194 out_edges = None
195 kind = None
196
197 def __init__(state):
198 state.in_event_names = []
199 state.out_state_names = []
200 state.out_edges = []
201 state.kind = KIND_STATE
202
203 def add_out_edge(state, edge):
204 for out_edge in state.out_edges:
205 if out_edge.to_state is edge.to_state:
206 if out_edge.style == edge.style:
207 out_edge.add_events(edge.events)
208 out_edge.add_actions(edge.actions)
209 return
Neels Hofmeyr46145e82018-03-25 01:02:35 +0100210 elif out_edge.to_state.get_label() == edge.to_state.get_label():
211 # sanity: there already is an edge to a state that a) is not identical to the target state of the
212 # newly added edge but b) has the same label.
213 raise Exception('Two distinct states exist with identical label: %r: states %r and %r.'
214 % (out_edge.to_state.get_label(), out_edge.to_state, edge.to_state))
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100215 state.out_edges.append(edge)
216
217 def get_label(state):
218 if state.label:
219 return state.label
220 l = [state.short_name]
221 if state.action:
222 if state.short_name == state.action:
223 l = []
224 l.append(state.action + '()')
225 return r'\n'.join(l)
226
227 def event_names(state):
228 event_names = []
229 for out_edge in state.out_edges:
230 event_names.extend(out_edge.event_names())
231 return event_names
232
233 def shape_str(state):
234 shape = BOX_SHAPES.get(state.kind, None)
235 if not shape:
236 return ''
237 return ',shape=%s' % shape
238
239 def __repr__(state):
240 return 'State(name=%r,short_name=%r,out=%d)' % (state.name, state.short_name, len(state.out_edges))
241
242class Fsm:
Neels Hofmeyra568af22017-10-23 04:03:19 +0200243 def __init__(fsm, struct_name, string_name, states_struct_name, from_file=None):
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100244 fsm.states = []
245 fsm.struct_name = struct_name
Neels Hofmeyra568af22017-10-23 04:03:19 +0200246 fsm.string_name = string_name
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100247 fsm.states_struct_name = states_struct_name
248 fsm.from_file = from_file
249 fsm.action_funcs = set()
250 fsm.event_names = set()
Neels Hofmeyra568af22017-10-23 04:03:19 +0200251 fsm.dot_name = fsm.all_names_sanitized()
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100252
253 def parse_states(fsm, src):
254 state = None
255 started = None
256
257 IN_EVENTS = 'events'
258 OUT_STATES = 'states'
259
260 lines = src.splitlines()
261
262 for line in lines:
263 state_name = state_starts(line)
264 if state_name:
265 state = State()
266 fsm.states.append(state)
267 started = None
268 state.name = state_name
269
270 if in_event_starts(line):
271 started = IN_EVENTS
272 if out_state_starts(line):
273 started = OUT_STATES
274
275 if not state or not started:
276 continue
277
278 tokens = states_or_events(line)
279 if started == IN_EVENTS:
280 state.in_event_names.extend(tokens)
281 elif started == OUT_STATES:
282 state.out_state_names.extend(tokens)
283 else:
284 err('ignoring: %r' % tokens)
285
286 a = parse_action(line)
287 if a:
288 state.action = a
289
290
291 for state in fsm.states:
292 if state.action:
293 fsm.action_funcs.add(state.action)
294 if state.in_event_names:
295 fsm.event_names.update(state.in_event_names)
296
297 fsm.make_states_short_names()
298 fsm.ref_out_states()
299
300 def make_states_short_names(fsm):
301 p = common_prefix([s.name for s in fsm.states])
302 for s in fsm.states:
303 s.short_name = s.name[len(p):]
304 return p
305
306 def make_events_short_names(fsm):
307 p = common_prefix(fsm.event_names)
308 for state in fsm.states:
309 for edge in state.out_edges:
310 for event in edge.events:
311 event.short_name = event.name[len(p):]
312
313 def ref_out_states(fsm):
314 for state in fsm.states:
315 for e in [Edge(fsm.find_state_by_name(n, True)) for n in state.out_state_names]:
316 state.add_out_edge(e)
317
318 def find_state_by_name(fsm, name, strict=False):
319 for state in fsm.states:
320 if state.name == name:
321 return state
322 if strict:
323 raise Exception("State not found: %r" % name);
324 return None
325
326 def find_state_by_action(fsm, action):
327 for state in fsm.states:
328 if state.action == action:
329 return state
330 return None
331
332 def add_special_state(fsm, additional_states, name, in_state=None,
333 out_state=None, event_name=None, kind=KIND_FUNC,
Neels Hofmeyr536534a2018-03-25 04:15:37 +0200334 state_action=None, label=None, edge_action=None,
335 style='dotted', arrow_head=None):
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100336 additional_state = None
337 for s in additional_states:
338 if s.short_name == name:
339 additional_state = s
340 break;
341
342 if not additional_state:
343 for s in fsm.states:
344 if s.short_name == name:
345 additional_state = s
346 break;
347
348 if kind == KIND_FUNC and not state_action:
349 state_action = name
350
351 if not additional_state:
352 additional_state = State()
353 additional_state.short_name = name
354 additional_state.action = state_action
355 additional_state.kind = kind
356 additional_state.label = label
357 additional_states.append(additional_state)
358
359 if out_state:
360 additional_state.out_state_names.append(out_state.name)
Neels Hofmeyr536534a2018-03-25 04:15:37 +0200361 additional_state.add_out_edge(Edge(out_state, event_name, style=style,
362 action=edge_action, arrow_head=arrow_head))
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100363
364 if in_state:
365 in_state.out_state_names.append(additional_state.name)
Neels Hofmeyr536534a2018-03-25 04:15:37 +0200366 in_state.add_out_edge(Edge(additional_state, event_name, style=style,
367 action=edge_action, arrow_head=arrow_head))
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100368
369
370 def find_event_edges(fsm, c_files):
371 # enrich state transitions between the states with event labels
372 func_to_state_transitions = listdict()
373 for c_file in c_files:
374 func_to_state_transitions.update( c_file.find_state_transitions(fsm.event_names) )
375
376 # edges between explicit states
377 for state in fsm.states:
378 transitions = func_to_state_transitions.get(state.action)
379 if not transitions:
380 continue
381
382 for to_state_name, event_name in transitions:
383 if not event_name:
384 continue
Neels Hofmeyrfcf79922018-03-25 01:03:26 +0100385 found = False
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100386 for out_edge in state.out_edges:
387 if out_edge.to_state.name == to_state_name:
388 out_edge.add_event_name(event_name)
Neels Hofmeyrfcf79922018-03-25 01:03:26 +0100389 found = True
390 if not found:
391 sys.stderr.write(
392 "ERROR: %s() triggers a transition to %s, but this is not allowed by the FSM definition\n"
393 % (state.action, to_state_name))
394 state.add_out_edge(Edge(fsm.find_state_by_name(to_state_name, True), event_name,
395 color='red'))
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100396
397 additional_states = []
398
399
400 # functions that aren't state actions but still effect state transitions
401 for func_name, transitions in func_to_state_transitions.items():
402 if func_name in fsm.action_funcs:
403 continue
404 for to_state_name, event_name in transitions:
405 to_state = fsm.find_state_by_name(to_state_name)
406 if not to_state:
407 continue
408 fsm.add_special_state(additional_states, func_name, None, to_state, event_name)
409
410
411 event_sources = c_files.find_event_sources(fsm.event_names)
412
413 for state in fsm.states:
414
415 for in_event_name in state.in_event_names:
416 funcs_for_in_event = event_sources.get(in_event_name)
417 if not funcs_for_in_event:
418 continue
419
420 found = False
421 for out_edge in state.out_edges:
422 if out_edge.has_event_name(in_event_name):
423 out_edge.action = r'\n'.join([(f + '()') for f in funcs_for_in_event
424 if f != state.action])
425
426 # if any functions that don't belong to a state trigger events, add
427 # them to the graph as well
428 additional_funcs = [f for f in funcs_for_in_event if f not in fsm.action_funcs]
429 for af in additional_funcs:
Neels Hofmeyr536534a2018-03-25 04:15:37 +0200430 fsm.add_special_state(additional_states, af, None, state, in_event_name,
431 arrow_head='halfopen')
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100432
433 fsm.states.extend(additional_states)
434
435 # do any existing action functions by chance call other action functions?
436 for state in fsm.states:
437 if not state.action:
438 continue
439 callers = c_files.find_callers(state.action)
440 if not callers:
441 continue
442 for other_state in fsm.states:
443 if other_state.action in callers:
444 other_state.add_out_edge(Edge(state, None, 'dotted'))
445
446 def add_fsm_alloc(fsm, c_files):
447
448 allocating_funcs = []
449 for c_file in c_files:
450 allocating_funcs.extend(c_file.fsm_allocators.get(fsm.struct_name, []))
451
452 starting_state = None
453 if fsm.states:
454 # assume the first state starts
455 starting_state = fsm.states[0]
456
457 additional_states = []
458 for func_name in allocating_funcs:
459 fsm.add_special_state(additional_states, func_name, None, starting_state)
460
461 fsm.states.extend(additional_states)
462
463 def add_cross_fsm_links(fsm, fsms, c_files, fsm_meta):
464 for state in fsm.states:
465 if not state.action:
466 continue
467 if state.kind == KIND_FSM:
468 continue
469 callers = c_files.find_callers(state.action)
470
471 if state.kind == KIND_FUNC:
472 callers.append(state.action)
473
474 if not callers:
475 continue
476
477 for caller in callers:
478 for calling_fsm in fsms:
479 if calling_fsm is fsm:
480 continue
481 calling_state = calling_fsm.find_state_by_action(caller)
482 if not calling_state:
483 continue
484 if calling_state.kind == KIND_FSM:
485 continue
486
487 label = None
488 if state.kind == KIND_STATE:
489 label=fsm.struct_name + ': ' + state.short_name
490 edge_action = caller
491 if calling_state.action == edge_action:
492 edge_action = None
Neels Hofmeyra568af22017-10-23 04:03:19 +0200493 calling_fsm.add_special_state(calling_fsm.states, fsm.dot_name,
494 calling_state, kind=KIND_FSM, edge_action=edge_action, label=' '.join(fsm.all_names()))
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100495
496 label = None
497 if calling_state.kind == KIND_STATE:
498 label=calling_fsm.struct_name + ': ' + calling_state.short_name
499 edge_action = caller
500 if state.action == edge_action:
501 edge_action = None
Neels Hofmeyra568af22017-10-23 04:03:19 +0200502 fsm.add_special_state(fsm.states, calling_fsm.dot_name, None,
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100503 state, kind=KIND_FSM, edge_action=edge_action,
504 label=label)
505
506 # meta overview
Neels Hofmeyra568af22017-10-23 04:03:19 +0200507 meta_called_fsm = fsm_meta.have_state(fsm.dot_name, KIND_FSM)
508 meta_calling_fsm = fsm_meta.have_state(calling_fsm.dot_name, KIND_FSM)
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100509 meta_calling_fsm.add_out_edge(Edge(meta_called_fsm))
510
511
512 def have_state(fsm, name, kind=KIND_STATE):
513 state = fsm.find_state_by_name(name)
514 if not state:
515 state = State()
516 state.name = name
517 state.short_name = name
518 state.kind = kind
519 fsm.states.append(state)
520 return state
521
522 def to_dot(fsm):
523 out = ['digraph G {', 'rankdir=LR;']
524
525 for state in fsm.states:
526 out.append('%s [label="%s"%s]' % (state.short_name, state.get_label(),
527 state.shape_str()))
528
529 for state in fsm.states:
530 for out_edge in state.out_edges:
531 attrs = []
532 labels = []
533 if out_edge.events:
534 labels.extend(out_edge.event_labels())
535 if out_edge.actions:
536 labels.extend(out_edge.action_labels())
537 if labels:
Neels Hofmeyr75ee4e82018-03-25 03:08:28 +0200538 label = r'\n'.join(labels)
539 else:
540 label = '-'
541 attrs.append('label="%s"' % label)
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100542 if out_edge.style:
543 attrs.append('style=%s'% out_edge.style)
Neels Hofmeyrfcf79922018-03-25 01:03:26 +0100544 if out_edge.color:
545 attrs.append('color=%s'% out_edge.color)
Neels Hofmeyr536534a2018-03-25 04:15:37 +0200546 if out_edge.arrow_head:
547 attrs.append('arrowhead=%s'% out_edge.arrow_head)
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100548 attrs_str = ''
549 if attrs:
550 attrs_str = ' [%s]' % (','.join(attrs))
551 out.append('%s->%s%s' % (state.short_name, out_edge.to_state.short_name, attrs_str))
552
553 out.append('}\n')
554
555 return '\n'.join(out)
556
Neels Hofmeyra568af22017-10-23 04:03:19 +0200557 def all_names(fsm):
558 n = []
559 if fsm.from_file:
560 n.append(os.path.basename(fsm.from_file.path))
561 if fsm.struct_name:
562 n.append(fsm.struct_name)
563 if fsm.string_name:
564 n.append(fsm.string_name)
565 return n
566
567 def all_names_sanitized(fsm, sep='_'):
568 n = sep.join(fsm.all_names())
569 n = re_insane_dot_name_chars.sub('_', n)
570 return n
571
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100572 def write_dot_file(fsm):
Neels Hofmeyra568af22017-10-23 04:03:19 +0200573 dot_path = '%s.dot' % ('_'.join(fsm.all_names()))
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100574 f = open(dot_path, 'w')
575 f.write(fsm.to_dot())
576 f.close()
577 print(dot_path)
578
579
580re_fsm = re.compile(r'struct osmo_fsm ([a-z_][a-z_0-9]*) =')
Neels Hofmeyra568af22017-10-23 04:03:19 +0200581re_fsm_string_name = re.compile(r'\bname = "([^"]*)"')
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100582re_fsm_states_struct_name = re.compile(r'\bstates = ([a-z_][a-z_0-9]*)\W*,')
583re_fsm_states = re.compile(r'struct osmo_fsm_state ([a-z_][a-z_0-9]*)\[\] =')
584re_func = re.compile(r'(\b[a-z_][a-z_0-9]*\b)\([^)]*\)\W*^{', re.MULTILINE)
585re_state_trigger = re.compile(r'osmo_fsm_inst_state_chg\([^,]+,\W*([A-Z_][A-Z_0-9]*)\W*,', re.M)
586re_fsm_alloc = re.compile(r'osmo_fsm_inst_alloc[_child]*\(\W*&([a-z_][a-z_0-9]*),', re.M)
587re_fsm_event_dispatch = re.compile(r'osmo_fsm_inst_dispatch\(\W*[^,]+,\W*([A-Z_][A-Z_0-9]*)\W*,', re.M)
Neels Hofmeyrbb22df32018-03-25 01:03:26 +0100588re_comment_multiline = re.compile(r'/\*.*?\*/', re.M | re.S)
589re_comment_single_line = re.compile(r'//.*$', re.M | re.S)
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100590
591class CFile():
592 def __init__(c_file, path):
593 c_file.path = path
594 c_file.src = open(path).read()
595 c_file.funcs = {}
596 c_file.fsm_allocators = listdict()
597
598 def extract_block(c_file, brace_open, brace_close, start):
599 pos = 0
600 try:
601 src = c_file.src
602 block_start = src.find(brace_open, start)
603
604 pos = block_start
605 level = 1
606 while level > 0:
607 pos += 1
608 if src[pos] == brace_open:
609 level += 1
610 elif src[pos] == brace_close:
611 level -= 1
612
613 return src[block_start+1:pos]
614 except:
615 print("Error while trying to extract a code block from %r char pos %d" % (c_file.path, pos))
616 print("Block start at char pos %d" % block_start)
617 try:
618 print(src[block_start - 20 : block_start + 20])
619 print('...')
620 print(src[pos - 20 : pos + 20])
621 except:
622 pass
623 return ''
624
625
626 def find_fsms(c_file):
627 fsms = []
628 for m in re_fsm.finditer(c_file.src):
629 struct_name = m.group(1)
630 struct_def = c_file.extract_block('{', '}', m.start())
Neels Hofmeyra568af22017-10-23 04:03:19 +0200631 string_name = (re_fsm_string_name.findall(struct_def) or [None])[0]
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100632 states_struct_name = re_fsm_states_struct_name.findall(struct_def)[0]
Neels Hofmeyra568af22017-10-23 04:03:19 +0200633 fsm = Fsm(struct_name, string_name, states_struct_name, c_file)
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100634 fsms.append(fsm)
635 return fsms
636
637 def find_fsm_states(c_file, fsms):
638 for m in re_fsm_states.finditer(c_file.src):
639 states_struct_name = m.group(1)
640 for fsm in fsms:
641 if states_struct_name == fsm.states_struct_name:
642 fsm.parse_states(c_file.extract_block('{', '}', m.start()))
643
644 def parse_functions(c_file):
645 funcs = {}
646 for m in re_func.finditer(c_file.src):
647 name = m.group(1)
648 func_src = c_file.extract_block('{', '}', m.start())
Neels Hofmeyrbb22df32018-03-25 01:03:26 +0100649 func_src = ''.join(re_comment_multiline.split(func_src))
650 func_src = ''.join(re_comment_single_line.split(func_src))
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100651 funcs[name] = func_src
652 c_file.funcs = funcs
653 c_file.find_fsm_allocators()
654
655 def find_callers(c_file, func_name):
656 func_call = func_name + '('
657 callers = []
658 for func_name, src in c_file.funcs.items():
659 if src.find(func_call) >= 0:
660 callers.append(func_name)
661 return callers
662
663 def find_fsm_allocators(c_file):
664 c_file.fsm_allocators = listdict()
665 for func_name, src in c_file.funcs.items():
666 for m in re_fsm_alloc.finditer(src):
667 fsm_struct_name = m.group(1)
668 c_file.fsm_allocators.add(fsm_struct_name, func_name)
669
670 def find_state_transitions(c_file, event_names):
671 TO_STATE = 'TO_STATE'
672 EVENT = 'EVENT'
673 func_to_state_transitions = listdict()
674
675 for func_name, src in c_file.funcs.items():
676 found_tokens = []
677
678 for m in re_state_trigger.finditer(src):
679 to_state = m.group(1)
680 found_tokens.append((m.start(), TO_STATE, to_state))
681
682 for event in event_names:
683 re_event = re.compile(r'\b(' + event + r')\b')
684 for m in re_event.finditer(src):
685 event = m.group(1)
686 found_tokens.append((m.start(), EVENT, event))
687
688 found_tokens = sorted(found_tokens)
689
690 last_event = None
691 for start, kind, name in found_tokens:
692 if kind == EVENT:
693 last_event = name
694 else:
695 func_to_state_transitions.add(func_name, (name, last_event))
696
697 return func_to_state_transitions
698
699
700 def find_event_sources(c_file, event_names):
701 c_file.event_sources = listdict()
702 for func_name, src in c_file.funcs.items():
703 for m in re_fsm_event_dispatch.finditer(src):
704 event_name = m.group(1)
705 c_file.event_sources.add(event_name, func_name)
706
707class CFiles(list):
708
709 def find_callers(c_files, func_name):
710 callers = []
711 for c_file in c_files:
712 callers.extend(c_file.find_callers(func_name))
713 return callers
714
715 def find_func_to_state_transitions(c_files):
716 func_to_state_transitions = listdict()
717 for c_file in c_files:
718 func_to_state_transitions.update( c_file.find_state_transitions(fsm.event_names) )
719 return func_to_state_transitions
720
721 def find_event_sources(c_files, event_names):
722 event_sources = listdict()
723 for c_file in c_files:
724 for event, sources in c_file.event_sources.items():
725 if event in event_names:
726 event_sources.extend(event, sources)
727 return event_sources
728
729c_files = CFiles()
730paths_seen = set()
731for path in sys.argv[1:]:
732 if path in paths_seen:
733 continue
734 paths_seen.add(path)
735 c_file = CFile(path)
736 c_files.append(c_file)
737
738for c_file in c_files:
739 c_file.parse_functions()
740
741fsms = []
742for c_file in c_files:
743 fsms.extend(c_file.find_fsms())
744
745for c_file in c_files:
746 c_file.find_fsm_states(fsms)
747 c_file.find_event_sources(fsms)
748
749for fsm in fsms:
750 fsm.find_event_edges(c_files)
751 fsm.add_fsm_alloc(c_files)
752
Neels Hofmeyra568af22017-10-23 04:03:19 +0200753fsm_meta = Fsm("meta", None, "meta")
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100754for fsm in fsms:
755 fsm.add_cross_fsm_links(fsms, c_files, fsm_meta)
756
757for fsm in fsms:
758 fsm.make_events_short_names()
759
760for fsm in fsms:
761 fsm.write_dot_file()
762
763fsm_meta.write_dot_file()
764
765
766# vim: tabstop=2 shiftwidth=2 expandtab