Minor fix to cleanup_drn. We should only use it when computingCost is
[dyninst.git] / dyninstAPI / src / unix2.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  * This file containts special versions of some of the fuctions in unix.C,
43  * which have been modified for the dyninstAPI library.
44  */
45 /*
46  * $Log: unix2.C,v $
47  * Revision 1.2  1997/05/02 01:43:33  buck
48  * Updated dyninstAPI library-only files to work with recent changes to code
49  * code included in Paradyn.
50  *
51  * Revision 1.1.1.1  1997/04/01 20:25:11  buck
52  * Update Maryland repository with latest from Wisconsin.
53  *
54  * Revision 1.1  1997/03/18 19:44:31  buck
55  * first commit of dyninst library.  Also includes:
56  *      moving templates from paradynd to dyninstAPI
57  *      converting showError into a function (in showerror.C)
58  *      many ifdefs for BPATCH_LIBRARY in dyinstAPI/src.
59  *
60  *
61  */
62
63 #include "util/h/headers.h"
64 #include "util/h/String.h"
65 #include "util/h/Vector.h"
66 #include "paradynd/src/showerror.h"
67 #include "dyninstAPI/src/os.h"
68 #include "dyninstAPI/src/util.h"
69
70 // the following are needed for handleSigChild
71 #include "dyninstAPI/src/process.h"
72 #include "dyninstAPI/src/instP.h"
73 #include "dyninstAPI/src/stats.h"
74 extern process *findProcess(int);
75
76 // The following were all defined in process.C (for no particular reason)
77 extern debug_ostream attach_cerr;
78 extern debug_ostream inferiorrpc_cerr;
79 extern debug_ostream shmsample_cerr;
80 extern debug_ostream forkexec_cerr;
81 extern debug_ostream metric_cerr;
82 extern debug_ostream signal_cerr;
83 extern debug_ostream sharedobj_cerr;
84
85
86 extern "C" {
87 #ifdef PARADYND_PVM
88 int pvmputenv (const char *);
89 int pvmendtask();
90 #endif
91 }
92
93 /*****************************************************************************
94  * dyninstAPI_forkNewProcess: starts a new process
95  * Returns true if succesfull.
96  * 
97  * Arguments:
98  *   file: file to execute
99  *   dir: working directory for the new process
100  *   argv: arguments to new process
101  *   envp: environment **** not in use
102  *   inputFile: where to redirect standard input **** unused
103  *   outputFile: where to redirect standard output **** unused
104  *   traceLink: handle or file descriptor of trace link (read only) **** unused
105  *   ioLink: handle or file descriptor of io link (read only) **** unused
106  *   pid: process id of new process
107  *   tid: thread id for main thread (needed by WindowsNT)
108  *   procHandle: handle for new process (needed by WindowsNT)
109  *   thrHandle: handle for main thread (needed by WindowsNT)
110  ****************************************************************************/
111 bool dyninstAPI_forkNewProcess(string file, string dir, vector<string> argv, 
112                     vector<string>envp, string inputFile, string outputFile,
113                     int &traceLink, int &ioLink, 
114                     int &pid, int & /*tid*/, 
115                     int & /*procHandle*/, int & /*thrHandle*/) {
116
117     //
118     // WARNING This code assumes that vfork is used, and a failed exec will
119     //   corectly change failed in the parent process.
120     //
121     
122     errno = 0;
123 #ifdef PARADYND_PVM
124 // must use fork, since pvmendtask will do some writing in the address space
125     pid = fork();
126     // fprintf(stderr, "FORK: pid=%d\n", pid);
127 #else
128     pid = vfork();
129 #endif
130
131     if (pid > 0) {
132
133         //*** parent
134
135         if (errno) {
136             sprintf(errorLine, "Unable to start %s: %s\n", file.string_of(), 
137                     sys_errlist[errno]);
138             logLine(errorLine);
139             showErrorCallback(68, (const char *) errorLine);
140             return false;
141         }
142
143         return true;
144
145     } else if (pid == 0) {
146         //*** child
147
148 #ifdef PARADYND_PVM
149         if (pvm_running)
150           pvmendtask(); 
151 #endif   
152
153         if ((dir.length() > 0) && (P_chdir(dir.string_of()) < 0)) {
154           sprintf(errorLine, "cannot chdir to '%s': %s\n", dir.string_of(), 
155                   sys_errlist[errno]);
156           logLine(errorLine);
157           P__exit(-1);
158         }
159
160         /* indicate our desire to be traced */
161         errno = 0;
162         OS::osTraceMe();
163         if (errno != 0) {
164           sprintf(errorLine, "ptrace error, exiting, errno=%d\n", errno);
165           logLine(errorLine);
166           logLine(sys_errlist[errno]);
167           showErrorCallback(69, string("Internal error: ") + 
168                                 string((const char *) errorLine)); 
169           P__exit(-1);   // double underscores are correct
170         }
171 #ifdef PARADYND_PVM
172         if (pvm_running && envp.size())
173           for (int ep=envp.size()-1; ep>=0; ep--) {
174             pvmputenv(envp[ep].string_of());
175           }
176 #endif
177         char **args;
178         args = new char*[argv.size()+1];
179         for (unsigned ai=0; ai<argv.size(); ai++)
180           args[ai] = P_strdup(argv[ai].string_of());
181         args[argv.size()] = NULL;
182         P_execvp(file.string_of(), args);
183
184         sprintf(errorLine, "paradynd: execv failed, errno=%d\n", errno);
185         logLine(errorLine);
186
187         logLine(sys_errlist[errno]);
188 {
189         int i=0;
190         while (args[i]) {
191           sprintf(errorLine, "argv %d = %s\n", i, args[i]);
192           logLine(errorLine);
193           i++;
194         }
195 }
196         P__exit(-1);
197         // not reached
198
199         return false;
200
201     } else { // pid == 0 --- error
202         sprintf(errorLine, "vfork failed, errno=%d\n", errno);
203         logLine(errorLine);
204         showErrorCallback(71, (const char *) errorLine);
205         return false;
206     }
207
208 }
209
210
211
212 /* 
213    TODO: cleanup handleSigChild. This function has a lot of code that
214    should be moved to a machine independent place (a lot of what is
215    going on here will probably have to be moved anyway once we move to
216    the dyninstAPI).
217
218    There is a different version of this function for WindowsNT. If any changes
219    are made here, they will probably also be needed in the NT version.
220
221    --mjrg
222 */
223
224 // TODO -- make this a process method
225 int dyninstAPI_handleSigChild(int pid, int status)
226 {
227
228 #ifdef rs6000_ibm_aix4_1
229     // On AIX, we get sigtraps on fork and load, and must handle
230     // these cases specially
231     extern bool handleAIXsigTraps(int, int);
232     if (handleAIXsigTraps(pid, status)) {
233       return 0;
234     }
235     /* else check for regular traps and signals */
236 #endif
237
238     // ignore signals from unknown processes
239     process *curr = findProcess(pid);
240     if (!curr) {
241        forkexec_cerr << "handleSigChild pid " << pid << " is an unknown process." << endl;
242        forkexec_cerr << "WIFSTOPPED=" << (WIFSTOPPED(status) ? "true" : "false");
243        if (WIFSTOPPED(status)) {
244           forkexec_cerr << "WSTOPSIG=" << WSTOPSIG(status);
245        }
246        forkexec_cerr << endl << flush;
247        return -1;
248     }
249
250     if (WIFSTOPPED(status)) {
251         int sig = WSTOPSIG(status);
252         switch (sig) {
253
254             case SIGTSTP:
255                 sprintf(errorLine, "process %d got SIGTSTP", pid);
256                 statusLine(errorLine);
257                 curr->Stopped();
258                 break;
259
260             case SIGTRAP: {
261                 // Note that there are now several uses for SIGTRAPs in paradynd.
262                 // The original use was to detect when a ptraced process had
263                 // started up.  Several have been added.  We must be careful
264                 // to make sure that uses of SIGTRAPs do not conflict.
265
266                 signal_cerr << "welcome to SIGTRAP for pid " << curr->getPid() << " status=" << curr->getStatusAsString() << endl;
267                 const bool wasRunning = (curr->status() == running);
268                 curr->status_ = stopped; // probably was 'neonatal'
269
270                 //If the list is not empty, it means some previous
271                 //instrumentation has yet need to be finished.
272                 if (instWList.size() != 0) {
273                     // cerr << "instWList is full" << endl;
274                     if(curr -> cleanUpInstrumentation(wasRunning)){
275                         break; // successfully processed the SIGTRAP
276                     }
277                 }
278
279                 if (curr->handleTrapIfDueToRPC()) {
280                    inferiorrpc_cerr << "processed RPC response in SIGTRAP" << endl;
281                    break;
282                 }
283
284                 if (curr->inExec) {
285                    // the process has executed a succesful exec, and is now
286                    // stopped at the exit of the exec call.
287
288                    forkexec_cerr << "SIGTRAP: inExec is true, so doing process::handleExec()!" << endl;
289                    string buffer = string("process ") + string(curr->getPid()) +
290                                    " has performed exec() syscall";
291                    statusLine(buffer.string_of());
292
293                    // call handleExec to clean our internal data structures, reparse
294                    // symbol table.  handleExec does not insert instrumentation or do
295                    // any propagation.  Why not?  Because it wouldn't be safe -- the
296                    // process hasn't yet been bootstrapped (run DYNINST, etc.)  Only
297                    // when we get the breakpoint at the end of DYNINSTinit() is it safe
298                    // to insert instrumentation and allocate timers & counters...
299                    curr->handleExec();
300
301                    // set reachedFirstBreak to false here, so we execute
302                    // the code below, and insert the initial instrumentation
303                    // in the new image. (Actually, this is already done by handleExec())
304                    curr->reachedFirstBreak = false;
305
306                    // fall through...
307                 }
308
309                 // Now we expect that this TRAP is the initial trap sent when a ptrace'd
310                 // process completes startup via exec (or when an exec syscall was
311                 // executed in an already-running process).
312                 // But we must query 'reachedFirstBreak' because on machines where we
313                 // attach/detach on pause/continue, a TRAP is generated on each pause!
314
315                 if (!curr->reachedFirstBreak) { // vrble should be renamed 'reachedFirstTrap'
316                    // cerr << "!reachedFirstBreak" << endl;
317                    string buffer = string("PID=") + string(pid);
318                    buffer += string(", passed trap at start of program");
319                    statusLine(buffer.string_of());
320
321                    // initializes the inferiorHeap
322                    // If libdyninst is dynamically linked, this can only be
323                    // called after libdyninst is loaded
324                    curr->initDyninstLib();
325
326 //                 (void)(curr->findDynamicLinkingInfo()); // SHOULD THIS BE HERE???
327
328                    curr->reachedFirstBreak = true;
329
330                 }
331                 else {
332                    signal_cerr << "SIGTRAP not handled for pid " << pid << " so just leaving process in stopped state" << endl << flush;
333                 }
334
335                 break;
336             }
337
338             case SIGSTOP:
339             case SIGINT: {
340                 signal_cerr << "welcome to SIGSTOP/SIGINT for proc pid " << curr->getPid() << endl;
341
342                 const processState prevStatus = curr->status_;
343                 // assert(prevStatus != stopped); (bombs on sunos and AIX, when lots of spurious sigstops are delivered)
344
345                 curr->status_ = stopped;
346                    // the following routines expect (and assert) this status.
347
348 #ifdef BPATCH_NOT_YET
349                 int result = curr->procStopFromDYNINSTinit();
350                 assert(result >=0 && result <= 2);
351                 if (result != 0) {
352                    forkexec_cerr << "processed SIGSTOP from DYNINSTinit for pid " << curr->getPid() << endl << flush;
353
354                    if (result == 1) {
355                       assert(curr->status_ == stopped);
356                       // DYNINSTinit() after normal startup, after fork, or after exec
357                       // syscall was made by a running program; leave paused
358                       // (tp->newProgramCallback() to paradyn will result in the process
359                       // being continued soon enough, assuming the applic was running,
360                       // which is true in all cases except when an applic is just being
361                       // started up).  Fall through (we want the status line to change)
362                    } else {
363                       assert(result == 2);
364                       break; // don't fall through...prog is finishing the inferiorRPC
365                    }
366                 }
367                 else if (curr->handleTrapIfDueToRPC()) {
368                    inferiorrpc_cerr << "processed RPC response in SIGSTOP" << endl;
369                    break; // don't want to execute ->Stopped() which changes status line
370                 }
371                 else if (curr->handleStopDueToExecEntry()) {
372                    // grabs data from DYNINST_bootstrap_info
373                    forkexec_cerr << "fork/exec -- handled stop before exec" << endl;
374                    string buffer = string("process ") + string(curr->getPid()) +
375                                    " performing exec() syscall...";
376                    statusLine(buffer.string_of());
377
378                    // note: status will now be 'running', since handleStopDueToExec()
379                    // did a continueProc() to let the exec() syscall go forward.
380                    assert(curr->status_ == running);
381                       // would neonatal be better? or exited?
382
383                    break; // don't want to change status line in conventional way
384                 }
385                 else {
386                    forkexec_cerr << "unhandled SIGSTOP for pid " << curr->getPid() << " so just leaving process in paused state." << endl << flush;
387                 }
388 #endif /* BPATCH_NOT_YET */
389
390                 curr->status_ = prevStatus; // so Stopped() below won't be a nop
391                 curr->Stopped();
392
393                 break;
394             }
395
396             case SIGILL:
397                signal_cerr << "welcome to SIGILL" << endl << flush;
398                curr->status_ = stopped;
399
400                if (curr->handleTrapIfDueToRPC()) {
401                   inferiorrpc_cerr << "processed RPC response in SIGILL" << endl; cerr.flush();
402
403                   break; // we don't forward the signal -- on purpose
404                }
405                else
406                   // fall through, on purpose
407                   ;
408
409             case SIGIOT:
410             case SIGBUS:
411 #if (defined(POWER_DEBUG) || defined(HP_DEBUG)) && (defined(rs6000_ibm_aix4_1) || defined(hppa1_1_hp_hpux))
412                 // In this way, we will detach from the application and we
413                 // will be able to attach again using gdb. We need to send
414                 // a kill -ILL pid signal to the application in order to
415                 // get here - naim
416 #if defined(rs6000_ibm_aix4_1)
417                 if (ptrace(PT_DETACH,pid,(int *) 1, SIGSTOP, NULL) == -1) {
418 #else
419                 if (ptrace(PT_DETACH, pid, 1, SIGSTOP, NULL) == -1) { 
420 #endif
421                   logLine("ptrace error\n");
422                 }
423 #else
424                 signal_cerr << "caught signal, dying...  (sig="
425                             << WSTOPSIG(status) << ")" << endl << flush;
426
427                 curr->status_ = stopped;
428                 curr->dumpImage();
429                 curr->continueWithForwardSignal(WSTOPSIG(status));
430 #endif
431                 break;
432
433             case SIGALRM:
434 #ifndef SHM_SAMPLING
435                 // Due to the DYNINSTin_sample variable, it's safest to launch
436                 // inferior-RPCs only when we know that the inferior is not in the
437                 // middle of processing an alarm-expire.  Otherwise, code that does
438                 // stuff like call DYNINSTstartWallTimer will appear to do nothing
439                 // (DYNINSTstartWallTimer will be invoked but will see that
440                 //  DYNINSTin_sample is set and so bails out!!!)
441                 // Ick.
442                 if (curr->existsRPCreadyToLaunch()) {
443                    curr -> status_ = stopped;
444                    (void)curr->launchRPCifAppropriate(true);
445                    break; // sure, we lose the SIGALARM, but so what.
446                 }
447                 else
448                    ; // no break, on purpose
449 #endif
450
451             case SIGCHLD:
452             case SIGUSR1:
453             case SIGUSR2:
454             case SIGVTALRM:
455             case SIGCONT:
456             case SIGSEGV:       // treadmarks needs this signal
457 #if (defined(POWER_DEBUG) || defined(HP_DEBUG)) && (defined(rs6000_ibm_aix4_1) || defined(hppa1_1_hp_hpux))
458                 // In this way, we will detach from the application and we
459                 // will be able to attach again using gdb - naim
460                 if (sig==SIGSEGV)
461                 {
462                   logLine("==> Detaching paradynd from the application...\n");
463 #if defined(rs6000_ibm_aix4_1)
464                   if (ptrace(PT_DETACH,pid,(int *) 1, SIGSTOP, NULL) == -1)
465 #else
466                   if (ptrace(PT_DETACH,pid, 1, SIGSTOP, NULL) == -1)
467 #endif
468                     logLine("ptrace error\n");
469                   break;
470                 }
471 #endif
472                 if (!curr->continueWithForwardSignal(WSTOPSIG(status))) {
473                      logLine("error  in forwarding  signal\n");
474                      showErrorCallback(38, "Error  in forwarding  signal");
475                      //P_abort();
476                 }
477
478                 break;
479
480 #ifdef notdef
481             // XXXX for debugging
482             case SIGSEGV:       // treadmarks needs this signal
483                 sprintf(errorLine, "DEBUG: forwarding signal (sig=%d, pid=%d)\n"
484                         , WSTOPSIG(status), pid);
485                 logLine(errorLine);
486 #endif
487             default:
488                 if (!curr->continueWithForwardSignal(WSTOPSIG(status))) {
489                      logLine("error  in forwarding  signal\n");
490                      P_abort();
491                 }
492                 break;
493
494         }
495     } else if (WIFEXITED(status)) {
496 #if defined(PARADYND_PVM)
497 //        if (pvm_running) {
498 //            PDYN_reportSIGCHLD (pid, WEXITSTATUS(status));
499 //      }
500 #endif
501         sprintf(errorLine, "Process %d has terminated\n", curr->getPid());
502         statusLine(errorLine);
503         logLine(errorLine);
504
505         printDyninstStats();
506         handleProcessExit(curr, WEXITSTATUS(status));
507     } else if (WIFSIGNALED(status)) {
508         sprintf(errorLine, "process %d has terminated on signal %d\n", curr->getPid(), WTERMSIG(status));
509         logLine(errorLine);
510         statusLine(errorLine);
511         handleProcessExit(curr, WTERMSIG(status));
512     } else {
513         sprintf(errorLine, "Unknown state %d from process %d\n", status, curr->getPid());
514         logLine(errorLine);
515         showErrorCallback(39,(const char *) errorLine);
516     }
517     return(0);
518 }