1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.statemachine;
19
20 import java.io.BufferedReader;
21 import java.io.FileInputStream;
22 import java.io.IOException;
23 import java.io.InputStreamReader;
24 import java.nio.charset.Charset;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33
34 public class StateMachineLogParser {
35
36 static Pattern fsmPattern = Pattern.compile("FSM-(\\d+):");
37
38 static Map<String, List<String>> getFsmEventMap(BufferedReader f) throws IOException {
39 String s = f.readLine();
40 Map<String, List<String>> map = new HashMap<String, List<String>>();
41 while (s != null) {
42 Matcher m = fsmPattern.matcher(s);
43 if (m.find()) {
44 String key = m.group(1);
45 if (!map.containsKey(key)) {
46 map.put(key, new ArrayList<String>());
47 }
48 map.get(key).add(s);
49 }
50 s = f.readLine();
51 }
52 return map;
53 }
54
55 static class Tuple {
56 final String state1;
57 final String state2;
58 final String event;
59
60 Tuple(String state1, String event, String state2) {
61 this.state1 = state1;
62 this.state2 = state2;
63 this.event = event;
64 }
65
66 @Override
67 public boolean equals(Object o) {
68 if (o instanceof Tuple) {
69 Tuple t = (Tuple) o;
70 return state1.equals(t.state1)
71 && state2.equals(t.state2)
72 && event.equals(t.event);
73 }
74 return false;
75 }
76
77 @Override
78 public int hashCode() {
79 return (state1 + event + state2).hashCode();
80 }
81
82 @Override
83 public String toString() {
84 return state1 + " + " + event + " = " + state2;
85 }
86 }
87
88 static Pattern hashcodePattern = Pattern.compile("(.+)@(.+)");
89 static Pattern subclassPattern = Pattern.compile("(.+)\\$(.+)");
90
91 static String cleanupState(String state) {
92 Matcher m = hashcodePattern.matcher(state);
93 if (m.find()) {
94 state = m.group(1);
95 }
96 m = subclassPattern.matcher(state);
97 if (m.find()) {
98 state = m.group(2);
99 }
100 return state;
101 }
102
103 static Pattern eventPattern = Pattern.compile("Received event (.+)@\\d+ in state (.+)@\\d+");
104 static Pattern transitionPattern = Pattern.compile("State transition (.+) -> (.+)");
105
106 static Set<Tuple> getStateTuples(Map<String, List<String>> fsms) {
107 Set<Tuple> tuples = new HashSet<Tuple>();
108 for (List<String> transitions : fsms.values()) {
109 String currentEvent = null;
110 for (String s : transitions) {
111
112 Matcher m = eventPattern.matcher(s);
113 if (m.find()) {
114 currentEvent = m.group(1);
115 continue;
116 }
117 m = transitionPattern.matcher(s);
118 if (m.find()) {
119 if (currentEvent == null) {
120 System.err.println("event is null");
121 }
122 String state1 = m.group(1);
123 String state2 = m.group(2);
124 tuples.add(new Tuple(cleanupState(state1),
125 currentEvent,
126 cleanupState(state2)));
127 continue;
128 }
129 if (s.contains("deferred")) {
130 currentEvent = currentEvent + "[deferred]";
131 }
132 }
133 }
134 return tuples;
135 }
136
137 static void drawDotGraph(Set<Tuple> tuples) {
138 Set<String> states = new HashSet<String>();
139 for (Tuple t : tuples) {
140 states.add(t.state1);
141 states.add(t.state2);
142 }
143
144 System.out.println("digraph finite_state_machine {");
145 for (String s : states) {
146 System.out.println("\tnode [ shape = circle ] " + s + ";");
147 }
148 for (Tuple t : tuples) {
149 System.out.println("\t" + t.state1 + " -> " + t.state2 + " [ label = \"" + t.event + "\" ];");
150 }
151 System.out.println("}");
152 }
153
154 public static void main(String[] args) throws Exception {
155 if (args.length == 0) {
156 System.out.println("Usage: StateMachineLogParser <logfile> | dot -Tsvg ");
157 System.exit(-1);
158 }
159 BufferedReader f = new BufferedReader(new InputStreamReader(new FileInputStream(args[0]), Charset.forName("UTF-8")));
160 Map<String, List<String>> fsms = getFsmEventMap(f);
161 Set<Tuple> tuples = getStateTuples(fsms);
162 drawDotGraph(tuples);
163 }
164 }