1 //standard header files
8 #include "llvm/Support/raw_ostream.h"
9 #include "llvm/Support/Host.h"
10 #include "llvm/Config/config.h"
13 #include "clang/Frontend/TextDiagnosticPrinter.h"
14 #include "clang/Lex/HeaderSearch.h"
15 #include "clang/Basic/FileManager.h"
16 #include "clang/Frontend/Utils.h"
17 #include "clang/Basic/TargetInfo.h"
18 #include "clang/Lex/Preprocessor.h"
19 #include "clang/Frontend/CompilerInstance.h"
20 #include "clang/AST/ASTConsumer.h"
21 #include "clang/Sema/Lookup.h"
22 #include "clang/Parse/ParseAST.h"
23 #include "clang/Basic/Version.h"
24 #include "clang/Driver/Driver.h"
25 #include "clang/Driver/Compilation.h"
28 #include "stringoper.h"
30 using namespace clang;
31 using namespace clang::driver;
34 class MyDiagnosticClient : public TextDiagnosticPrinter // My diagnostic Client
37 MyDiagnosticClient(llvm::raw_ostream &os, const DiagnosticOptions &diags, bool OwnsOutputStream = false):TextDiagnosticPrinter(os, diags, OwnsOutputStream = false){}
38 virtual void HandleDiagnostic(Diagnostic::Level DiagLevel, const DiagnosticInfo &Info)
40 TextDiagnosticPrinter::HandleDiagnostic(DiagLevel, Info); // print diagnostic information
41 if(DiagLevel > 2) // if error/fatal error stop the program
48 class FindStates : public ASTConsumer
50 list<string> transitions;
51 list<string> cReactions;
53 string name_of_machine;
61 virtual void Initialize(ASTContext &ctx)//run after the AST is constructed before the consumer starts to work
63 fsloc = new FullSourceLoc(* new SourceLocation(), ctx.getSourceManager());
69 virtual void HandleTopLevelDecl(DeclGroupRef DGR)// traverse all top level declarations
72 std::string line, output, event;
73 llvm::raw_string_ostream x(output);
74 for (DeclGroupRef::iterator i = DGR.begin(), e = DGR.end(); i != e; ++i)
76 const Decl *decl = *i;
77 loc = decl->getLocation();
80 //cout<<decl->getKind()<<"ss\n";
81 if(decl->getKind()==35)
85 if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
87 if(tagDecl->isStruct() || tagDecl->isClass()) //is it a struct or class
92 if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
95 DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
96 //cout<<namedDecl->getNameAsString()<<" sss\n";
97 recursive_visit(declCont);
104 void recursive_visit(const DeclContext *declCont) //recursively visit all decls hidden inside namespaces
106 std::string line, output, event;
107 llvm::raw_string_ostream x(output);
109 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i)
111 const Decl *decl = *i;
112 //std::cout<<"a "<<decl->getDeclKindName()<<"\n";
113 loc = decl->getLocation();
116 if(decl->getKind()==35)
120 else if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
122 if(tagDecl->isStruct() || tagDecl->isClass()) //is it a structure or class
127 else if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
129 DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
130 //cout<<namedDecl->getNameAsString()<<" sss\n";
131 recursive_visit(declCont);
138 void struct_class(const Decl *decl) // works with struct or class decl
140 string output, line, ret, trans, event;
141 llvm::raw_string_ostream x(output);
143 line = sd.get_line_of_code(x.str());
146 const TagDecl *tagDecl = dyn_cast<TagDecl>(decl);
147 const NamedDecl *namedDecl = dyn_cast<NamedDecl>(decl);
148 if(sd.is_derived(line))
150 const CXXRecordDecl *cRecDecl = dyn_cast<CXXRecordDecl>(decl);
152 if(sd.find_events(cRecDecl, line))
154 events.push_back(namedDecl->getNameAsString());
155 cout<<"New event: "<<namedDecl->getNameAsString()<<"\n";
157 else if(name_of_machine == "")
159 ret = sd.find_name_of_machine(cRecDecl, line);
163 name_of_machine = ret.substr(0,pos);
164 name_of_start = ret.substr(pos+1);
165 cout<<"Name of the state machine: "<<name_of_machine<<"\n";
166 cout<<"Name of the first state: "<<name_of_start<<"\n";
171 ret = sd.find_states(cRecDecl, line);
174 const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl);
175 //states.push_back(namedDecl->getNameAsString());
176 std::cout << "New state: " << namedDecl->getNameAsString() << "\n";
177 states.push_back(ret);
179 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i)
181 const Decl *decl = *i;
182 if (decl->getKind()==26)
186 line = sd.clean_spaces(sd.cut_type(output));
187 ret = sd.find_transitions(namedDecl->getNameAsString(),line);
190 num = sd.count(ret,';')+1;
191 for(int i = 0;i<num;i++)
198 if(pos==-1) cReactions.push_back(ret);
199 else cReactions.push_back(ret.substr(0,pos));
204 if(pos==-1) transitions.push_back(ret);
205 else transitions.push_back(ret.substr(0,pos));
208 if(i!=num-1) ret = ret.substr(pos+1);
213 if(decl->getKind()==35) method_decl(decl);
219 void method_decl(const Decl *decl)
221 string output, line, event;
222 llvm::raw_string_ostream x(output);
226 line = sd.get_return(x.str());
227 if(sd.test_model(line,"result"))
229 const FunctionDecl *fDecl = dyn_cast<FunctionDecl>(decl);
230 const ParmVarDecl *pvd = fDecl->getParamDecl(0);
231 QualType qt = pvd->getOriginalType();
232 event = qt.getAsString();
233 if(event[event.length()-1]=='&') event = event.substr(0,event.length()-2);
234 event = event.substr(event.rfind(" ")+1);
235 line = dyn_cast<NamedDecl>(decl)->getQualifiedNameAsString();
236 line = sd.cut_namespaces(line.substr(0,line.rfind("::")));
239 find_return_stmt(decl->getBody(),line);
240 for(list<string>::iterator i = cReactions.begin();i!=cReactions.end();i++)
243 if(line.compare(event)==0)
252 void find_return_stmt(Stmt *statemt,string event)
254 if(statemt->getStmtClass() == 99) test_stmt(dyn_cast<CaseStmt>(statemt)->getSubStmt(), event);
257 for (Stmt::child_range range = statemt->children(); range; ++range)
259 test_stmt(*range, event);
264 void test_stmt(Stmt *stmt, string event)
266 const SourceManager &sman = fsloc->getManager();
269 type = stmt->getStmtClass();
272 case 8 : find_return_stmt(dyn_cast<DoStmt>(stmt)->getBody(), event); // do
274 case 86 : find_return_stmt(dyn_cast<ForStmt>(stmt)->getBody(), event); // for
276 case 88 : find_return_stmt(dyn_cast<IfStmt>(stmt)->getThen(), event); //if then
277 find_return_stmt(dyn_cast<IfStmt>(stmt)->getElse(), event); //if else
279 case 90 : find_return_stmt(dyn_cast<LabelStmt>(stmt)->getSubStmt(), event); //label
281 case 98 : line = sman.getCharacterData(dyn_cast<ReturnStmt>(stmt)->getReturnLoc());
282 line = sd.get_line_of_code(line).substr(6);
283 line = line.substr(0,line.find("("));
284 if(sd.test_model(line,"transit"))
286 param = sd.get_params(line);
287 transitions.push_back(event.append(",").append(param));
290 case 99 : find_return_stmt(stmt, event);
292 case 101 : find_return_stmt(dyn_cast<SwitchStmt>(stmt)->getBody(), event); // switch
294 case 102 : find_return_stmt(dyn_cast<WhileStmt>(stmt)->getBody(), event); // while
299 void save_to_file(std::string output) // save all to the output file
301 nbrStates = states.size();
302 string state, str, context, ctx;
303 int pos1, pos2, cnt, subs;
304 ofstream filestr(output.c_str());
305 //std::cout<<output<<"\n";
306 filestr<<"digraph "<< name_of_machine<< " {\n";
307 context = name_of_machine;
308 for(list<string>::iterator i = states.begin();i!=states.end();i++) // write all states in the context of the automaton
311 cnt = sd.count(state,',');
314 pos1 = state.find(",");
315 ctx = sd.cut_namespaces(state.substr(pos1+1));
316 //std::cout<<name_of_machine.length();
317 if(ctx.compare(0,context.length(),context)==0)
319 filestr<<sd.cut_namespaces(state.substr(0,pos1))<<";\n";
326 pos1 = state.find(",");
327 pos2 = state.rfind(",");
328 ctx = sd.cut_namespaces(state.substr(pos1+1,pos2-pos1-1));
329 //std::cout<<ctx<<" "<<context<<"\n";
330 if(ctx.compare(0,context.length(),context)==0)
332 filestr<<sd.cut_namespaces(state.substr(0,pos1))<<";\n";
336 filestr<<name_of_start<<" [peripheries=2] ;\n";
338 while(!states.empty()) // substates ?
340 state = states.front();
341 filestr<<"subgraph cluster"<<subs<<" {\n";
342 pos1 = state.find(",");
343 pos2 = state.rfind(",");
344 context = sd.cut_namespaces(state.substr(0,pos1));
345 filestr<<"label=\""<<context<<"\";\n";
346 filestr<<sd.cut_namespaces(state.substr(pos2+1))<<" [peripheries=2] ;\n";
348 //std::cout<<states.size();
349 for(list<string>::iterator i = states.begin();i!=states.end();i++)
352 cnt = sd.count(state,',');
353 //std::cout<<state<<" \n";
356 pos1 = state.find(",");
357 ctx = sd.cut_namespaces(state.substr(pos1+1));
358 if(ctx.compare(0,context.length(),context)==0)
360 filestr<<sd.cut_namespaces(state.substr(0,pos1))<<";\n";
367 pos1 = state.find(",");
368 pos2 = state.rfind(",");
369 ctx = sd.cut_namespaces(state.substr(pos1+1,pos2-pos1-1));
370 if(ctx.compare(0,context.length(),context)==0) filestr<<sd.cut_namespaces(state.substr(0,pos1))<<";\n";
376 for(list<string>::iterator i = transitions.begin();i!=transitions.end();i++) // write all transitions
379 pos1 = state.find(",");
380 filestr<<sd.cut_namespaces(state.substr(0,pos1))<<"->";
381 pos2 = state.rfind(",");
382 filestr<<sd.cut_namespaces(state.substr(pos2+1));
383 filestr<<"[label=\""<<sd.cut_namespaces(state.substr(pos1+1,pos2-pos1-1))<<"\"];\n";
388 void print_stats() // print statistics
390 cout<<"\n"<<"Statistics: \n";
391 cout<<"Number of states: "<<nbrStates<<"\n";
392 cout<<"Number of events: "<<events.size()<<"\n";
393 cout<<"Number of transitions: "<<transitions.size()<<"\n";
399 int main(int argc, char **argv)
401 string inputFilename = "";
402 string outputFilename = "graph.dot"; // initialize output Filename
403 MyDiagnosticClient *mdc = new MyDiagnosticClient(llvm::errs(), * new DiagnosticOptions());
404 llvm::IntrusiveRefCntPtr<DiagnosticIDs> dis(new DiagnosticIDs());
405 Diagnostic diag(dis,mdc);
406 FileManager fm( * new FileSystemOptions());
407 SourceManager sm (diag, fm);
408 HeaderSearch *headers = new HeaderSearch(fm);
410 Driver TheDriver(LLVM_PREFIX "/bin", llvm::sys::getHostTriple(), "", false, false, diag);
411 TheDriver.setCheckInputsExist(true);
412 TheDriver.CCCIsCXX = 1;
413 CompilerInvocation compInv;
414 llvm::SmallVector<const char *, 16> Args(argv, argv + argc);
415 llvm::OwningPtr<Compilation> C(TheDriver.BuildCompilation(Args.size(),
417 const driver::JobList &Jobs = C->getJobs();
418 const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin());
419 const driver::ArgStringList &CCArgs = Cmd->getArguments();
420 for(unsigned i = 0; i<Args.size();i++) // find -o in ArgStringList
422 if(strncmp(Args[i],"-o",2)==0)
424 if(strlen(Args[i])>2)
426 string str = Args[i];
427 outputFilename = str.substr(2);
429 else outputFilename = Args[i+1];
434 CompilerInvocation::CreateFromArgs(compInv,
435 const_cast<const char **>(CCArgs.data()),
436 const_cast<const char **>(CCArgs.data())+CCArgs.size(),
439 HeaderSearchOptions hsopts = compInv.getHeaderSearchOpts();
440 hsopts.ResourceDir = LLVM_PREFIX "/lib/clang/" CLANG_VERSION_STRING;
441 LangOptions lang = compInv.getLangOpts();
442 CompilerInvocation::setLangDefaults(lang, IK_ObjCXX);
443 TargetInfo *ti = TargetInfo::CreateTargetInfo(diag, compInv.getTargetOpts());
444 ApplyHeaderSearchOptions(*headers, hsopts, lang, ti->getTriple());
445 FrontendOptions f = compInv.getFrontendOpts();
446 inputFilename = f.Inputs[0].second;
448 cout<<"Input filename: "<<inputFilename<<"\n"; // print Input filename
449 cout<<"Output filename: "<<outputFilename<<"\n"; // print Output filename
452 Preprocessor pp(diag, lang, *ti, sm, *headers);
453 pp.getBuiltinInfo().InitializeBuiltins(pp.getIdentifierTable(), lang);
455 InitializePreprocessor(pp, compInv.getPreprocessorOpts(),hsopts,f);
457 const FileEntry *file = fm.getFile(inputFilename);
458 sm.createMainFileID(file);
459 IdentifierTable tab(lang);
460 Builtin::Context builtins(*ti);
462 ASTContext ctx(lang, sm, *ti, tab, * new SelectorTable(), builtins,0);
463 mdc->BeginSourceFile(lang, &pp);//start using diagnostic
464 ParseAST(pp, &c, ctx, false, false);
465 mdc->EndSourceFile(); //end using diagnostic
466 if(c.states.size()>0) c.save_to_file(outputFilename);
467 else cout<<"No state machine was found\n";