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