Move process::pdFlavor assignment out of PVM conditional code.
[dyninst.git] / paradynd / src / main.C
1 /*
2  * Copyright (c) 1996-1999 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.91 2000/10/20 19:41:54 zandy 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;
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 int main(unsigned argc, char *argv[]) {
251
252   initialize_debug_flag();
253
254   string *dir = new string("");  
255 #if !defined(i386_unknown_nt4_0)
256     {
257         char *pdkill;
258         pdkill = getenv( "PARADYND_DEBUG" );
259         if( pdkill && ( *pdkill == 'y' || *pdkill == 'Y' ) ) {
260             int pid = getpid();
261             cerr << "breaking for debug in controllerMainLoop...pid=" << pid << endl;
262 #if defined(i386_unknown_nt4_0)
263             DebugBreak();
264 #elif defined(sparc_sun_solaris2_4) || defined(i386_unknown_solaris2_5)
265             sleep(20);
266 #else
267             kill(pid, SIGSTOP);
268 #endif
269         }
270     }
271 #endif // !defined(i386_unknown_nt4_0)
272
273 #if !defined(i386_unknown_nt4_0)
274     struct sigaction act;
275
276     // int i = 1;
277     // while (i) {};
278
279
280 #if defined(sparc_sun_sunos4_1_3) || defined(sparc_sun_solaris2_4)
281     act.sa_handler = (void (*)(...)) sigtermHandler;
282 #else
283     act.sa_handler = (void (*)(int)) sigtermHandler;
284 #endif
285     act.sa_flags   = 0;
286
287     /* for AIX - default (non BSD) library does not restart - jkh 7/26/95 */
288 #if defined(SA_RESTART)
289     act.sa_flags  |= SA_RESTART;
290 #endif
291
292     sigfillset(&act.sa_mask);
293
294     if (sigaction(SIGTERM, &act, 0) == -1) {
295         perror("sigaction(SIGTERM)");
296         abort();
297     }
298 #endif
299
300 #ifdef DETACH_ON_THE_FLY
301     initDetachOnTheFly();
302 #endif
303
304 #if defined(i386_unknown_nt4_0)
305     // Windows NT needs to initialize winsock library
306     WORD wsversion = MAKEWORD(2,0);
307     WSADATA wsadata;
308     WSAStartup(wsversion, &wsadata);
309 #endif
310
311     process::programName = argv[0];
312     // process command line args passed in
313     // pd_flag == 1 --> started by paradyn
314     bool aflag;
315     aflag = RPC_undo_arg_list (pd_flavor, argc, argv, pd_machine,
316                                pd_known_socket_portnum, pd_flag);
317     if (!aflag || pd_debug) {
318         if (!aflag) cerr << "Invalid/incomplete command-line args:" << endl;
319         cerr << "   -z<flavor";
320         if (pd_flavor.length()) cerr << "=" << pd_flavor;
321         cerr << "> -l<flag";
322         if (pd_flag) cerr << "=" << pd_flag;
323         cerr << "> -m<hostmachine";
324         if (pd_machine.length()) cerr << "=" << pd_machine;
325         cerr << "> -p<hostport";
326         if (pd_known_socket_portnum) cerr << "=" << pd_known_socket_portnum;
327         cerr << ">" << endl;
328         if (process::dyninstName.length())
329             cerr << "   -L<library=" << process::dyninstName << ">" << endl;
330         if (!aflag) cleanUpAndExit(-1);
331     }
332
333     aflag = RPC_make_arg_list(process::arg_list,
334                               pd_known_socket_portnum, pd_flag, 0,
335                               pd_machine, true);
336     assert(aflag);
337     string flav_arg(string("-z")+ pd_flavor);
338     process::arg_list += flav_arg;
339     machine_name = getNetworkName();
340
341     // kill(getpid(),SIGSTOP);
342
343     //
344     // See if we should fork an app process now.
345     //
346
347     // We want to find two things
348     // First, get the current working dir (PWD)
349     dir = new string(getenv("PWD"));
350
351     // Second, put the inferior application and its command line
352     // arguments into cmdLine. Basically, loop through argv until
353     // we find -runme, and put everything after it into cmdLine.
354     vector<string> cmdLine;
355     unsigned int argNum = 0;
356     while ((argNum < argc) && (strcmp(argv[argNum], "-runme")))
357       argNum++;
358     // Okay, argNum is the command line argument which is "-runme"
359     argNum++;
360     // Copy everything from argNum to < argc
361     for (unsigned int i = argNum; i < argc; i++)
362       cmdLine += argv[i];
363
364     process::pdFlavor = pd_flavor;
365 #ifdef PARADYND_PVM
366     // There are 3 ways to get here
367     //     started by pvm_spawn from first paradynd -- must report back
368     //     started by rsh, rexec, ugly code --> connect via socket
369     //     started by exec --> use pipe
370     
371     // int pvm_id = pvm_mytid();
372     int pvmParent = PvmSysErr;
373
374 #ifdef PDYN_DEBUG
375     cerr << "pd_flavor: " << pd_flavor.string_of() << endl;
376 #endif
377     if (pd_flavor == string("pvm")) {
378        pvmParent = pvm_parent();
379
380        if (pvmParent == PvmSysErr) {
381           fprintf(stdout, "Unable to connect to PVM daemon, is PVM running?\n");
382           fflush(stdout);
383           cleanUpAndExit(-1);
384         }
385        else
386           pvm_running = true;
387     }
388
389     if (pvm_running && pvmParent != PvmNoParent) {
390       // started by pvm_spawn
391       // TODO -- report error here
392       if (!PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 0)) {
393         cleanUpAndExit(-1);
394       }
395       tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, pd_machine,
396                      NULL, NULL, 2);
397       assert(tp);
398
399       tp->reportSelf (machine_name, argv[0], getpid(), "pvm");
400     } else if ( pd_flavor == "mpi" ) {
401       // Both IRIX and AIX MPI job-launchers will start paradynd,
402       // which must report to paradyn
403       // the pdRPC is allocated and reportSelf is called
404       tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, 
405                      pd_machine, NULL, NULL, 2);
406       assert(tp);
407
408       tp->reportSelf(machine_name, argv[0], getpid(), "mpi");
409     } else if (pd_flag == 2) {
410        // manual startup
411         tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, pd_machine, 
412                        NULL, NULL, 2);
413         assert(tp);
414         //assert(tp->net_obj()); // shouldn't this be part of pdRPC::pdRPC?
415         if (!tp->net_obj()) {
416             cerr << "Failed to establish connection to Paradyn on "
417                  << pd_machine << " port " << pd_known_socket_portnum << endl;
418             cleanUpAndExit(-1);
419         }
420         tp->reportSelf(machine_name, argv[0], getpid(), pd_flavor);
421
422         if (pvm_running
423             && !PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 1))
424             cleanUpAndExit(-1);
425
426     } else if (!pd_flag) {
427       // not started by pvm_spawn; rather, started via rsh/rexec --> use socket
428       int pid = fork();
429       if (pid == 0) {
430         // configStdIO(true);
431         // setup socket
432         // TODO -- report error here
433         
434         // We must get a connection with paradyn before starting any other daemons,
435         // or else one of the daemons we start (in PDYN_initForPVM), may get our
436         // connection.
437         tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, pd_machine,
438                        NULL, NULL, 2);
439         assert(tp);
440
441         if (pvm_running && !PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 1)) {
442             cleanUpAndExit(-1);
443         }
444
445       } else if (pid > 0) {
446         P__exit(-1);
447       } else {
448         cerr << "Fatal error on paradyn daemon: fork failed." << endl;
449         cerr.flush();
450         cleanUpAndExit(-1);
451       }
452     } else {
453        // started via exec   --> use pipe
454        // TODO -- report error here
455       if (pvm_running && !PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 1)) {
456           cleanUpAndExit(-1);
457       }
458       // already setup on this FD.
459       // disconnect from controlling terminal 
460       OS::osDisconnect();
461       tp = new pdRPC(0, NULL, NULL, 2);
462       assert(tp);
463     }
464     assert(tp);
465 #else
466
467     if ( pd_flavor == "mpi" ) {
468       // Both IRIX and AIX MPI job-launchers will start paradynd,
469       // which must report to paradyn
470       // the pdRPC is allocated and reportSelf is called
471       tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, 
472                      pd_machine, NULL, NULL, 2);
473       assert(tp);
474
475       tp->reportSelf(machine_name, argv[0], getpid(), "mpi");
476     } else if (pd_flag == 2) {
477        // manual startup
478         tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, pd_machine, 
479                        NULL, NULL, 2);
480         assert(tp);
481         tp->reportSelf(machine_name, argv[0], getpid(), pd_flavor);
482
483     } else if (!pd_flag) {
484 #if !defined(i386_unknown_nt4_0)
485       int pid = fork();
486 #else
487       int pid = 0;
488 #endif
489       if (pid == 0) {
490         // configStdIO(true);
491         // setup socket
492
493         tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, pd_machine, 
494                        NULL, NULL, 2);
495         assert(tp);
496
497         if (cmdLine.size()) {
498             tp->reportSelf(machine_name, argv[0], getpid(), pd_flavor);
499         }
500     } else if (!pd_flag) {
501       } else if (pid > 0) {
502         P__exit(-1);
503       } else {
504         cerr << "Fatal error on paradyn daemon: fork failed." << endl;
505         cerr.flush();
506         cleanUpAndExit(-1);
507       }
508     } else {
509       OS::osDisconnect();
510
511 #if !defined(i386_unknown_nt4_0)
512                 PDSOCKET sock = 0;
513 #else
514                 PDSOCKET sock = _get_osfhandle(0);
515 #endif // defined(i386_unknown_nt4_0)
516       tp = new pdRPC(sock, NULL, NULL, 2);
517       assert(tp);
518
519       // configStdIO(false);
520     }
521 #endif
522
523 #if defined(MT_THREAD)
524     statusLine(V_paradyndMT);
525 #else
526     statusLine(V_paradynd);
527 #endif
528
529     // Note -- it is important that this daemon receives all mdl info
530     // before starting a process
531     aflag = mdl_get_initial(pd_flavor, tp);
532     assert(aflag);
533
534     initLibraryFunctions();
535     if (!init()) 
536       abort();
537
538 #ifdef mips_sgi_irix6_4
539     struct utsname unameInfo;
540     if ( P_uname(&unameInfo) == -1 )
541     {
542         perror("uname");
543         return false;
544     }
545
546     // osName is used in irix.C and process.C
547     osName = unameInfo.sysname;
548
549     if ( pd_flavor == "mpi" && osName.prefixed_by("IRIX") )
550     {
551       if ( !execIrixMPIProcess(cmdLine) )
552         return(0);
553     }
554     else
555 #endif
556         if (cmdLine.size()) {
557             vector<string> envp;
558             addProcess(cmdLine, envp, *dir); // ignore return val (is this right?)
559         }
560
561     controllerMainLoop(true);
562     return(0);
563 }
564