remove compiler warning
[dyninst.git] / paradynd / src / main.C
1 /*
2  * Copyright (c) 1996-2000 Barton P. Miller
3  * 
4  * We provide the Paradyn Parallel Performance Tools (below
5  * described as Paradyn") on an AS IS basis, and do not warrant its
6  * validity or performance.  We reserve the right to update, modify,
7  * or discontinue this software at any time.  We shall have no
8  * obligation to supply such updates or modifications or any other
9  * form of support to you.
10  * 
11  * This license is for research uses.  For such uses, there is no
12  * charge. We define "research use" to mean you may freely use it
13  * inside your organization for whatever purposes you see fit. But you
14  * may not re-distribute Paradyn or parts of Paradyn, in any form
15  * source or binary (including derivatives), electronic or otherwise,
16  * to any other organization or entity without our permission.
17  * 
18  * (for other uses, please contact us at paradyn@cs.wisc.edu)
19  * 
20  * All warranties, including without limitation, any warranty of
21  * merchantability or fitness for a particular purpose, are hereby
22  * excluded.
23  * 
24  * By your use of Paradyn, you understand and agree that we (or any
25  * other person or entity with proprietary rights in Paradyn) are
26  * under no obligation to provide either maintenance services,
27  * update services, notices of latent defects, or correction of
28  * defects for Paradyn.
29  * 
30  * Even if advised of the possibility of such damages, under no
31  * circumstances shall we (or any other person or entity with
32  * proprietary rights in the software licensed hereunder) be liable
33  * to you or any third party for direct, indirect, or consequential
34  * damages of any character regardless of type of action, including,
35  * without limitation, loss of profits, loss of use, loss of good
36  * will, or computer failure or malfunction.  You agree to indemnify
37  * us (and any other person or entity with proprietary rights in the
38  * software licensed hereunder) for any and all liability it may
39  * incur to third parties resulting from your use of Paradyn.
40  */
41
42 // $Id: main.C,v 1.96 2001/08/23 14:44:14 schendel Exp $
43
44 #include "common/h/headers.h"
45 #include "pdutil/h/makenan.h"
46 #include "common/h/Ident.h"
47
48 #if defined(MT_THREAD)
49 extern "C" const char V_paradyndMT[];
50 #else
51 extern "C" const char V_paradynd[];
52 #endif //MT_THREAD
53 extern "C" const char V_libpdutil[];
54
55 #if defined(MT_THREAD)
56 Ident V_id(V_paradyndMT,"Paradyn");
57 #else
58 Ident V_id(V_paradynd,"Paradyn");
59 #endif
60 Ident V_Uid(V_libpdutil,"Paradyn");
61
62 #include "rtinst/h/rtinst.h"
63
64 #include "dyninstAPI/src/symtab.h"
65 #include "dyninstAPI/src/process.h"
66 #include "dyninstAPI/src/inst.h"
67 #include "dyninstAPI/src/instP.h"
68 #include "dyninstAPI/src/ast.h"
69 #include "dyninstAPI/src/util.h"
70 #include "dyninstAPI/src/dyninstP.h"
71 #include "paradynd/src/metric.h"
72 #include "paradynd/src/comm.h"
73 #include "paradynd/src/internalMetrics.h"
74 #include "common/h/machineType.h"
75 #include "paradynd/src/init.h"
76 #include "paradynd/src/perfStream.h"
77 #include "paradynd/src/mdld.h"
78
79 pdRPC *tp = NULL;
80
81 #ifdef PARADYND_PVM
82 #include "pvm_support.h"
83 extern "C" {
84 #include <pvm3.h>
85 }
86 #endif     
87
88
89 bool pvm_running = false;
90
91 static string machine_name;
92 string osName;
93 int pd_debug=0;
94
95 int ready;
96
97 #ifdef mips_sgi_irix6_4
98 extern bool execIrixMPIProcess(vector<string> &argv);
99 #endif
100
101 #ifdef DETACH_ON_THE_FLY
102 extern void initDetachOnTheFly();
103 #endif
104
105 /*
106  * These variables are global so that we can easily find out what
107  * machine/socket/etc we're connected to paradyn on; we may need to
108  * start up other paradynds (such as on the CM5), and need this later.
109  */
110 string pd_machine;
111 static int pd_known_socket_portnum=0;
112 static int pd_flag=0;
113 static string pd_flavor;
114 #if !defined(i386_unknown_nt4_0)
115 int termWin_port = -1;
116 #endif
117
118 void configStdIO(bool closeStdIn)
119 {
120     int nullfd;
121
122     /* now make stdin, out and error things that we can't hurt us */
123     if ((nullfd = open("/dev/null", O_RDWR, 0)) < 0) {
124         abort();
125     }
126
127     if (closeStdIn) (void) dup2(nullfd, 0);
128     (void) dup2(nullfd, 1);
129     (void) dup2(nullfd, 2);
130
131     if (nullfd > 2) close(nullfd);
132 }
133
134 void sigtermHandler()
135 {
136   showErrorCallback(98,"paradynd has been terminated");
137 }
138
139 // Cleanup for pvm and exit.
140 // This function must be called when we exit, to clean up and exit from pvm.
141 // Now also cleans up shm segs by deleting all processes  -ari
142 void cleanUpAndExit(int status) {
143 #ifdef PARADYND_PVM
144   if (pvm_running)
145     PDYN_exit_pvm();
146 #endif
147
148 #if !defined(i386_unknown_nt4_0)
149   // delete the trace socket file
150   unlink(traceSocketPath.string_of());
151 #endif
152
153 #ifdef SHM_SAMPLING_DEBUG
154    cerr << "paradynd cleanUpAndExit: deleting all process structures now" << endl;
155 #endif
156
157   // Fry all processes
158   extern vector<process*> processVec;
159
160   for (unsigned lcv=0; lcv < processVec.size(); lcv++) {
161      process *theProc = processVec[lcv];
162      if (theProc == NULL)
163         continue; // process has already been cleaned up
164
165 #if defined(i386_unknown_linux2_0) || defined(alpha_dec_osf4_0)
166      // Try to be a bit smarter when we clean up the processes - kill
167      // all processes that were created, leave all processes that were
168      // attached to running.  This should really go into the process class,
169      // but I hesitate to do that this close to the release (3.0)
170      // -nick (24-Mar-2000)
171      int pid = theProc->getPid();
172      bool wasAttachedTo = theProc->wasCreatedViaAttach();
173 #endif
174      delete theProc; // calls process::~process, which fries the shm seg
175 #if defined(i386_unknown_linux2_0) || defined(alpha_dec_osf4_0)
176      if (!wasAttachedTo) OS::osKill(pid);
177 #endif
178
179      processVec[lcv] = NULL; // probably not needed here.
180   }
181
182   P_exit(status);
183 }
184
185 // TODO
186 // mdc - I need to clean this up
187 #if !defined(i386_unknown_nt4_0)
188 bool
189 RPC_undo_arg_list (string &flavor, unsigned argc, char **arg_list, 
190                    string &machine, int &well_known_socket,int &termWin_port, int &flag)
191 #else
192 RPC_undo_arg_list (string &flavor, unsigned argc, char **arg_list, 
193                    string &machine, int &well_known_socket, int &flag)
194 #endif
195 {
196   char *ptr;
197   bool b_well_known=false; // found well-known socket port num
198 #if !defined(i386_unknown_nt4_0)
199   bool b_termWin=false; // found termWin socket port num
200 #endif
201   bool b_machine = false, b_flag = false, b_flavor=false;
202
203   for (unsigned loop=0; loop < argc; ++loop) {
204       // stop at the -runme argument since the rest are for the application
205       //   process we are about to spawn
206       if (!P_strcmp(arg_list[loop], "-runme")) break;
207       if (!P_strncmp(arg_list[loop], "-p", 2)) {
208           well_known_socket = P_strtol (arg_list[loop] + 2, &ptr, 10);
209           if (ptr == (arg_list[loop] + 2))
210             return(false);
211           b_well_known = true;
212 #if !defined(i386_unknown_nt4_0)
213       }else if (!P_strncmp(arg_list[loop], "-P", 2)) {
214           termWin_port = P_strtol (arg_list[loop] + 2, &ptr, 10);
215           if (ptr == (arg_list[loop] + 2))
216             return(false);
217           b_termWin= true;
218 #endif
219       }else if (!P_strncmp(arg_list[loop], "-V", 2)) { // optional
220           cout << V_id << endl;
221       }
222       else if (!P_strncmp(arg_list[loop], "-v", 2)) {
223           pd_debug++;
224           //cerr << "paradynd: -v flag is obsolete (and ignored)" << endl;
225       }
226       else if (!P_strncmp(arg_list[loop], "-L", 2)) {
227           // this is an optional specification of the runtime library,
228           // overriding PARADYN_LIB (primarily for debugging/testing purposes)
229           process::dyninstName = (arg_list[loop] + 2);
230           if (!process::dyninstName.length()) return false;
231       }
232       else if (!P_strncmp(arg_list[loop], "-m", 2)) {
233           machine = (arg_list[loop] + 2);
234           if (!machine.length()) return false;
235           b_machine = true;
236       }
237       else if (!P_strncmp(arg_list[loop], "-l", 2)) {
238           flag = P_strtol (arg_list[loop] + 2, &ptr, 10);
239           if (ptr == (arg_list[loop] + 2))
240             return(false);
241           b_flag = true;
242       }
243       else if (!P_strncmp(arg_list[loop], "-z", 2)) {
244           flavor = (arg_list[loop]+2);
245           if (!flavor.length()) return false;
246           b_flavor = true;
247       }
248   }
249
250   // verify required parameters
251 #if !defined(i386_unknown_nt4_0)
252   return (b_flag && b_machine && b_well_known && b_termWin && b_flavor);
253 #else
254   return (b_flag && b_machine && b_well_known && b_flavor);
255 #endif
256 }
257
258 // PARADYND_DEBUG_XXX
259 static void initialize_debug_flag(void) {
260   char *p;
261
262   if ( (p=getenv("PARADYND_DEBUG_INFRPC")) ) {
263     pd_debug_infrpc = 1;
264   }
265
266   if ( (p=getenv("PARADYND_DEBUG_CATCHUP")) ) {
267     pd_debug_catchup = 1;
268   }
269 }
270
271
272 static
273 void
274 PauseIfDesired( void )
275 {
276         char *pdkill = getenv( "PARADYND_DEBUG" );
277         if( pdkill && ( *pdkill == 'y' || *pdkill == 'Y' ) )
278         {
279                 int pid = getpid();
280                 cerr << "breaking for debug in controllerMainLoop...pid=" << pid << endl;
281 #if defined(i386_unknown_nt4_0)
282                 DebugBreak();
283 #elif defined(sparc_sun_solaris2_4) || defined(i386_unknown_solaris2_5)
284                 bool bCont = false;
285                 while( !bCont )
286                 {
287                         sleep( 1 );
288                 }
289 #else
290                 kill(pid, SIGSTOP);
291 #endif
292         }
293 }
294
295
296 #if !defined(i386_unknown_nt4_0)
297 static
298 void
299 InitSigTermHandler( void )
300 {
301     struct sigaction act;
302
303     // int i = 1;
304     // while (i) {};
305
306
307 #if defined(sparc_sun_sunos4_1_3) || defined(sparc_sun_solaris2_4)
308     act.sa_handler = (void (*)(...)) sigtermHandler;
309 #else
310     act.sa_handler = (void (*)(int)) sigtermHandler;
311 #endif
312     act.sa_flags   = 0;
313
314     /* for AIX - default (non BSD) library does not restart - jkh 7/26/95 */
315 #if defined(SA_RESTART)
316     act.sa_flags  |= SA_RESTART;
317 #endif
318
319     sigfillset(&act.sa_mask);
320
321     if (sigaction(SIGTERM, &act, 0) == -1) {
322         perror("sigaction(SIGTERM)");
323         abort();
324     }
325 }
326 #endif // !defined(i386_unknown_nt4_0)
327
328 #if defined(i386_unknown_nt4_0)
329 static
330 void
331 InitWinsock( void )
332 {
333     // Windows NT needs to initialize winsock library
334     WORD wsversion = MAKEWORD(2,0);
335     WSADATA wsadata;
336     WSAStartup(wsversion, &wsadata);
337 }
338 #endif // defined(i386_unknown_nt4_0)
339
340
341
342
343 static
344 void
345 InitForMPI( char* argv[], const string& pd_machine )
346 {
347         // Both IRIX and AIX MPI job-launchers will start paradynd,
348         // which must report to paradyn
349         // the pdRPC is allocated and reportSelf is called
350         tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, 
351              pd_machine, NULL, NULL, 2);
352         assert(tp != NULL);
353
354         tp->reportSelf(machine_name, argv[0], getpid(), "mpi");
355 }
356
357
358
359
360 static
361 void
362 InitManuallyStarted( char* argv[],  const string& pd_machine )
363 {
364         bool reported = false;
365
366         // report back to our front end
367         tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, 
368                                         pd_machine, NULL, NULL, 2);
369         assert(tp);
370         if (!tp->net_obj())
371         {
372                 cerr << "Failed to establish connection to Paradyn on "
373                          << pd_machine << " port " << pd_known_socket_portnum << endl;
374                 cleanUpAndExit(-1);
375         }
376         tp->reportSelf(machine_name, argv[0], getpid(), pd_flavor);
377         reported = true;
378
379 #if defined(PARADYND_PVM)
380         // are we designated for monitoring a PVM application?
381         if (pvm_running
382                 && !PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 1))
383         {
384                 cleanUpAndExit(-1);
385         }
386 #endif // PARADYND_PVM
387 }
388
389
390
391 static
392 void
393 InitRemotelyStarted( char* argv[], const string& pd_machine, bool report )
394
395   // we are a remote daemon started by rsh/rexec or some other
396   // use socket to connect back to our front end
397     
398   // setup socket
399   
400   // We must get a connection with paradyn before starting any 
401   // other daemons, or else one of the daemons we start 
402   // (in PDYN_initForPVM), may get our connection.
403   tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, 
404                  pd_machine, NULL, NULL, 2);
405   assert( tp != NULL );
406   
407   // ??? is this the right thing to do? it was in the
408   // non-PVM version of the code, but not in the PVM version
409   // we decide whether to report if the cmdLine was empty or not
410   if( report )
411     {
412       tp->reportSelf( machine_name, argv[0], getpid(), pd_flavor );
413     }
414 }
415
416
417
418 static
419 void
420 InitLocallyStarted( char* []
421 #if PARADYND_PVM
422                     argv
423 #endif
424                     , const string& 
425 #ifdef PARADYND_PVM
426                     pd_machine 
427 #endif
428 )
429 {
430 #ifdef PARADYND_PVM
431         // check if we are designated to monitor a PVM application
432         if( pvm_running && 
433                 !PDYN_initForPVM( argv, pd_machine, pd_known_socket_portnum, 1 ))
434         {
435                 // TODO -- report error here
436                 cleanUpAndExit( -1 );
437         }
438 #endif // PARADYND_PVM
439
440         // connect to our front end using our stdout
441         OS::osDisconnect();
442
443 #if !defined(i386_unknown_nt4_0)
444         PDSOCKET sock = 0;
445 #else
446         PDSOCKET sock = _get_osfhandle(0);
447 #endif // defined(i386_unknown_nt4_0)
448         tp = new pdRPC(sock, NULL, NULL, 2);
449
450         // note that we do not need to report to our front end in this case
451 }
452
453
454 #if defined(PARADYND_PVM)
455 static
456 void
457 InitForPVM( char* argv[], const string& pd_machine )
458 {
459         // Check whether we are we are being created for a PVM application.
460         // Also, check whether we are the first PVM paradynd or if were started 
461         // by the first paradynd using pvm_spawn()
462     int pvmParent = pvm_parent();
463
464         if (pvmParent == PvmSysErr)
465         {
466                 cout << "Unable to connect to PVM daemon; is PVM running?\n" << endl;
467                 cleanUpAndExit(-1);
468         }
469         else
470         {
471                 pvm_running = true;
472         }
473
474     if (pvm_running && (pvmParent != PvmNoParent))
475         {
476                 // we were started using pvm_spawn() by another paradynd
477                 if (!PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 0))
478                 {
479                         // TODO -- report error here
480                         cleanUpAndExit(-1);
481                 }
482
483                 // report to our front end
484                 tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, 
485                                                 pd_machine, NULL, NULL, 2);
486                 assert(tp);
487
488                 tp->reportSelf (machine_name, argv[0], getpid(), "pvm");
489     }
490 }
491 #endif // PARADYND_PVM
492
493
494
495 //
496 // Note: the pd_flag variable is set from the argument to the -l command
497 // line switch.  It has the following meaning:
498 //
499 // pd_flag == 0 => remote daemon started by Paradyn using rsh/rexec
500 // pd_flag == 1 => local daemon started by Paradyn using fork/exec
501 // pd_flag == 2 => daemon started manually
502 //
503
504
505 int
506 main( int argc, char* argv[] )
507 {
508         PauseIfDesired();
509         initialize_debug_flag();
510
511 #if !defined(i386_unknown_nt4_0)
512         InitSigTermHandler();
513 #endif // defined(i386_unknown_nt4_0)
514
515
516 #ifdef DETACH_ON_THE_FLY
517     initDetachOnTheFly();
518 #endif
519
520 #if defined(i386_unknown_nt4_0)
521         InitWinsock();
522 #endif // defined(i386_unknown_nt4_0)
523
524
525         //
526     // process command line args passed in
527         //
528     process::programName = argv[0];
529     bool aflag;
530 #if !defined(i386_unknown_nt4_0)
531     aflag = RPC_undo_arg_list (pd_flavor, argc, argv, pd_machine,
532                                pd_known_socket_portnum,termWin_port, pd_flag);
533 #else 
534     aflag = RPC_undo_arg_list (pd_flavor, argc, argv, pd_machine,
535                                pd_known_socket_portnum,pd_flag);
536 #endif
537     if (!aflag || pd_debug)
538         {
539                 if (!aflag)
540                 {
541                         cerr << "Invalid/incomplete command-line args:" << endl;
542                 }
543                 cerr << "   -z<flavor";
544                 if (pd_flavor.length())
545                 {
546                         cerr << "=" << pd_flavor;
547                 }
548                 cerr << "> -l<flag";
549                 if (pd_flag)
550                 {
551                         cerr << "=" << pd_flag;
552                 }
553                 cerr << "> -m<hostmachine";
554                 if (pd_machine.length()) 
555                 {
556                         cerr << "=" << pd_machine;
557                 }
558                 cerr << "> -p<hostport";
559                 if (pd_known_socket_portnum)
560                 {
561                         cerr << "=" << pd_known_socket_portnum;
562                 }
563                 cerr << ">" << endl;
564                 if (process::dyninstName.length())
565                 {
566                         cerr << "   -L<library=" << process::dyninstName << ">" << endl;
567                 }
568                 if (!aflag)
569                 {
570                         cleanUpAndExit(-1);
571                 }
572     }
573
574 #if !defined(i386_unknown_nt4_0)
575 extern PDSOCKET connect_Svr(string machine,int port);
576     PDSOCKET stdout_fd=INVALID_PDSOCKET;
577     if ((stdout_fd = connect_Svr(pd_machine,termWin_port)) == INVALID_PDSOCKET)
578         cleanUpAndExit(-1);
579     if (write(stdout_fd,"from_paradynd\n",strlen("from_paradynd\n")) <= 0)
580         cleanUpAndExit(-1);
581
582     dup2(stdout_fd,1);
583     dup2(stdout_fd,2);
584 #endif
585
586 #if !defined(i386_unknown_nt4_0)
587     aflag = RPC_make_arg_list(process::arg_list,
588                               pd_known_socket_portnum, termWin_port,pd_flag, 0,
589                               pd_machine, true);
590 #else
591     aflag = RPC_make_arg_list(process::arg_list,
592                               pd_known_socket_portnum, pd_flag, 0,
593                               pd_machine, true);
594 #endif 
595     assert(aflag);
596     string flav_arg(string("-z")+ pd_flavor);
597     process::arg_list += flav_arg;
598     machine_name = getNetworkName();
599
600     //
601     // See if we should fork an app process now.
602     //
603
604     // We want to find two things
605     // First, get the current working dir (PWD)
606     string* dir = new string(getenv("PWD"));
607
608     // Second, put the inferior application and its command line
609     // arguments into cmdLine. Basically, loop through argv until
610     // we find -runme, and put everything after it into cmdLine.
611     vector<string> cmdLine;
612     unsigned int argNum = 0;
613     while ((argNum < (unsigned int)argc) && (strcmp(argv[argNum], "-runme")))
614         {
615                 argNum++;
616         }
617     // Okay, argNum is the command line argument which is "-runme" - skip it
618     argNum++;
619     // Copy everything from argNum to < argc
620         // this is the command that is to be issued
621     for (unsigned int i = argNum; i < (unsigned int)argc; i++)
622         {
623                 cmdLine += argv[i];
624         }
625         // note - cmdLine could be empty here, if the -runme flag were not given
626
627
628     // There are several ways that we might have been started.
629         // We need to connect to Paradyn differently depending on which 
630         // method was used.
631         //
632         // Use our own stdin if:
633         //   started as local daemon by Paradyn using fork+exec
634         //
635         // Use a socket described in our command-line args if:
636         //   started as remote daemon by rsh/rexec
637         //   started manually on command line
638         //   started by MPI
639         //   started as PVM daemon by another Paradyn daemon using pvm_spawn() 
640         //
641
642
643     process::pdFlavor = pd_flavor;
644 #ifdef PDYN_DEBUG
645     cerr << "pd_flavor: " << pd_flavor.string_of() << endl;
646 #endif
647
648
649 #ifdef PARADYND_PVM
650     if (pd_flavor == string("pvm"))
651         {
652                 InitForPVM( argv, pd_machine );
653         }
654 #endif // PARADYND_PVM
655
656         if( tp == NULL )
657         {
658                 // we haven't yet reported to our front end
659
660                 if( pd_flavor == "mpi" )
661                 {
662                         InitForMPI( argv, pd_machine );
663                 }
664                 else if( pd_flag == 0 )
665                 {
666                         // we are a remote daemon started by rsh/rexec or some other
667                         InitRemotelyStarted( argv, pd_machine, (cmdLine.size() > 0) );
668                 }
669                 else if( pd_flag == 1 )
670                 {
671                         // we were started by a local front end using fork+exec
672                         InitLocallyStarted( argv, pd_machine );
673                 }
674                 else if( pd_flag == 2 )
675                 {
676                         // we were started manually (i.e., from the command line)
677                         InitManuallyStarted( argv, pd_machine );
678                 }
679         }
680
681         // by now, we should have a connection to our front end
682         if( tp == NULL )
683         {
684                 if( (pd_flag < 0) || (pd_flag > 2) )
685                 {
686                         cerr << "Paradyn daemon: invalid -l value " << pd_flag << " given." << endl;
687                 }
688                 else
689                 {
690                         cerr << "Paradyn daemon: invalid command-line options seen" << endl;
691                 }
692                 cleanUpAndExit(-1);
693         }
694     assert( tp != NULL );
695
696 #if defined(MT_THREAD)
697     statusLine(V_paradyndMT);
698 #else
699     statusLine(V_paradynd);
700 #endif
701
702     // Note -- it is important that this daemon receives all mdl info
703     // before starting a process
704     aflag = mdl_get_initial(pd_flavor, tp);
705     assert(aflag);
706
707     initLibraryFunctions();
708     if (!init()) 
709         {
710                 abort();
711         }
712
713 #ifdef mips_sgi_irix6_4
714     struct utsname unameInfo;
715     if ( P_uname(&unameInfo) == -1 )
716     {
717         perror("uname");
718         return false;
719     }
720
721     // osName is used in irix.C and process.C
722     osName = unameInfo.sysname;
723
724     if ( pd_flavor == "mpi" && osName.prefixed_by("IRIX") )
725     {
726       if ( !execIrixMPIProcess(cmdLine) )
727         return(0);
728     }
729     else
730 #endif
731
732         // spawn the given process, if necessary
733         if (cmdLine.size())
734         {
735                 vector<string> envp;
736                 addProcess(cmdLine, envp, *dir); // ignore return val (is this right?)
737         }
738
739
740         // start handling events
741     controllerMainLoop(true);
742     return(0);
743 }
744