Dyninst API library now uses fork() instead of vfork() when creating
[dyninst.git] / dyninstAPI / src / unix.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 #include "util/h/headers.h"
44 #include "util/h/String.h"
45 #include "util/h/Vector.h"
46 #include "paradynd/src/showerror.h"
47 #include "dyninstAPI/src/os.h"
48 #include "dyninstAPI/src/util.h"
49
50 #ifndef BPATCH_LIBRARY
51 #include "paradynd/src/perfStream.h"
52 #include "paradynd/src/main.h"
53 #endif
54
55 // the following are needed for handleSigChild
56 #include "dyninstAPI/src/process.h"
57 #include "dyninstAPI/src/instP.h"
58 #include "dyninstAPI/src/stats.h"
59 extern process *findProcess(int);
60
61 // The following were all defined in process.C (for no particular reason)
62 extern debug_ostream attach_cerr;
63 extern debug_ostream inferiorrpc_cerr;
64 extern debug_ostream shmsample_cerr;
65 extern debug_ostream forkexec_cerr;
66 extern debug_ostream metric_cerr;
67 extern debug_ostream signal_cerr;
68 extern debug_ostream sharedobj_cerr;
69
70
71 extern "C" {
72 #ifdef PARADYND_PVM
73 int pvmputenv (const char *);
74 int pvmendtask();
75 #endif
76 }
77
78 /*****************************************************************************
79  * forkNewProcess: starts a new process, setting up trace and io links between
80  *                the new process and the daemon
81  * Returns true if succesfull.
82  * 
83  * Arguments:
84  *   file: file to execute
85  *   dir: working directory for the new process
86  *   argv: arguments to new process
87  *   envp: environment **** not in use
88  *   inputFile: where to redirect standard input
89  *   outputFile: where to redirect standard output
90  *   traceLink: handle or file descriptor of trace link (read only)
91  *   ioLink: handle or file descriptor of io link (read only)
92  *   pid: process id of new process
93  *   tid: thread id for main thread (needed by WindowsNT)
94  *   procHandle: handle for new process (needed by WindowsNT)
95  *   thrHandle: handle for main thread (needed by WindowsNT)
96  ****************************************************************************/
97 bool forkNewProcess(string file, string dir, vector<string> argv, 
98                     vector<string>envp, string inputFile, string outputFile,
99                     int &traceLink, int &ioLink, 
100                     int &pid, int & /*tid*/, 
101                     int & /*procHandle*/, int & /*thrHandle*/) {
102
103 #ifndef BPATCH_LIBRARY
104     // Strange, but using socketpair here doesn't seem to work OK on SunOS.
105     // Pipe works fine.
106     // r = P_socketpair(AF_UNIX, SOCK_STREAM, (int) NULL, tracePipe);
107     int tracePipe[2];
108     int r = P_pipe(tracePipe);
109     if (r) {
110         // P_perror("socketpair");
111         string msg = string("Unable to create trace pipe for program '") + file +
112                        string("': ") + string(sys_errlist[errno]);
113         showErrorCallback(68, msg);
114         return false;
115     }
116
117     // ioPipe is used to redirect the child's stdout & stderr to a pipe which is in
118     // turn read by the parent via the process->ioLink socket.
119     int ioPipe[2];
120
121     // r = P_socketpair(AF_UNIX, SOCK_STREAM, (int) NULL, ioPipe);
122     r = P_pipe(ioPipe);
123     if (r) {
124         // P_perror("socketpair");
125         string msg = string("Unable to create IO pipe for program '") + file +
126                        string("': ") + string(sys_errlist[errno]);
127         showErrorCallback(68, msg);
128         return false;
129     }
130 #endif
131
132     //
133     // WARNING This code assumes that vfork is used, and a failed exec will
134     //   corectly change failed in the parent process.
135     //
136     
137     errno = 0;
138 #if defined(PARADYND_PVM) || defined(BPATCH_LIBRARY)
139 // must use fork, since pvmendtask will do some writing in the address space
140     pid = fork();
141     // fprintf(stderr, "FORK: pid=%d\n", pid);
142 #else
143     pid = vfork();
144 #endif
145
146     if (pid > 0) {
147
148         //*** parent
149
150         if (errno) {
151             sprintf(errorLine, "Unable to start %s: %s\n", file.string_of(), 
152                     sys_errlist[errno]);
153             logLine(errorLine);
154             showErrorCallback(68, (const char *) errorLine);
155             return false;
156         }
157
158 #ifndef BPATCH_LIBRARY
159         close(tracePipe[1]);
160            // parent never writes trace records; it only receives them.
161
162         close(ioPipe[1]);
163            // parent closes write end of io pipe; child closes its read end.
164            // pipe output goes to the parent's read fd (ret->ioLink); pipe input
165            // comes from the child's write fd.  In short, when the child writes to
166            // its stdout/stderr, it gets sent to the pipe which in turn sends it to
167            // the parent's ret->ioLink fd for reading.
168
169         traceLink = tracePipe[0];
170         ioLink = ioPipe[0];
171 #endif
172         return true;
173
174     } else if (pid == 0) {
175         //*** child
176
177 #ifdef PARADYND_PVM
178         if (pvm_running)
179           pvmendtask(); 
180 #endif   
181
182 #ifndef BPATCH_LIBRARY
183         // handle stdio.
184
185         // We only write to ioPipe.  Hence we close ioPipe[0], the read end.  Then we
186         // call dup2() twice to assign our stdout and stderr to the write end of the
187         // pipe.
188         close(ioPipe[0]);
189         dup2(ioPipe[1], 1);
190            // assigns fd 1 (stdout) to be a copy of ioPipe[1].  (Since stdout is already
191            // in use, dup2 will first close it then reopen it with the characteristics
192            // of ioPipe[1].)
193            // In short, stdout gets redirected towards the write end of the pipe.
194            // The read end of the pipe is read by the parent (paradynd), not by us.
195
196         dup2(ioPipe[1], 2); // redirect fd 2 (stderr) to the pipe, like above.
197
198         // We're not using ioPipe[1] anymore; close it.
199         if (ioPipe[1] > 2) close (ioPipe[1]);
200
201         // Now that stdout is going to a pipe, it'll (unfortunately) be block buffered
202         // instead of the usual line buffered (when it goes to a tty).  In effect the
203         // stdio library is being a little too clever for our purposes.  We don't want
204         // the "bufferedness" to change.  So we set it back to line-buffered.
205         // The command to do this is setlinebuf(stdout) [stdio.h call]  But we don't
206         // do it here, since the upcoming execve() would undo our work [execve keeps
207         // fd's but resets higher-level stdio information, which is recreated before
208         // execution of main()]  So when do we do it?  In rtinst's DYNINSTinit
209         // (RTposix.c et al.)
210
211         // setup stderr for rest of exec try.
212         FILE *childError = P_fdopen(2, "w");
213
214         P_close(tracePipe[0]);
215
216         if (P_dup2(tracePipe[1], 3) != 3) {
217             fprintf(childError, "dup2 failed\n");
218             fflush(childError);
219             P__exit(-1);
220         }
221
222         /* close if higher */
223         if (tracePipe[1] > 3) close(tracePipe[1]);
224
225         if ((dir.length() > 0) && (P_chdir(dir.string_of()) < 0)) {
226           sprintf(errorLine, "cannot chdir to '%s': %s\n", dir.string_of(), 
227                   sys_errlist[errno]);
228           logLine(errorLine);
229           P__exit(-1);
230         }
231
232         /* see if I/O needs to be redirected */
233         if (inputFile.length()) {
234             int fd = P_open(inputFile.string_of(), O_RDONLY, 0);
235             if (fd < 0) {
236                 fprintf(childError, "stdin open of %s failed\n", inputFile.string_of());
237                 fflush(childError);
238                 P__exit(-1);
239             } else {
240                 dup2(fd, 0);
241                 P_close(fd);
242             }
243         }
244
245         if (outputFile.length()) {
246             int fd = P_open(outputFile.string_of(), O_WRONLY|O_CREAT, 0444);
247             if (fd < 0) {
248                 fprintf(childError, "stdout open of %s failed\n", outputFile.string_of());
249                 fflush(childError);
250                 P__exit(-1);
251             } else {
252                 dup2(fd, 1); // redirect fd 1 (stdout) to a copy of descriptor "fd"
253                 P_close(fd); // not using descriptor fd any more; close it.
254             }
255         }
256 #endif
257
258         /* indicate our desire to be traced */
259         errno = 0;
260         OS::osTraceMe();
261         if (errno != 0) {
262           sprintf(errorLine, "ptrace error, exiting, errno=%d\n", errno);
263           logLine(errorLine);
264           logLine(sys_errlist[errno]);
265           showErrorCallback(69, string("Internal error: ") + 
266                                 string((const char *) errorLine)); 
267           P__exit(-1);   // double underscores are correct
268         }
269 #ifdef PARADYND_PVM
270         if (pvm_running && envp.size())
271           for (int ep=envp.size()-1; ep>=0; ep--) {
272             pvmputenv(envp[ep].string_of());
273           }
274 #endif
275 #ifndef BPATCH_LIBRARY
276         // hand off info about how to start a paradynd to the application.
277         //   used to catch rexec calls, and poe events.
278         //
279         char paradynInfo[1024];
280         sprintf(paradynInfo, "PARADYN_MASTER_INFO= ");
281         for (unsigned i=0; i < process::arg_list.size(); i++) {
282             const char *str;
283
284             str = P_strdup(process::arg_list[i].string_of());
285             if (!strcmp(str, "-l1")) {
286                 strcat(paradynInfo, "-l0");
287             } else {
288                 strcat(paradynInfo, str);
289             }
290             strcat(paradynInfo, " ");
291         }
292         P_putenv(paradynInfo);
293 #endif
294
295         char **args;
296         args = new char*[argv.size()+1];
297         for (unsigned ai=0; ai<argv.size(); ai++)
298           args[ai] = P_strdup(argv[ai].string_of());
299         args[argv.size()] = NULL;
300         P_execvp(file.string_of(), args);
301
302         sprintf(errorLine, "paradynd: execv failed, errno=%d\n", errno);
303         logLine(errorLine);
304
305         logLine(sys_errlist[errno]);
306 {
307         int i=0;
308         while (args[i]) {
309           sprintf(errorLine, "argv %d = %s\n", i, args[i]);
310           logLine(errorLine);
311           i++;
312         }
313 }
314         P__exit(-1);
315         // not reached
316
317         return false;
318
319     } else { // pid == 0 --- error
320         sprintf(errorLine, "vfork failed, errno=%d\n", errno);
321         logLine(errorLine);
322         showErrorCallback(71, (const char *) errorLine);
323         return false;
324     }
325
326 }
327
328
329
330 /* 
331    TODO: cleanup handleSigChild. This function has a lot of code that
332    should be moved to a machine independent place (a lot of what is
333    going on here will probably have to be moved anyway once we move to
334    the dyninstAPI).
335
336    There is a different version of this function for WindowsNT. If any changes
337    are made here, they will probably also be needed in the NT version.
338
339    --mjrg
340 */
341
342 // TODO -- make this a process method
343 int handleSigChild(int pid, int status)
344 {
345
346 #ifdef rs6000_ibm_aix4_1
347     // On AIX, we get sigtraps on fork and load, and must handle
348     // these cases specially
349     extern bool handleAIXsigTraps(int, int);
350     if (handleAIXsigTraps(pid, status)) {
351       return 0;
352     }
353     /* else check for regular traps and signals */
354 #endif
355
356     // ignore signals from unknown processes
357     process *curr = findProcess(pid);
358     if (!curr) {
359        forkexec_cerr << "handleSigChild pid " << pid << " is an unknown process." << endl;
360        forkexec_cerr << "WIFSTOPPED=" << (WIFSTOPPED(status) ? "true" : "false");
361        if (WIFSTOPPED(status)) {
362           forkexec_cerr << "WSTOPSIG=" << WSTOPSIG(status);
363        }
364        forkexec_cerr << endl << flush;
365        return -1;
366     }
367
368     if (WIFSTOPPED(status)) {
369         int sig = WSTOPSIG(status);
370         switch (sig) {
371
372             case SIGTSTP:
373                 sprintf(errorLine, "process %d got SIGTSTP", pid);
374                 statusLine(errorLine);
375                 curr->Stopped();
376                 break;
377
378             case SIGTRAP: {
379                 // Note that there are now several uses for SIGTRAPs in paradynd.
380                 // The original use was to detect when a ptraced process had
381                 // started up.  Several have been added.  We must be careful
382                 // to make sure that uses of SIGTRAPs do not conflict.
383
384                 signal_cerr << "welcome to SIGTRAP for pid " << curr->getPid() << " status=" << curr->getStatusAsString() << endl;
385                 const bool wasRunning = (curr->status() == running);
386                 curr->status_ = stopped;
387
388                 // Check to see if the TRAP is due to a system call exit which
389                 // we were waiting for, in order to launch an inferior rpc safely.
390                 if (curr->isRPCwaitingForSysCallToComplete()) {
391                    inferiorrpc_cerr << "got SIGTRAP indicating syscall completion!"
392                                     << endl;
393                    curr->setRPCwaitingForSysCallToComplete(false);
394
395                    if (curr->launchRPCifAppropriate(false, // not running
396                                                     true   // finishing a syscall
397                                                     )) {
398                       inferiorrpc_cerr << "syscall completion: rpc launched ok, as expected " << endl;
399                    }
400                    else
401                      inferiorrpc_cerr << "syscall completion: failed to launch rpc" << endl;
402                    break;
403                 }
404
405                 // check to see if trap is due to dlopen or dlcose event
406                 if(curr->isDynamicallyLinked()){
407                     if(curr->handleIfDueToSharedObjectMapping()){
408                         // continue proc?
409                         if(wasRunning) curr->continueProc();
410                         break;
411                     }
412                 }
413
414                 //If the list is not empty, it means some previous
415                 //instrumentation has yet need to be finished.
416                 if (instWList.size() != 0) {
417                     // cerr << "instWList is full" << endl;
418                     if(curr -> cleanUpInstrumentation(wasRunning)){
419                         break; // successfully processed the SIGTRAP
420                     }
421                 }
422
423                 if (curr->handleTrapIfDueToRPC()) {
424                    inferiorrpc_cerr << "processed RPC response in SIGTRAP" << endl;
425                    break;
426                 }
427
428                 if (curr->inExec) {
429                    // the process has executed a succesful exec, and is now
430                    // stopped at the exit of the exec call.
431
432                    forkexec_cerr << "SIGTRAP: inExec is true, so doing process::handleExec()!" << endl;
433                    string buffer = string("process ") + string(curr->getPid()) +
434                                    " has performed exec() syscall";
435                    statusLine(buffer.string_of());
436
437                    // call handleExec to clean our internal data structures, reparse
438                    // symbol table.  handleExec does not insert instrumentation or do
439                    // any propagation.  Why not?  Because it wouldn't be safe -- the
440                    // process hasn't yet been bootstrapped (run DYNINST, etc.)  Only
441                    // when we get the breakpoint at the end of DYNINSTinit() is it safe
442                    // to insert instrumentation and allocate timers & counters...
443                    curr->handleExec();
444
445 #ifndef BPATCH_LIBRARY
446                    // need to start resourceBatchMode here because we will call
447                    //  tryToReadAndProcessBootstrapInfo which
448                    // calls tp->resourceBatchMode(false)
449                    tp->resourceBatchMode(true);
450 #endif
451
452                    // set reachedFirstBreak to false here, so we execute
453                    // the code below, and insert the initial instrumentation
454                    // in the new image. (Actually, this is already done by handleExec())
455                    curr->reachedFirstBreak = false;
456
457                    // fall through...
458                 }
459
460                 // Now we expect that this TRAP is the initial trap sent when a ptrace'd
461                 // process completes startup via exec (or when an exec syscall was
462                 // executed in an already-running process).
463                 // But we must query 'reachedFirstBreak' because on machines where we
464                 // attach/detach on pause/continue, a TRAP is generated on each pause!
465
466                 if (!curr->reachedFirstBreak) { // vrble should be renamed 'reachedFirstTrap'
467                    // cerr << "!reachedFirstBreak" << endl;
468                    string buffer = string("PID=") + string(pid);
469                    buffer += string(", passed trap at start of program");
470                    statusLine(buffer.string_of());
471
472                    // check for DYNINST symbols and initializes the inferiorHeap
473                    // If libdyninst is dynamically linked, this can only be
474                    // called after libdyninst is loaded
475                    curr->initDyninstLib();
476
477                    curr->reachedFirstBreak = true;
478
479                    buffer=string("PID=") + string(pid) + ", installing call to DYNINSTinit()";
480                    statusLine(buffer.string_of());
481
482                    curr->installBootstrapInst();
483                         
484                    // now, let main() and then DYNINSTinit() get invoked.  As it
485                    // completes, DYNINSTinit() does a DYNINSTbreakPoint, at which time
486                    // we read bootstrap information "sent back" (in a global vrble),
487                    // propagate metric instances, do tp->newProgramCallbackFunc(), etc.
488                    if (!curr->continueProc()) {
489                       cerr << "continueProc after installBootstrapInst() failed, so DYNINSTinit() isn't being invoked yet, which it should!" << endl;
490                    }
491                    else {
492                       buffer=string("PID=") + string(pid) + ", running DYNINSTinit()...";
493                       statusLine(buffer.string_of());
494                    }
495                 }
496                 else {
497                    signal_cerr << "SIGTRAP not handled for pid " << pid << " so just leaving process in stopped state" << endl << flush;
498                 }
499
500                 break;
501             }
502
503             case SIGSTOP:
504             case SIGINT: {
505                 signal_cerr << "welcome to SIGSTOP/SIGINT for proc pid " << curr->getPid() << endl;
506
507                 const processState prevStatus = curr->status_;
508                 // assert(prevStatus != stopped); (bombs on sunos and AIX, when lots of spurious sigstops are delivered)
509
510                 curr->status_ = stopped;
511                    // the following routines expect (and assert) this status.
512
513                 int result = curr->procStopFromDYNINSTinit();
514                 assert(result >=0 && result <= 2);
515                 if (result != 0) {
516                    forkexec_cerr << "processed SIGSTOP from DYNINSTinit for pid " << curr->getPid() << endl << flush;
517
518                    if (result == 1) {
519                       assert(curr->status_ == stopped);
520                       // DYNINSTinit() after normal startup, after fork, or after exec
521                       // syscall was made by a running program; leave paused
522                       // (tp->newProgramCallback() to paradyn will result in the process
523                       // being continued soon enough, assuming the applic was running,
524                       // which is true in all cases except when an applic is just being
525                       // started up).  Fall through (we want the status line to change)
526                    } else {
527                       assert(result == 2);
528                       break; // don't fall through...prog is finishing the inferiorRPC
529                    }
530                 }
531                 else if (curr->handleTrapIfDueToRPC()) {
532                    inferiorrpc_cerr << "processed RPC response in SIGSTOP" << endl;
533                    break; // don't want to execute ->Stopped() which changes status line
534                 }
535 #ifndef BPATCH_LIBRARY
536                 else if (curr->handleStopDueToExecEntry()) {
537                    // grabs data from DYNINST_bootstrap_info
538                    forkexec_cerr << "fork/exec -- handled stop before exec" << endl;
539                    string buffer = string("process ") + string(curr->getPid()) +
540                                    " performing exec() syscall...";
541                    statusLine(buffer.string_of());
542
543                    // note: status will now be 'running', since handleStopDueToExec()
544                    // did a continueProc() to let the exec() syscall go forward.
545                    assert(curr->status_ == running);
546                       // would neonatal be better? or exited?
547
548                    break; // don't want to change status line in conventional way
549                 }
550 #endif /* BPATCH_LIBRARY */
551                 else {
552                    forkexec_cerr << "unhandled SIGSTOP for pid " << curr->getPid() << " so just leaving process in paused state." << endl << flush;
553                 }
554                 curr->status_ = prevStatus; // so Stopped() below won't be a nop
555                 curr->Stopped();
556
557                 break;
558             }
559
560             case SIGILL:
561                signal_cerr << "welcome to SIGILL" << endl << flush;
562                curr->status_ = stopped;
563
564                if (curr->handleTrapIfDueToRPC()) {
565                   inferiorrpc_cerr << "processed RPC response in SIGILL" << endl; cerr.flush();
566
567                   break; // we don't forward the signal -- on purpose
568                }
569                else
570                   // fall through, on purpose
571                   ;
572
573             case SIGIOT:
574             case SIGBUS:
575 #if (defined(POWER_DEBUG) || defined(HP_DEBUG)) && (defined(rs6000_ibm_aix4_1) || defined(hppa1_1_hp_hpux))
576                 // In this way, we will detach from the application and we
577                 // will be able to attach again using gdb. We need to send
578                 // a kill -ILL pid signal to the application in order to
579                 // get here - naim
580 #if defined(rs6000_ibm_aix4_1)
581                 if (ptrace(PT_DETACH,pid,(int *) 1, SIGSTOP, NULL) == -1) {
582 #else
583                 if (ptrace(PT_DETACH, pid, 1, SIGSTOP, NULL) == -1) { 
584 #endif
585                   logLine("ptrace error\n");
586                 }
587 #else
588                 signal_cerr << "caught signal, dying...  (sig="
589                             << WSTOPSIG(status) << ")" << endl << flush;
590
591                 curr->status_ = stopped;
592                 curr->dumpImage();
593                 curr->continueWithForwardSignal(WSTOPSIG(status));
594 #endif
595                 break;
596
597             case SIGALRM:
598 #ifndef SHM_SAMPLING
599                 // Due to the DYNINSTin_sample variable, it's safest to launch
600                 // inferior-RPCs only when we know that the inferior is not in the
601                 // middle of processing an alarm-expire.  Otherwise, code that does
602                 // stuff like call DYNINSTstartWallTimer will appear to do nothing
603                 // (DYNINSTstartWallTimer will be invoked but will see that
604                 //  DYNINSTin_sample is set and so bails out!!!)
605                 // Ick.
606                 if (curr->existsRPCreadyToLaunch()) {
607                    curr -> status_ = stopped;
608                    (void)curr->launchRPCifAppropriate(true, false);
609                    break; // sure, we lose the SIGALARM, but so what.
610                 }
611                 else
612                    ; // no break, on purpose
613 #endif
614
615             case SIGCHLD:
616             case SIGUSR1:
617             case SIGUSR2:
618             case SIGVTALRM:
619             case SIGCONT:
620             case SIGSEGV:       // treadmarks needs this signal
621 #if (defined(POWER_DEBUG) || defined(HP_DEBUG)) && (defined(rs6000_ibm_aix4_1) || defined(hppa1_1_hp_hpux))
622                 // In this way, we will detach from the application and we
623                 // will be able to attach again using gdb - naim
624                 if (sig==SIGSEGV)
625                 {
626                   logLine("==> Detaching paradynd from the application...\n");
627 #if defined(rs6000_ibm_aix4_1)
628                   if (ptrace(PT_DETACH,pid,(int *) 1, SIGSTOP, NULL) == -1)
629 #else
630                   if (ptrace(PT_DETACH,pid, 1, SIGSTOP, NULL) == -1)
631 #endif
632                     logLine("ptrace error\n");
633                   break;
634                 }
635 #endif
636                 if (!curr->continueWithForwardSignal(WSTOPSIG(status))) {
637                      logLine("error  in forwarding  signal\n");
638                      showErrorCallback(38, "Error  in forwarding  signal");
639                      //P_abort();
640                 }
641
642                 break;
643
644 #ifdef notdef
645             // XXXX for debugging
646             case SIGSEGV:       // treadmarks needs this signal
647                 sprintf(errorLine, "DEBUG: forwarding signal (sig=%d, pid=%d)\n"
648                         , WSTOPSIG(status), pid);
649                 logLine(errorLine);
650 #endif
651             default:
652                 if (!curr->continueWithForwardSignal(WSTOPSIG(status))) {
653                      logLine("error  in forwarding  signal\n");
654                      P_abort();
655                 }
656                 break;
657
658         }
659     } else if (WIFEXITED(status)) {
660 #if defined(PARADYND_PVM)
661 //        if (pvm_running) {
662 //            PDYN_reportSIGCHLD (pid, WEXITSTATUS(status));
663 //      }
664 #endif
665         sprintf(errorLine, "Process %d has terminated\n", curr->getPid());
666         statusLine(errorLine);
667         logLine(errorLine);
668
669         printDyninstStats();
670         handleProcessExit(curr, WEXITSTATUS(status));
671     } else if (WIFSIGNALED(status)) {
672         sprintf(errorLine, "process %d has terminated on signal %d\n", curr->getPid(), WTERMSIG(status));
673         logLine(errorLine);
674         statusLine(errorLine);
675         handleProcessExit(curr, WTERMSIG(status));
676     } else {
677         sprintf(errorLine, "Unknown state %d from process %d\n", status, curr->getPid());
678         logLine(errorLine);
679         showErrorCallback(39,(const char *) errorLine);
680     }
681     return(0);
682 }
683
684
685 void checkProcStatus() {
686    /* check for status change on inferior processes, or the arrival of
687       a signal. */
688
689    int wait_status;
690    int wait_pid = process::waitProcs(&wait_status);
691    if (wait_pid > 0) {
692       if (handleSigChild(wait_pid, wait_status) < 0) {
693          cerr << "handleSigChild failed for pid " << wait_pid << endl;
694       }
695    }
696 }
697
698
699 bool  OS::osKill(int pid) {
700   return (P_kill(pid,9)==0);
701 }