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