new param to newResource
[dyninst.git] / paradynd / src / perfStream.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 #ifdef PARADYND_PVM
43 extern "C" {
44 #include <pvm3.h>
45 }
46 #include "pvm_support.h"
47 #endif
48
49 #include "util/h/headers.h"
50 #include "rtinst/h/rtinst.h"
51 #include "rtinst/h/trace.h"
52 #include "dyninstAPI/src/symtab.h"
53 #include "dyninstAPI/src/process.h"
54 #include "dyninstAPI/src/inst.h"
55 #include "dyninstAPI/src/instP.h"
56 #include "dyninstAPI/src/dyninstP.h"
57 #include "paradynd/src/metric.h"
58 #include "dyninstAPI/src/util.h"
59 #include "paradynd/src/comm.h"
60 #include "dyninstAPI/src/stats.h"
61 #include "paradynd/src/debugger.h"
62 #include "paradynd/src/main.h"
63 #include "paradynd/src/association.h"
64 #include "paradynd/src/init.h"
65 #include "paradynd/src/context.h"
66 #include "paradynd/src/perfStream.h"
67 #include "dyninstAPI/src/os.h"
68 #include "paradynd/src/mdld.h"
69 #include "paradynd/src/showerror.h"
70 #include "paradynd/src/main.h"
71 #include "util/h/debugOstream.h"
72
73 // trace data streams
74 #include "util/h/Dictionary.h"
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 string traceSocketPath; /* file path for trace socket */
86 int traceConnectInfo;
87 int traceSocketPort;
88
89 static void createResource(int pid, traceHeader *header, struct _newresource *r);
90 static void reportMemory(int pid, traceHeader *header, struct _traceMemory *r) ;
91
92 bool firstSampleReceived = false;
93
94 double cyclesPerSecond = 0;
95 extern time64 firstRecordTime; // util.C
96
97 // Read output data from process curr. 
98 void processAppIO(process *curr)
99 {
100     int ret;
101     char lineBuf[256];
102
103     ret = read(curr->ioLink, lineBuf, sizeof(lineBuf)-1);
104     if (ret < 0) {
105         //statusLine("read error");
106         //showErrorCallback(23, "Read error");
107         //cleanUpAndExit(-2);
108         string msg = string("Read error on IO stream from PID=") +
109                      string(curr->getPid()) + string(": ") +
110                      string(sys_errlist[errno]) + 
111                      string("\nNo more data will be received from this process.");
112         showErrorCallback(23, msg);
113         P_close(curr->ioLink);
114         curr->ioLink = -1;
115         return;
116     } else if (ret == 0) {
117         /* end of file -- process exited */
118         P_close(curr->ioLink);
119         curr->ioLink = -1;
120         string msg = string("Process ") + string(curr->getPid()) + string(" exited");
121         statusLine(msg.string_of());
122         handleProcessExit(curr,0);
123         return;
124     }
125
126     // null terminate it
127     lineBuf[ret] = '\0';
128
129     // forward the data to the paradyn process.
130     tp->applicationIO(curr->getPid(), ret, lineBuf);
131        // note: this is an async igen call, so the results may not appear right away.
132 }
133
134
135 char errorLine[1024];
136
137 void logLine(const char *line)
138 {
139     static char fullLine[1024];
140
141     strcat(fullLine, line);
142        // Ack!  Possible overflow!  Possible bug!
143        // If you put a '\n' at the end of every string passed to a call
144        // to logLine (and the string is < 1000 chars) then you'll be okay.
145        // Otherwise, watch out!
146
147     if (fullLine[strlen(fullLine)-1] == '\n') {
148         tp->applicationIO(0, strlen(fullLine), fullLine);
149         fullLine[0] = '\0';
150     }
151 }
152
153 void statusLine(const char *line)
154 {
155   tp->reportStatus(line);
156 }
157
158 // New with paradynd-->paradyn buffering.  When true, it tells the
159 // buffering routine to flush (to paradyn); otherwise, it would flush
160 // only when the buffer was full, hurting response time.
161 extern bool BURST_HAS_COMPLETED;
162
163 extern vector<process*> processVec;
164 extern process* findProcess(int); // should become a static method of class process
165
166 // trace data streams
167 extern bool TRACE_BURST_HAS_COMPLETED;
168 unsigned mid_hash(const unsigned &mid) {return mid;}
169 dictionary_hash<unsigned, unsigned> traceOn(mid_hash);
170
171 // Read trace data from process curr.
172 void processTraceStream(process *curr)
173 {
174     int ret;
175     traceStream sid;
176     char *recordData;
177     traceHeader header;
178     struct _association *a;
179
180     ret = read(curr->traceLink, &(curr->buffer[curr->bufEnd]), 
181                sizeof(curr->buffer)-curr->bufEnd);
182
183     if (ret < 0) {
184         //statusLine("read error, exiting");
185         //showErrorCallback(23, "Read error");
186         //curr->traceLink = -1;
187         //cleanUpAndExit(-2);
188         string msg = string("Read error on trace stream from PID=") +
189                      string(curr->getPid()) + string(": ") +
190                      string(sys_errlist[errno]) + 
191                      string("\nNo more data will be received from this process");
192         showErrorCallback(23, msg);
193         P_close(curr->traceLink);
194         curr->traceLink = -1;
195         return;
196     } else if (ret == 0) {
197         /* end of file */
198         // process exited unexpectedly
199         //string buffer = string("Process ") + string(curr->pid);
200         //buffer += string(" has exited unexpectedly");
201         //statusLine(P_strdup(buffer.string_of()));
202         //showErrorCallback(11, P_strdup(buffer.string_of()));
203         string msg = string("Process ") + string(curr->getPid()) + string(" exited");
204         statusLine(msg.string_of());
205         P_close(curr->traceLink);
206         curr->traceLink = -1;
207         handleProcessExit(curr,0);
208         return;
209     }
210
211     curr->bufEnd += ret;
212     curr->bufStart = 0;
213
214     while (curr->bufStart < curr->bufEnd) {
215         if (curr->bufEnd - curr->bufStart < (sizeof(traceStream) + sizeof(header))) {
216             break;
217         }
218
219         if (curr->bufStart % WORDSIZE != 0)     /* Word alignment check */
220             break;                      /* this will re-align by shifting */
221
222         memcpy(&sid, &(curr->buffer[curr->bufStart]), sizeof(traceStream));
223         curr->bufStart += sizeof(traceStream);
224
225         memcpy(&header, &(curr->buffer[curr->bufStart]), sizeof(header));
226         curr->bufStart += sizeof(header);
227
228         curr->bufStart = ALIGN_TO_WORDSIZE(curr->bufStart);
229         if (header.length % WORDSIZE != 0) {
230             sprintf(errorLine, "Warning: non-aligned length (%d) received on traceStream.  Type=%d\n", header.length, header.type);
231             logLine(errorLine);
232             showErrorCallback(36,(const char *) errorLine);
233         }
234             
235         if (curr->bufEnd - curr->bufStart < (unsigned)header.length) {
236             /* the whole record isn't here yet */
237             curr->bufStart -= sizeof(traceStream) + sizeof(header);
238             break;
239         }
240
241         recordData = &(curr->buffer[curr->bufStart]);
242         curr->bufStart +=  header.length;
243
244         if (!firstRecordTime)
245             firstRecordTime = header.wall;
246             // firstRecordTime is used by getCurrentTime() in util.C when arg passed
247             // is 'true', but noone seems to do that...so firstRecordTime is not a
248             // terribly important vrble (but, for now at least, it's used in metric.C)
249
250         // callback to paradyn (okay if this callback is done more than once; paradyn
251         // detects this.  This is important since right now, we're also gonna do this
252         // callback when starting a new process; needed since SHM_SAMPLING might well
253         // start sending samples to paradyn before any trace records were received
254         // here.)
255         static bool done_yet = false;
256         if (!done_yet) {
257            tp->firstSampleCallback(curr->getPid(), (double) (header.wall/1000000.0));
258            done_yet = true;
259         }
260         switch (header.type) {
261 #if defined(SHM_SAMPLING) && defined(MT_THREAD)
262             case TR_THREAD:
263                 createThread((traceThread *) ((void*)recordData));
264                 break;
265             case TR_THRSELF:
266                 updateThreadId((traceThrSelf *) ((void*)recordData));
267                 break;
268 #endif
269             case TR_NEW_RESOURCE:
270 //              cerr << "paradynd: received a new resource from pid " << curr->getPid() << "; processing now" << endl;
271                 createResource(curr->getPid(), &header, (struct _newresource *) ((void*)recordData));
272                    // createResource() is in this file, below
273                 break;
274
275             case TR_NEW_MEMORY:
276                 reportMemory(curr->getPid(), &header, (struct _traceMemory *) ((void*)recordData));
277                 break;
278
279             case TR_NEW_ASSOCIATION:
280                 a = (struct _association *) ((void*)recordData);
281                 newAssoc(curr, a->abstraction, a->type, a->key, a->value);
282                 break;
283
284 #ifndef SHM_SAMPLING
285             case TR_SAMPLE:
286                 // metric_cerr << "got something from pid " << curr->getPid() << endl;
287
288                  // sprintf(errorLine, "Got data from process %d\n", curr->getPid());
289                  // logLine(errorLine);
290 //              assert(curr->getFirstRecordTime());
291                 processSample(curr->getPid(), &header, (traceSample *) ((void*)recordData));
292                    // in metric.C
293                 firstSampleReceived = true;
294                 break;
295 #endif
296
297             case TR_EXIT:
298                 sprintf(errorLine, "process %d exited\n", curr->getPid());
299                 logLine(errorLine);
300                 printAppStats((struct endStatsRec *) ((void*)recordData),
301                               cyclesPerSecond);
302                 printDyninstStats();
303                 P_close(curr->traceLink);
304                 curr->traceLink = -1;
305                 handleProcessExit(curr, 0);
306                 break;
307
308 #ifndef SHM_SAMPLING
309             case TR_COST_UPDATE:
310                 processCost(curr, &header, (costUpdate *) ((void*)recordData));
311                    // in metric.C
312                 break;
313 #endif
314
315             case TR_CP_SAMPLE:
316                 // critical path sample
317                 extern void processCP(process *, traceHeader *, cpSample *);
318                 processCP(curr, &header, (cpSample *) recordData);
319                 break;
320
321             case TR_EXEC_FAILED:
322                 { int pid = *(int *)recordData;
323                   process *p = findProcess(pid);
324                   p->inExec = false;
325                   p->execFilePath = string("");
326                 }
327                 break;
328
329             case TR_DATA:
330                 extern void batchTraceData(int, int, int, char *);
331                 batchTraceData(0, sid, header.length, recordData);
332                 traceOn[sid] = 1;
333                 break;
334
335             default:
336                 sprintf(errorLine, "Got unknown record type %d on sid %d\n", 
337                     header.type, sid);
338                 logLine(errorLine);
339                 sprintf(errorLine, "Received bad trace data from process %d.", curr->getPid());
340                 showErrorCallback(37,(const char *) errorLine);
341         }
342     }
343     BURST_HAS_COMPLETED = true; // will force a batch-flush very soon
344
345     // trace data streams
346     for (unsigned w = 0; w<traceOn.keys().size(); w++) {
347         if (traceOn.values()[w]) {
348              extern void batchTraceData(int, int, int, char *);
349              int k;
350              TRACE_BURST_HAS_COMPLETED = true;
351              // will force a trace-batch-flush very soon
352              batchTraceData(0, (k = traceOn.keys()[w]), 0, (char *)NULL);
353              traceOn[k] = 0;
354              //sprintf(errorLine, "$$$Tag burst with mid %d\n", k);
355              //logLine(errorLine);
356         }
357     }
358
359     /* copy those bits we have to the base */
360     memcpy(curr->buffer, &(curr->buffer[curr->bufStart]), 
361         curr->bufEnd - curr->bufStart);
362     curr->bufEnd = curr->bufEnd - curr->bufStart;
363 }
364
365 void doDeferredRPCs() {
366    // Any RPCs waiting to be performed?  If so, and if it's safe to
367    // perform one, then launch one.
368    for (unsigned lcv=0; lcv < processVec.size(); lcv++) {
369       process *proc = processVec[lcv];
370       if (proc == NULL) continue; // proc must've died and has itself cleaned up
371       if (proc->status() == exited) continue;
372       if (proc->status() == neonatal) continue; // not sure if this is appropriate
373       
374       bool wasLaunched = proc->launchRPCifAppropriate(proc->status() == running);
375       // do we need to do anything with 'wasLaunched'?
376       if (wasLaunched) {
377          inferiorrpc_cerr << "launched an inferior RPC";
378          inferiorrpc_cerr << endl;
379       }
380    }
381 }
382
383
384 void ioFunc()
385 {
386      printf("in SIG child func\n");
387      fflush(stdout);
388 }
389
390 #ifdef SHM_SAMPLING
391 static void checkAndDoShmSampling(time64 &pollTimeUSecs) {
392    // We assume that nextShmSampleTime (synched to getCurrWallTime())
393    // has already been set.  If the curr time is >= to this, then
394    // we should sample immediately, and update nextShmSampleTime to
395    // be nextShmSampleTime + sampleInterval, unless it is <= currTime,
396    // in which case we set it to currTime + sampleInterval.
397
398    // QUESTION: should we sample a given process while it's paused?  While it's
399    //           in the middle of an inferiorRPC?  For now, the answer is no
400    //           to both.
401
402    static time64 nextMajorSampleTime = 0;
403    static time64 nextMinorSampleTime = 0;
404
405    const time64 currWallTime = getCurrWallTime();
406       // checks for rollback
407
408    bool doMajorSample = false; // so far...
409    bool doMinorSample = false; // so far...
410
411    bool forNextTimeDoMinorSample = false; // so far...
412
413    if (currWallTime >= nextMajorSampleTime)
414       doMajorSample = true;
415    else if (currWallTime >= nextMinorSampleTime)
416       doMinorSample = true;
417    else
418       // it's not time to do anything.
419       return;
420
421    // Do shared memory sampling (for all processes) now!
422
423    // Loop thru all processes.  For each, process inferiorIntCounters,
424    // inferiorWallTimers, and inferiorProcessTimers.  But don't
425    // sample while an inferiorRPC is pending for that process, or for
426    // a non-running process.
427
428    for (unsigned lcv=0; lcv < processVec.size(); lcv++) {
429       process *theProc = processVec[lcv];
430       if (theProc == NULL)
431          continue; // proc died & had its structures cleaned up
432
433       // Don't sample paused/exited/neonatal processes, or even running processes
434       // that haven't been bootstrapped yet (i.e. haven't called DYNINSTinit yet),
435       // or processes that are in the middle of an inferiorRPC (we like for
436       // inferiorRPCs to finish up quickly).
437       if (theProc->status_ != running) {
438          //shmsample_cerr << "(-" << theProc->getStatusAsString() << "-)";
439          continue;
440       }
441       else if (!theProc->isBootstrappedYet()) {
442          //shmsample_cerr << "(-*-)" << endl;
443          continue;
444       }
445       else if (theProc->existsRPCinProgress()) {
446          //shmsample_cerr << "(-~-)" << endl;
447          continue;
448       }
449
450       if (doMajorSample) {
451          //shmsample_cerr << "(-Y-)" << endl;
452
453          if (!theProc->doMajorShmSample(currWallTime)) {
454             // The major sample didn't complete all of its work, so we
455             // schedule a minor sample for sometime in the near future
456             // (before the next major sample)
457
458             shmsample_cerr << "a minor sample will be needed" << endl;
459
460             forNextTimeDoMinorSample = true;
461          }
462       }
463       else if (doMinorSample) {
464          shmsample_cerr << "trying needed minor sample..."; cerr.flush();
465
466          if (!theProc->doMinorShmSample()) {
467             // The minor sample didn't complete all of its work, so
468             // schedule another one.
469             forNextTimeDoMinorSample = true;
470
471             shmsample_cerr << "it failed" << endl; cerr.flush();
472          }
473          else {
474             shmsample_cerr << "it succeeded" << endl; cerr.flush();
475          }
476       }
477    } // loop thru the processes
478
479    // And now, do the internal metrics
480    if (doMajorSample)
481       reportInternalMetrics(true);
482
483    // Here, we should probably flush the batch buffer (whether for a major
484    // sample or a minor one)
485    extern void flush_batch_buffer(); // metric.C (should be in this file)
486    flush_batch_buffer();
487
488    // Take currSamplingRate (which has values such as 0.2, 0.4, 0.8, 1.6, etc.)
489    // and multiply by a million to get the # of usecs per sample.
490    extern float currSamplingRate; // dynrpc.C
491    assert(currSamplingRate > 0);
492    const time64 shmSamplingInterval =
493               (time64)((double)currSamplingRate * 1000000.0);
494
495    if (doMajorSample) {
496       // If we just did a major sample, then we schedule the next major sample,
497       // and reset the next minor sample time.
498       nextMajorSampleTime += shmSamplingInterval;
499       if (nextMajorSampleTime <= currWallTime)
500          nextMajorSampleTime = currWallTime + shmSamplingInterval;
501    }
502
503    if (forNextTimeDoMinorSample) {
504       // If a minor sample is needed, then we schedule it.  For now, let's
505       // assume that a minor sample is always scheduled for now plus
506       // one-fourth of the original sampling rate...i.e. for now + (0.2 sec/4) =
507       // now + (0.05 sec), i.e. now + 50 milliseconds.
508 // temp: one-tenth of original sample rate...i.e. for now + 0.02 sec (+20 millisec)
509
510 //      nextMinorSampleTime = currWallTime + 50000; // 50ms = 50000us
511       nextMinorSampleTime = currWallTime + 20000; // 20ms = 20000us
512       if (nextMinorSampleTime > nextMajorSampleTime)
513          // oh, never mind, we'll just do the major sample which is going to
514          // happen first anyway.
515          nextMinorSampleTime = nextMajorSampleTime;
516    }
517    else {
518       // we don't need to do a minor sample next time, so reset nextMinorSampleTime
519       nextMinorSampleTime = nextMajorSampleTime;
520    }
521
522    time64 nextAnyKindOfSampleTime = nextMajorSampleTime;
523    if (nextMinorSampleTime < nextAnyKindOfSampleTime)
524       nextAnyKindOfSampleTime = nextMinorSampleTime;
525
526    assert(nextAnyKindOfSampleTime >= currWallTime);
527    const time64 shmSamplingTimeout = nextAnyKindOfSampleTime - currWallTime;
528
529    if (shmSamplingTimeout < pollTimeUSecs)
530       pollTimeUSecs = shmSamplingTimeout;
531 }
532 #endif
533
534
535 /*
536  * Wait for a data from one of the inferiors or a request to come in.
537  *
538  */
539
540 void controllerMainLoop(bool check_buffer_first)
541 {
542     int ct;
543     int width;
544     fd_set readSet;
545     fd_set errorSet;
546     struct timeval pollTime;
547     int traceSocket_fd;
548
549     // TODO - i am the guilty party - this will go soon - mdc
550 #ifdef PARADYND_PVM
551 #ifdef notdef
552     int fd_num, *fd_ptr;
553     if (pvm_mytid() < 0)
554       {
555         printf("pvm not working\n");
556         _exit(-1);
557       }
558     fd_num = pvm_getfds(&fd_ptr);
559     assert(fd_num == 1);
560 #endif
561 #endif
562
563 //    cerr << "welcome to controllerMainLoop...pid=" << getpid() << endl;
564 //    kill(getpid(), SIGSTOP);
565 //    cerr << "doing controllerMainLoop..." << endl;
566
567
568     /***
569        set up a socket to be used to create a trace link
570        by inferior processes that are not forked 
571        directly by this daemon.
572        This is a unix domain socket, which is bound to the file
573           <P_tmpdir>/paradynd.<pid>
574        where <P_tmpdir> is a constant defined in stdio.h (usually "/tmp" or
575        "/usr/tmp"), and <pid> is the pid of the paradynd process.
576
577        This socket is currently being used in two cases: when a
578        process forks and when we attach to a running process.  In the
579        fork case, the socket path can be passed in the environment (so
580        any name for the file would be ok), but in the attach case the
581        name is passed as an argument to DYNINSTinit. Since we
582        currently can only pass integer values as arguments, we use the
583        file name paradynd.<pid>, so that we need only to pass the pid
584        as the argument to DYNINSTinit, which can then determine the
585        full file name.
586
587        traceSocket_fd is the file descriptor of a socket, ready to receive
588        connections.
589        It represents a socket created with socket(); listen()
590        In other words, one which we intend to call accept() on.
591        (See perfStream.C -- the call to RPC_getConnect(traceSocket_fd))
592     ***/
593
594 #if !defined(i386_unknown_nt4_0)
595     traceSocketPath = string(P_tmpdir) + string("paradynd.") + string(getpid());
596     // unlink it, in case the file was left around from a previous run
597     unlink(traceSocketPath.string_of());
598
599     if (!RPC_setup_socket_un(traceSocket_fd, traceSocketPath.string_of())) {
600       perror("paradynd -- can't setup socket");
601       cleanUpAndExit(-1);
602     }
603     traceConnectInfo = getpid();
604 #else
605     traceSocketPort = RPC_setup_socket(traceSocket_fd, PF_INET, SOCK_STREAM);
606     if (traceSocketPort < 0) {
607       perror("paradynd -- can't setup socket");
608       cleanUpAndExit(-1);
609     }
610     traceConnectInfo = traceSocketPort;
611 #endif
612
613
614     while (1) {
615 //        doSignals();
616
617         FD_ZERO(&readSet);
618         FD_ZERO(&errorSet);
619         width = 0;
620         unsigned p_size = processVec.size();
621         for (unsigned u=0; u<p_size; u++) {
622             if (processVec[u] == NULL)
623                continue;
624
625             if (processVec[u]->traceLink >= 0)
626               FD_SET(processVec[u]->traceLink, &readSet);
627             if (processVec[u]->traceLink > width)
628               width = processVec[u]->traceLink;
629
630             if (processVec[u]->ioLink >= 0)
631               FD_SET(processVec[u]->ioLink, &readSet);
632             if (processVec[u]->ioLink > width)
633               width = processVec[u]->ioLink;
634         }
635
636         // add traceSocket_fd, which accept()'s new connections (from processes
637         // not launched via createProcess() [process.C], such as when a process
638         // forks, or when we attach to an already-running process).
639         if (traceSocket_fd > 0) FD_SET(traceSocket_fd, &readSet);
640         if (traceSocket_fd > width) width = traceSocket_fd;
641
642         // add our igen connection with the paradyn process.
643         FD_SET(tp->get_fd(), &readSet);
644         FD_SET(tp->get_fd(), &errorSet);
645         if (tp->get_fd() > width) width = tp->get_fd();
646
647 #ifdef PARADYND_PVM
648         // add connection to pvm daemon.
649         /***
650           There is a problem here since pvm_getfds is not implemented on 
651           libpvmshmem which we use on solaris (a call to pvm_getfds returns
652           PvmNotImpl).
653           If we cannot use pvm_getfds here, the only alternative is to use polling.
654           To keep the code simple, I am using polling in all cases.
655         ***/
656 #ifdef notdef // not in use because pvm_getfds is not implemented on all platforms
657         fd_num = pvm_getfds(&fd_ptr);
658         assert(fd_num == 1);
659         FD_SET(fd_ptr[0], &readSet);
660         if (fd_ptr[0] > width)
661           width = fd_ptr[0];
662 #endif
663 #endif
664
665 #ifdef SHM_SAMPLING
666 // When _not_ shm sampling, rtinst defines a global vrble called
667 // DYNINSTin_sample, which is set to true while the application samples
668 // itself due to an alarm-expire.  When this variable is set, a call to
669 // DYNINSTstartProcessTimer() et al. will return immediately, taking
670 // no action.  This is of course a bad thing to happen.
671 // So: when not shm sampling, we mustn't do an inferiorRPC here.
672 // So we only do inferiorRPC here when SHM_SAMPLING.
673 // (What do we do when non-shm-sampling?  We wait until we're sure
674 // that we're not in the middle of processing a timer.  One way to do
675 // that is to manually reset DYNINSTin_sample when doing an RPC, and
676 // then restoring its initial value when done.  Instead, we wait for an
677 // ALARM signal to be delivered, and do pending RPCs just before we forward
678 // the signal.  Assuming ALARM signals aren't recursive, this should do the
679 // trick.  Ick...yet another reason to kill the ALARM signal and go with shm
680 // sampling.
681
682         doDeferredRPCs();
683 #endif
684         extern void doDeferedRPCasyncXDRWrite();
685         doDeferedRPCasyncXDRWrite();
686
687 #if !defined(i386_unknown_nt4_0)
688         time64 pollTimeUSecs = 50000;
689            // this is the time (rather arbitrarily) chosen fixed time length
690            // in which to check for signals, etc.
691 #else
692         // Windows NT wait happens in WaitForDebugEvent (in pdwinnt.C)
693         time64 pollTimeUSecs = 0;
694 #endif
695
696 #ifdef SHM_SAMPLING
697         checkAndDoShmSampling(pollTimeUSecs);
698            // does shm sampling of each process, as appropriate.
699            // may update pollTimeUSecs.
700 #endif 
701
702         pollTime.tv_sec  = pollTimeUSecs / 1000000;
703         pollTime.tv_usec = pollTimeUSecs % 1000000;
704
705         // This fd may have been read from prior to entering this loop
706         // There may be some bytes lying around
707         if (check_buffer_first) {
708           bool no_stuff_there = P_xdrrec_eof(tp->net_obj());
709           while (!no_stuff_there) {
710             T_dyninstRPC::message_tags ret = tp->waitLoop();
711             if (ret == T_dyninstRPC::error) {
712               // assume the client has exited, and leave.
713               cleanUpAndExit(-1);
714             }
715             no_stuff_there = P_xdrrec_eof(tp->net_obj());
716           }
717         }
718
719         // TODO - move this into an os dependent area
720         ct = P_select(width+1, &readSet, NULL, &errorSet, &pollTime);
721
722         if (ct > 0) {
723
724             if (traceSocket_fd >= 0 && FD_ISSET(traceSocket_fd, &readSet)) {
725               // Either (1) a process we're measuring has forked, and the child
726               // process is asking for a new connection, or (2) a process we've
727               // attached to is asking for a new connection.
728
729               processNewTSConnection(traceSocket_fd); // context.C
730             }
731
732             unsigned p_size = processVec.size();
733             for (unsigned u=0; u<p_size; u++) {
734                 if (processVec[u] == NULL)
735                    continue; // process structure has been deallocated
736
737                 if (processVec[u] && processVec[u]->traceLink >= 0 && 
738                        FD_ISSET(processVec[u]->traceLink, &readSet)) {
739                     processTraceStream(processVec[u]);
740
741                     /* in the meantime, the process may have died, setting
742                        processVec[u] to NULL */
743
744                     /* clear it in case another process is sharing it */
745                     if (processVec[u] &&
746                         processVec[u]->traceLink >= 0)
747                            // may have been set to -1
748                        FD_CLR(processVec[u]->traceLink, &readSet);
749                 }
750
751                 if (processVec[u] && processVec[u]->ioLink >= 0 && 
752                        FD_ISSET(processVec[u]->ioLink, &readSet)) {
753                     processAppIO(processVec[u]);
754
755                     // app can (conceivably) die in processAppIO(), resulting
756                     // in a processVec[u] to NULL.
757
758                     /* clear it in case another process is sharing it */
759                     if (processVec[u] && processVec[u]->ioLink >= 0)
760                        // may have been set to -1
761                        FD_CLR(processVec[u]->ioLink, &readSet);
762                 }
763             }
764
765             if (FD_ISSET(tp->get_fd(), &errorSet)) {
766                 // paradyn is gone so we go too.
767                 cleanUpAndExit(-1);
768             }
769
770             // Check if something has arrived from Paradyn on our igen link.
771             if (FD_ISSET(tp->get_fd(), &readSet)) {
772               bool no_stuff_there = false;
773               while(!no_stuff_there) {
774                 T_dyninstRPC::message_tags ret = tp->waitLoop();
775                 if (ret == T_dyninstRPC::error) {
776                   // assume the client has exited, and leave.
777                   cleanUpAndExit(-1);
778                 }
779                 no_stuff_there = P_xdrrec_eof(tp->net_obj());
780               }
781             }
782             while (tp->buffered_requests()) {
783               T_dyninstRPC::message_tags ret = tp->process_buffered();
784               if (ret == T_dyninstRPC::error)
785                 cleanUpAndExit(-1);
786             }
787
788 #ifdef PARADYND_PVM
789 #ifdef notdef // not in use because of the problems with pvm_getfds. See comment above.
790             // message on pvmd channel
791             int res;
792             fd_num = pvm_getfds(&fd_ptr);
793             assert(fd_num == 1);
794             if (FD_ISSET(fd_ptr[0], &readSet)) {
795                 // res == -1 --> error
796                 res = PDYN_handle_pvmd_message();
797                 // handle pvm message
798             }
799 #endif
800 #endif
801         }
802
803 #ifdef PARADYND_PVM
804         // poll for messages from the pvm daemon, and handle the message if 
805         // there is one.
806         // See comments above on the problems with pvm_getfds.
807         if (pvm_running) {
808           PDYN_handle_pvmd_message();
809         }
810 #endif
811
812 #ifndef SHM_SAMPLING
813         // the ifdef is here because when shm sampling, reportInternalMetrics is
814         // already done.
815         reportInternalMetrics(false);
816 #endif
817
818 //      doSignals();
819         extern void checkProcStatus(); // check status of inferior processes
820         checkProcStatus();
821     }
822 }
823
824
825 static void createResource(int pid, traceHeader *header, struct _newresource *r)
826 {
827     char *tmp;
828     char *name;
829     // resource *res;
830     vector<string> parent_name;
831     resource *parent = NULL;
832     unsigned type;
833     
834     switch (r->type) {
835     case RES_TYPE_STRING: type = MDL_T_STRING; break;
836     case RES_TYPE_INT:    type = MDL_T_INT; break;
837     default: 
838       string msg = string("Invalid resource type reported on trace stream from PID=")
839                    + string(pid);
840       showErrorCallback(36,msg);
841       return;
842     }
843
844     name = r->name;
845     do {
846         tmp = strchr(name, '/');
847         if (tmp) {
848             *tmp = '\0';
849             tmp++;
850             parent_name += name;
851             name = tmp;
852         }
853     } while (tmp);
854
855     if ((parent = resource::findResource(parent_name)) && name != r->name) {
856       resource::newResource(parent, NULL, r->abstraction, name,
857                             header->wall, "", type,
858                             true);
859     }
860     else {
861       string msg = string("Unknown resource '") + string(r->name) +
862                    string("' reported on trace stream from PID=") +
863                    string(pid);
864       showErrorCallback(36,msg);
865     }
866
867 }
868
869 // report a piece of shared-memory
870 static void reportMemory(int pid, traceHeader *header, struct _traceMemory *r)
871 {
872     char        *name   = r->name;
873     int         va = r->va ;
874     unsigned    memSize = r->memSize ;
875     unsigned    blkSize = r->blkSize ;
876
877     printf("reportMemory(%d, %s, %d, %u, %u)\n", pid, name, va, memSize, blkSize) ;
878     tp->resourceBatchMode(true);
879     tp->memoryInfoCallback(0, name, va, memSize, blkSize) ;
880     tp->resourceBatchMode(false);
881 }