libutap  0.93
Uppaal Timed Automata Parser
signalflow.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 /* libutap - Uppaal Timed Automata Parser.
4  Copyright (C) 2005-2006 Uppsala University and Aalborg University.
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Lesser General Public License
8  as published by the Free Software Foundation; either version 2.1 of
9  the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public
17  License along with this library; if not, write to the Free Software
18  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  USA
20 */
21 
22 #include "utap/signalflow.h"
23 #include "utap/common.h"
24 
25 #include <iostream>
26 #include <sstream>
27 #include <string>
28 #include <iterator>
29 #include <algorithm>
30 #include <functional>
31 #include <cassert>
32 
33 using std::list;
34 using std::deque;
35 using std::map;
36 using std::set;
37 using std::ostream;
38 using std::istream;
39 using std::string;
40 using std::ostream_iterator;
41 using std::cerr;
42 using std::endl;
44 using UTAP::SignalFlow;
45 using UTAP::Partitioner;
47 using namespace UTAP::Constants;
48 
49 
50 SignalFlow::SignalFlow(const char* _title, TimedAutomataSystem& tas):
51  verbosity(0), title(_title), cTA(NULL), cP(NULL), inp(false), out(false),
52  sync(false), paramsExpanded(false)
53 {
54 /*
55  * Visit all processes in the system.
56  * FIXME: it does not take care of autocompleted parameters,
57  * unfolding is intricate and is done outside UTAP.
58  */
59  list<instance_t> &ps(tas.getProcesses());
60  for (list<instance_t>::iterator it=ps.begin(), e=ps.end(); it!=e; ++it)
61  {
62  visitProcess(*it);
63  }
64 }
65 
66 void SignalFlow::printForTron(ostream &os)
67 {
68  for (procs_t::const_iterator p=procs.begin(), e=procs.end(); p!=e; ++p)
69  {
70  os << (*p)->name << ":\n "; // automaton:
71  // input channels,
72  for_each((*p)->inChans.begin(), (*p)->inChans.end(),
73  print<const char*>(os, " "));
74  os << ",\n ";
75  // output channels,
76  for_each((*p)->outChans.begin(), (*p)->outChans.end(),
77  print<const char*>(os, " "));
78  os << ",\n ";
79  str2strs_t::const_iterator v;
80  // input variables(channels),
81  for (v = (*p)->rdVars.begin(); v != (*p)->rdVars.end(); ++v)
82  {
83  os << " " << v->first << "(";
84  for_each(v->second.begin(), v->second.end(),
85  print<const char*>(os, " "));
86  os << ")";
87  }
88  os << ",\n ";
89  // output variables(channels);
90  for (v = (*p)->wtVars.begin(); v != (*p)->wtVars.end(); ++v)
91  {
92  os << " " << v->first << "(";
93  for_each(v->second.begin(), v->second.end(),
94  print<const char*>(os, " "));
95  os << ")";
96  }
97  os << "\n;\n";
98  }
99 }
100 
101 void SignalFlow::printProcsForDot(ostream &os, bool erd)
102 {
103 /* Enumerate processes with common look-attributes */
104  os << " subgraph procs {\n";
105  if (erd)
106  {
107  os<<" node [shape=rectangle,style=filled];\n";
108  }
109  else
110  {
111  os<<" node [shape=ellipse,style=filled];\n";
112  }
113  os << " ";
114 
115  for (procs_t::const_iterator p=procs.begin(), e=procs.end(); p!=e; ++p)
116  os << (*p)->name << "; ";
117  os << "\n }\n";
118 }
119 
120 void SignalFlow::printVarsForDot(ostream &os, bool ranked, bool erd)
121 {
122  // draw variables
123  /* specify attributes for all nodes */
124  os << " subgraph cluste_vars {\n";
125  os << " style=invis;\n";
126  if (ranked)
127  {
128  os << " rank=min;\n";
129  }
130  if (erd)
131  {
132  os << " node [shape=diamond,color=blue];\n ";
133  }
134  else
135  {
136  os << " node [shape=rectangle,color=blue];\n ";
137  }
138  for (strs_t::const_iterator i=variables.begin(), e=variables.end();
139  i!=e; ++i)
140  {
141  if (transmitters.find(*i) == transmitters.end())
142  {
143  os << *i << "; "; // const variables are slim
144  }
145  else
146  {
147  os << *i << "[style=bold]; "; // others bold
148  }
149  }
150  os << "\n }\n";
151 }
152 
154 {
155  /* 'variable write' edges are bold */
156  os<<" edge [style=bold,color=blue,fontcolor=blue,weight=100];\n";
157  /* draw variable write edges */
158  for (procs_t::const_iterator a=procs.begin(), ae=procs.end(); a!=ae; ++a)
159  {
160  if (!(*a)->wtVars.empty())
161  {
162  for (str2strs_t::const_iterator v=(*a)->wtVars.begin(),
163  ve=(*a)->wtVars.end(); v!=ve; ++v)
164  {
165  os<<" "<<(*a)->name<< " -> " << v->first << " [label=\"(";
166  /* enumerate channels when variable is accessed */
167  for_each(v->second.begin(), v->second.end(),
168  print<const char*>(os, ","));
169  os<<")\"];\n";
170  }
171  }
172  }
173 }
174 
176 {
177  /* 'variable read' edges are slim */
178  os<<" edge [style=solid,color=blue,fontcolor=blue,weight=1];\n";
179  /* draw variable edges */
180  for (procs_t::const_iterator a=procs.begin(),ae=procs.end(); a!=ae; ++a)
181  {
182  if (!(*a)->rdVars.empty())
183  {
184  for (str2strs_t::const_iterator v = (*a)->rdVars.begin(),
185  ve=(*a)->rdVars.end(); v!=ve; ++v)
186  {
187  os<<" " << v->first << " -> " <<(*a)->name<<" [label=\"(";
188  /* enumerate channels when variable is accessed */
189  for_each(v->second.begin(), v->second.end(),
190  print<const char*>(os, ","));
191  os<<")\"];\n";
192  }
193  }
194  }
195 }
196 
198 {
199  /* channels appear only on I/O edges */
200  const char* src = "NO_SRC";
201  const char* dst = "NO_DST";
202  /* build an index of outgoing edges sorted by destination TA */
203  for (procs_t::const_iterator a=procs.begin(),ae=procs.end(); a!=ae; ++a)
204  {
205  /* traverse all outgoing channels */
206  for (strs_t::const_iterator i=(*a)->outChans.begin(),
207  e=(*a)->outChans.end(); i!=e; ++i)
208  {
209  str2procs_t::const_iterator dests = receivers.find(*i);
210  if (dests != receivers.end())
211  { // there are destinations
212  for (procs_t::const_iterator rec = dests->second.begin();
213  rec != dests->second.end(); ++rec)
214  {
215  (*a)->outEdges[*rec].insert(*i);
216  }
217  }
218  else
219  { // no destination
220  (*a)->outEdges[NULL].insert(*i);
221  }
222  }
223  }
224  bool noDst = false;
225  /* display all edges: */
226  os<<" edge[style=solid,color=black,fontcolor=black,weight=50];\n";
227  for (procs_t::const_iterator a=procs.begin(),ae=procs.end(); a!=ae; ++a)
228  {
229  /* display outgoing edges: */
230  for (proc2strs_t::const_iterator edge = (*a)->outEdges.begin(),
231  edgeEnd=(*a)->outEdges.end(); edge!=edgeEnd; ++edge)
232  {
233  // display edge:
234  if (edge->first != NULL)
235  {
236  //normal destination TA
237  os<<" " <<(*a)->name << " -> " << edge->first->name
238  << " [label=\"[";
239  }
240  else
241  { // there was no destination TA
242  if (!noDst)
243  {
244  os<<" "<<dst
245  <<" [style=filled,fillcolor=red];\n";
246  noDst = true;
247  }
248  os<<" " <<(*a)->name << " -> " << dst
249  << " [label=\"[";
250  }
251  // enumerate all channels on the edge:
252  strs_t::const_iterator ch=edge->second.begin();
253  while (true)
254  {
255  os<< *ch;
256  ++ch;
257  if (ch != edge->second.end())
258  {
259  os << ",";
260  }
261  else
262  {
263  break;
264  }
265  }
266  os << "]\"];\n";
267  }
268  /* by now all inps with sources are displayed as outputs
269  * search and display inputs w/o sources */
270  bool noSrc = false;
271  for (strs_t::const_iterator i=(*a)->inChans.begin(),
272  e=(*a)->inChans.end(); i!=e; ++i)
273  {
274  str2procs_t::const_iterator trans = transmitters.find(*i);
275  if (trans==transmitters.end())
276  {
277  if (!noSrc)
278  {
279  os<<" "<<src
280  <<" [style=filled,fillcolor=red];\n";
281  noSrc = true;
282  }
283  os<<" "<<src<<" -> " << (*a)->name << " [label=\""
284  << *i << "\"];\n";
285  }
286  }
287  }
288 }
289 
290 void SignalFlow::printChansSeparateForDot(ostream &os, bool ranked, bool erd)
291 {
292  /* channels displayed as separate nodes (like variables) */
293  os<< " subgraph cluste_chans {\n";
294  os<< " style=invis;\n";
295  if (ranked)
296  {
297  os<<" rank=max;\n";
298  }
299  if (erd)
300  {
301  os<<" node [shape=diamond,color=red];\n ";
302  }
303  else
304  {
305  os<<" node [shape=diamond,color=red];\n ";
306  }
307  for_each(channels.begin(), channels.end(),
308  print<const char*>(os, "; "));
309  os<< "\n }\n";
310 
311  for (procs_t::const_iterator a=procs.begin(),ae=procs.end(); a!=ae; ++a)
312  {
313  for (strs_t::const_iterator i=(*a)->inChans.begin(),
314  e=(*a)->inChans.end(); i!=e; ++i)
315  {
316  os << " " << *i << " -> " << (*a)->name << ";\n";
317  }
318  for (strs_t::const_iterator i=(*a)->outChans.begin(),
319  e=(*a)->outChans.end(); i!=e; ++i)
320  {
321  os << " " << (*a)->name << " -> " << *i << " [style=bold];\n";
322  }
323  }
324 }
325 
326 void SignalFlow::printForDot(ostream &os, bool ranked, bool erd, bool cEdge)
327 {
328  char* name = strcpy(new char[strlen(title)+1], title);
329  for (uint32_t i=0, len=strlen(name); i<len; ++i)
330  {
331  if (!isalpha((unsigned char)name[i])) // Cast to avoid undefined behavior.
332  {
333  name[i]='_';
334  }
335  }
336 
337  os << "digraph " << name << " {\n";
338  os << " model=subset; remincross=true;\n";
339 // os << " edge [decorate];\n"; // use 'dot -Edecorate' if you wish
340  delete [] name;
341 
342  if (!procs.empty()) printProcsForDot(os, erd);
343 
344 /* Enumerate variables, with common look-attributes */
345  if (!variables.empty()) {
346  printVarsForDot(os, ranked, erd);
349  }
350 
351 /* enumerate draw channels */
352  if (!channels.empty()) {
353  if (cEdge) printChansOnEdgesForDot(os);
354  else printChansSeparateForDot(os, ranked, erd);
355  }
356  os << "}" << endl;
357 }
358 
360 {
361  if (!paramsExpanded)
362  {
363  if (0<=cP->templ->parameters.getIndexOf(s.getName()))
364  {
365  // is it parameter? find the corresponding global symbol(s)
366  map<symbol_t, expression_t>::iterator e = cP->mapping.find(s);
367  if (e != cP->mapping.end())
368  {
369  paramsExpanded = true;
370  visitExpression(e->second);
371  paramsExpanded = false;
372  }
373  else
374  {
375  cerr << "mapping param '"<< s.getName() << "' failed"<<endl;
376  exit(EXIT_FAILURE);
377  }
378  return false;
379  }
380  else if (0<=cP->templ->frame.getIndexOf(s.getName()))
381  {
382  // is it local symbol? discard, no observable I/O here
383  return false;
384  }
385  }
386  // it must be global/shared
387 /* if(s.getFrame().hasParent()) {// should not have a parent
388  cerr << s.getName() <<" assumed to be global but it is not" << endl;
389  exit(EXIT_FAILURE);
390  }*/
391  return true;
392 }
393 
394 void SignalFlow::addChan(const std::string &s, strs_t &ids, str2procs_t& index)
395 {
396  const char* s2;
397  strs_t::iterator it = channels.find(s.c_str());
398  if (it == channels.end()) {
399  s2 = strdup(s.c_str());
400  channels.insert(s2);
401  } else s2 = *it;
402  ids.insert(s2);
403  index[s2].insert(cTA);
404  cChan = s2;
405 }
406 
408  str2procs_t& index)
409 {
410  if (checkParams(s))
411  {
412  ids[s.getName().c_str()].insert(cChan);
413  variables.insert(s.getName().c_str());
414  index[s.getName().c_str()].insert(cTA);
415  }
416 }
417 
418 static const char* noChan = "-";
419 
421 {
422  cP = &p;
423  procs.insert(cTA = new proc_t(p.uid.getName().c_str()));
424  processes.insert(p.uid.getName().c_str());
425 
426  const template_t* temp = p.templ;
427  deque<state_t>::const_iterator s = temp->states.begin();
428  while (s != temp->states.end())
429  {
430  cChan = noChan; // invariants should not use shared
431  visitExpression(s->invariant);
432  ++s;
433  }
434  deque<edge_t>::const_iterator t;
435  for (t = temp->edges.begin(); t != temp->edges.end(); ++t)
436  {
437  cChan = noChan;// guards should not use shared
438  visitExpression(t->guard);
439  visitExpression(t->sync);
440  visitExpression(t->assign);
441  }
442 }
443 
445 {
446  if (e.empty())
447  {
448  return;
449  }
450 
451  switch (e.getKind())
452  {
453  case PLUS:
454  case MINUS:
455  case MULT:
456  case DIV:
457  case MOD:
458  case BIT_AND:
459  case BIT_OR:
460  case BIT_XOR:
461  case BIT_LSHIFT:
462  case BIT_RSHIFT:
463  case AND:
464  case OR:
465  case MIN:
466  case MAX:
467  case FRACTION:
468  /*******************************************************
469  * Relational operators
470  */
471  case LT:
472  case LE:
473  case EQ:
474  case NEQ:
475  case GE:
476  case GT:
477  /*******************************************************
478  * Unary operators
479  */
480  case NOT:
481  for (uint32_t i=0; i<e.getSize(); ++i)
482  {
483  inp = true; out = false;
484  visitExpression(e[i]);
485  }
486  break;
487  case FORALL:
488  case EXISTS:
489  case SUM:
490  // the first expression is an inline (local) declaration (don't care)
491  // the second is side-effect-free, hence read-only expression
492  assert(e.getSize()==2);
493  inp = true; out = false;
494  visitExpression(e[1]);
495  break;
496 
497  /*******************************************************
498  * Assignment operators
499  */
500  case ASSIGN:
501  case ASSPLUS:
502  case ASSMINUS:
503  case ASSDIV:
504  case ASSMOD:
505  case ASSMULT:
506  case ASSAND:
507  case ASSOR:
508  case ASSXOR:
509  case ASSLSHIFT:
510  case ASSRSHIFT:
511  inp = false; out = true;
512  visitExpression(e[0]);
513  for (uint32_t i=1; i<e.getSize(); ++i)
514  {
515  inp = true;
516  out = false;
517  visitExpression(e[i]);
518  }
519  break;
520 
521  /******************************************************
522  * Additional constants used by ExpressionProgram's and
523  * the TypeCheckBuilder (but not by the parser, although
524  * some of then ought to be used, FIXME).
525  */
526  case IDENTIFIER:
527  if (sync) { // channel synchronization
528  if (checkParams(e.getSymbol())) {
529  chanString.append(e.getSymbol().getName());
530  }
531  } else { // variable access
532  if (e.getType().getKind() == CONSTANT) { // constant
533  assert(!out);
534  break;
535  }
536  symbol_t sym = e.getSymbol();
537  if (sym.getFrame().hasParent()) { // local variable
538  if (refparams.size()==0) break; // local process variable
539  // else: local function variable
540  exprref_t::const_iterator exi = refparams.top().find(sym);
541  if (exi != refparams.top().end()) { // passed by reference
542  visitExpression(exi->second);
543  break;
544  }
545  exi = valparams.top().find(sym);
546  if (exi != valparams.top().end()) { // passed by value
547  if (inp) {
548  pushIO();
549  out = false;// any writes to local copy are lost
550  visitExpression(exi->second);
551  popIO();
552  }
553  break;
554  }
555  // else: local function variable but not parameter, don't care
556  } else { // global variable
557  if (inp) addVar(sym, cTA->rdVars, receivers);
558  if (out) addVar(sym, cTA->wtVars, transmitters);
559  }
560  }
561  break;
562  case CONSTANT:
563  if (sync) {
564  std::ostringstream os;
565  os << e.getValue();
566  chanString.append(os.str());
567  }
568  break;
569  case ARRAY:
570  visitExpression(e[0]);
571  if (sync) chanString.append("[");
572  pushIO();
573  for (uint32_t i=1; i<e.getSize(); ++i)
574  {
575  inp = true;
576  out = false;
577  visitExpression(e[i]);
578  }
579  popIO();
580  if (sync) {
581  chanString.append("]");
582  }
583  break;
584  case RATE:
585  case POSTINCREMENT:
586  case PREINCREMENT:
587  case POSTDECREMENT:
588  case PREDECREMENT:
589  inp = true;
590  out = true;
591  visitExpression(e[0]);
592  break;
593  case UNARY_MINUS:
594  inp = true;
595  out = false;
596  visitExpression(e[0]);
597  break;
598  case LIST:
599  for (uint32_t i=0; i<e.getSize(); ++i)
600  {
601  inp = true;
602  out = false;
603  visitExpression(e[i]);
604  }
605  break;
606  case DOT:// dot expression has only one subexpression?
607  for (uint32_t i=0; i<e.getSize(); ++i)
608  visitExpression(e[i]);
609  break;
610  case INLINEIF:
611  pushIO();
612  inp = true;
613  out = false;
614  visitExpression(e[0]);
615  for (uint32_t i=1; i<e.getSize(); ++i)
616  {
617  popIO();
618  pushIO();
619  visitExpression(e[i]);
620  }
621  popIO();
622  break;
623  case COMMA:
624  pushIO();
625  for (uint32_t i=0; i<e.getSize()-1; ++i)
626  {
627  inp = true;
628  out = false;
629  visitExpression(e[i]);
630  }
631  popIO();
632  visitExpression(e[e.getSize()-1]);
633  break;
634  case SYNC:
635  switch(e.getSync())
636  {
637  case SYNC_QUE:
638  inp = true;
639  out = false;
640  break;
641  case SYNC_BANG:
642  inp = false;
643  out = true;
644  break;
645  case SYNC_CSP:
646  // Marius may want to hack this differently.
647  inp = false;
648  out = false;
649  }
650  sync = true;
651  chanString.clear();
652  visitExpression(e[0]);
653  if (inp) {
655 // std::cerr << cTA->name << " receives on " << chanString << endl;
656  }
657  if (out) {
659 // std::cerr << cTA->name << " sends on " << chanString << endl;
660  }
661  sync = false;
662  break;
663  case FUNCALL:
664  {// create a map of parameter symbol to argument expression
665  const symbol_t& fnsym = e.getSymbol();// function symbol
666  assert(fnsym.getType().getKind()==FUNCTION);
667  const function_t* fn = static_cast<const function_t*>(fnsym.getData());
668  BlockStatement* body = fn->body;
669  const frame_t& frame = body->getFrame();
670  refparams.push(exprref_t());
671  valparams.push(exprref_t());
672  for (size_t i=0; i<e.getSize()-1; ++i) {
673  symbol_t param = frame[i];
674  const type_t& ptype = param.getType();
675  if (ptype.is(REF)) refparams.top()[param] = e[i+1];
676  else valparams.top()[param] = e[i+1];
677  }
678  body->accept(this);// see which parameters are touched
679  refparams.pop();
680  valparams.pop();
681  }
682  break;
683 
684  default:
685  cerr << "unsupported kind (" << e.getKind()
686  << ") of expression, please report it to developers."<<endl;
687  exit(1);
688  }
689 }
690 
692 {
693  return 0;
694 }
695 
697 {
698  visitExpression(stat->expr);
699  return 0;
700 }
701 
703 {
704 // FixMe: there is mysterious field called symbol, do smth about it.
705  return stat->stat->accept(this);
706 }
707 
709 {
710  visitExpression(stat->init);
711  visitExpression(stat->cond);
712  visitExpression(stat->step);
713  return stat->stat->accept(this);
714 }
715 
717 {
718  visitExpression(stat->cond);
719  return stat->stat->accept(this);
720 }
721 
723 {
724  int32_t res = stat->stat->accept(this);
725  visitExpression(stat->cond);
726  return res;
727 }
728 
730 {
731  int32_t res = 0;
732  BlockStatement::iterator it = stat->begin();
733  while (it != stat->end())
734  {
735  res = (*it)->accept(this);
736  ++it;
737  }
738  return res;
739 }
740 
742 {
743  visitExpression(stat->cond);
744  return visitBlockStatement(stat);
745 }
746 
748 {
749  visitExpression(stat->cond);
750  return visitBlockStatement(stat);
751 }
752 
754 {
755  return visitBlockStatement(stat);
756 }
757 
759 {
760  visitExpression(stat->cond);
761  int32_t res = stat->trueCase->accept(this);
762  if (stat->falseCase)
763  {
764  return stat->falseCase->accept(this);
765  }
766  else return res;
767 }
769 {
770  return 0;
771 }
772 
774 {
775  return 0;
776 }
777 
779 {
780  return 0;
781 }
782 
784 {
785  visitExpression(stat->value);
786  return 0;
787 }
788 
790 {
791  procs_t::const_iterator p;
792  for (p = procs.begin(); p != procs.end(); ++p) delete *p;
793  strs_t::const_iterator c;
794  for (c = channels.begin(); c != channels.end(); ++c) delete *c;
795 }
796 
798 {
799  strs_t::const_iterator s;
800  for (s=chansInp.begin(); s!=chansInp.end(); ++s) free((char*)*s);
801  for (s=chansOut.begin(); s!=chansOut.end(); ++s) free((char*)*s);
802  chansInp.clear(); chansOut.clear();
803 }
804 
805 inline void Partitioner::printViolation(const proc_t* proc, const char* var)
806 {
807  if (verbosity>=1)
808  cerr << "Violated rule \"" << rule << "\" for process \""
809  << proc->name << "\" accessing \"" << var << "\"" << endl;
810 }
811 
817 void Partitioner::addProcs(const strs_t& chans, const str2procs_t& index,
818  procs_t& result, procs_t& exclude)
819 {
820 
821  for (strs_t::const_iterator c=chans.begin(), ce=chans.end(); c!=ce; ++c)
822  { // take each channel from the given set
823  str2procs_t::const_iterator c2p;
824  c2p = index.find(*c);//find processes that use the channel
825  if (c2p != index.end()) {
826  for (procs_t::const_iterator p=c2p->second.begin(),
827  pe=c2p->second.end(); p!=pe; ++p)
828  {
829  if (exclude.erase(*p) > 0) {// if process was excluded
830  procsBad.insert(*p);// report as inconsistent process
831  printViolation(*p, *c);
832  } else if (result.find(*p) == result.end() &&
833  procsBad.find(*p) == procsBad.end()) {
834  // if process has not been added nor excluded, then add it:
835  if (verbosity>=3)
836  cerr << "Adding \""<<(*p)->name <<"\" using \""<<(*c)
837  <<"\" by rule \""<<rule<<"\""<<endl;
838  result.insert(*p);
839  }
840  }
841  }
842  }
843 }
844 
851  strs_t& result, strs_t& exclude)
852 {
853  for (procs_t::const_iterator p=procs.begin(), pe=procs.end(); p!=pe; ++p)
854  {// take each process from the list
855  for (strs_t::const_iterator c=(*p)->inChans.begin(),
856  ce=(*p)->inChans.end(); c!=ce; ++c)
857  { // consider the input channels used by the process
858  if (observable.find(*c) == observable.end()) {// if not observed
859  if (exclude.erase(*c) > 0) {// if it's excluded
860  chansBad.insert(*c); // report inconsistency
861  printViolation(*p, *c);
862  } else if (result.find(*c) == result.end() &&
863  chansBad.find(*c) == chansBad.end()) {
864  if (verbosity>=3)
865  cerr << "Adding \""<<(*c)<<"\" because of \""
866  <<(*p)->name <<"\" by rule \""<<rule
867  <<"\""<<endl;
868  result.insert(*c);
869  }
870  }
871  }
872  for (strs_t::const_iterator c=(*p)->outChans.begin(),
873  ce=(*p)->outChans.end(); c!=ce; ++c)
874  { // conside the output channels used by p
875  if (observable.find(*c) == observable.end()) {// if not observed
876  if (exclude.erase(*c) > 0) {// if it's excluded
877  chansBad.insert(*c);// report inconsistency
878  printViolation(*p, *c);
879  } else if (result.find(*c) == result.end() &&
880  chansBad.find(*c) == chansBad.end()) {
881  if (verbosity>=3)
882  cerr << "Adding \""<<(*c)<<"\" because of \""
883  <<(*p)->name <<"\" by rule \""<<rule
884  <<"\""<<endl;
885  result.insert(*c);
886  }
887  }
888  }
889  }
890 }
891 
897  strs_t& exclude)
898 {
899  for (procs_t::const_iterator p=procs.begin(), pe=procs.end(); p!=pe; ++p)
900  { // take each process p
901  assert(*p);
902  for (str2strs_t::const_iterator v2c=(*p)->rdVars.begin(),
903  v2ce=(*p)->rdVars.end(); v2c!=v2ce; ++v2c)
904  { // consider the variables being read
905  if (!includes(observable.begin(), observable.end(),
906  v2c->second.begin(), v2c->second.end(), less_str()))
907  {// contains non-observable channels
908  if (exclude.erase(v2c->first) > 0) {
909  varsBad.insert(v2c->first);
910  printViolation(*p, v2c->first);
911  } else if (result.find(v2c->first) == result.end() &&
912  varsBad.find(v2c->first) == varsBad.end()) {
913  if (verbosity>=3)
914  cerr << "Adding \""<<(v2c->first)<<"\" because of \""
915  <<(*p)->name <<"\" by rule \""<<rule<<"\""<<endl;
916  result.insert(v2c->first);
917  }
918  }// else used only observably
919  }
920  for (str2strs_t::const_iterator v2c=(*p)->wtVars.begin(),
921  v2ce=(*p)->wtVars.end(); v2c!=v2ce; ++v2c)
922  { // consider the variables being written to by p
923  if (!includes(observable.begin(), observable.end(),
924  v2c->second.begin(), v2c->second.end(), less_str()))
925  {// contains non-observable channels
926  if (exclude.erase(v2c->first) > 0) {
927  varsBad.insert(v2c->first);
928  printViolation(*p, v2c->first);
929  } else if (result.find(v2c->first) == result.end() &&
930  varsBad.find(v2c->first) == varsBad.end()) {
931  if (verbosity>=3)
932  cerr << "Adding \""<<(v2c->first)<<"\" because of \""
933  <<(*p)->name <<"\" by rule \""<<rule<<"\""<<endl;
934  result.insert(v2c->first);
935  }
936  }// else used only observably
937  }
938  }
939 }
940 
946  procs_t& exclude)
947 {
948  str2procs_t::const_iterator v2p;
949  str2strs_t::const_iterator v2c;
950  for (strs_t::const_iterator v=vars.begin(), ve=vars.end(); v!=ve; ++v)
951  { // take every variable v from the vars list
952  v2p=receivers.find(*v); // find list of processes that read from v
953  if (v2p != receivers.end()) {
954  for(procs_t::const_iterator p=v2p->second.begin(),
955  pe=v2p->second.end(); p!=pe; ++p)
956  {// take each process reading the value of v
957  v2c = (*p)->rdVars.find(*v);
958  if (v2c != (*p)->rdVars.end()) {
959  if (!includes(observable.begin(), observable.end(),
960  v2c->second.begin(), v2c->second.end(),
961  less_str()))
962  {// has unobservable synchronizations
963  if (exclude.erase(*p) > 0) {
964  procsBad.insert(*p);
965  printViolation(*p, *v);
966  } else if (procs.find(*p) == procs.end() &&
967  procsBad.find(*p) == procsBad.end()) {
968  if (verbosity>=3)
969  cerr << "Adding \""<<(*p)->name
970  <<"\" using \""<<(*v)
971  <<"\" by rule \""<<rule<<"\""<<endl;
972  procs.insert(*p);
973  }
974  }// else used observably
975  } else {
976  if (verbosity>=1)
977  cerr<<"addProcsByVars could not find in rdVars"<<endl;
978  }
979  }
980  } else {
981  if (verbosity>=1)
982  cerr << "addProcsByVars could not find readers"<<endl;
983  }
984  v2p=transmitters.find(*v);
985  if (v2p != transmitters.end()) {
986  for(procs_t::const_iterator p=v2p->second.begin(),
987  pe=v2p->second.end(); p!=pe; ++p)
988  { // consider the process p which writes to v
989  v2c = (*p)->wtVars.find(*v);
990  if (v2c != (*p)->wtVars.end()) {
991  if (!includes(observable.begin(), observable.end(),
992  v2c->second.begin(), v2c->second.end(),
993  less_str()))
994  {// has unobservable synchronizations
995  if (exclude.erase(*p) > 0) {
996  procsBad.insert(*p);
997  printViolation(*p, *v);
998  } else if (procs.find(*p) == procs.end() &&
999  procsBad.find(*p) == procsBad.end()) {
1000  if (verbosity>=3)
1001  cerr << "Adding \""<<(*p)->name
1002  <<"\" using \""<<(*v)
1003  <<"\" by rule \""<<rule<<"\""<<endl;
1004  procs.insert(*p);
1005  }
1006  }// else used observably
1007  } else {
1008  if (verbosity>=1)
1009  cerr<<"addProcsByVars could not find in wtVars"<<endl;
1010  }
1011  }
1012  } else {
1013  if (verbosity>=1)
1014  cerr<<"addProcsByVars could not find writers for "<<(*v)<<endl;
1015  }
1016  }
1017 }
1018 
1019 char* get_token(istream& in)
1020 {
1021  string buffer="";
1022  char c;
1023  while (in.get(c)) {
1024  if (isalnum((unsigned char)c) || c=='_' || c=='[' || c==']') buffer += c;
1025  else {
1026  if (c=='(') while (in.get(c) && c!=')') ; // ignore "(...)"
1027  if (buffer.length()>0)
1028  return strdup(buffer.c_str());
1029  }
1030  }
1031  if (buffer.length()>0) return strdup(buffer.c_str());
1032  else return NULL;
1033 }
1034 
1035 int Partitioner::partition(istream& ioinfo)
1036 {
1037  char* token;
1038  strs_t inputs, outputs;
1039  std::ios::fmtflags flags = ioinfo.flags();
1040  ioinfo.unsetf(std::ios::skipws);
1041  token = get_token(ioinfo);
1042  if (token) {
1043  if (strcmp(token, "input")!=0) {
1044  cerr << "\"input\" is expected instead of \""<<token<<"\"" << endl;
1045  free(token); // get rid of "input"
1046  exit(EXIT_FAILURE);
1047  }
1048  free(token); // get rid of "input"
1049  while ((token = get_token(ioinfo)) != NULL &&
1050  strcmp(token, "output") != 0)
1051  inputs.insert(token);
1052 
1053  if (token == NULL) {
1054  cerr << "\"output\" expected, but EOF found" << endl;
1055  exit(EXIT_FAILURE);
1056  }
1057  free(token);// get rid of "output"
1058  while ((token = get_token(ioinfo)) != NULL &&
1059  strcmp(token, "precision")!=0 && strcmp(token, "timeout")!=0)
1060  outputs.insert(token);
1061  if (token != NULL) free(token);
1062  }
1063  ioinfo.flags(flags);
1064  int res = partition(inputs, outputs);
1065  strs_t::const_iterator s;
1066  for (s=inputs.begin(); s!=inputs.end(); ++s) free((char*)*s);
1067  for (s=outputs.begin(); s!=outputs.end(); ++s) free((char*)*s);
1068  return res;
1069 }
1070 
1071 int Partitioner::partition(const strs_t& inputs, const strs_t& outputs)
1072 {
1073  procsEnv.clear(); procsIUT.clear();
1074  chansIntEnv.clear(); chansIntIUT.clear();
1075  varsEnv.clear(); varsIUT.clear();
1076  strs_t::const_iterator s;
1077  for (s=chansInp.begin(); s!=chansInp.end(); ++s) free((char*)*s);
1078  for (s=chansOut.begin(); s!=chansOut.end(); ++s) free((char*)*s);
1079  chansInp.clear(); chansOut.clear();
1080 
1081  for (s=inputs.begin(); s!=inputs.end(); ++s)
1082  chansInp.insert(strdup(*s));
1083  for (s=outputs.begin(); s!=outputs.end(); ++s)
1084  chansOut.insert(strdup(*s));
1085 
1086  observable.clear();
1087  observable.insert(chansInp.begin(), chansInp.end());
1088  observable.insert(chansOut.begin(), chansOut.end());
1089 
1090  if (verbosity>=3) {
1091  cerr << "Inputs: " << chansInp << endl;
1092  cerr << "Outputs: " << chansOut << endl;
1093  }
1094 
1095  size_t oldProgress=0, progress=0;
1096  do {
1097  oldProgress = progress;
1098 /* Environment processes shout on inputs and listens to outputs, while IUT
1099  * processes shout on outputs and listen to inputs. */
1100  rule="transmitters on input channels belong to Env";
1101  addProcs(inputs, transmitters, procsEnv, procsIUT);
1102  rule="receivers on output channels belong to Env";
1103  addProcs(outputs, receivers, procsEnv, procsIUT);
1104  rule="receivers on input channels belong IUT";
1105  addProcs(inputs, receivers, procsIUT, procsEnv);
1106  rule="transmitters on output channels belong IUT";
1107  addProcs(outputs, transmitters, procsIUT, procsEnv);
1108 
1109 /* 1) channels, that are not declared as inputs/outputs, are non-observable,
1110  * called internal.*/
1111 
1112 /* 2) internal channel belongs to environment (IUT) if it is used by
1113  * environment (IUT) process (respectively). Model is inconsistent and
1114  * cannot be partitioned if the internal channel is used by both environment
1115  * and IUT. */
1116  rule="internal channel belongs to Env if it is used by Env";
1117  addIntChans(procsEnv, chansIntEnv, chansIntIUT);
1118  rule="internal channel belongs to IUT if it is used by IUT";
1119  addIntChans(procsIUT, chansIntIUT, chansIntEnv);
1120 
1121 /* 3) process belongs to environment (IUT) if it uses the internal environment
1122  * (IUT) channel (respectively). */
1123  rule="process belongs to Env if it shouts on internal Env channel";
1124  addProcs(chansIntEnv, transmitters, procsEnv, procsIUT);
1125  rule="process belongs to Env if it listens to internal Env channel";
1126  addProcs(chansIntEnv, receivers, procsEnv, procsIUT);
1127  rule="process belongs to IUT if it shouts on internal IUT channel";
1128  addProcs(chansIntIUT, transmitters, procsIUT, procsEnv);
1129  rule="process belongs to IUT if it listens to internal IUT channel";
1130  addProcs(chansIntIUT, receivers, procsIUT, procsEnv);
1131 
1132 /* 4) variable belongs to environment (IUT) if it is accessed by environment
1133  * (IUT) process without observable input/output channel synchronization.
1134  * Variable is not cathegorized (can be either) if accessed consistently
1135  * only during observable input/output channel synchronization. */
1136  rule="variable belongs to Env if accessed by Env without observable sync";
1137  addIntVars(procsEnv, varsEnv, varsIUT);
1138  rule="variable belongs to IUT if accessed by IUT without observable sync";
1139  addIntVars(procsIUT, varsIUT, varsEnv);
1140 
1141 /* 5) process belongs to environment (IUT) if it accesses environment (IUT)
1142  * variable (respectively) without observable channel synchronization. */
1143  rule="process belongs to Env if it access Env variable without observable synchronization";
1144  addProcsByVars(varsEnv, procsEnv, procsIUT);
1145  rule="process belongs to IUT if it access IUT variable without observable synchronization";
1146  addProcsByVars(varsIUT, procsIUT, procsEnv);
1147  progress = procsEnv.size() + procsIUT.size() + procsBad.size();
1148  } while (progress>oldProgress);
1149  procs_t::const_iterator p;
1150 
1151  if (verbosity>=3) {
1152  cerr << "==== Partitioned =========================================\n";
1153  cerr << "Env procs: " << procsEnv << endl;
1154  cerr << "Env chans: " << chansIntEnv << endl;
1155  cerr << "Env vars: " << varsEnv << endl;
1156  cerr << "----------------------------------------------------------\n";
1157  cerr << "IUT procs: " << procsIUT << endl;
1158  cerr << "IUT chans: " << chansIntIUT << endl;
1159  cerr << "IUT vars: " << varsIUT << endl;
1160  cerr << "==========================================================\n";
1161  }
1162  if (verbosity>=1) {
1163  if (!procsBad.empty())
1164  cerr << "Inconsistent procs: " << procsBad << endl;
1165  if (!chansBad.empty())
1166  cerr << "Inconsistent chans: " << chansBad << endl;
1167  if (!varsBad.empty())
1168  cerr << "Inconsistent vars: " << varsBad << endl;
1169  }
1170  if (verbosity>=2) {
1171  procs_t leftovers;
1172  for (p=procs.begin(); p!=procs.end(); ++p)
1173  if (procsEnv.find(*p) == procsEnv.end() &&
1174  procsIUT.find(*p) == procsIUT.end())
1175  leftovers.insert(*p);
1176  if (!leftovers.empty()) {
1177  cerr << "==== Not partitioned: ====================================\n";
1178  cerr << "procs: " << leftovers << endl;
1179  }
1180  }
1181 
1182  if (!procsBad.empty() || !chansBad.empty() || !varsBad.empty()) return 2;
1183  if (progress==procs.size()) return 0; // all procs are partitioned
1184  else return 1;// some left unpartitioned
1185 }
1186 
1187 #define BADSTYLE "style=filled,fillcolor=\"#FF8080\""
1188 #define IUTSTYLE "style=filled,fillcolor=\"#B8C0FF\""
1189 #define ENVSTYLE "style=filled,fillcolor=\"#C8FFC8\""
1190 #define MEDSTYLE "style=filled,fillcolor=\"#C0C0C0\""
1191 
1192 inline void set_remove(SignalFlow::strs_t& from,
1193  const SignalFlow::strs_t& what)
1194 {
1195  for (SignalFlow::strs_t::const_iterator b=what.begin(), e=what.end();
1196  b!=e; ++b)
1197  from.erase(*b);
1198 }
1199 
1200 struct surround: public std::unary_function<const char*, void>
1201 {
1202  ostream &out;
1203  const char* pre;
1204  const char* post;
1205  surround(ostream &os, const char* prefix, const char* postfix):
1206  out(os), pre(prefix), post(postfix) {}
1207  void operator()(const char* x) {
1208  out << pre << x << post;
1209  }
1210 };
1211 
1212 
1213 void Partitioner::printForDot(ostream &os, bool ranked, bool erd, bool cEdge)
1214 {
1215  char* name = strcpy(new char[strlen(title)+1], title);
1216  for (uint32_t i=0; i<strlen(name); ++i)
1217  {
1218  if (!isalpha((unsigned char)name[i]))
1219  {
1220  name[i]='_';
1221  }
1222  }
1223 
1224  os << "digraph " << name << " {\n";
1225  delete [] name;
1226  os << "// printer friendly options, fill A4 landscape:\n"
1227  " size=\"10.2,7.8\"; ratio=fill; margin=0.2;\n"
1228  " nodesep=0.3; // separation of labelled edges\n\n"
1229  "// fdp options:\n"
1230  " K=1.25;\n\n"
1231  "// neato options:\n"
1232  " epsilon=0.0001;\n\n"
1233  " node[fontname=\"Helvetica-Bold\"];\n"
1234  " edge[fontname=\"Helvetica\",fontsize=10];\n\n"
1235  "// legend:\n"
1236  "// process[shape=ellipse]; int[shape=rectangle]; chan[shape=diamond];\n\n";
1237 
1238  procs_t::const_iterator p, pe;
1239  strs_t procsR(processes), chansR(channels), varsR(variables); // remaining
1240 
1241  ostream_iterator<const char*> osi(os, "; ");
1242 
1243  if (!procsBad.empty()) {
1244  os << "// processes in conflict:\n"
1245  " node [shape=ellipse,peripheries=1," BADSTYLE "];\n ";
1246  for (p = procsBad.begin(), pe = procsBad.end(); p!=pe; ++p) {
1247  os << (*p)->name << "; ";
1248  procsR.erase((*p)->name);
1249  }
1250  os << endl;
1251  }
1252  if (!chansBad.empty()) {
1253  os << "// channels in conflict:\n"
1254  " node [shape=diamond,peripheries=1," BADSTYLE "];\n ";
1255  for_each(chansBad.begin(), chansBad.end(),
1256  surround(os, "\"", "\"; "));
1257 // copy(chansBad.begin(), chansBad.end(), osi);
1258  set_remove(chansR, chansBad);
1259  os << endl;
1260  }
1261  if (!varsBad.empty()) {
1262  os << "// variables in conflict:\n"
1263  " node [shape=diamond,peripheries=1," BADSTYLE "];\n ";
1264  copy(varsBad.begin(), varsBad.end(), osi);
1265  set_remove(varsR, varsBad);
1266  os << endl;
1267  }
1268 
1269  if (!procsIUT.empty()) {
1270  os << "// IUT processes:\n";
1271  os << " node [shape=ellipse,peripheries=1," IUTSTYLE "];\n ";
1272  for (p = procsIUT.begin(), pe = procsIUT.end(); p!=pe; ++p) {
1273  os << (*p)->name << "; ";
1274  procsR.erase((*p)->name);
1275  }
1276  os << endl;
1277  }
1278  if (!chansIntIUT.empty()) {
1279  os << "// IUT channels:\n";
1280  os << " node [shape=diamond,peripheries=1," IUTSTYLE "];\n ";
1281  for_each(chansIntIUT.begin(), chansIntIUT.end(),
1282  surround(os, "\"", "\"; "));
1283 // copy(chansIntIUT.begin(), chansIntIUT.end(), osi);
1284  set_remove(chansR, chansIntIUT);
1285  os << endl;
1286  }
1287  if (!varsIUT.empty()) {
1288  os << "// IUT variables:\n";
1289  os << " node [shape=rectangle,peripheries=1," IUTSTYLE "];\n ";
1290  copy(varsIUT.begin(), varsIUT.end(), osi);
1291  set_remove(varsR, varsIUT);
1292  os << endl;
1293  }
1294 
1295  if (!chansOut.empty()) {
1296  os << "// observable output channels (controlled by IUT):\n"
1297  " node [shape=diamond,peripheries=2," IUTSTYLE "];\n ";
1298  for_each(chansOut.begin(), chansOut.end(),
1299  surround(os, "\"", "\"; "));
1300 // copy(chansOut.begin(), chansOut.end(), osi);
1301  set_remove(chansR, chansOut);
1302  os << endl;
1303  }
1304  if (!chansInp.empty()) {
1305  os << "// observable input channels (controlled by Env):\n"
1306  " node [shape=diamond,peripheries=2," ENVSTYLE "];\n ";
1307  for_each(chansInp.begin(), chansInp.end(),
1308  surround(os, "\"", "\"; "));
1309 // copy(chansInp.begin(), chansInp.end(), osi);
1310  set_remove(chansR, chansInp);
1311  os << endl;
1312  }
1313 
1314  if (!procsEnv.empty()) {
1315  os << "// Env processes:\n";
1316  os << " node [shape=ellipse,peripheries=1," ENVSTYLE "];\n ";
1317  for (p = procsEnv.begin(), pe = procsEnv.end(); p!=pe; ++p) {
1318  os << (*p)->name << "; ";
1319  procsR.erase((*p)->name);
1320  }
1321  os << endl;
1322  }
1323  if (!chansIntEnv.empty()) {
1324  os << "// Env channels:\n";
1325  os << " node [shape=diamond,peripheries=1," ENVSTYLE "];\n ";
1326  for_each(chansIntEnv.begin(), chansIntEnv.end(),
1327  surround(os, "\"", "\"; "));
1328 // copy(chansIntEnv.begin(), chansIntEnv.end(), osi);
1329  set_remove(chansR, chansIntEnv);
1330  os << endl;
1331  }
1332  if (!varsEnv.empty()) {
1333  os << "// Env variables:\n";
1334  os << " node [shape=rectangle,peripheries=1," ENVSTYLE "];\n ";
1335  copy(varsEnv.begin(), varsEnv.end(), osi);
1336  set_remove(varsR, varsEnv);
1337  os << endl;
1338  }
1339  os << "// set attributes for non-partitioned procs/chans/vars:\n";
1340  if (!procsR.empty()) {
1341  os << " node [shape=ellipse,peripheries=1," MEDSTYLE "];\n";
1342  copy(procsR.begin(), procsR.end(), osi);
1343  os << endl;
1344  }
1345  if (!chansR.empty()) {
1346  os << " node [shape=diamond,peripheries=1," MEDSTYLE "];\n";
1347  for_each(chansR.begin(), chansR.end(),
1348  surround(os, "\"", "\"; "));
1349 // copy(chansR.begin(), chansR.end(), osi);
1350  os << endl;
1351  }
1352  if (!varsR.empty()) {
1353  os << " node [shape=rectangle,peripheries=1," MEDSTYLE "];\n";
1354  copy(varsR.begin(), varsR.end(), osi);
1355  os << endl;
1356  }
1357 
1358 
1359 /* draw variable edges */
1360  str2strs_t::const_iterator v2c;
1361  os << "// edges for write to variable:\n"
1362  << " edge [style=bold];\n";
1363  for (p = procs.begin(), pe = procs.end(); p!=pe; ++p) {
1364  if (!(*p)->wtVars.empty()) {
1365  for (v2c = (*p)->wtVars.begin(); v2c != (*p)->wtVars.end(); ++v2c){
1366  os<<" "<<(*p)->name<< " -> " << v2c->first << " [label=\"(";
1367 /* enumerate channels when variable is accessed */
1368  for_each(v2c->second.begin(), v2c->second.end(),
1369  print<const char*>(os, ","));
1370  os<<")\"];\n";
1371  }
1372  }
1373  }
1374  os << "// edges for read of variable:\n"
1375  << " edge [style=solid];\n";
1376  for (p = procs.begin(), pe = procs.end(); p!=pe; ++p) {
1377  if (!(*p)->rdVars.empty()) {
1378  for (v2c = (*p)->rdVars.begin(); v2c != (*p)->rdVars.end(); ++v2c){
1379  os<<" " << v2c->first << " -> " <<(*p)->name<<" [label=\"(";
1380 /* enumerate channels when variable is accessed */
1381  for_each(v2c->second.begin(), v2c->second.end(),
1382  print<const char*>(os, ","));
1383  os<<")\"];\n";
1384  }
1385  }
1386  }
1387 
1388 /* enumerate draw channels */
1389  if (!channels.empty())
1390  {
1391  ;
1392  if (!cEdge)
1393  { /* channels displayed as separate nodes (like variables) */
1394  os << "// channel transmit edges:\n"
1395  << " edge [style=bold];\n";
1396  for (p = procs.begin(), pe = procs.end(); p!=pe; ++p) {
1397  for (strs_t::const_iterator c=(*p)->outChans.begin(),
1398  ce=(*p)->outChans.end(); c!=ce; ++c)
1399  {
1400  os << " " << (*p)->name << " -> \"" << *c << "\";\n";
1401  }
1402  }
1403  os << "// channel receive edges:\n"
1404  << " edge [style=solid];\n";
1405  for (p = procs.begin(), pe = procs.end(); p!=pe; ++p) {
1406  for (strs_t::const_iterator c=(*p)->inChans.begin(),
1407  ce=(*p)->inChans.end(); c!=ce; ++c)
1408  {
1409  os << " \"" << *c << "\" -> " << (*p)->name << ";\n";
1410  }
1411  }
1412  } else { /* channels are only on edges */
1413  const char* src = "NO_SRC";
1414  const char* dst = "NO_DST";
1415  /* build an index of outgoing edges sorted by destination TA */
1416  for (p = procs.begin(), pe = procs.end(); p!=pe; ++p)
1417  {
1418  /* traverse all outgoing channels */
1419  for (strs_t::const_iterator c=(*p)->outChans.begin(),
1420  ce=(*p)->outChans.end(); c!=ce; ++c)
1421  {
1422  str2procs_t::const_iterator dests = receivers.find(*c);
1423  if (dests != receivers.end())
1424  { // there are destinations
1425  for (procs_t::const_iterator rec =
1426  dests->second.begin();
1427  rec != dests->second.end(); ++rec)
1428  {
1429  (*p)->outEdges[*rec].insert(*c);
1430  }
1431  }
1432  else
1433  { // no destination
1434  (*p)->outEdges[NULL].insert(*c);
1435  }
1436  }
1437  }
1438  /* display all edges: */
1439  bool noDst = false;
1440  os<<" edge[style=solid];\n";
1441  for (p = procs.begin(), pe = procs.end(); p!=pe; ++p)
1442  {
1443  /* display outgoing edges: */
1444  for (proc2strs_t::const_iterator edge = (*p)->outEdges.begin();
1445  edge != (*p)->outEdges.end(); ++edge)
1446  {
1447  // display edge:
1448  if (edge->first != NULL)
1449  {
1450  //normal destination TA
1451  os<<" " <<(*p)->name << " -> " << edge->first->name
1452  << " [label=\"[";
1453  }
1454  else
1455  { // there was no destination TA
1456  if (!noDst)
1457  {
1458  os<<" "<<dst
1459  <<" [style=filled,fillcolor=red];\n";
1460  noDst = true;
1461  }
1462  os<<" " <<(*p)->name << " -> " << dst
1463  << " [label=\"[";
1464  }
1465  // enumerate all channels on the edge:
1466  strs_t::const_iterator ch=edge->second.begin();
1467  while (true)
1468  {
1469  os<< "\"" << *ch << "\"";
1470  ++ch;
1471  if (ch != edge->second.end())
1472  {
1473  os << ",";
1474  }
1475  else
1476  {
1477  break;
1478  }
1479  }
1480  os << "]\"];\n";
1481  }
1482  /* by now all inps with sources are displayed as outputs
1483  * search and display inputs w/o sources */
1484  bool noSrc = false;
1485  for (strs_t::const_iterator c=(*p)->inChans.begin(),
1486  ce=(*p)->inChans.end(); c!=ce; ++c)
1487  {
1488  str2procs_t::const_iterator trans = transmitters.find(*c);
1489  if (trans==transmitters.end())
1490  {
1491  if (!noSrc)
1492  {
1493  os<<" "<<src
1494  <<" [style=filled,fillcolor=red];\n";
1495  noSrc = true;
1496  }
1497  os<<" "<<src<<" -> " << (*p)->name << " [label=\""
1498  << *c << "\"];\n";
1499  }
1500  }
1501  }
1502  }
1503  }
1504  os << "}" << endl;
1505 }
1506 
1508 {
1509  for (procs_t::const_iterator p=procsEnv.begin(); p!=procsEnv.end(); ++p)
1510  procs.insert((*p)->name);
1511 }
1512 
1514 {
1515  for (procs_t::const_iterator p=procsIUT.begin(); p!=procsIUT.end(); ++p)
1516  procs.insert((*p)->name);
1517 }
1518 
1519 
1521 {
1522  /* FIXME: find global variable if the variable is local process parameter*/
1523  distancesUpToDate = false;
1524  const char* dot = strchr(var, '.');
1525  size_t length = dot == NULL ? strlen(var) : dot - var;
1526  char* name = strncpy(new char[length+1], var, length);
1527  name[length] = '\0';
1528 
1529  if (variables.find(name)==variables.end()) {
1530  cerr << "Variable not found: " << var << endl;
1531  delete [] name;
1532  return;
1533  }
1534  dist_t* distance = NULL;
1535  str2dist_t::iterator i = distances.find(name);
1536  if (i==distances.end()) {
1537  varNeedles.insert(name);
1538  distances[name] = dist_t(0, 1, 0);
1539  //distance = &distances[name]; // FIXME: work-in-progress.
1540  } else {
1541  distance = &(i->second);
1542  if (distance->hops==0 && distance->distance==0) {
1543  /* double the complexity if mentioned several times */
1544  distance->complexity = 2 * distance->complexity;
1545  } else {
1546  distance->hops = 0;
1547  distance->distance = 0;
1548  }
1549  delete [] name; // appearently a copy is in varNeedles
1550  }
1551 }
1552 
1554 {
1555  distancesUpToDate = false;
1556  const char* dot = strchr(proc, '.');
1557  size_t length = dot == NULL ? strlen(proc) : dot - proc;
1558  char* name = strncpy(new char[length+1], proc, length);
1559  name[length] = '\0';
1560 
1561  if (processes.find(name)==processes.end()) {
1562  cerr << "AddNeedle: Process not found: " << proc << endl;
1563  delete [] name;
1564  return;
1565  }
1566 
1567  dist_t* distance = NULL;
1568  str2dist_t::iterator i = distances.find(name);
1569  if (i==distances.end()) {
1570  procNeedles.insert(name);
1571  distances[name] = dist_t(0, calcComplexity(name), 0);
1572  //distance = &distances[name]; // FIXME: work-in-progress.
1573  } else {
1574  distance = &(i->second);
1575  if (distance->hops==0 && distance->distance==0) {
1576  /* double the complexity if mentioned several times */
1577  distance->complexity = 2 * distance->complexity;
1578  } else {
1579  distance->hops = 0;
1580  distance->distance = 0;
1581  }
1582  delete [] name; // appearently a copy is in procNeedles
1583  }
1584 }
1585 
1586 void DistanceCalculator::updateDistancesFromVariable(const char* name,
1587  const dist_t* d)
1588 {
1589  // find all processes that transmit/write on this variable:
1590  str2procs_t::const_iterator t = transmitters.find(name);
1591  if (t == transmitters.end()) return;
1592  for (procs_t::const_iterator p=t->second.begin(),e=t->second.end();
1593  p!=e; ++p)
1594  {
1595  dist_t* distance = NULL;
1596  str2dist_t::iterator i = distances.find((*p)->name);
1597  if (i==distances.end()) {
1598  procNeedles.insert(strcpy(new char[strlen((*p)->name)+1],
1599  (*p)->name));
1600  distances[(*p)->name] = dist_t(d->hops+1,
1601  calcComplexity((*p)->name),
1602  d->distance + d->complexity);
1603  distance = &distances[(*p)->name];
1604  } else {
1605  distance = &(i->second);
1606  if (distance->distance <= d->distance + d->complexity)
1607  continue; // old distance is shorter, no changes to propagate
1608  else {
1609  distance->hops = d->hops+1;
1610  distance->distance = d->distance + d->complexity;
1611  }
1612  }
1613  updateDistancesFromProcess((*p)->name, distance);
1614  }
1615 }
1616 
1617 void DistanceCalculator::updateDistancesFromProcess(const char* name,
1618  const dist_t* d)
1619 {
1620  /* find the process structure representing this process */
1621  procs_t::const_iterator s = procs.begin();
1622  while (s != procs.end())
1623  if (strcmp((*s)->name, name)==0) break;
1624  else ++s;
1625  if (s == procs.end()) {
1626  cerr << "UpdateDistancesFromProcess: Process not found: "<<name<<endl;
1627  return;
1628  }
1629  /* for each channel the process listens to: */
1630  for (strs_t::const_iterator c=(*s)->inChans.begin(),ce=(*s)->inChans.end();
1631  c!=ce; ++c) {
1632  /* find all processes that send on this channel: */
1633  str2procs_t::const_iterator t = transmitters.find(*c);
1634  if (t == transmitters.end()) continue;
1635  /* update the distances for each transmitting process */
1636  for (procs_t::const_iterator p=t->second.begin(),e=t->second.end();
1637  p!=e; ++p)
1638  {
1639  dist_t* distance = NULL;
1640  str2dist_t::iterator i = distances.find((*p)->name);
1641  if (i==distances.end()) {
1642  procNeedles.insert(strcpy(new char[strlen((*p)->name)+1],
1643  (*p)->name));
1644  distances[(*p)->name] = dist_t(d->hops+1,
1645  calcComplexity((*p)->name),
1646  d->distance + d->complexity);
1647  distance = &distances[(*p)->name];
1648  } else {
1649  distance = &(i->second);
1650  if (distance->distance <= d->distance + d->complexity)
1651  continue; // old distance is shorter no changes to propagate
1652  else {
1653  distance->hops = d->hops+1;
1654  distance->distance = d->distance + d->complexity;
1655  }
1656  }
1657  updateDistancesFromProcess((*p)->name, distance);
1658  }
1659  }
1660  /* for each variable the process reads from: */
1661  for (str2strs_t::const_iterator v=(*s)->rdVars.begin(),
1662  ve=(*s)->rdVars.end(); v!=ve; ++v)
1663  {
1664  dist_t* distance = NULL;
1665  str2dist_t::iterator i = distances.find(v->first);
1666  if (i==distances.end()) {
1667  varNeedles.insert(strcpy(new char[strlen(v->first)+1], v->first));
1668  distances[v->first] = dist_t(d->hops+1, 1,
1669  d->distance + d->complexity);
1670  distance = &distances[v->first];
1671  } else {
1672  distance = &(i->second);
1673  if (distance->distance <= d->distance + d->complexity)
1674  continue; // old distance is shorter, no changes to propagate
1675  else {
1676  distance->hops = d->hops+1;
1677  distance->distance = d->distance + d->complexity;
1678  }
1679  }
1680  updateDistancesFromVariable(v->first, distance);
1681  }
1682 }
1683 
1684 int DistanceCalculator::calcComplexity(const char* process)
1685 {
1686  int result = 0;
1687  list<instance_t> &ps(taSystem.getProcesses());
1688  for (list<instance_t>::iterator i=ps.begin(), e=ps.end(); i!=e; ++i)
1689  {
1690  if (i->uid.getName()==process) {
1691  const template_t* temp = i->templ;
1692  deque<edge_t>::const_iterator t = i->templ->edges.begin();
1693  while (t != temp->edges.end()) { result++; t++; }
1694  }
1695  }
1696  return result;
1697 }
1698 
1699 void DistanceCalculator::printProcsForDot(std::ostream &os, bool erd)
1700 {
1701 /* Enumerate processes with common look-attributes */
1702  os << " subgraph procs {\n";
1703  if (erd)
1704  {
1705  os<<" node [shape=rectangle,style=filled];\n";
1706  }
1707  else
1708  {
1709  os<<" node [shape=ellipse,style=filled];\n";
1710  }
1711  os << " ";
1712 
1713  for (procs_t::const_iterator p=procs.begin(), e=procs.end(); p!=e; ++p)
1714  {
1715  str2dist_t::const_iterator d = distances.find((*p)->name);
1716  if (d==distances.end()) {
1717  os << (*p)->name << "[label=\"\\N\\n(( ?, ?))\"]; ";
1718  } else {
1719  os << (*p)->name << "[label=\"\\N\\n((" << d->second.hops
1720  <<", "<< d->second.distance <<"))\"]; ";
1721  }
1722  }
1723  os << "\n }\n";
1724 }
1725 
1726 void DistanceCalculator::printVarsForDot(std::ostream &os, bool ranked,
1727  bool erd)
1728 {
1729  // draw variables
1730  /* specify attributes for all nodes */
1731  os << " subgraph cluste_vars {\n";
1732  os << " style=invis;\n";
1733  if (ranked)
1734  {
1735  os << " rank=min;\n";
1736  }
1737  if (erd)
1738  {
1739  os << " node [shape=diamond,color=blue];\n ";
1740  }
1741  else
1742  {
1743  os << " node [shape=rectangle,color=blue];\n ";
1744  }
1745  for (strs_t::const_iterator i=variables.begin(), e=variables.end();
1746  i!=e; ++i)
1747  {
1748  str2dist_t::const_iterator d = distances.find(*i);
1749  if (d==distances.end()) {
1750  os << *i << "[label=\"\\N\\n(( ?, ?))\"";
1751  } else {
1752  os << *i << "[label=\"\\N\\n((" << d->second.hops
1753  <<", "<< d->second.distance <<"))\"";
1754  }
1755  if (transmitters.find(*i) == transmitters.end()) os << "]; "; // const
1756  else os << ",style=bold]; "; // others bold
1757  }
1758  os << "\n }\n";
1759 }
1760 
1762 {
1763  for(strs_t::iterator i=varNeedles.begin(),e=varNeedles.end(); i!=e; ++i)
1764  delete [] (*i);
1765  varNeedles.clear();
1766 
1767  for(strs_t::iterator i=procNeedles.begin(),e=procNeedles.end(); i!=e; ++i)
1768  delete [] (*i);
1769  procNeedles.clear();
1770 }
1771 
1772 uint32_t DistanceCalculator::getDistance(const char* element)
1773 {
1774  if (!distancesUpToDate) updateDistances();
1775 
1776  str2dist_t::iterator f = distances.find(element);
1777  if (f == distances.end()) {
1778  //cerr << "GetDistance: Process not found: " << element << endl;
1779  return INT_MAX;
1780  }
1781  return f->second.distance;
1782 }
1783 
1784 void DistanceCalculator::printForDot(ostream &os, bool ranked, bool erd,
1785  bool cEdged)
1786 {
1787  if (!distancesUpToDate) updateDistances();
1788 
1789  SignalFlow::printForDot(os, ranked, erd, cEdged);
1790 }
1791 
1793 {
1794  /* cleanup the old stuff: put all distances at infinity=INT_MAX: */
1795  for (str2dist_t::iterator i=distances.begin(), e=distances.end();
1796  i!=e; ++i)
1797  {
1798  if (i->second.hops>0) {
1799  i->second.hops = INT_MAX;
1800  i->second.distance = INT_MAX;
1801  }
1802  }
1803  /* calculate distances from variable needles: */
1804  for(strs_t::iterator i=varNeedles.begin(),e=varNeedles.end();
1805  i!=e; ++i)
1806  {
1807  str2dist_t::iterator f = distances.find(*i);
1808  if (f == distances.end()) {
1809  cerr << "Variable not found: " << *i << endl;
1810  exit(EXIT_FAILURE);
1811  }
1812  updateDistancesFromVariable(*i, &(f->second));
1813  }
1814  /* calculate distances from process needles: */
1815  for(strs_t::iterator i=procNeedles.begin(),e=procNeedles.end();
1816  i!=e; ++i)
1817  {
1818  str2dist_t::iterator f = distances.find(*i);
1819  if (f == distances.end()) {
1820  cerr << "Process not found: " << *i << endl;
1821  exit(EXIT_FAILURE);
1822  }
1823  updateDistancesFromProcess(*i, &(f->second));
1824  }
1825  distancesUpToDate = true;
1826 }
1827 
expression_t value
Definition: statement.h:214
virtual void printChansOnEdgesForDot(std::ostream &os)
Definition: signalflow.cpp:197
Class SignalFlow is for analysing UPPAAL specifications and extracting the timed automata input/outpu...
Definition: signalflow.h:56
const char * cChan
Definition: signalflow.h:88
Constants::kind_t getKind() const
Returns the kind of the expression.
Definition: expression.cpp:182
Partial instance of a template.
Definition: system.h:329
#define MEDSTYLE
int32_t visitWhileStatement(WhileStatement *stat) override
Definition: signalflow.cpp:716
static ParserBuilder * ch
Definition: parser.cc:105
struct template_t * templ
Definition: system.h:336
int32_t visitBreakStatement(BreakStatement *stat) override
Definition: signalflow.cpp:768
void addProcsByVars(const strs_t &vars, procs_t &procs, procs_t &exclude)
Take all variables and add all accessing processes to the list.
Definition: signalflow.cpp:945
expression_t cond
Definition: statement.h:165
Statement * falseCase
Definition: statement.h:185
void printForDot(std::ostream &os, bool ranked, bool erd, bool cEdged) override
Print I/O information in DOT format into given output stream.
frame_t getFrame()
Definition: statement.h:141
void visitExpression(const expression_t &)
Definition: signalflow.cpp:444
void printProcsForDot(std::ostream &os, bool erd) override
bool empty() const
Returns true if this is an empty expression.
Definition: expression.cpp:671
bool hasParent() const
Returns true if this frame has a parent.
Definition: symbols.cpp:468
DistanceCalculator is used in TargetFirst heuristic search order of Uppaal.
Definition: signalflow.h:266
int32_t visitCaseStatement(CaseStatement *stat) override
Definition: signalflow.cpp:747
std::deque< state_t > states
Locations.
Definition: system.h:358
const_iterator end() const
Definition: statement.cpp:215
expression_t cond
Definition: statement.h:183
expression_t cond
Definition: statement.h:155
void addIntVars(const procs_t &procs, strs_t &result, strs_t &exclude)
Takes the variables of each process from procs list and adds them to the result list.
Definition: signalflow.cpp:896
virtual void printVarsReadForDot(std::ostream &os)
Definition: signalflow.cpp:175
virtual void printForDot(std::ostream &os, bool ranked, bool erd, bool cEdged)
Print I/O information in DOT format into given output stream.
Definition: signalflow.cpp:326
virtual ~SignalFlow()
All strings are from TASystem (don&#39;t dispose TASystem before SignalFlow).
Definition: signalflow.cpp:789
Statement * stat
Definition: statement.h:109
int32_t visitIfStatement(IfStatement *stat) override
Definition: signalflow.cpp:758
A reference to a symbol.
Definition: symbols.h:107
std::string getName() const
Returns the name (identifier) of this symbol.
Definition: symbols.cpp:228
symbol_t getSymbol()
Returns the symbol of a variable reference.
Definition: expression.cpp:718
void fillWithIUTProcs(strs_t &procs)
void visitProcess(instance_t &)
Definition: signalflow.cpp:420
std::vector< Statement * >::iterator iterator
Definition: statement.h:131
static const char * noChan
Definition: signalflow.cpp:418
int32_t visitContinueStatement(ContinueStatement *stat) override
Definition: signalflow.cpp:773
frame_t parameters
The parameters.
Definition: system.h:332
#define IUTSTYLE
Statement class for the iterator loop-construction.
Definition: statement.h:91
void addChan(const std::string &, strs_t &, str2procs_t &)
Definition: signalflow.cpp:394
int32_t visitDefaultStatement(DefaultStatement *stat) override
Definition: signalflow.cpp:753
int32_t visitEmptyStatement(EmptyStatement *stat) override
System visitor pattern extracts read/write information from UCode.
Definition: signalflow.cpp:691
void printVarsForDot(std::ostream &os, bool ranked, bool erd) override
#define ENVSTYLE
virtual void printProcsForDot(std::ostream &os, bool erd)
Definition: signalflow.cpp:101
expression_t expr
Definition: statement.h:58
std::set< proc_t * > procs_t
Definition: signalflow.h:76
int32_t visitBlockStatement(BlockStatement *stat) override
Definition: signalflow.cpp:729
std::list< instance_t > & getProcesses()
Returns the processes of the system.
Definition: system.cpp:956
Partitions the system into environment and IUT according to TRON assumptions.
Definition: signalflow.h:228
std::map< const char *, strs_t > str2strs_t
Definition: signalflow.h:67
void set_remove(SignalFlow::strs_t &from, const SignalFlow::strs_t &what)
void addProcessNeedle(const char *proc)
adds a variable needle to I/O map
int partition(const strs_t &inputs, const strs_t &outputs)
void updateDistances()
Recalculates the distances to needles.
strs_t variables
Definition: signalflow.h:85
std::deque< edge_t > edges
Edges.
Definition: system.h:360
std::set< const char *, const less_str > strs_t
Definition: signalflow.h:64
virtual void printChansSeparateForDot(std::ostream &os, bool ranked, bool erd)
Definition: signalflow.cpp:290
std::stack< exprref_t > valparams
Definition: signalflow.h:93
std::stack< exprref_t > refparams
Definition: signalflow.h:92
str2procs_t receivers
Definition: signalflow.h:84
virtual void printVarsForDot(std::ostream &os, bool ranked, bool erd)
Definition: signalflow.cpp:120
int32_t visitIterationStatement(IterationStatement *stat) override
Definition: signalflow.cpp:702
uint32_t getDistance(const char *element)
Finds a distance measure for given element.
print – template for pretty printing lists.
Definition: signalflow.h:175
bool checkParams(const symbol_t &s)
Definition: signalflow.cpp:359
str2procs_t transmitters
Definition: signalflow.h:84
void fillWithEnvProcs(strs_t &procs)
int32_t visitReturnStatement(ReturnStatement *stat) override
Definition: signalflow.cpp:783
std::map< const symbol_t, expression_t > exprref_t
Definition: signalflow.h:78
const_iterator begin() const
Definition: statement.cpp:210
void addVariableNeedle(const char *var)
adds a variable needle to I/O map
SignalFlow(const char *_title, TimedAutomataSystem &ta)
Analyse the system and extract I/O information:
Definition: signalflow.cpp:50
int32_t accept(StatementVisitor *visitor) override
Definition: statement.cpp:243
proc_t * cTA
Definition: signalflow.h:86
char * get_token(istream &in)
A reference to a frame.
Definition: symbols.h:183
void printForDot(std::ostream &os, bool ranked, bool erd, bool cEdged) override
Print I/O information in DOT format into given output stream.
A reference to a type.
Definition: type.h:93
std::map< symbol_t, expression_t > mapping
The arguments.
Definition: system.h:333
type_t getType() const
Returns the type of this symbol.
Definition: symbols.cpp:205
void addVar(const symbol_t &, str2strs_t &, str2procs_t &)
Definition: signalflow.cpp:407
void printForTron(std::ostream &os)
Print I/O information in TRON format into given output stream.
Definition: signalflow.cpp:66
int32_t visitSwitchStatement(SwitchStatement *stat) override
Definition: signalflow.cpp:741
strs_t processes
Definition: signalflow.h:85
A reference to an expression.
Definition: expression.h:70
expression_t cond
Definition: statement.h:120
Information about a function.
Definition: system.h:110
expression_t init
Definition: statement.h:78
int32_t visitForStatement(ForStatement *stat) override
Definition: signalflow.cpp:708
type_t getType() const
Returns the type of the expression.
Definition: expression.cpp:611
void * getData()
Returns the user data of this symbol.
Definition: symbols.cpp:216
void addProcs(const strs_t &chans, const str2procs_t &index, procs_t &result, procs_t &exclude)
Adds processes to the result which use the channels from chans list according to index.
Definition: signalflow.cpp:817
virtual int32_t accept(StatementVisitor *visitor)=0
expression_t step
Definition: statement.h:80
int32_t getValue() const
Returns the value field of this expression.
Definition: expression.cpp:623
expression_t cond
Definition: statement.h:108
size_t getSize() const
Returns the number of subexpression.
Definition: expression.cpp:379
expression_t cond
Definition: statement.h:79
std::string chanString
Definition: signalflow.h:89
Statement * trueCase
Definition: statement.h:184
bool is(Constants::kind_t kind) const
Returns true if the type has kind kind or if type is a prefix, RANGE or REF type and the getChild()...
Definition: type.cpp:176
void printViolation(const proc_t *process, const char *variable)
Definition: signalflow.cpp:805
#define BADSTYLE
std::map< const char *, procs_t, less_str > str2procs_t
Definition: signalflow.h:77
frame_t getFrame()
Get frame this symbol belongs to.
Definition: symbols.cpp:199
int32_t visitExprStatement(ExprStatement *stat) override
Definition: signalflow.cpp:696
Constants::synchronisation_t getSync() const
Returns the synchronisation type of SYNC operations.
Definition: expression.cpp:641
Statement * stat
Definition: statement.h:81
void free(void *)
const char * title
Definition: signalflow.h:82
void addIntChans(const procs_t &procs, strs_t &result, strs_t &exclude)
Takes the internal channels of each process from procs list and adds them to the result list...
Definition: signalflow.cpp:850
int32_t visitDoWhileStatement(DoWhileStatement *stat) override
Definition: signalflow.cpp:722
int32_t getIndexOf(const std::string &name) const
Returns the index of the symbol with the given name.
Constants::kind_t getKind() const
Returns the kind of type object.
Definition: type.cpp:120
virtual void printVarsWriteForDot(std::ostream &os)
Definition: signalflow.cpp:153
instance_t * cP
Definition: signalflow.h:87
int32_t visitAssertStatement(UTAP::AssertStatement *stat) override
Definition: signalflow.cpp:778
symbol_t uid
The name.
Definition: system.h:331