2 * Copyright (c) 1996-2000 Barton P. Miller
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.
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.
18 * (for other uses, please contact us at paradyn@cs.wisc.edu)
20 * All warranties, including without limitation, any warranty of
21 * merchantability or fitness for a particular purpose, are hereby
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.
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.
42 // $Id: main.C,v 1.92 2000/12/07 20:15:37 pcroth Exp $
44 #include "common/h/headers.h"
45 #include "pdutil/h/makenan.h"
46 #include "common/h/Ident.h"
48 #if defined(MT_THREAD)
49 extern "C" const char V_paradyndMT[];
51 extern "C" const char V_paradynd[];
53 extern "C" const char V_libpdutil[];
55 #if defined(MT_THREAD)
56 Ident V_id(V_paradyndMT,"Paradyn");
58 Ident V_id(V_paradynd,"Paradyn");
60 Ident V_Uid(V_libpdutil,"Paradyn");
62 #include "rtinst/h/rtinst.h"
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"
82 #include "pvm_support.h"
89 bool pvm_running = false;
91 static string machine_name;
97 #ifdef mips_sgi_irix6_4
98 extern bool execIrixMPIProcess(vector<string> &argv);
101 #ifdef DETACH_ON_THE_FLY
102 extern void initDetachOnTheFly();
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.
110 static string pd_machine;
111 static int pd_known_socket_portnum=0;
112 static int pd_flag=0;
113 static string pd_flavor;
115 void configStdIO(bool closeStdIn)
119 /* now make stdin, out and error things that we can't hurt us */
120 if ((nullfd = open("/dev/null", O_RDWR, 0)) < 0) {
124 if (closeStdIn) (void) dup2(nullfd, 0);
125 (void) dup2(nullfd, 1);
126 (void) dup2(nullfd, 2);
128 if (nullfd > 2) close(nullfd);
131 void sigtermHandler()
133 showErrorCallback(98,"paradynd has been terminated");
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) {
145 #if !defined(i386_unknown_nt4_0)
146 // delete the trace socket file
147 unlink(traceSocketPath.string_of());
150 #ifdef SHM_SAMPLING_DEBUG
151 cerr << "paradynd cleanUpAndExit: deleting all process structures now" << endl;
155 extern vector<process*> processVec;
157 for (unsigned lcv=0; lcv < processVec.size(); lcv++) {
158 process *theProc = processVec[lcv];
160 continue; // process has already been cleaned up
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();
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);
176 processVec[lcv] = NULL; // probably not needed here.
183 // mdc - I need to clean this up
185 RPC_undo_arg_list (string &flavor, unsigned argc, char **arg_list,
186 string &machine, int &well_known_socket, int &flag)
189 bool b_well_known=false; // found well-known socket port num
190 bool b_machine = false, b_flag = false, b_flavor=false;
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))
202 else if (!P_strncmp(arg_list[loop], "-V", 2)) { // optional
203 cout << V_id << endl;
205 else if (!P_strncmp(arg_list[loop], "-v", 2)) {
207 //cerr << "paradynd: -v flag is obsolete (and ignored)" << endl;
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;
215 else if (!P_strncmp(arg_list[loop], "-m", 2)) {
216 machine = (arg_list[loop] + 2);
217 if (!machine.length()) return false;
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))
226 else if (!P_strncmp(arg_list[loop], "-z", 2)) {
227 flavor = (arg_list[loop]+2);
228 if (!flavor.length()) return false;
233 // verify required parameters
234 return (b_flag && b_machine && b_well_known && b_flavor);
237 // PARADYND_DEBUG_XXX
238 static void initialize_debug_flag(void) {
241 if ( (p=getenv("PARADYND_DEBUG_INFRPC")) ) {
245 if ( (p=getenv("PARADYND_DEBUG_CATCHUP")) ) {
246 pd_debug_catchup = 1;
253 PauseIfDesired( void )
255 char *pdkill = getenv( "PARADYND_DEBUG" );
256 if( pdkill && ( *pdkill == 'y' || *pdkill == 'Y' ) )
259 cerr << "breaking for debug in controllerMainLoop...pid=" << pid << endl;
260 #if defined(i386_unknown_nt4_0)
262 #elif defined(sparc_sun_solaris2_4) || defined(i386_unknown_solaris2_5)
275 #if !defined(i386_unknown_nt4_0)
278 InitSigTermHandler( void )
280 struct sigaction act;
286 #if defined(sparc_sun_sunos4_1_3) || defined(sparc_sun_solaris2_4)
287 act.sa_handler = (void (*)(...)) sigtermHandler;
289 act.sa_handler = (void (*)(int)) sigtermHandler;
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;
298 sigfillset(&act.sa_mask);
300 if (sigaction(SIGTERM, &act, 0) == -1) {
301 perror("sigaction(SIGTERM)");
305 #endif // !defined(i386_unknown_nt4_0)
307 #if defined(i386_unknown_nt4_0)
312 // Windows NT needs to initialize winsock library
313 WORD wsversion = MAKEWORD(2,0);
315 WSAStartup(wsversion, &wsadata);
317 #endif // defined(i386_unknown_nt4_0)
324 InitForMPI( char* argv[], const string& pd_machine )
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);
333 tp->reportSelf(machine_name, argv[0], getpid(), "mpi");
341 InitManuallyStarted( char* argv[], const string& pd_machine )
343 bool reported = false;
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);
351 cerr << "Failed to establish connection to Paradyn on "
352 << pd_machine << " port " << pd_known_socket_portnum << endl;
355 tp->reportSelf(machine_name, argv[0], getpid(), pd_flavor);
358 #if defined(PARADYND_PVM)
359 // are we designated for monitoring a PVM application?
361 && !PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 1))
365 #endif // PARADYND_PVM
372 InitRemotelyStarted( char* argv[], const string& pd_machine, bool report )
374 // we are a remote daemon started by rsh/rexec or some other
375 // use socket to connect back to our front end
377 // we fork ourselves and the child process becomes the "real" daemon
378 #if !defined(i386_unknown_nt4_0)
380 #else // !defined(i386_unknown_nt4_0)
382 #endif // !defined(i386_unknown_nt4_0)
385 // we are the child process - we are the "true" daemon
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 );
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
401 tp->reportSelf( machine_name, argv[0], getpid(), pd_flavor );
404 #if defined(PARADYND_PVM)
406 !PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 1))
408 // TODO -- report error here
411 #endif // defined(PARADYND_PVM)
415 // we are the parent process - if the fork succeeded, exit
422 cerr << "Paradyn daemon: Fatal error: fork failed." << endl;
432 InitLocallyStarted( char* argv[], const string& pd_machine )
435 // check if we are designated to monitor a PVM application
437 !PDYN_initForPVM( argv, pd_machine, pd_known_socket_portnum, 1 ))
439 // TODO -- report error here
440 cleanUpAndExit( -1 );
442 #endif // PARADYND_PVM
444 // connect to our front end using our stdout
447 #if !defined(i386_unknown_nt4_0)
450 PDSOCKET sock = _get_osfhandle(0);
451 #endif // defined(i386_unknown_nt4_0)
452 tp = new pdRPC(sock, NULL, NULL, 2);
454 // note that we do not need to report to our front end in this case
458 #if defined(PARADYND_PVM)
461 InitForPVM( char* argv[], const string& pd_machine )
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();
468 if (pvmParent == PvmSysErr)
470 cout << "Unable to connect to PVM daemon; is PVM running?\n" << endl;
478 if (pvm_running && (pvmParent != PvmNoParent))
480 // we were started using pvm_spawn() by another paradynd
481 if (!PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 0))
483 // TODO -- report error here
487 // report to our front end
488 tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM,
489 pd_machine, NULL, NULL, 2);
492 tp->reportSelf (machine_name, argv[0], getpid(), "pvm");
495 #endif // PARADYND_PVM
500 // Note: the pd_flag variable is set from the argument to the -l command
501 // line switch. It has the following meaning:
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
510 main( int argc, char* argv[] )
513 initialize_debug_flag();
515 #if !defined(i386_unknown_nt4_0)
516 InitSigTermHandler();
517 #endif // defined(i386_unknown_nt4_0)
520 #ifdef DETACH_ON_THE_FLY
521 initDetachOnTheFly();
524 #if defined(i386_unknown_nt4_0)
526 #endif // defined(i386_unknown_nt4_0)
530 // process command line args passed in
532 process::programName = argv[0];
534 aflag = RPC_undo_arg_list (pd_flavor, argc, argv, pd_machine,
535 pd_known_socket_portnum, pd_flag);
536 if (!aflag || pd_debug)
540 cerr << "Invalid/incomplete command-line args:" << endl;
542 cerr << " -z<flavor";
543 if (pd_flavor.length())
545 cerr << "=" << pd_flavor;
550 cerr << "=" << pd_flag;
552 cerr << "> -m<hostmachine";
553 if (pd_machine.length())
555 cerr << "=" << pd_machine;
557 cerr << "> -p<hostport";
558 if (pd_known_socket_portnum)
560 cerr << "=" << pd_known_socket_portnum;
563 if (process::dyninstName.length())
565 cerr << " -L<library=" << process::dyninstName << ">" << endl;
573 aflag = RPC_make_arg_list(process::arg_list,
574 pd_known_socket_portnum, pd_flag, 0,
577 string flav_arg(string("-z")+ pd_flavor);
578 process::arg_list += flav_arg;
579 machine_name = getNetworkName();
582 // See if we should fork an app process now.
585 // We want to find two things
586 // First, get the current working dir (PWD)
587 string* dir = new string(getenv("PWD"));
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")))
598 // Okay, argNum is the command line argument which is "-runme" - skip it
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++)
606 // note - cmdLine could be empty here, if the -runme flag were not given
609 // There are several ways that we might have been started.
610 // We need to connect to Paradyn differently depending on which
613 // Use our own stdin if:
614 // started as local daemon by Paradyn using fork+exec
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
620 // started as PVM daemon by another Paradyn daemon using pvm_spawn()
623 process::pdFlavor = pd_flavor;
625 cerr << "pd_flavor: " << pd_flavor.string_of() << endl;
630 if (pd_flavor == string("pvm"))
632 InitForPVM( argv, pd_machine );
634 #endif // PARADYND_PVM
638 // we haven't yet reported to our front end
640 if( pd_flavor == "mpi" )
642 InitForMPI( argv, pd_machine );
644 else if( pd_flag == 0 )
646 // we are a remote daemon started by rsh/rexec or some other
647 InitRemotelyStarted( argv, pd_machine, (cmdLine.size() > 0) );
649 else if( pd_flag == 1 )
651 // we were started by a local front end using fork+exec
652 InitLocallyStarted( argv, pd_machine );
654 else if( pd_flag == 2 )
656 // we were started manually (i.e., from the command line)
657 InitManuallyStarted( argv, pd_machine );
661 // by now, we should have a connection to our front end
664 if( (pd_flag < 0) || (pd_flag > 2) )
666 cerr << "Paradyn daemon: invalid -l value " << pd_flag << " given." << endl;
670 cerr << "Paradyn daemon: invalid command-line options seen" << endl;
674 assert( tp != NULL );
676 #if defined(MT_THREAD)
677 statusLine(V_paradyndMT);
679 statusLine(V_paradynd);
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);
687 initLibraryFunctions();
693 #ifdef mips_sgi_irix6_4
694 struct utsname unameInfo;
695 if ( P_uname(&unameInfo) == -1 )
701 // osName is used in irix.C and process.C
702 osName = unameInfo.sysname;
704 if ( pd_flavor == "mpi" && osName.prefixed_by("IRIX") )
706 if ( !execIrixMPIProcess(cmdLine) )
712 // spawn the given process, if necessary
716 addProcess(cmdLine, envp, *dir); // ignore return val (is this right?)
720 // start handling events
721 controllerMainLoop(true);