libutap  0.93
Uppaal Timed Automata Parser
tracer.cpp
Go to the documentation of this file.
1 // -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; indent-tabs-mode: nil; -*-
2 
3 /* tracer - Utility for printing UPPAAL XTR trace files.
4  Copyright (C) 2006 Uppsala University and Aalborg University.
5  Copyright (C) 2017 Aalborg University.
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Lesser General Public License
9  as published by the Free Software Foundation; either version 2.1 of
10  the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License for more details.
16 
17  You should have received a copy of the GNU Lesser General Public
18  License along with this library; if not, write to the Free Software
19  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20  USA
21 */
22 
23 #include <cassert>
24 #include <cstdlib>
25 #include <cstring>
26 #include <fstream>
27 #include <iostream>
28 #include <limits>
29 #include <map>
30 #include <stdexcept>
31 #include <string>
32 #include <vector>
33 
34 /* This utility takes an UPPAAL model in the UPPAAL intermediate
35  * format and a UPPAAL XTR trace file and prints trace to stdout in a
36  * human readable format.
37  *
38  * The utility basically contains two parsers: One for the
39  * intermediate format and one for the XTR format. You may want to use
40  * them a starting point for writing analysis tools.
41  *
42  * Notice that the intermediate format uses a global numbering of
43  * clocks, variables, locations, etc. This is in contrast to the XTR
44  * format, which makes a clear distinction between e.g. clocks and
45  * variables and uses process local number of locations and
46  * edges. Care must be taken to convert between these two numbering
47  * schemes.
48  */
49 
50 using std::ostream;
51 using std::istream;
52 using std::ifstream;
53 using std::cout;
54 using std::cerr;
55 using std::endl;
56 using std::string;
57 using std::vector;
58 using std::map;
59 
60 
61 /* Representation of a memory cell.
62  */
63 struct cell_t
64 {
65  enum type_t: int { CONST, CLOCK, VAR, META, SYS_META, COST, LOCATION, FIXED };
66  enum flags_t: int { NONE, COMMITTED, URGENT };
68  type_t type;
69 
71  string name;
72 
73  union
74  {
75  int value;
76  struct
77  {
78  int nr;
79  } clock;
80  struct
81  {
82  int min;
83  int max;
84  int init;
85  int nr;
86  } var;
87  struct
88  {
89  int min;
90  int max;
91  int init;
92  int nr;
93  } meta;
94  struct
95  {
96  int min;
97  int max;
98  } sys_meta;
99  struct
100  {
101  flags_t flags;
102  int process;
103  int invariant;
104  } location;
105  struct
106  {
107  int min;
108  int max;
109  } fixed;
110  };
111 };
112 
113 /* Representation of a process.
114  */
115 struct process_t
116 {
117  int initial;
118  string name;
119  vector<int> locations;
120  vector<int> edges;
121 };
122 
123 /* Representation of an edge.
124  */
125 struct edge_t
126 {
127  int process;
128  int source;
129  int target;
130  int guard;
131  int sync;
132  int update;
133 };
134 
135 /* The UPPAAL model in intermediate format.
136  */
137 static vector<cell_t> layout;
138 static vector<int> instructions;
139 static vector<process_t> processes;
140 static vector<edge_t> edges;
141 static map<int,string> expressions;
142 
143 /* For convenience we keep the size of the system here.
144  */
145 static size_t processCount = 0;
146 static size_t variableCount = 0;
147 static size_t clockCount = 0;
148 
149 /* These are mappings from variable and clock indicies to
150  * the names of these variables and clocks.
151  */
152 static vector<string> clocks;
153 static vector<string> variables;
154 
155 /* Thrown by parser upon parse errors.
156  */
157 class invalid_format : public std::runtime_error
158 {
159 public:
160  explicit invalid_format(const string& arg) : runtime_error(arg) {}
161 };
162 
163 /* Reads one line from file. Skips comments.
164  */
165 bool read(istream& file, string& str)
166 {
167  do
168  {
169  if (!getline(file, str))
170  {
171  return false;
172  }
173  } while (!str.empty() && str[0] == '#');
174  return true;
175 }
176 
177 /* Reads one line and asserts that it contains a (terminating) dot
178  */
179 istream& readdot(istream& is)
180 {
181  string str;
182  getline(is, str);
183  if (str.empty())
184  {
185  getline(is, str);
186  }
187  if (str != ".")
188  {
189  cerr << "Expecting a line with '.' but got '" << str << "'" << endl;
190  assert(false);
191  exit(EXIT_FAILURE);
192  }
193  return is;
194 }
195 
196 inline
197 istream& skipspaces(istream& is)
198 {
199  while (is.peek() == ' ')
200  {
201  is.get();
202  }
203  return is;
204 }
205 
206 /* Parser for intermediate format.
207  */
208 void loadIF(istream& file)
209 {
210  string str;
211  string section;
212  char name[32];
213  int index;
214 
215  while (getline(file, section))
216  {
217  if (section == "layout")
218  {
219  cell_t cell;
220  while (read(file, str) && !str.empty() && !isspace(str[0]))
221  {
222  char s[5];
223  auto cstr = str.c_str();
224 
225  if (sscanf(cstr, "%d:clock:%d:%31s", &index,
226  &cell.clock.nr, name) == 3)
227  {
228  cell.type = cell_t::CLOCK;
229  cell.name = name;
230  clocks.emplace_back(name);
231  clockCount++;
232  }
233  else if (sscanf(cstr, "%d:const:%d", &index,
234  &cell.value) == 2)
235  {
236  cell.type = cell_t::CONST;
237  }
238  else if (sscanf(cstr, "%d:var:%d:%d:%d:%d:%31s", &index,
239  &cell.var.min, &cell.var.max, &cell.var.init,
240  &cell.var.nr, name) == 6)
241  {
242  cell.type = cell_t::VAR;
243  cell.name = name;
244  variables.emplace_back(name);
245  variableCount++;
246  }
247  else if (sscanf(cstr, "%d:meta:%d:%d:%d:%d:%31s", &index,
248  &cell.meta.min, &cell.meta.max, &cell.meta.init,
249  &cell.meta.nr, name) == 6)
250  {
251  cell.type = cell_t::META;
252  cell.name = name;
253  variables.emplace_back(name);
254  variableCount++;
255  }
256  else if (sscanf(cstr, "%d:sys_meta:%d:%d:%31s", &index,
257  &cell.sys_meta.min, &cell.sys_meta.max, name) == 4)
258  {
259  cell.type = cell_t::SYS_META;
260  cell.name = name;
261  }
262  else if (sscanf(cstr, "%d:location::%31s", &index, name) == 2)
263  {
264  cell.type = cell_t::LOCATION;
265  cell.location.flags = cell_t::NONE;
266  cell.name = name;
267  }
268  else if (sscanf(cstr, "%d:location:committed:%31s", &index, name) == 2)
269  {
270  cell.type = cell_t::LOCATION;
271  cell.location.flags = cell_t::COMMITTED;
272  cell.name = name;
273  }
274  else if (sscanf(cstr, "%d:location:urgent:%31s", &index, name) == 2)
275  {
276  cell.type = cell_t::LOCATION;
277  cell.location.flags = cell_t::URGENT;
278  cell.name = name;
279  }
280  else if (sscanf(cstr, "%d:static:%d:%d:%31s", &index,
281  &cell.fixed.min, &cell.fixed.max,
282  name) == 4)
283  {
284  cell.type = cell_t::FIXED;
285  cell.name = name;
286  }
287  else if (sscanf(cstr, "%d:%5s", &index, s) == 2
288  && strcmp(s, "cost") == 0)
289  {
290  cell.type = cell_t::COST;
291  }
292  else
293  {
294  throw invalid_format(str);
295  }
296 
297  layout.push_back(cell);
298  }
299 #if defined(ENABLE_CORA) || defined(ENABLE_PRICED)
300  cell.type = cell_t::VAR;
301  cell.var.min = std::numeric_limits<int32_t>::min();
302  cell.var.max = std::numeric_limits<int32_t>::max();
303  cell.var.init = 0;
304 
305  cell.name = "infimum_cost";
306  cell.var.nr = variableCount++;
307  variables.push_back(cell.name);
308  layout.push_back(cell);
309 
310  cell.name = "offset_cost";
311  cell.var.nr = variableCount++;
312  variables.push_back(cell.name);
313  layout.push_back(cell);
314 
315  for (size_t i=1; i<clocks.size(); ++i) {
316  cell.name = "#rate[";
317  cell.name.append(clocks[i]);
318  cell.name.append("]");
319  cell.var.nr = variableCount++;
320  variables.push_back(cell.name);
321  layout.push_back(cell);
322  }
323 #endif
324  }
325  else if (section == "instructions")
326  {
327  while (read(file, str) && !str.empty() && (!isspace(str[0]) || str[0]=='\t'))
328  {
329  int address;
330  int values[4];
331  if (str[0]=='\t')
332  { // skip pretty-printed instruction text
333  continue;
334  }
335  int cnt = sscanf(str.c_str(), "%d:%d%d%d%d", &address,
336  &values[0], &values[1], &values[2], &values[3]);
337  if (cnt < 2)
338  {
339  throw invalid_format("In instruction section");
340  }
341 
342  for (int i = 0; i < cnt-1; ++i)
343  {
344  instructions.push_back(values[i]);
345  }
346  }
347  }
348  else if (section == "processes")
349  {
350  while (read(file, str) && !str.empty() && !isspace(str[0]))
351  {
352  process_t process;
353  if (sscanf(str.c_str(), "%d:%d:%31s",
354  &index, &process.initial, name) != 3)
355  {
356  throw invalid_format("In process section");
357  }
358  process.name = name;
359  processes.push_back(process);
360  processCount++;
361  }
362  }
363  else if (section == "locations")
364  {
365  while (read(file, str) && !str.empty() && !isspace(str[0]))
366  {
367  int index;
368  int process;
369  int invariant;
370 
371  if (sscanf(str.c_str(), "%d:%d:%d", &index, &process, &invariant) != 3)
372  {
373  throw invalid_format("In location section");
374  }
375 
376  layout[index].location.process = process;
377  layout[index].location.invariant = invariant;
378  processes[process].locations.push_back(index);
379  }
380  }
381  else if (section == "edges")
382  {
383  while (read(file, str) && !str.empty() && !isspace(str[0]))
384  {
385  edge_t edge;
386 
387  if (sscanf(str.c_str(), "%d:%d:%d:%d:%d:%d", &edge.process,
388  &edge.source, &edge.target,
389  &edge.guard, &edge.sync, &edge.update) != 6)
390  {
391  throw invalid_format("In edge section");
392  }
393 
394  processes[edge.process].edges.push_back(edges.size());
395  edges.push_back(edge);
396  }
397  }
398  else if (section == "expressions")
399  {
400  while (read(file, str) && !str.empty() && !isspace(str[0]))
401  {
402  if (sscanf(str.c_str(), "%d", &index) != 1)
403  {
404  throw invalid_format("In expression section");
405  }
406 
407  /* Find expression string (after the third colon). */
408  auto *s = str.c_str();
409  int cnt = 3;
410  while (cnt && *s)
411  {
412  cnt -= (*s == ':');
413  s++;
414  }
415  if (cnt)
416  {
417  throw invalid_format("In expression section");
418  }
419 
420  /* Trim white space. */
421  while (*s && isspace(*s))
422  {
423  s++;
424  }
425  auto *t = s + strlen(s) - 1;
426  while (t >= s && isspace(*t))
427  {
428  t--;
429  }
430 
431  expressions[index] = string(s, t+1);
432  }
433  }
434  else
435  {
436  throw invalid_format("Unknown section");
437  }
438  }
439 };
440 
441 /* A bound for a clock constraint. A bound consists of a value and a
442  * bit indicating whether the bound is strict or not.
443  */
444 struct bound_t
445 {
446  int value : 31; // The value of the bound
447  bool strict : 1; // True if the bound is strict
448 };
449 
450 /* The bound (infinity, <).
451  */
452 static bound_t infinity = { std::numeric_limits<int32_t>::max() >> 1, true };
453 
454 /* The bound (0, <=).
455  */
456 static bound_t zero = { 0, false };
457 
458 /* A symbolic state. A symbolic state consists of a location vector, a
459  * variable vector and a zone describing the possible values of the
460  * clocks in a symbolic manner.
461  */
462 class State
463 {
464 public:
465  State();
466  explicit State(istream& file);
467  State(const State& s) = delete;
468  State(State&& s) = delete;
469  ~State();
470 
471  int &getLocation(int i) { return locations[i]; }
472  int &getVariable(int i) { return integers[i]; }
473  bound_t &getConstraint(int i, int j) { return dbm[i * clockCount + j]; }
474 
475  int getLocation(int i) const { return locations[i]; }
476  int getVariable(int i) const { return integers[i]; }
477  bound_t getConstraint(int i, int j) const { return dbm[i * clockCount + j]; }
478 private:
479  vector<int> locations;
480  vector<int> integers;
481  bound_t *dbm;
482  void allocate();
483 };
484 
485 State::~State()
486 {
487  delete[] dbm;
488 }
489 
490 State::State()
491 {
492  /* Allocate. */
493  locations.resize(processCount);
494  integers.resize(variableCount);
495  dbm = new bound_t[clockCount * clockCount];
496 
497  /* Fill with default values. */
498  std::fill(dbm, dbm + clockCount * clockCount, infinity);
499 
500  /* Set diagonal and lower bounds to zero. */
501  for (size_t i = 0; i < clockCount; i++)
502  {
503  getConstraint(0, i) = zero;
504  getConstraint(i, i) = zero;
505  }
506 }
507 
508 State::State(istream& file): State()
509 {
510  /* Read locations. */
511  for (auto& l: locations)
512  {
513  file >> l;
514  }
515  file >> readdot;
516 
517  /* Read DBM. */
518  int i, j, bnd;
519  while (file >> i >> j >> bnd)
520  {
521  file >> readdot;
522  getConstraint(i, j).value = bnd >> 1;
523  getConstraint(i, j).strict = bnd & 1;
524  }
525  file.clear();
526  file >> readdot;
527 
528  /* Read integers. */
529  for (auto& v: integers)
530  {
531  file >> v;
532  }
533  file >> readdot;
534 }
535 
536 struct Edge
537 {
538  int process;
539  int edge;
540  vector<int> select;
541 };
542 
543 /* A transition consists of one or more edges. Edges are indexes from
544  * 0 in the order they appear in the input file.
545  */
546 struct Transition
547 {
548  vector<Edge> edges;
549  explicit Transition(istream& file);
550 };
551 
552 Transition::Transition(istream& file)
553 {
554  int process, edge, select;
555  while (file >> process >> edge)
556  {
557  Edge e{process, edge};
558  file >> skipspaces;
559  while (file.peek()!='\n' && file.peek()!=';')
560  {
561  if (file >> select)
562  {
563  e.select.push_back(select);
564  }
565  else
566  {
567  cerr << "Transition format error" << endl;
568  exit(EXIT_FAILURE);
569  }
570  file >> skipspaces;
571  }
572  if (file.get()=='\n') // old format without ';'
573  { // old format indexes edges from 1, hence convert to 0-base
574  e.edge--;
575  }
576  edges.push_back(e);
577  }
578  file.clear();
579  file >> readdot;
580 }
581 
582 /* Output operator for a symbolic state. Prints the location vector,
583  * the variables and the zone of the symbolic state.
584  */
585 ostream &operator << (ostream &o, const State &state)
586 {
587  /* Print location vector. */
588  for (size_t p = 0; p < processCount; p++)
589  {
590  int idx = processes[p].locations[state.getLocation(p)];
591  cout << processes[p].name << '.' << layout[idx].name << " ";
592  }
593 
594  /* Print variables. */
595  for (size_t v = 0; v < variableCount; v++)
596  {
597  cout << variables[v] << "=" << state.getVariable(v) << ' ';
598  }
599 
600  /* Print clocks. */
601  for (size_t i = 0; i < clockCount; i++)
602  {
603  for (size_t j = 0; j < clockCount; j++)
604  {
605  if (i != j)
606  {
607  bound_t bnd = state.getConstraint(i, j);
608 
609  if (bnd.value != infinity.value)
610  {
611  cout << clocks[i] << "-" << clocks[j]
612  << (bnd.strict ? "<" : "<=") << bnd.value << " ";
613  }
614  }
615  }
616  }
617 
618  return o;
619 }
620 
621 /* Output operator for a transition. Prints all edges in the
622  * transition including the source, destination, guard,
623  * synchronisation and assignment.
624  */
625 ostream &operator << (ostream &o, const Transition &t)
626 {
627  for (auto& edge: t.edges)
628  {
629  int eid = processes[edge.process].edges[edge.edge];
630  int src = edges[eid].source;
631  int dst = edges[eid].target;
632  int guard = edges[eid].guard;
633  int sync = edges[eid].sync;
634  int update = edges[eid].update;
635  cout << processes[edge.process].name << '.' << layout[src].name
636  << " -> "
637  << processes[edge.process].name << '.' << layout[dst].name;
638  if (!edge.select.empty()) {
639  auto s=edge.select.begin(), se=edge.select.end();
640  cout << " [" << *s;
641  while (++s != se) cout << "," << *s;
642  cout << "]";
643  }
644  cout << " {"
645  << expressions[guard] << "; " << expressions[sync] << "; " << expressions[update]
646  << ";} ";
647  }
648 
649  return o;
650 }
651 
652 /* Read and print a trace file.
653  */
654 void loadTrace(istream& file)
655 {
656  /* Read and print trace. */
657  cout << "State: " << State(file) << endl;
658  for (;;)
659  {
660  /* Skip white space. */
661  file >> skipspaces;
662 
663  /* A dot terminates the trace. */
664  if (file.peek() == '.')
665  {
666  file.get();
667  break;
668  }
669 
670  /* Read a state and a transition. */
671  State state(file);
672  Transition transition(file);
673 
674  /* Print transition and state. */
675  cout << "\nTransition: " << transition << endl
676  << "\nState: " << state << endl;
677  }
678 }
679 
680 
681 int main(int argc, char *argv[])
682 {
683  try
684  {
685  if (argc < 3)
686  {
687  printf("Synopsis: %s <if> <trace>\n", argv[0]);
688  exit(1);
689  }
690 
691  /* Load model in intermediate format.
692  */
693  if (strcmp(argv[1], "-") == 0)
694  {
695  loadIF(std::cin);
696  }
697  else
698  {
699  ifstream file(argv[1]);
700  if (!file)
701  {
702  perror(argv[1]);
703  exit(EXIT_FAILURE);
704  }
705  loadIF(file);
706  file.close();
707  }
708 
709  /* Load trace.
710  */
711  ifstream file(argv[2]);
712  if (!file)
713  {
714  perror(argv[2]);
715  exit(EXIT_FAILURE);
716  }
717  loadTrace(file);
718  file.close();
719  }
720  catch (std::exception &e)
721  {
722  cerr << "Cought exception: " << e.what() << endl;
723  }
724 }
void loadTrace(istream &file)
Definition: tracer.cpp:654
istream & readdot(istream &is)
Definition: tracer.cpp:179
static map< int, string > expressions
Definition: tracer.cpp:141
ostream & operator<<(ostream &o, const State &state)
Definition: tracer.cpp:585
static size_t variableCount
Definition: tracer.cpp:146
static size_t clockCount
Definition: tracer.cpp:147
static vector< edge_t > edges
Definition: tracer.cpp:140
static vector< string > variables
Definition: tracer.cpp:153
static bound_t zero
Definition: tracer.cpp:456
static vector< cell_t > layout
Definition: tracer.cpp:137
static vector< process_t > processes
Definition: tracer.cpp:139
static size_t processCount
Definition: tracer.cpp:145
static vector< int > instructions
Definition: tracer.cpp:138
bool read(istream &file, string &str)
Definition: tracer.cpp:165
istream & skipspaces(istream &is)
Definition: tracer.cpp:197
void loadIF(istream &file)
Definition: tracer.cpp:208
static vector< string > clocks
Definition: tracer.cpp:152
static bound_t infinity
Definition: tracer.cpp:452
int main(int argc, char *argv[])
Definition: tracer.cpp:681