blob: ce94a4eb074c1ea58562586f026aed62cbe6c481 [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:
140 def __init__(edge, to_state, event_name=None, style=None, action=None):
141 edge.to_state = to_state
142 edge.style = style
143 edge.events = []
144 edge.actions = []
145 edge.add_event_name(event_name)
146 edge.add_action(action)
147
148 def add_event_name(edge, event_name):
149 if not event_name:
150 return
151 edge.add_event(Event(event_name))
152
153 def add_event(edge, event):
154 if not event:
155 return
156 if event in edge.events:
157 return
158 edge.events.append(event)
159
160 def add_events(edge, events):
161 for event in events:
162 edge.add_event(event)
163
164 def add_action(edge, action):
165 if not action or action in edge.actions:
166 return
167 edge.actions.append(action)
168
169 def add_actions(edge, actions):
170 for action in actions:
171 edge.add_action(action)
172
173 def event_names(edge):
174 return sorted([event.name for event in edge.events])
175
176 def event_labels(edge):
177 return sorted([event.short_name for event in edge.events])
178
179 def action_labels(edge):
180 return sorted([action + '()' for action in edge.actions])
181
182 def has_event_name(edge, event_name):
183 return event_name in edge.event_names()
184
185class State:
186 name = None
187 short_name = None
188 action = None
189 label = None
190 in_event_names = None
191 out_state_names = None
192 out_edges = None
193 kind = None
194
195 def __init__(state):
196 state.in_event_names = []
197 state.out_state_names = []
198 state.out_edges = []
199 state.kind = KIND_STATE
200
201 def add_out_edge(state, edge):
202 for out_edge in state.out_edges:
203 if out_edge.to_state is edge.to_state:
204 if out_edge.style == edge.style:
205 out_edge.add_events(edge.events)
206 out_edge.add_actions(edge.actions)
207 return
Neels Hofmeyr46145e82018-03-25 01:02:35 +0100208 elif out_edge.to_state.get_label() == edge.to_state.get_label():
209 # sanity: there already is an edge to a state that a) is not identical to the target state of the
210 # newly added edge but b) has the same label.
211 raise Exception('Two distinct states exist with identical label: %r: states %r and %r.'
212 % (out_edge.to_state.get_label(), out_edge.to_state, edge.to_state))
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100213 state.out_edges.append(edge)
214
215 def get_label(state):
216 if state.label:
217 return state.label
218 l = [state.short_name]
219 if state.action:
220 if state.short_name == state.action:
221 l = []
222 l.append(state.action + '()')
223 return r'\n'.join(l)
224
225 def event_names(state):
226 event_names = []
227 for out_edge in state.out_edges:
228 event_names.extend(out_edge.event_names())
229 return event_names
230
231 def shape_str(state):
232 shape = BOX_SHAPES.get(state.kind, None)
233 if not shape:
234 return ''
235 return ',shape=%s' % shape
236
237 def __repr__(state):
238 return 'State(name=%r,short_name=%r,out=%d)' % (state.name, state.short_name, len(state.out_edges))
239
240class Fsm:
Neels Hofmeyra568af22017-10-23 04:03:19 +0200241 def __init__(fsm, struct_name, string_name, states_struct_name, from_file=None):
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100242 fsm.states = []
243 fsm.struct_name = struct_name
Neels Hofmeyra568af22017-10-23 04:03:19 +0200244 fsm.string_name = string_name
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100245 fsm.states_struct_name = states_struct_name
246 fsm.from_file = from_file
247 fsm.action_funcs = set()
248 fsm.event_names = set()
Neels Hofmeyra568af22017-10-23 04:03:19 +0200249 fsm.dot_name = fsm.all_names_sanitized()
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100250
251 def parse_states(fsm, src):
252 state = None
253 started = None
254
255 IN_EVENTS = 'events'
256 OUT_STATES = 'states'
257
258 lines = src.splitlines()
259
260 for line in lines:
261 state_name = state_starts(line)
262 if state_name:
263 state = State()
264 fsm.states.append(state)
265 started = None
266 state.name = state_name
267
268 if in_event_starts(line):
269 started = IN_EVENTS
270 if out_state_starts(line):
271 started = OUT_STATES
272
273 if not state or not started:
274 continue
275
276 tokens = states_or_events(line)
277 if started == IN_EVENTS:
278 state.in_event_names.extend(tokens)
279 elif started == OUT_STATES:
280 state.out_state_names.extend(tokens)
281 else:
282 err('ignoring: %r' % tokens)
283
284 a = parse_action(line)
285 if a:
286 state.action = a
287
288
289 for state in fsm.states:
290 if state.action:
291 fsm.action_funcs.add(state.action)
292 if state.in_event_names:
293 fsm.event_names.update(state.in_event_names)
294
295 fsm.make_states_short_names()
296 fsm.ref_out_states()
297
298 def make_states_short_names(fsm):
299 p = common_prefix([s.name for s in fsm.states])
300 for s in fsm.states:
301 s.short_name = s.name[len(p):]
302 return p
303
304 def make_events_short_names(fsm):
305 p = common_prefix(fsm.event_names)
306 for state in fsm.states:
307 for edge in state.out_edges:
308 for event in edge.events:
309 event.short_name = event.name[len(p):]
310
311 def ref_out_states(fsm):
312 for state in fsm.states:
313 for e in [Edge(fsm.find_state_by_name(n, True)) for n in state.out_state_names]:
314 state.add_out_edge(e)
315
316 def find_state_by_name(fsm, name, strict=False):
317 for state in fsm.states:
318 if state.name == name:
319 return state
320 if strict:
321 raise Exception("State not found: %r" % name);
322 return None
323
324 def find_state_by_action(fsm, action):
325 for state in fsm.states:
326 if state.action == action:
327 return state
328 return None
329
330 def add_special_state(fsm, additional_states, name, in_state=None,
331 out_state=None, event_name=None, kind=KIND_FUNC,
332 state_action=None, label=None, edge_action=None):
333 additional_state = None
334 for s in additional_states:
335 if s.short_name == name:
336 additional_state = s
337 break;
338
339 if not additional_state:
340 for s in fsm.states:
341 if s.short_name == name:
342 additional_state = s
343 break;
344
345 if kind == KIND_FUNC and not state_action:
346 state_action = name
347
348 if not additional_state:
349 additional_state = State()
350 additional_state.short_name = name
351 additional_state.action = state_action
352 additional_state.kind = kind
353 additional_state.label = label
354 additional_states.append(additional_state)
355
356 if out_state:
357 additional_state.out_state_names.append(out_state.name)
358 additional_state.add_out_edge(Edge(out_state, event_name, 'dotted',
359 action=edge_action))
360
361 if in_state:
362 in_state.out_state_names.append(additional_state.name)
363 in_state.add_out_edge(Edge(additional_state, event_name, 'dotted',
364 action=edge_action))
365
366
367 def find_event_edges(fsm, c_files):
368 # enrich state transitions between the states with event labels
369 func_to_state_transitions = listdict()
370 for c_file in c_files:
371 func_to_state_transitions.update( c_file.find_state_transitions(fsm.event_names) )
372
373 # edges between explicit states
374 for state in fsm.states:
375 transitions = func_to_state_transitions.get(state.action)
376 if not transitions:
377 continue
378
379 for to_state_name, event_name in transitions:
380 if not event_name:
381 continue
382 for out_edge in state.out_edges:
383 if out_edge.to_state.name == to_state_name:
384 out_edge.add_event_name(event_name)
385
386 additional_states = []
387
388
389 # functions that aren't state actions but still effect state transitions
390 for func_name, transitions in func_to_state_transitions.items():
391 if func_name in fsm.action_funcs:
392 continue
393 for to_state_name, event_name in transitions:
394 to_state = fsm.find_state_by_name(to_state_name)
395 if not to_state:
396 continue
397 fsm.add_special_state(additional_states, func_name, None, to_state, event_name)
398
399
400 event_sources = c_files.find_event_sources(fsm.event_names)
401
402 for state in fsm.states:
403
404 for in_event_name in state.in_event_names:
405 funcs_for_in_event = event_sources.get(in_event_name)
406 if not funcs_for_in_event:
407 continue
408
409 found = False
410 for out_edge in state.out_edges:
411 if out_edge.has_event_name(in_event_name):
412 out_edge.action = r'\n'.join([(f + '()') for f in funcs_for_in_event
413 if f != state.action])
414
415 # if any functions that don't belong to a state trigger events, add
416 # them to the graph as well
417 additional_funcs = [f for f in funcs_for_in_event if f not in fsm.action_funcs]
418 for af in additional_funcs:
419 fsm.add_special_state(additional_states, af, None, state, in_event_name)
420
421 fsm.states.extend(additional_states)
422
423 # do any existing action functions by chance call other action functions?
424 for state in fsm.states:
425 if not state.action:
426 continue
427 callers = c_files.find_callers(state.action)
428 if not callers:
429 continue
430 for other_state in fsm.states:
431 if other_state.action in callers:
432 other_state.add_out_edge(Edge(state, None, 'dotted'))
433
434 def add_fsm_alloc(fsm, c_files):
435
436 allocating_funcs = []
437 for c_file in c_files:
438 allocating_funcs.extend(c_file.fsm_allocators.get(fsm.struct_name, []))
439
440 starting_state = None
441 if fsm.states:
442 # assume the first state starts
443 starting_state = fsm.states[0]
444
445 additional_states = []
446 for func_name in allocating_funcs:
447 fsm.add_special_state(additional_states, func_name, None, starting_state)
448
449 fsm.states.extend(additional_states)
450
451 def add_cross_fsm_links(fsm, fsms, c_files, fsm_meta):
452 for state in fsm.states:
453 if not state.action:
454 continue
455 if state.kind == KIND_FSM:
456 continue
457 callers = c_files.find_callers(state.action)
458
459 if state.kind == KIND_FUNC:
460 callers.append(state.action)
461
462 if not callers:
463 continue
464
465 for caller in callers:
466 for calling_fsm in fsms:
467 if calling_fsm is fsm:
468 continue
469 calling_state = calling_fsm.find_state_by_action(caller)
470 if not calling_state:
471 continue
472 if calling_state.kind == KIND_FSM:
473 continue
474
475 label = None
476 if state.kind == KIND_STATE:
477 label=fsm.struct_name + ': ' + state.short_name
478 edge_action = caller
479 if calling_state.action == edge_action:
480 edge_action = None
Neels Hofmeyra568af22017-10-23 04:03:19 +0200481 calling_fsm.add_special_state(calling_fsm.states, fsm.dot_name,
482 calling_state, kind=KIND_FSM, edge_action=edge_action, label=' '.join(fsm.all_names()))
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100483
484 label = None
485 if calling_state.kind == KIND_STATE:
486 label=calling_fsm.struct_name + ': ' + calling_state.short_name
487 edge_action = caller
488 if state.action == edge_action:
489 edge_action = None
Neels Hofmeyra568af22017-10-23 04:03:19 +0200490 fsm.add_special_state(fsm.states, calling_fsm.dot_name, None,
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100491 state, kind=KIND_FSM, edge_action=edge_action,
492 label=label)
493
494 # meta overview
Neels Hofmeyra568af22017-10-23 04:03:19 +0200495 meta_called_fsm = fsm_meta.have_state(fsm.dot_name, KIND_FSM)
496 meta_calling_fsm = fsm_meta.have_state(calling_fsm.dot_name, KIND_FSM)
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100497 meta_calling_fsm.add_out_edge(Edge(meta_called_fsm))
498
499
500 def have_state(fsm, name, kind=KIND_STATE):
501 state = fsm.find_state_by_name(name)
502 if not state:
503 state = State()
504 state.name = name
505 state.short_name = name
506 state.kind = kind
507 fsm.states.append(state)
508 return state
509
510 def to_dot(fsm):
511 out = ['digraph G {', 'rankdir=LR;']
512
513 for state in fsm.states:
514 out.append('%s [label="%s"%s]' % (state.short_name, state.get_label(),
515 state.shape_str()))
516
517 for state in fsm.states:
518 for out_edge in state.out_edges:
519 attrs = []
520 labels = []
521 if out_edge.events:
522 labels.extend(out_edge.event_labels())
523 if out_edge.actions:
524 labels.extend(out_edge.action_labels())
525 if labels:
526 attrs.append('label="%s"' % (r'\n'.join(labels)))
527 if out_edge.style:
528 attrs.append('style=%s'% out_edge.style)
529 attrs_str = ''
530 if attrs:
531 attrs_str = ' [%s]' % (','.join(attrs))
532 out.append('%s->%s%s' % (state.short_name, out_edge.to_state.short_name, attrs_str))
533
534 out.append('}\n')
535
536 return '\n'.join(out)
537
Neels Hofmeyra568af22017-10-23 04:03:19 +0200538 def all_names(fsm):
539 n = []
540 if fsm.from_file:
541 n.append(os.path.basename(fsm.from_file.path))
542 if fsm.struct_name:
543 n.append(fsm.struct_name)
544 if fsm.string_name:
545 n.append(fsm.string_name)
546 return n
547
548 def all_names_sanitized(fsm, sep='_'):
549 n = sep.join(fsm.all_names())
550 n = re_insane_dot_name_chars.sub('_', n)
551 return n
552
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100553 def write_dot_file(fsm):
Neels Hofmeyra568af22017-10-23 04:03:19 +0200554 dot_path = '%s.dot' % ('_'.join(fsm.all_names()))
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100555 f = open(dot_path, 'w')
556 f.write(fsm.to_dot())
557 f.close()
558 print(dot_path)
559
560
561re_fsm = re.compile(r'struct osmo_fsm ([a-z_][a-z_0-9]*) =')
Neels Hofmeyra568af22017-10-23 04:03:19 +0200562re_fsm_string_name = re.compile(r'\bname = "([^"]*)"')
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100563re_fsm_states_struct_name = re.compile(r'\bstates = ([a-z_][a-z_0-9]*)\W*,')
564re_fsm_states = re.compile(r'struct osmo_fsm_state ([a-z_][a-z_0-9]*)\[\] =')
565re_func = re.compile(r'(\b[a-z_][a-z_0-9]*\b)\([^)]*\)\W*^{', re.MULTILINE)
566re_state_trigger = re.compile(r'osmo_fsm_inst_state_chg\([^,]+,\W*([A-Z_][A-Z_0-9]*)\W*,', re.M)
567re_fsm_alloc = re.compile(r'osmo_fsm_inst_alloc[_child]*\(\W*&([a-z_][a-z_0-9]*),', re.M)
568re_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 +0100569re_comment_multiline = re.compile(r'/\*.*?\*/', re.M | re.S)
570re_comment_single_line = re.compile(r'//.*$', re.M | re.S)
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100571
572class CFile():
573 def __init__(c_file, path):
574 c_file.path = path
575 c_file.src = open(path).read()
576 c_file.funcs = {}
577 c_file.fsm_allocators = listdict()
578
579 def extract_block(c_file, brace_open, brace_close, start):
580 pos = 0
581 try:
582 src = c_file.src
583 block_start = src.find(brace_open, start)
584
585 pos = block_start
586 level = 1
587 while level > 0:
588 pos += 1
589 if src[pos] == brace_open:
590 level += 1
591 elif src[pos] == brace_close:
592 level -= 1
593
594 return src[block_start+1:pos]
595 except:
596 print("Error while trying to extract a code block from %r char pos %d" % (c_file.path, pos))
597 print("Block start at char pos %d" % block_start)
598 try:
599 print(src[block_start - 20 : block_start + 20])
600 print('...')
601 print(src[pos - 20 : pos + 20])
602 except:
603 pass
604 return ''
605
606
607 def find_fsms(c_file):
608 fsms = []
609 for m in re_fsm.finditer(c_file.src):
610 struct_name = m.group(1)
611 struct_def = c_file.extract_block('{', '}', m.start())
Neels Hofmeyra568af22017-10-23 04:03:19 +0200612 string_name = (re_fsm_string_name.findall(struct_def) or [None])[0]
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100613 states_struct_name = re_fsm_states_struct_name.findall(struct_def)[0]
Neels Hofmeyra568af22017-10-23 04:03:19 +0200614 fsm = Fsm(struct_name, string_name, states_struct_name, c_file)
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100615 fsms.append(fsm)
616 return fsms
617
618 def find_fsm_states(c_file, fsms):
619 for m in re_fsm_states.finditer(c_file.src):
620 states_struct_name = m.group(1)
621 for fsm in fsms:
622 if states_struct_name == fsm.states_struct_name:
623 fsm.parse_states(c_file.extract_block('{', '}', m.start()))
624
625 def parse_functions(c_file):
626 funcs = {}
627 for m in re_func.finditer(c_file.src):
628 name = m.group(1)
629 func_src = c_file.extract_block('{', '}', m.start())
Neels Hofmeyrbb22df32018-03-25 01:03:26 +0100630 func_src = ''.join(re_comment_multiline.split(func_src))
631 func_src = ''.join(re_comment_single_line.split(func_src))
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100632 funcs[name] = func_src
633 c_file.funcs = funcs
634 c_file.find_fsm_allocators()
635
636 def find_callers(c_file, func_name):
637 func_call = func_name + '('
638 callers = []
639 for func_name, src in c_file.funcs.items():
640 if src.find(func_call) >= 0:
641 callers.append(func_name)
642 return callers
643
644 def find_fsm_allocators(c_file):
645 c_file.fsm_allocators = listdict()
646 for func_name, src in c_file.funcs.items():
647 for m in re_fsm_alloc.finditer(src):
648 fsm_struct_name = m.group(1)
649 c_file.fsm_allocators.add(fsm_struct_name, func_name)
650
651 def find_state_transitions(c_file, event_names):
652 TO_STATE = 'TO_STATE'
653 EVENT = 'EVENT'
654 func_to_state_transitions = listdict()
655
656 for func_name, src in c_file.funcs.items():
657 found_tokens = []
658
659 for m in re_state_trigger.finditer(src):
660 to_state = m.group(1)
661 found_tokens.append((m.start(), TO_STATE, to_state))
662
663 for event in event_names:
664 re_event = re.compile(r'\b(' + event + r')\b')
665 for m in re_event.finditer(src):
666 event = m.group(1)
667 found_tokens.append((m.start(), EVENT, event))
668
669 found_tokens = sorted(found_tokens)
670
671 last_event = None
672 for start, kind, name in found_tokens:
673 if kind == EVENT:
674 last_event = name
675 else:
676 func_to_state_transitions.add(func_name, (name, last_event))
677
678 return func_to_state_transitions
679
680
681 def find_event_sources(c_file, event_names):
682 c_file.event_sources = listdict()
683 for func_name, src in c_file.funcs.items():
684 for m in re_fsm_event_dispatch.finditer(src):
685 event_name = m.group(1)
686 c_file.event_sources.add(event_name, func_name)
687
688class CFiles(list):
689
690 def find_callers(c_files, func_name):
691 callers = []
692 for c_file in c_files:
693 callers.extend(c_file.find_callers(func_name))
694 return callers
695
696 def find_func_to_state_transitions(c_files):
697 func_to_state_transitions = listdict()
698 for c_file in c_files:
699 func_to_state_transitions.update( c_file.find_state_transitions(fsm.event_names) )
700 return func_to_state_transitions
701
702 def find_event_sources(c_files, event_names):
703 event_sources = listdict()
704 for c_file in c_files:
705 for event, sources in c_file.event_sources.items():
706 if event in event_names:
707 event_sources.extend(event, sources)
708 return event_sources
709
710c_files = CFiles()
711paths_seen = set()
712for path in sys.argv[1:]:
713 if path in paths_seen:
714 continue
715 paths_seen.add(path)
716 c_file = CFile(path)
717 c_files.append(c_file)
718
719for c_file in c_files:
720 c_file.parse_functions()
721
722fsms = []
723for c_file in c_files:
724 fsms.extend(c_file.find_fsms())
725
726for c_file in c_files:
727 c_file.find_fsm_states(fsms)
728 c_file.find_event_sources(fsms)
729
730for fsm in fsms:
731 fsm.find_event_edges(c_files)
732 fsm.add_fsm_alloc(c_files)
733
Neels Hofmeyra568af22017-10-23 04:03:19 +0200734fsm_meta = Fsm("meta", None, "meta")
Neels Hofmeyr0898a002016-11-16 14:36:29 +0100735for fsm in fsms:
736 fsm.add_cross_fsm_links(fsms, c_files, fsm_meta)
737
738for fsm in fsms:
739 fsm.make_events_short_names()
740
741for fsm in fsms:
742 fsm.write_dot_file()
743
744fsm_meta.write_dot_file()
745
746
747# vim: tabstop=2 shiftwidth=2 expandtab