Updated processing of pd_flag variable so that startup logic is shared
[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.92 2000/12/07 20:15:37 pcroth 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 static string pd_machine;
111 static int pd_known_socket_portnum=0;
112 static int pd_flag=0;
113 static string pd_flavor;
114
115 void configStdIO(bool closeStdIn)
116 {
117     int nullfd;
118
119     /* now make stdin, out and error things that we can't hurt us */
120     if ((nullfd = open("/dev/null", O_RDWR, 0)) < 0) {
121         abort();
122     }
123
124     if (closeStdIn) (void) dup2(nullfd, 0);
125     (void) dup2(nullfd, 1);
126     (void) dup2(nullfd, 2);
127
128     if (nullfd > 2) close(nullfd);
129 }
130
131 void sigtermHandler()
132 {
133   showErrorCallback(98,"paradynd has been terminated");
134 }
135
136 // Cleanup for pvm and exit.
137 // This function must be called when we exit, to clean up and exit from pvm.
138 // Now also cleans up shm segs by deleting all processes  -ari
139 void cleanUpAndExit(int status) {
140 #ifdef PARADYND_PVM
141   if (pvm_running)
142     PDYN_exit_pvm();
143 #endif
144
145 #if !defined(i386_unknown_nt4_0)
146   // delete the trace socket file
147   unlink(traceSocketPath.string_of());
148 #endif
149
150 #ifdef SHM_SAMPLING_DEBUG
151    cerr << "paradynd cleanUpAndExit: deleting all process structures now" << endl;
152 #endif
153
154   // Fry all processes
155   extern vector<process*> processVec;
156
157   for (unsigned lcv=0; lcv < processVec.size(); lcv++) {
158      process *theProc = processVec[lcv];
159      if (theProc == NULL)
160         continue; // process has already been cleaned up
161
162 #if defined(i386_unknown_linux2_0) || defined(alpha_dec_osf4_0)
163      // Try to be a bit smarter when we clean up the processes - kill
164      // all processes that were created, leave all processes that were
165      // attached to running.  This should really go into the process class,
166      // but I hesitate to do that this close to the release (3.0)
167      // -nick (24-Mar-2000)
168      int pid = theProc->getPid();
169      bool wasAttachedTo = theProc->wasCreatedViaAttach();
170 #endif
171      delete theProc; // calls process::~process, which fries the shm seg
172 #if defined(i386_unknown_linux2_0) || defined(alpha_dec_osf4_0)
173      if (!wasAttachedTo) OS::osKill(pid);
174 #endif
175
176      processVec[lcv] = NULL; // probably not needed here.
177   }
178
179   P_exit(status);
180 }
181
182 // TODO
183 // mdc - I need to clean this up
184 bool
185 RPC_undo_arg_list (string &flavor, unsigned argc, char **arg_list, 
186                    string &machine, int &well_known_socket, int &flag)
187 {
188   char *ptr;
189   bool b_well_known=false; // found well-known socket port num
190   bool b_machine = false, b_flag = false, b_flavor=false;
191
192   for (unsigned loop=0; loop < argc; ++loop) {
193       // stop at the -runme argument since the rest are for the application
194       //   process we are about to spawn
195       if (!P_strcmp(arg_list[loop], "-runme")) break;
196       if (!P_strncmp(arg_list[loop], "-p", 2)) {
197           well_known_socket = P_strtol (arg_list[loop] + 2, &ptr, 10);
198           if (ptr == (arg_list[loop] + 2))
199             return(false);
200           b_well_known = true;
201       }
202       else if (!P_strncmp(arg_list[loop], "-V", 2)) { // optional
203           cout << V_id << endl;
204       }
205       else if (!P_strncmp(arg_list[loop], "-v", 2)) {
206           pd_debug++;
207           //cerr << "paradynd: -v flag is obsolete (and ignored)" << endl;
208       }
209       else if (!P_strncmp(arg_list[loop], "-L", 2)) {
210           // this is an optional specification of the runtime library,
211           // overriding PARADYN_LIB (primarily for debugging/testing purposes)
212           process::dyninstName = (arg_list[loop] + 2);
213           if (!process::dyninstName.length()) return false;
214       }
215       else if (!P_strncmp(arg_list[loop], "-m", 2)) {
216           machine = (arg_list[loop] + 2);
217           if (!machine.length()) return false;
218           b_machine = true;
219       }
220       else if (!P_strncmp(arg_list[loop], "-l", 2)) {
221           flag = P_strtol (arg_list[loop] + 2, &ptr, 10);
222           if (ptr == (arg_list[loop] + 2))
223             return(false);
224           b_flag = true;
225       }
226       else if (!P_strncmp(arg_list[loop], "-z", 2)) {
227           flavor = (arg_list[loop]+2);
228           if (!flavor.length()) return false;
229           b_flavor = true;
230       }
231   }
232
233   // verify required parameters
234   return (b_flag && b_machine && b_well_known && b_flavor);
235 }
236
237 // PARADYND_DEBUG_XXX
238 static void initialize_debug_flag(void) {
239   char *p;
240
241   if ( (p=getenv("PARADYND_DEBUG_INFRPC")) ) {
242     pd_debug_infrpc = 1;
243   }
244
245   if ( (p=getenv("PARADYND_DEBUG_CATCHUP")) ) {
246     pd_debug_catchup = 1;
247   }
248 }
249
250
251 static
252 void
253 PauseIfDesired( void )
254 {
255         char *pdkill = getenv( "PARADYND_DEBUG" );
256         if( pdkill && ( *pdkill == 'y' || *pdkill == 'Y' ) )
257         {
258                 int pid = getpid();
259                 cerr << "breaking for debug in controllerMainLoop...pid=" << pid << endl;
260 #if defined(i386_unknown_nt4_0)
261                 DebugBreak();
262 #elif defined(sparc_sun_solaris2_4) || defined(i386_unknown_solaris2_5)
263                 bool bCont = false;
264                 while( !bCont )
265                 {
266                         sleep( 1 );
267                 }
268 #else
269                 kill(pid, SIGSTOP);
270 #endif
271         }
272 }
273
274
275 #if !defined(i386_unknown_nt4_0)
276 static
277 void
278 InitSigTermHandler( void )
279 {
280     struct sigaction act;
281
282     // int i = 1;
283     // while (i) {};
284
285
286 #if defined(sparc_sun_sunos4_1_3) || defined(sparc_sun_solaris2_4)
287     act.sa_handler = (void (*)(...)) sigtermHandler;
288 #else
289     act.sa_handler = (void (*)(int)) sigtermHandler;
290 #endif
291     act.sa_flags   = 0;
292
293     /* for AIX - default (non BSD) library does not restart - jkh 7/26/95 */
294 #if defined(SA_RESTART)
295     act.sa_flags  |= SA_RESTART;
296 #endif
297
298     sigfillset(&act.sa_mask);
299
300     if (sigaction(SIGTERM, &act, 0) == -1) {
301         perror("sigaction(SIGTERM)");
302         abort();
303     }
304 }
305 #endif // !defined(i386_unknown_nt4_0)
306
307 #if defined(i386_unknown_nt4_0)
308 static
309 void
310 InitWinsock( void )
311 {
312     // Windows NT needs to initialize winsock library
313     WORD wsversion = MAKEWORD(2,0);
314     WSADATA wsadata;
315     WSAStartup(wsversion, &wsadata);
316 }
317 #endif // defined(i386_unknown_nt4_0)
318
319
320
321
322 static
323 void
324 InitForMPI( char* argv[], const string& pd_machine )
325 {
326         // Both IRIX and AIX MPI job-launchers will start paradynd,
327         // which must report to paradyn
328         // the pdRPC is allocated and reportSelf is called
329         tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, 
330              pd_machine, NULL, NULL, 2);
331         assert(tp != NULL);
332
333         tp->reportSelf(machine_name, argv[0], getpid(), "mpi");
334 }
335
336
337
338
339 static
340 void
341 InitManuallyStarted( char* argv[],  const string& pd_machine )
342 {
343         bool reported = false;
344
345         // report back to our front end
346         tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, 
347                                         pd_machine, NULL, NULL, 2);
348         assert(tp);
349         if (!tp->net_obj())
350         {
351                 cerr << "Failed to establish connection to Paradyn on "
352                          << pd_machine << " port " << pd_known_socket_portnum << endl;
353                 cleanUpAndExit(-1);
354         }
355         tp->reportSelf(machine_name, argv[0], getpid(), pd_flavor);
356         reported = true;
357
358 #if defined(PARADYND_PVM)
359         // are we designated for monitoring a PVM application?
360         if (pvm_running
361                 && !PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 1))
362         {
363                 cleanUpAndExit(-1);
364         }
365 #endif // PARADYND_PVM
366 }
367
368
369
370 static
371 void
372 InitRemotelyStarted( char* argv[], const string& pd_machine, bool report )
373
374         // we are a remote daemon started by rsh/rexec or some other
375         // use socket to connect back to our front end
376
377         // we fork ourselves and the child process becomes the "real" daemon
378 #if !defined(i386_unknown_nt4_0)
379         int pid = fork();
380 #else // !defined(i386_unknown_nt4_0)
381         int pid = 0;
382 #endif // !defined(i386_unknown_nt4_0)
383         if (pid == 0)
384         {
385                 // we are the child process - we are the "true" daemon
386
387                 // setup socket
388
389                 // We must get a connection with paradyn before starting any 
390                 // other daemons, or else one of the daemons we start 
391                 // (in PDYN_initForPVM), may get our connection.
392                 tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, 
393                                                 pd_machine, NULL, NULL, 2);
394                 assert( tp != NULL );
395
396                 // ??? is this the right thing to do? it was in the
397                 // non-PVM version of the code, but not in the PVM version
398                 // we decide whether to report if the cmdLine was empty or not
399                 if( report )
400                 {
401                         tp->reportSelf( machine_name, argv[0], getpid(), pd_flavor );
402                 }
403
404 #if defined(PARADYND_PVM)
405                 if (pvm_running && 
406                         !PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 1))
407                 {
408                         // TODO -- report error here
409                         cleanUpAndExit(-1);
410                 }
411 #endif // defined(PARADYND_PVM)
412         }
413         else
414         {
415                 // we are the parent process - if the fork succeeded, exit
416                 if (pid > 0)
417                 {
418                         P__exit(0);
419                 }
420                 else
421                 {
422                         cerr << "Paradyn daemon: Fatal error: fork failed." << endl;
423                         cleanUpAndExit(-1);
424                 }
425         }
426 }
427
428
429
430 static
431 void
432 InitLocallyStarted( char* argv[], const string& pd_machine )
433 {
434 #if PARADYND_PVM
435         // check if we are designated to monitor a PVM application
436         if( pvm_running && 
437                 !PDYN_initForPVM( argv, pd_machine, pd_known_socket_portnum, 1 ))
438         {
439                 // TODO -- report error here
440                 cleanUpAndExit( -1 );
441         }
442 #endif // PARADYND_PVM
443
444         // connect to our front end using our stdout
445         OS::osDisconnect();
446
447 #if !defined(i386_unknown_nt4_0)
448         PDSOCKET sock = 0;
449 #else
450         PDSOCKET sock = _get_osfhandle(0);
451 #endif // defined(i386_unknown_nt4_0)
452         tp = new pdRPC(sock, NULL, NULL, 2);
453
454         // note that we do not need to report to our front end in this case
455 }
456
457
458 #if defined(PARADYND_PVM)
459 static
460 void
461 InitForPVM( char* argv[], const string& pd_machine )
462 {
463         // Check whether we are we are being created for a PVM application.
464         // Also, check whether we are the first PVM paradynd or if were started 
465         // by the first paradynd using pvm_spawn()
466     int pvmParent = pvm_parent();
467
468         if (pvmParent == PvmSysErr)
469         {
470                 cout << "Unable to connect to PVM daemon; is PVM running?\n" << endl;
471                 cleanUpAndExit(-1);
472         }
473         else
474         {
475                 pvm_running = true;
476         }
477
478     if (pvm_running && (pvmParent != PvmNoParent))
479         {
480                 // we were started using pvm_spawn() by another paradynd
481                 if (!PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 0))
482                 {
483                         // TODO -- report error here
484                         cleanUpAndExit(-1);
485                 }
486
487                 // report to our front end
488                 tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, 
489                                                 pd_machine, NULL, NULL, 2);
490                 assert(tp);
491
492                 tp->reportSelf (machine_name, argv[0], getpid(), "pvm");
493     }
494 }
495 #endif // PARADYND_PVM
496
497
498
499 //
500 // Note: the pd_flag variable is set from the argument to the -l command
501 // line switch.  It has the following meaning:
502 //
503 // pd_flag == 0 => remote daemon started by Paradyn using rsh/rexec
504 // pd_flag == 1 => local daemon started by Paradyn using fork/exec
505 // pd_flag == 2 => daemon started manually
506 //
507
508
509 int
510 main( int argc, char* argv[] )
511 {
512         PauseIfDesired();
513         initialize_debug_flag();
514
515 #if !defined(i386_unknown_nt4_0)
516         InitSigTermHandler();
517 #endif // defined(i386_unknown_nt4_0)
518
519
520 #ifdef DETACH_ON_THE_FLY
521     initDetachOnTheFly();
522 #endif
523
524 #if defined(i386_unknown_nt4_0)
525         InitWinsock();
526 #endif // defined(i386_unknown_nt4_0)
527
528
529         //
530     // process command line args passed in
531         //
532     process::programName = argv[0];
533     bool aflag;
534     aflag = RPC_undo_arg_list (pd_flavor, argc, argv, pd_machine,
535                                pd_known_socket_portnum, pd_flag);
536     if (!aflag || pd_debug)
537         {
538                 if (!aflag)
539                 {
540                         cerr << "Invalid/incomplete command-line args:" << endl;
541                 }
542                 cerr << "   -z<flavor";
543                 if (pd_flavor.length())
544                 {
545                         cerr << "=" << pd_flavor;
546                 }
547                 cerr << "> -l<flag";
548                 if (pd_flag)
549                 {
550                         cerr << "=" << pd_flag;
551                 }
552                 cerr << "> -m<hostmachine";
553                 if (pd_machine.length()) 
554                 {
555                         cerr << "=" << pd_machine;
556                 }
557                 cerr << "> -p<hostport";
558                 if (pd_known_socket_portnum)
559                 {
560                         cerr << "=" << pd_known_socket_portnum;
561                 }
562                 cerr << ">" << endl;
563                 if (process::dyninstName.length())
564                 {
565                         cerr << "   -L<library=" << process::dyninstName << ">" << endl;
566                 }
567                 if (!aflag)
568                 {
569                         cleanUpAndExit(-1);
570                 }
571     }
572
573     aflag = RPC_make_arg_list(process::arg_list,
574                               pd_known_socket_portnum, pd_flag, 0,
575                               pd_machine, true);
576     assert(aflag);
577     string flav_arg(string("-z")+ pd_flavor);
578     process::arg_list += flav_arg;
579     machine_name = getNetworkName();
580
581     //
582     // See if we should fork an app process now.
583     //
584
585     // We want to find two things
586     // First, get the current working dir (PWD)
587     string* dir = new string(getenv("PWD"));
588
589     // Second, put the inferior application and its command line
590     // arguments into cmdLine. Basically, loop through argv until
591     // we find -runme, and put everything after it into cmdLine.
592     vector<string> cmdLine;
593     unsigned int argNum = 0;
594     while ((argNum < (unsigned int)argc) && (strcmp(argv[argNum], "-runme")))
595         {
596                 argNum++;
597         }
598     // Okay, argNum is the command line argument which is "-runme" - skip it
599     argNum++;
600     // Copy everything from argNum to < argc
601         // this is the command that is to be issued
602     for (unsigned int i = argNum; i < (unsigned int)argc; i++)
603         {
604                 cmdLine += argv[i];
605         }
606         // note - cmdLine could be empty here, if the -runme flag were not given
607
608
609     // There are several ways that we might have been started.
610         // We need to connect to Paradyn differently depending on which 
611         // method was used.
612         //
613         // Use our own stdin if:
614         //   started as local daemon by Paradyn using fork+exec
615         //
616         // Use a socket described in our command-line args if:
617         //   started as remote daemon by rsh/rexec
618         //   started manually on command line
619         //   started by MPI
620         //   started as PVM daemon by another Paradyn daemon using pvm_spawn() 
621         //
622
623     process::pdFlavor = pd_flavor;
624 #ifdef PDYN_DEBUG
625     cerr << "pd_flavor: " << pd_flavor.string_of() << endl;
626 #endif
627
628
629 #ifdef PARADYND_PVM
630     if (pd_flavor == string("pvm"))
631         {
632                 InitForPVM( argv, pd_machine );
633         }
634 #endif // PARADYND_PVM
635
636         if( tp == NULL )
637         {
638                 // we haven't yet reported to our front end
639
640                 if( pd_flavor == "mpi" )
641                 {
642                         InitForMPI( argv, pd_machine );
643                 }
644                 else if( pd_flag == 0 )
645                 {
646                         // we are a remote daemon started by rsh/rexec or some other
647                         InitRemotelyStarted( argv, pd_machine, (cmdLine.size() > 0) );
648                 }
649                 else if( pd_flag == 1 )
650                 {
651                         // we were started by a local front end using fork+exec
652                         InitLocallyStarted( argv, pd_machine );
653                 }
654                 else if( pd_flag == 2 )
655                 {
656                         // we were started manually (i.e., from the command line)
657                         InitManuallyStarted( argv, pd_machine );
658                 }
659         }
660
661         // by now, we should have a connection to our front end
662         if( tp == NULL )
663         {
664                 if( (pd_flag < 0) || (pd_flag > 2) )
665                 {
666                         cerr << "Paradyn daemon: invalid -l value " << pd_flag << " given." << endl;
667                 }
668                 else
669                 {
670                         cerr << "Paradyn daemon: invalid command-line options seen" << endl;
671                 }
672                 cleanUpAndExit(-1);
673         }
674     assert( tp != NULL );
675
676 #if defined(MT_THREAD)
677     statusLine(V_paradyndMT);
678 #else
679     statusLine(V_paradynd);
680 #endif
681
682     // Note -- it is important that this daemon receives all mdl info
683     // before starting a process
684     aflag = mdl_get_initial(pd_flavor, tp);
685     assert(aflag);
686
687     initLibraryFunctions();
688     if (!init()) 
689         {
690                 abort();
691         }
692
693 #ifdef mips_sgi_irix6_4
694     struct utsname unameInfo;
695     if ( P_uname(&unameInfo) == -1 )
696     {
697         perror("uname");
698         return false;
699     }
700
701     // osName is used in irix.C and process.C
702     osName = unameInfo.sysname;
703
704     if ( pd_flavor == "mpi" && osName.prefixed_by("IRIX") )
705     {
706       if ( !execIrixMPIProcess(cmdLine) )
707         return(0);
708     }
709     else
710 #endif
711
712         // spawn the given process, if necessary
713         if (cmdLine.size())
714         {
715                 vector<string> envp;
716                 addProcess(cmdLine, envp, *dir); // ignore return val (is this right?)
717         }
718
719
720         // start handling events
721     controllerMainLoop(true);
722     return(0);
723 }
724