force records to be word aligned.
[dyninst.git] / rtinst / src / RTunix.c
1 /*
2  * This file contains the implementation of runtime dynamic instrumentation
3  *   functions for a processor running UNIX.
4  *
5  * $Log: RTunix.c,v $
6  * Revision 1.4  1993/12/13 19:48:12  hollings
7  * force records to be word aligned.
8  *
9  * Revision 1.3  1993/10/19  15:29:58  hollings
10  * new simpler primitives.
11  *
12  * Revision 1.2  1993/08/26  19:43:17  hollings
13  * added uarea mapping code.
14  *
15  * Revision 1.1  1993/07/02  21:49:35  hollings
16  * Initial revision
17  *
18  *
19  */
20 #include <stdio.h>
21 #include <sys/time.h>
22 #include <sys/resource.h>
23 #include <sys/param.h>
24 #include <signal.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <assert.h>
28 #include <nlist.h>
29
30 #include <h/rtinst.h>
31 #include <h/trace.h>
32
33 #define MILLION 1000000
34 extern int DYNINSTmappedUarea;
35 extern int *_p_1, *_p_2;
36
37 time64 inline DYNINSTgetUserTime()
38 {
39     int first;
40     time64 now;
41     struct rusage ru;
42
43     if (DYNINSTmappedUarea) {
44 retry:
45          first = *_p_1;
46          now = first;
47          now *= MILLION;
48          now += *_p_2;
49          if (*_p_1 != first) goto retry;
50      } else {
51          getrusage(RUSAGE_SELF, &ru);
52          now = ru.ru_utime.tv_sec;
53          now *= MILLION;
54          now += ru.ru_utime.tv_usec;
55      }
56      return(now);
57 }
58
59 void DYNINSTbreakPoint()
60 {
61     kill(getpid(), SIGSTOP);
62 }
63
64 void DYNINSTstartProcessTimer(tTimer *timer)
65 {
66     time64 temp;
67
68     if (timer->counter == 0) {
69          temp = DYNINSTgetUserTime();
70          timer->start = temp;
71          timer->normalize = MILLION;
72     }
73     /* this must be last to prevent race conditions */
74     timer->counter++;
75 }
76
77 void DYNINSTstopProcessTimer(tTimer *timer)
78 {
79     time64 now;
80     struct rusage ru;
81
82     /* don't stop a counter that is not running */
83     if (!timer->counter) return;
84
85
86     /* Warning - there is a window between setting now, and mutex that
87           can cause time to go backwards by the time to execute the
88           instructions between these two points.  This is not a cummlative error
89           and should not affect samples.  This was done (rather than re-sampling
90           now because the cost of computing now is so high).
91     */
92     if (timer->counter == 1) {
93         now = DYNINSTgetUserTime();
94         timer->snapShot = now - timer->start + timer->total;
95         timer->mutex = 1;
96         timer->counter = 0;
97         timer->total = timer->snapShot;
98         timer->mutex = 0;
99         if (now < timer->start) {
100              getrusage(RUSAGE_SELF, &ru);
101              printf("end before start\n");
102              sigpause(0xffff);
103         }
104     } else {
105         timer->counter--;
106     }
107 }
108
109
110 void DYNINSTstartWallTimer(tTimer *timer)
111 {
112     struct timeval tv;
113
114     if (timer->counter == 0) {
115          gettimeofday(&tv, NULL);
116          timer->start = tv.tv_sec;
117          timer->start *= (time64) MILLION;
118          timer->start += tv.tv_usec;
119          timer->normalize = MILLION;
120     }
121     /* this must be last to prevent race conditions */
122     timer->counter++;
123 }
124
125 void DYNINSTstopWallTimer(tTimer *timer)
126 {
127     time64 now;
128     struct timeval tv;
129
130     /* don't stop a counter that is not running */
131     if (!timer->counter) return;
132
133     if (timer->counter == 1) {
134          gettimeofday(&tv, NULL);
135          now = tv.tv_sec;
136          now *= MILLION;
137          now += tv.tv_usec;
138          /* see note before StopProcess time for warning about this mutex */
139          timer->snapShot = timer->total + now - timer->start;
140          timer->mutex = 1;
141          timer->counter = 0;
142          timer->total = timer->snapShot;
143          timer->mutex = 0;
144     } else {
145         timer->counter--;
146     }
147 }
148
149 time64 startWall;
150
151 volatile int DYNINSTpauseDone = 0;
152
153 /*
154  * change the variable to let the process proceed.
155  *
156  */
157 void DYNINSTcontinueProcess()
158 {
159     DYNINSTpauseDone = 1;
160 }
161
162 /*
163  * pause the process and let only USR2 signal handlers run until a SIGUSR1.
164  *    arrives.
165  *
166  */
167 void DYNINSTpauseProcess()
168 {
169     int mask;
170     int sigs;
171
172     sigs = ((1 << (SIGUSR2-1)) | (1 << (SIGUSR1-1)) | (1 << (SIGTSTP-1)));
173     mask = ~sigs;
174     DYNINSTpauseDone = 0;
175     while (!DYNINSTpauseDone) {
176 #ifdef notdef
177        sigpause(mask);
178        // temporary busy wait until we figure out what the TSD is up to. 
179        printf("out of sigpuase\n");
180 #endif
181     }
182 }
183
184 void DYNINSTinit(int skipBreakpoint)
185 {
186     int val;
187     int sigs;
188     char *interval;
189     struct timeval tv;
190     struct sigvec alarmVector;
191     struct sigvec pauseVector;
192     extern void DYNINSTsampleValues();
193
194     startWall = 0;
195
196     /*
197      * Define the signal handlers for stopping a process.
198      *
199      */
200     pauseVector.sv_handler = DYNINSTpauseProcess;
201     sigs = ((1 << (SIGUSR2-1)) | (1 << (SIGUSR1-1)) | (1 << (SIGTSTP-1)));
202     pauseVector.sv_mask = ~sigs;
203     pauseVector.sv_flags = 0;
204     sigvec(SIGPROF, &pauseVector, NULL);
205
206     signal(SIGUSR1, DYNINSTcontinueProcess);
207
208     /* define the alarm signal vector. We block all signals while sampling.  
209      *  This prevents race conditions where signal handlers cause timers to 
210      *  be started and stopped.
211      */
212     alarmVector.sv_handler = DYNINSTsampleValues;
213     alarmVector.sv_mask = ~0;
214     alarmVector.sv_flags = 0;
215
216
217     if (interval = (char *) getenv("DYNINSTsampleInterval")) {
218         sigvec(SIGALRM, &alarmVector, NULL);
219         val = atoi(interval);
220         ualarm(val, val);
221     } 
222
223     DYNINSTmappedUarea = DYNINSTmapUarea();
224
225     /*
226      * pause the process and wait for additional info.
227      *
228      */
229     if (!skipBreakpoint) DYNINSTbreakPoint();
230 }
231
232 void DYNINSTexit()
233 {
234 }
235
236 /*
237  * generate a trace record onto the named stream.
238  *
239  */
240 void DYNINSTgenerateTraceRecord(traceStream sid, short type, short length,
241     void *eventData)
242 {
243     int ret;
244     int count;
245     struct rusage ru;
246     struct timeval tv;
247     char buffer[1024];
248     traceHeader header;
249     static Boolean pipeGone = False;
250
251     if (pipeGone) return;
252
253     gettimeofday(&tv, NULL);
254     header.wall = tv.tv_sec;
255     header.wall *= (time64) MILLION;
256     header.wall += tv.tv_usec;
257     header.wall -= startWall;
258
259 #ifdef notdef
260     if (DYNINSTmappedUarea) {
261         header.process = *_p_1;
262         header.process *= MILLION;
263         header.process += *_p_2;
264     } else {
265 #endif
266         getrusage(RUSAGE_SELF, &ru);
267         header.process = ru.ru_utime.tv_sec;
268         header.process *= (time64) MILLION;
269         header.process += ru.ru_utime.tv_usec;
270 #ifdef notdef
271     }
272 #endif
273
274     /* round length off to a word aligned unit */
275     length = ALIGN_TO_WORDSIZE(length);
276
277     header.type = type;
278     header.length = length;
279     count = 0;
280     memcpy(&buffer[count], &sid, sizeof(traceStream));
281     count += sizeof(traceStream);
282
283     memcpy(&buffer[count], &header, sizeof(header));
284     count += sizeof(header);
285
286     memcpy(&buffer[count], eventData, length);
287     count += length;
288
289     /* on this platorm, we have a pipe to the controller process */
290     ret = write(CONTROLLER_FD, buffer, count);
291     if (ret != count) {
292         extern char *sys_errlist[];
293         (void) close(CONTROLLER_FD);
294
295         printf("unable to write trace record %s\n", sys_errlist[errno]);
296         printf("disabling further data logging\n");
297         pipeGone = True;
298     }
299 }
300
301 time64 lastValue[200];
302 double lastTime[200];
303
304 void DYNINSTreportTimer(tTimer *timer)
305 {
306     int i,j;
307     time64 now;
308     double value;
309     time64 total;
310     struct rusage ru;
311     struct timeval tv;
312     traceSample sample;
313
314     if (timer->mutex) {
315         total = timer->snapShot;
316     } else if (timer->counter) {
317         /* timer is running */
318         if (timer->type == processTime) {
319             getrusage(RUSAGE_SELF, &ru);
320             now = ru.ru_utime.tv_sec;
321             now *= (time64) MILLION;
322             now += ru.ru_utime.tv_usec;
323         } else {
324             gettimeofday(&tv, NULL);
325             now = tv.tv_sec;
326             now *= MILLION;
327             now += tv.tv_usec;
328         }
329         total = now - timer->start + timer->total;
330     } else {
331         total = timer->total;
332     }
333     if (total < lastValue[timer->id.id]) {
334          printf("time regressed\n");
335          sigpause(0xffff);
336     }
337     lastValue[timer->id.id] = total;
338
339     sample.id = timer->id;
340     sample.value = ((double) total) / (double) timer->normalize;
341
342     DYNINSTgenerateTraceRecord(0, TR_SAMPLE, sizeof(sample), &sample);
343     /* printf("raw sample %d = %f\n", sample.id.id, sample.value); */
344 }
345
346 DYNINSTfork(void *arg, int pid)
347 {
348     int sid = 0;
349     traceFork forkRec;
350
351     printf("fork called with pid = %d\n", pid);
352     if (pid > 0) {
353         forkRec.ppid = getpid();
354         forkRec.pid = pid;
355         forkRec.npids = 1;
356         forkRec.stride = 0;
357         DYNINSTgenerateTraceRecord(sid,TR_FORK,sizeof(forkRec), &forkRec);
358     } else {
359         /* set up signals and stop at a break point */
360         DYNINSTinit(1);
361         sigpause();
362     }
363 }