changed the code such that tcl/tk closes the STDIN channel
[dyninst.git] / codeCoverage / src / main.C
1 #include <stdio.h>
2 #include <iostream.h>
3 #include <stdlib.h>
4
5 #ifdef sparc_sun_solaris2_4
6
7 #include <string.h>
8 #include <fstream.h>
9 #include <limits.h>
10 #include <unistd.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <tcl.h>
14 #include <tk.h>
15 #include <pthread.h>
16
17 #endif
18
19 #ifdef sparc_sun_solaris2_4
20
21 #include <CCcommon.h>
22 #include <FunctionCoverage.h>
23 #include <CCPreInstrument.h>
24 #include <CCOnDemandInstrument.h>
25
26 #endif
27
28 #ifdef sparc_sun_solaris2_4
29
30 typedef struct {
31         Tcl_Interp* interp;
32         CodeCoverage* codeCoverage;
33 }ARGS;
34
35 Tk_Window mainWindow = NULL;
36 Tk_Window textMessage = NULL;
37 Tk_Window fileDisplay = NULL;
38 Tk_Window fileList = NULL;
39 Tk_Window funcList = NULL;
40 Tk_Window startButton = NULL;
41 Tk_Window refreshButton = NULL;
42 Tk_Window statusMessage = NULL;
43
44 bool executionStarted = false;
45 bool isRunning = false;
46
47 void statusUpdateProcedure(ClientData clientData){
48         char buffer[1024];
49         CodeCoverage* codeCoverage = ((ARGS*)clientData)->codeCoverage;
50         Tcl_Interp* interp = ((ARGS*)clientData)->interp;
51         
52         if(codeCoverage->getTclStatusUpdateString(buffer,1024))
53                 Tcl_Eval(interp,buffer);
54
55         Tcl_CreateTimerHandler(2000,statusUpdateProcedure,clientData);
56 }
57
58 void* codeCoverageThread(void* arg){
59
60         CodeCoverage* codeCoverage = ((ARGS*)arg)->codeCoverage;
61         Tcl_Interp* interp = ((ARGS*)arg)->interp;
62
63         int errorCode = Error_OK;
64
65         /** insert the initial instrumentation code for the functions
66           * that are selected to be instrumented for code coverage
67           */
68         errorCode = codeCoverage->instrumentInitial();
69         if(errorCode < Error_OK){
70                 codeCoverage->terminate();
71                 return NULL;
72         }
73
74         if(interp)
75                 Tcl_Eval(interp,"set alreadyStarted 1");
76
77         isRunning = true;
78
79         /** runs the mutatee after the instrumentation */
80         errorCode = codeCoverage->run();
81         if(errorCode < Error_OK){
82                 codeCoverage->terminate();
83                 return NULL;
84         }
85
86         if(interp)
87                 Tcl_Eval(interp,"set isTerminated 1");
88
89         return arg;
90 }
91
92 void exitCodeCoverage(ClientData clientData){
93         CodeCoverage* codeCoverage = ((ARGS*)clientData)->codeCoverage;
94         codeCoverage->terminate();
95         //delete codeCoverage;
96         cerr << "information: terminating code coverage execution ..." << endl;
97 }
98
99 void startButtonHandler(ClientData clientData,XEvent* eventPtr){
100         if(!eventPtr)
101                 return;
102
103         CodeCoverage* codeCoverage = ((ARGS*)clientData)->codeCoverage;
104         if(!executionStarted){
105                 pthread_attr_t attr;
106                 pthread_t newThreadId;
107                 pthread_attr_init(&attr);
108                 if(pthread_create(&newThreadId,&attr,
109                                   codeCoverageThread,(void*)clientData)){
110                         codeCoverage->terminate();
111                         cerr << "ERROR : Can not create graphical"
112                              << "  interface pthread..." << endl;
113                         exit(-1);
114                 }
115                 executionStarted = true;
116         }
117 }
118
119 void refreshButtonHandler(ClientData clientData,XEvent* eventPtr);
120
121 void graphUpdateProcedure(ClientData clientData){
122
123         char buffer[1024];
124         CodeCoverage* codeCoverage = ((ARGS*)clientData)->codeCoverage;
125         Tcl_Interp* interp = ((ARGS*)clientData)->interp;
126         
127         refreshButtonHandler(clientData,NULL);
128         
129         strcpy(buffer,"UpdateAfterRefresh .fileFrame.displayPanel.text \
130                 .fileFrame.message \
131                 .fileFrame.status \
132                 .menuFrame.listFrame.fileListFrame \
133                 .menuFrame.listFrame.funcListFrame \
134                 .menuFrame.listFrame.graphPanel \
135                 globalDataStructure \
136                 globalExecutionMap \
137                 globalFrequencyMap");
138  
139         Tcl_Eval(interp,buffer);
140
141         Tcl_CreateTimerHandler(5000,graphUpdateProcedure,clientData);
142 }
143
144 void refreshButtonHandler(ClientData clientData,XEvent* eventPtr){
145         /*
146         if(!eventPtr)
147                 return;
148         */
149
150         CodeCoverage* codeCoverage = ((ARGS*)clientData)->codeCoverage;
151         Tcl_Interp* interp = ((ARGS*)clientData)->interp;
152         
153         if(!isRunning){
154                 /*
155                 Tcl_Eval(interp,".fileFrame.status configure -text \
156                          \"To refresh, please wait untill mutatee starts...\"");
157                 */
158                 return;
159         }
160
161         char tclFileName[124];
162         sprintf(tclFileName,"./.updateExecuted.tcl.%d",(int)getpid());
163         ofstream tclFile;
164         tclFile.open(tclFileName,ios::out);
165         codeCoverage->getTclTkExecutedLines(tclFile);
166
167         if(Tcl_EvalFile(interp,tclFileName) != TCL_OK){
168                 codeCoverage->terminate();
169                 cerr << "ERROR: Update execution string is not valid..."
170                      << endl;
171                 exit(-1);
172         }
173
174         tclFile.close();
175         unlink(tclFileName);
176 }
177
178 void prepareTclTkGlobalDataStucture(ARGS* passedArguments) {
179
180         CodeCoverage* codeCoverage = passedArguments->codeCoverage;
181         Tcl_Interp* interp = passedArguments->interp;
182
183         char tclFileName[124];
184         sprintf(tclFileName,"./.contructDS.tcl.%d",(int)getpid());
185         ofstream tclFile;
186         tclFile.open(tclFileName,ios::out);
187         codeCoverage->getTclTkMenuListCreation(tclFile);
188
189         if(Tcl_EvalFile(interp,tclFileName) != TCL_OK){
190                 codeCoverage->terminate();
191                 cerr << "ERROR: Menu list preparation string is not valid..."
192                      << endl;
193                 exit(-1);
194         }
195         tclFile.close();
196         unlink(tclFileName);
197 }
198
199 int Tcl_AppInit(Tcl_Interp* interp){
200         return (interp ? TCL_OK : TCL_OK);
201 }
202
203 Tcl_Interp* initTclTk(CodeCoverage* codeCoverage,int interval){
204
205         Tcl_Interp* interp = Tcl_CreateInterp();
206         if(!interp) 
207                 return NULL;
208
209         if(Tcl_Init(interp) == TCL_ERROR)
210                 return NULL;
211
212         Tcl_Channel channel = Tcl_GetStdChannel(TCL_STDIN);
213         if(channel){
214                 if(Tcl_Close(interp,channel) != TCL_OK)
215                         cerr << "warning : Tcl_Channel for TCL_STDIN  can not be closed" << endl;
216         }
217         else
218                 cerr << "warning : Tcl_Channel for TCL_STDIN is not valid" << endl;
219
220         if(Tk_Init(interp) == TCL_ERROR)
221                 return NULL;
222
223         char buffer[124];
224         sprintf(buffer,"set deletionInterval %d",interval);
225         Tcl_Eval(interp,buffer);
226
227         char tcktkFilePath[1024];
228         char* p = getenv("DYNINST_ROOT");
229         if(!p){
230                 codeCoverage->terminate();
231                 cerr << "ERROR: DYNINST_ROOT environment variable is not set..."
232                      << endl;
233                 exit(-1);
234         }
235         sprintf(tcktkFilePath,"%s/core/codeCoverage/src/interface.tcl",p);
236
237         if(Tcl_EvalFile(interp,tcktkFilePath) != TCL_OK){
238                 codeCoverage->terminate();
239                 cerr << "ERROR: Interface preparation script can not "
240                      << "be run correctly (" << tcktkFilePath << ")..."
241                      << endl;
242                 exit(-1);
243         }
244
245         mainWindow = Tk_MainWindow(interp);
246         textMessage = Tk_NameToWindow(interp,
247                                 ".fileFrame.message",mainWindow);
248         fileDisplay = Tk_NameToWindow(interp,
249                                 ".fileFrame.displayPanel.text",mainWindow);
250         fileList = Tk_NameToWindow(interp,
251                                 ".menuFrame.listFrame.fileListFrame.list",mainWindow);
252         funcList = Tk_NameToWindow(interp,
253                                 ".menuFrame.listFrame.funcListFrame",mainWindow);
254         startButton = Tk_NameToWindow(interp,
255                                 ".menuFrame.buttonFrame.start",mainWindow);
256         refreshButton = Tk_NameToWindow(interp,
257                                 ".menuFrame.buttonFrame.refresh",mainWindow);
258         statusMessage = Tk_NameToWindow(interp,
259                                 ".menuFrame.buttonFrame.quit",mainWindow);
260
261         if(!mainWindow || !textMessage || !fileDisplay || !fileList ||
262            !funcList || !startButton || !refreshButton || !statusMessage){
263                 codeCoverage->terminate();
264                 cerr << "ERROR: Some of the interface widgets can not be accessed..."
265                      << endl;
266                 exit(-1);
267         }
268
269         codeCoverage->setTclTkSupport(interp,".fileFrame.status");
270
271         return interp;
272 }
273
274 Tcl_Interp* initTclTkForView(){
275
276         Tcl_Interp* interp = Tcl_CreateInterp();
277         if(!interp)
278                 return NULL;
279
280         if(Tcl_Init(interp) == TCL_ERROR)
281                 return NULL;
282         if(Tk_Init(interp) == TCL_ERROR)
283                 return NULL;
284
285         Tcl_Eval(interp,"set deletionInterval 0");
286
287         char tcktkFilePath[1024];
288         char* p = getenv("DYNINST_ROOT");
289         if(!p){
290                 cerr << "ERROR: DYNINST_ROOT environment variable is not set..."
291                      << endl;
292                 exit(-1);
293         }
294         sprintf(tcktkFilePath,"%s/core/codeCoverage/src/interface.tcl",p);
295
296         if(Tcl_EvalFile(interp,tcktkFilePath) != TCL_OK){
297                 cerr << "ERROR: Interface preparation script can not "
298                      << "be run correctly (" << tcktkFilePath << ")..."
299                      << endl;
300                 exit(-1);
301         }
302         if((Tcl_Eval(interp,"destroy .menuFrame.buttonFrame.start") != TCL_OK) ||
303            (Tcl_Eval(interp,"destroy .menuFrame.buttonFrame.refresh") != TCL_OK) ||
304            (Tcl_Eval(interp,"destroy .fileFrame.status") != TCL_OK))
305         {
306                 cerr << "ERROR: Destroying some widgets do not work poperly..." << endl;
307                 exit(-1);
308         }
309
310         return interp;
311 }
312
313 #endif
314
315 /** function to show the usage of code coverage tool 
316   * one usage is to run and produce the coverage results
317   * in a binary file and the other is to view the coverage results
318   */
319 void printUsage(char* s,bool d=false){
320         cerr << "Usage_1 : " << s << " [--interface] [--deletion <interval>] [--dominator] \\" << endl
321              << "                 [--ondemand] [--suffix <outfile suffix>] \\" << endl
322              << "                 --run <executable> <arguments>" << endl;
323         cerr << "Usage_2 : " << s << " --view <fileName>" << endl;
324         cerr << "Usage_3 : " << s << " --xview <fileName>" << endl;
325
326         if(d)
327           cerr 
328              << endl
329              << "Information : " << endl
330              << endl
331              << "--deletion     : Interval to delete instrumentation code in seconds." << endl
332              << "                 Default value is 0, that is no deletion of " << endl
333              << "                 instrumentation code." << endl
334              << "--dominator    : Flag to make this tool use dominator information." << endl
335              << "                 All basic blocks is used by default." << endl
336              << "--suffix       : The suffix of the output file, generated by appending" << endl
337              << "                 to the name of executable.Output file contains " << endl
338              << "                 coverage information" << endl
339              << "--ondemand     : Flag to instrument functions when called first time." << endl
340              << "                 By default, the functions with source line information" << endl
341              << "                 is pre-instrumented." << endl
342              << "--run          : The executable and its arguments to run is given after" << endl
343              << "                 this flag. This flag HAS to come after all other flags" << endl
344              << "--view         : To view the output file generated from coverage data" << endl
345              << "                 in text format. The output file is generated if it is" << endl
346              << "                 executed in Usage_1 format." << endl
347              << "--xview         : To view the output file generated from coverage data" << endl
348              << "                 using graphical interface." << endl
349              << "--interface    : To run the code coverage with its Tcl/Tk" << endl
350              << "                 based grahical user interface." << endl
351              << endl << endl;
352
353         exit(0);
354 }
355
356 /** main function */
357 int main(int argc,char* argv[]){
358 #ifdef sparc_sun_solaris2_4
359
360         bool useInterface = false;
361         bool useDominator = false;
362         bool useOnDemand = false;
363         char* suffix = ".dyncov";
364         int interval = 0;
365         int execIndex = 0;
366         char* p = NULL;
367         int errorCode = Error_OK;
368         Tcl_Interp* interp = NULL;
369
370
371         if(argc < 3)
372                 printUsage(argv[0],true);
373
374         if((argc == 3) && !strncmp("--view",argv[1],6)){
375                 return CodeCoverage::viewCodeCoverageInfo(argv[2]);
376         }
377         else if((argc == 3) && !strncmp("--xview",argv[1],7)){
378                 interp = initTclTkForView(); 
379                 if(!interp){
380                         cerr << "ERROR : The Tcl/Tk interpreter"
381                              << " can not be created..." << endl; 
382                         exit(-1);
383                 }
384
385                 char tclFileName[124];
386                 sprintf(tclFileName,"./.view.tcl.%d",(int)getpid());
387                 ofstream tclFile;
388                 tclFile.open(tclFileName,ios::out);
389
390                 errorCode = CodeCoverage::getTclTkMenuListForView(argv[2],tclFile);
391
392                 if(errorCode != Error_OK){
393                         cerr << "ERROR: Coverage file can not be parsed properly..."
394                              << endl;
395                         exit(-1);
396                 }
397
398                 if(Tcl_EvalFile(interp,tclFileName) != TCL_OK){
399                         cerr << "ERROR: Menu list preparation string is not valid..."
400                              << endl;
401                         exit(-1);
402                 }
403
404                 tclFile.close();
405                 unlink(tclFileName);
406
407                 Tk_Main(argc,argv,Tcl_AppInit);
408                 Tcl_DeleteInterp(interp);
409
410                 return errorCode;
411         }
412
413         for(int i=1;i<argc;i++){
414                 if(!strncmp("--del",argv[i],5)){
415                         i++;
416                         if(!strncmp("--",argv[i],2))
417                                 printUsage(argv[0]);
418                         interval = strtol(argv[i],&p,10);
419                         if(argv[i] == p)
420                                 printUsage(argv[0]);
421                 }
422                 else if(!strncmp("--dom",argv[i],5))
423                         useDominator = true;
424                 else if(!strncmp("--ond",argv[i],5))
425                         useOnDemand = true;
426                 else if(!strncmp("--run",argv[i],5)){
427                         execIndex = i+1;
428                         break;
429                 }
430                 else if(!strncmp("--suf",argv[i],5)){
431                         i++;
432                         if(!strncmp("--",argv[i],2))
433                                 printUsage(argv[0]);
434                         suffix = argv[i];
435                 }
436                 else if(!strncmp("--int",argv[i],5)){
437                         useInterface = true;
438                 }
439                 else
440                         printUsage(argv[0]);
441         }
442
443         if(!execIndex || (execIndex == argc))
444                  printUsage(argv[0]);
445
446         struct stat statBuffer;
447         if(stat(argv[execIndex],&statBuffer) < 0){
448                 cerr << "ERROR : Executable " << argv[execIndex] 
449                      << " does not exist" << endl;
450                 exit(-100);
451         }
452
453         CodeCoverage* codeCoverage = NULL;
454
455         /** create the corresponding code coverage object */
456         if(useOnDemand)
457                 codeCoverage = new CCOnDemandInstrument;
458         else
459                 codeCoverage = new CCPreInstrument;
460
461         errorCode = Error_OK;
462
463         /** initialize the necessary BPatch obejcts */
464         errorCode = codeCoverage->initialize(argv+execIndex,interval,
465                                              useDominator,suffix);
466         if(errorCode < Error_OK){
467                 codeCoverage->terminate();
468                 exit(errorCode);
469         }
470
471         if(useInterface){
472                 interp = initTclTk(codeCoverage,interval); 
473                 if(!interp){
474                         codeCoverage->terminate();
475                         cerr << "ERROR : The Tcl/Tk interpreter"
476                              << " can not be created..." << endl; 
477                         exit(-1);
478                 }
479         }
480
481         ARGS* passedArguments = new ARGS;
482         passedArguments->interp = interp;
483         passedArguments->codeCoverage = codeCoverage;
484
485         if(useInterface){
486                 Tcl_CreateExitHandler(exitCodeCoverage,
487                                      (ClientData)passedArguments);
488
489                 Tk_CreateEventHandler(startButton,ButtonReleaseMask,
490                                       startButtonHandler,
491                                       (ClientData)passedArguments);
492
493                 Tk_CreateEventHandler(refreshButton,ButtonPressMask,
494                                       refreshButtonHandler,
495                                       (ClientData)passedArguments);
496
497                 Tcl_CreateTimerHandler(1000,statusUpdateProcedure,
498                                        (ClientData)passedArguments);
499
500                 Tcl_CreateTimerHandler(5000,graphUpdateProcedure,
501                                        (ClientData)passedArguments);
502         }
503
504         /** instrument a breakpoint to the beginning of the exit handle
505           * to catch the termination of the mutatee 
506           */
507         errorCode = codeCoverage->instrumentExitHandle();
508         if(errorCode < Error_OK){
509                 codeCoverage->terminate();
510                 Tcl_DeleteInterp(interp);
511                 exit(errorCode);
512         }
513
514         /** select functions whose source code line information
515           * is available in the executable 
516           */
517         errorCode = codeCoverage->selectFunctions();
518         if(errorCode < Error_OK){
519                 codeCoverage->terminate();
520                 Tcl_DeleteInterp(interp);
521                 exit(errorCode);
522         }
523
524         if(useInterface) {
525                 prepareTclTkGlobalDataStucture(passedArguments);
526                 Tk_Main(argc,argv,Tcl_AppInit);
527                 Tcl_DeleteInterp(interp);
528         }
529         else 
530                 codeCoverageThread((void*)passedArguments);
531
532         delete passedArguments;
533
534         return Error_OK;
535 #else
536         cerr << endl
537              << "IMPORTANT Information : " << endl
538              << "\tCodeCoverage Tool is not implemented for" << endl
539              << "\tthis platform...." << endl << endl << endl;
540
541         printUsage(argv[0],true);
542
543         return 0;
544 #endif
545 }