Changes made for nonblocking write
[dyninst.git] / paradynd / src / main.C
1 /*
2  * Copyright (c) 1996 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 /*
43  * Main loop for the default paradynd.
44  *
45  * $Log: main.C,v $
46  * Revision 1.64  1997/05/17 19:58:51  lzheng
47  * Changes made for nonblocking write
48  *
49  * Revision 1.63  1997/05/13 14:26:04  sec
50  * Removed a simple assert
51  *
52  * Revision 1.62  1997/05/07 19:01:15  naim
53  * Getting rid of old support for threads and turning it off until the new
54  * version is finished. Additionally, new superTable, baseTable and superVector
55  * classes for future support of multiple threads. The fastInferiorHeap class has
56  * also changed - naim
57  *
58  * Revision 1.61  1997/03/29 02:08:53  sec
59  * Changed the daemon poe to mpi
60  *
61  * Revision 1.60  1997/03/23 16:57:48  zhichen
62  * added code to set process:pdFlavor
63  *
64  * Revision 1.59  1997/03/14 18:50:57  zhichen
65  * Added reportSelf in the case when the daemons were started by
66  * COW DJM. Search for 'Tempest' for the change
67  *
68  * Revision 1.58  1997/02/26 23:46:35  mjrg
69  * First part of WindowsNT port: changes for compiling with Visual C++;
70  * moved unix specific code to unix.C file
71  *
72  * Revision 1.57  1997/02/21 20:15:50  naim
73  * Moving files from paradynd to dyninstAPI + eliminating references to
74  * dataReqNode from the ast class. This is the first pre-dyninstAPI commit! - naim
75  *
76  * Revision 1.56  1997/02/18 21:25:15  sec
77  * Added poe support
78  *
79  * Revision 1.55  1997/01/21 20:07:47  mjrg
80  * Changed to unix domain socket for trace stream
81  * Replaced calls to uname by calls to libutil function getHostName
82  *
83  * Revision 1.54  1997/01/16 22:07:15  tamches
84  * moved RPC_undo_arg_list here from util lib
85  *
86  * Revision 1.53  1997/01/15 00:28:09  tamches
87  * added some debug msgs
88  *
89  * Revision 1.52  1996/12/16 23:10:16  mjrg
90  * bug fixes to fork/exec on all platforms, partial fix to fork on AIX
91  *
92  * Revision 1.51  1996/12/06 09:37:55  tamches
93  * cleanUpAndExit() moved here (from .h file); it now also
94  * calls destructors for all processes, which should clean up
95  * process state like shm segments.
96  *
97  * Revision 1.50  1996/11/29 19:41:08  newhall
98  * Cleaned up some code.  Moved code that was duplicated in inst-sparc-solaris.C
99  * and inst-sparc-sunos.C to inst-sparc.C.  Bug fix to process::findFunctionIn.
100  *
101  * Revision 1.49  1996/11/26 16:08:09  naim
102  * Fixing asserts - naim
103  *
104  * Revision 1.48  1996/09/26 18:58:44  newhall
105  * added support for instrumenting dynamic executables on sparc-solaris
106  * platform
107  *
108  * Revision 1.47  1996/08/16 21:19:13  tamches
109  * updated copyright for release 1.1
110  *
111  * Revision 1.46  1996/08/12 16:27:08  mjrg
112  * Code cleanup: removed cm5 kludges and some unused code
113  *
114  * Revision 1.45  1996/07/18 19:39:14  naim
115  * Minor fix to give proper error message when the pvm daemon runs out of
116  * virtual memory - naim
117  *
118  * Revision 1.44  1996/05/31  23:57:48  tamches
119  * code to change send socket buffer size moved to comm.h
120  * removed handshaking code w/paradyn (where we sent "PARADYND" plus
121  * the pid) [wasn't being used and contributed to freeze in paradyn UI]
122  *
123  * Revision 1.43  1996/05/09 21:27:42  newhall
124  * increased the socket send buffer size from 4K to 32K on sunos and hpux
125  *
126  * Revision 1.42  1996/05/08  23:54:50  mjrg
127  * added support for handling fork and exec by an application
128  * use /proc instead of ptrace on solaris
129  * removed warnings
130  *
131  * Revision 1.41  1996/02/09 22:13:43  mjrg
132  * metric inheritance now works in all cases
133  * paradynd now always reports to paradyn when a process is ready to run
134  * fixed aggregation to handle first samples and addition of new components
135  *
136  * Revision 1.40  1996/01/29 22:09:23  mjrg
137  * Added metric propagation when new processes start
138  * Adjust time to account for clock differences between machines
139  * Daemons don't enable internal metrics when they are not running any processes
140  * Changed CM5 start (paradynd doesn't stop application at first breakpoint;
141  * the application stops only after it starts the CM5 daemon)
142  *
143  */
144
145 #include "util/h/headers.h"
146 #include "util/h/makenan.h"
147
148 #include "rtinst/h/rtinst.h"
149
150 #include "dyninstAPI/src/symtab.h"
151 #include "dyninstAPI/src/process.h"
152 #include "dyninstAPI/src/inst.h"
153 #include "dyninstAPI/src/instP.h"
154 #include "dyninstAPI/src/ast.h"
155 #include "dyninstAPI/src/util.h"
156 #include "dyninstAPI/src/dyninstP.h"
157 #include "paradynd/src/metric.h"
158 #include "paradynd/src/comm.h"
159 #include "paradynd/src/internalMetrics.h"
160 #include "util/h/machineType.h"
161 #include "paradynd/src/init.h"
162 #include "paradynd/src/perfStream.h"
163 #include "dyninstAPI/src/clock.h"
164 #include "paradynd/src/mdld.h"
165
166 pdRPC *tp;
167
168 #ifdef PARADYND_PVM
169 #include "pvm_support.h"
170 extern "C" {
171 #include <pvm3.h>
172 }
173 #endif     
174
175
176 bool pvm_running = false;
177
178 static string machine_name;
179
180 int ready;
181
182 /*
183  * These variables are global so that we can easily find out what
184  * machine/socket/etc we're connected to paradyn on; we may need to
185  * start up other paradynds (such as on the CM5), and need this later.
186  */
187 static string pd_machine;
188 static int pd_known_socket_portnum;
189 static int pd_flag;
190 static string pd_flavor;
191
192 void configStdIO(bool closeStdIn)
193 {
194     int nullfd;
195
196     /* now make stdin, out and error things that we can't hurt us */
197     if ((nullfd = open("/dev/null", O_RDWR, 0)) < 0) {
198         abort();
199     }
200
201     if (closeStdIn) (void) dup2(nullfd, 0);
202     (void) dup2(nullfd, 1);
203     (void) dup2(nullfd, 2);
204
205     if (nullfd > 2) close(nullfd);
206 }
207
208 void sigtermHandler()
209 {
210   showErrorCallback(98,"paradynd has been terminated");
211 }
212
213 // Cleanup for pvm and exit.
214 // This function must be called when we exit, to clean up and exit from pvm.
215 // Now also cleans up shm segs by deleting all processes  -ari
216 void cleanUpAndExit(int status) {
217 #ifdef PARADYND_PVM
218   if (pvm_running)
219     PDYN_exit_pvm();
220 #endif
221
222   // delete the trace socket file
223   unlink(traceSocketPath.string_of());
224
225 #ifdef SHM_SAMPLING_DEBUG
226    cerr << "paradynd cleanUpAndExit: deleting all process structures now" << endl;
227 #endif
228
229   // Fry all processes
230   extern vector<process*> processVec;
231
232   for (unsigned lcv=0; lcv < processVec.size(); lcv++) {
233      process *theProc = processVec[lcv];
234      if (theProc == NULL)
235         continue; // process has already been cleaned up
236
237      delete theProc; // calls process::~process, which fries the shm seg
238
239      processVec[lcv] = NULL; // probably not needed here.
240   }
241
242   P_exit(status);
243 }
244
245 // TODO
246 // mdc - I need to clean this up
247 bool
248 RPC_undo_arg_list (string& flavor, int argc, char **arg_list, string &machine,
249                    int &well_known_socket, int &flag,
250                    int &firstPVM)
251 {
252   char *ptr;
253   bool b_well_known=false; // found well-known socket port num
254   bool b_first=false;
255   bool b_machine = false, b_flag = false, b_flavor=false;
256
257   for (int loop=0; loop < argc; ++loop) {
258       // stop at the -runme argument since the rest are for the application
259       //   process we are about to spawn
260       if (!strcmp(arg_list[loop], "-runme")) break;
261       if (!P_strncmp(arg_list[loop], "-p", 2)) {
262           well_known_socket = P_strtol (arg_list[loop] + 2, &ptr, 10);
263           if (ptr == (arg_list[loop] + 2))
264             return(false);
265           b_well_known = true;
266       }
267       else if (!P_strncmp(arg_list[loop], "-v", 2)) {
268           firstPVM = P_strtol (arg_list[loop] + 2, &ptr, 10);
269           if (ptr == (arg_list[loop] + 2))
270             return(false);
271           b_first = true;
272       }
273       else if (!P_strncmp(arg_list[loop], "-m", 2)) {
274           machine = (arg_list[loop] + 2);
275           if (!machine.length()) return false;
276           b_machine = true;
277       }
278       else if (!P_strncmp(arg_list[loop], "-l", 2)) {
279           flag = P_strtol (arg_list[loop] + 2, &ptr, 10);
280           if (ptr == (arg_list[loop] + 2))
281             return(false);
282           b_flag = true;
283       }
284       else if (!P_strncmp(arg_list[loop], "-z", 2)) {
285           flavor = (arg_list[loop]+2);
286           if (!flavor.length()) return false;
287           b_flavor = true;
288       }
289   }
290
291   return (b_flag && b_first && b_machine && b_well_known && b_flavor);
292 }
293
294 int main(int argc, char *argv[]) {
295     //cerr << "welcome to paradynd, args are:" << endl;
296     //for (unsigned lcv=0; lcv < argc; lcv++) {
297     //   cerr << argv[lcv] << endl;
298     //}
299
300     struct sigaction act;
301
302     // int i = 1;
303     // while (i) {};
304
305
306 #if defined(sparc_sun_sunos4_1_3) || defined(sparc_sun_solaris2_4)
307     act.sa_handler = (void (*)(...)) sigtermHandler;
308 #else
309     act.sa_handler = (void (*)(int)) sigtermHandler;
310 #endif
311     act.sa_flags   = 0;
312
313     /* for AIX - default (non BSD) library does not restart - jkh 7/26/95 */
314 #if defined(SA_RESTART)
315     act.sa_flags  |= SA_RESTART;
316 #endif
317
318     sigfillset(&act.sa_mask);
319
320     if (sigaction(SIGTERM, &act, 0) == -1) {
321         perror("sigaction(SIGTERM)");
322         abort();
323     }
324
325     process::programName = argv[0];
326     // process command line args passed in
327     // pd_flag == 1 --> started by paradyn
328     int pvm_first;
329     bool aflag;
330     aflag = RPC_undo_arg_list (pd_flavor, argc, argv, pd_machine,
331                                pd_known_socket_portnum, pd_flag, 
332                                pvm_first);
333     assert(aflag);
334     aflag = RPC_make_arg_list(process::arg_list,
335                               pd_known_socket_portnum, pd_flag, 0,
336                               pd_machine, true);
337     assert(aflag);
338     string flav_arg(string("-z")+ pd_flavor);
339     process::arg_list += flav_arg;
340     machine_name = getHostName();
341
342     // kill(getpid(),SIGSTOP);
343
344     //
345     // See if we should fork an app process now.
346     //
347     vector<string> cmdLine;
348     for (int i=0; argv[i]; i++) {
349         if (!strcmp(argv[i], "-runme")) {
350              // next arg is the command to run.
351              int j;
352              for (j=0,i++; argv[i]; i++,j++) {
353                  cmdLine += argv[i];
354              }
355         }
356     }
357 #ifdef PARADYND_PVM
358     // There are 3 ways to get here
359     //     started by pvm_spawn from first paradynd -- must report back
360     //     started by rsh, rexec, ugly code --> connect via socket
361     //     started by exec --> use pipe
362     
363     // int pvm_id = pvm_mytid();
364     int pvmParent = PvmSysErr;
365
366     cerr << "pd_flavor: " << pd_flavor.string_of() << endl ;
367     process::pdFlavor = pd_flavor ;
368     if (pd_flavor == string("pvm")) {
369        pvmParent = pvm_parent();
370
371        if (pvmParent == PvmSysErr) {
372           fprintf(stdout, "Unable to connect to PVM daemon, is PVM running?\n");
373           fflush(stdout);
374           cleanUpAndExit(-1);
375         }
376        else
377           pvm_running = true;
378     }
379
380     if (pvm_running && pvmParent != PvmNoParent) {
381       // started by pvm_spawn
382       // TODO -- report error here
383       if (!PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 0)) {
384         cleanUpAndExit(-1);
385       }
386       tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, pd_machine,
387                      NULL, NULL, 2);
388       assert(tp);
389
390       tp->reportSelf (machine_name, argv[0], getpid(), "pvm");
391     } else if(pd_flavor == string("mpi")) {
392       // the executables which are started by poe (for mpi daemon) must report to paradyn
393       // the pdRPC is allocated and reportSelf is called
394       tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, 
395                      pd_machine, NULL, NULL, 2);
396       assert(tp);
397
398       tp->reportSelf(machine_name, argv[0], getpid(), "mpi");
399     } else if (!pd_flag) {
400       // not started by pvm_spawn; rather, started via rsh/rexec --> use socket
401       int pid = fork();
402       if (pid == 0) {
403         // configStdIO(true);
404         // setup socket
405         // TODO -- report error here
406         
407         // We must get a connection with paradyn before starting any other daemons,
408         // or else one of the daemons we start (in PDYN_initForPVM), may get our
409         // connection.
410         tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, pd_machine,
411                        NULL, NULL, 2);
412         assert(tp);
413         //Tempest, in the case of blizzard_cow, all daemons should report themselves
414         if (pd_flavor == string("cow")) {
415                 cerr << "rsh, rexec, reportSelf " 
416                      << machine_name.string_of() 
417                      << argv[0] << endl ;
418                 tp->reportSelf (machine_name, argv[0], getpid(), "cow");
419         }
420       
421
422         if (pvm_running && !PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 1)) {
423             cleanUpAndExit(-1);
424         }
425
426       } else if (pid > 0) {
427         P__exit(-1);
428       } else {
429         cerr << "Fatal error on paradyn daemon: fork failed." << endl;
430         cerr.flush();
431         cleanUpAndExit(-1);
432       }
433     } else {
434        // started via exec   --> use pipe
435        // TODO -- report error here
436       if (pvm_running && !PDYN_initForPVM (argv, pd_machine, pd_known_socket_portnum, 1)) {
437           cleanUpAndExit(-1);
438       }
439       // already setup on this FD.
440       // disconnect from controlling terminal 
441       OS::osDisconnect();
442       tp = new pdRPC(0, NULL, NULL, 2);
443       assert(tp);
444     }
445     assert(tp);
446 #else
447
448     if(pd_flavor == string("mpi")) {
449       // not put here, only up above since PARADYND_PVM is always set
450       assert(0);
451     } else if (!pd_flag) {
452       int pid = fork();
453       if (pid == 0) {
454         // configStdIO(true);
455         // setup socket
456
457         tp = new pdRPC(AF_INET, pd_known_socket_portnum, SOCK_STREAM, pd_machine, 
458                        NULL, NULL, 2);
459         assert(tp);
460
461         if (cmdLine.size()) {
462             tp->reportSelf(machine_name, argv[0], getpid(), pd_flavor);
463         }
464       } else if (pid > 0) {
465         P__exit(-1);
466       } else {
467         cerr << "Fatal error on paradyn daemon: fork failed." << endl;
468         cerr.flush();
469         cleanUpAndExit(-1);
470       }
471     } else {
472       OS::osDisconnect();
473       tp = new pdRPC(0, NULL, NULL, 2);
474       assert(tp);
475
476       // configStdIO(false);
477     }
478 #endif
479
480     cyclesPerSecond = timing_loop() * 1000000;
481
482     // Note -- it is important that this daemon receives all mdl info
483     // before starting a process
484     aflag = mdl_get_initial(pd_flavor, tp);
485     assert(aflag);
486
487     initLibraryFunctions();
488     if (!init()) 
489       abort();
490
491     if (cmdLine.size()) {
492          //logLine("paradynd: cmdLine is non-empty so we'll be calling addProcess now!\n") ; 
493          //cerr << "cmdLine is:" << endl;
494          //for (unsigned lcv=0; lcv < cmdLine.size(); lcv++)
495          //   cerr << cmdLine[lcv] << endl;
496
497          vector<string> envp;
498          addProcess(cmdLine, envp, string("")); // ignore return val (is this right?)
499     }
500
501     controllerMainLoop(true);
502 }