update rtinst library to add hardware level wall and cpu time retrieval funcs
[dyninst.git] / rtinst / src / RTetc-linux.c
1 /*
2  * Copyright (c) 1996-2000 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 /* $Id: RTetc-linux.c,v 1.19 2000/11/20 23:17:07 schendel Exp $ */
43
44 /************************************************************************
45  * RTetc-linux.c: clock access functions, etc.
46 ************************************************************************/
47
48 #include <signal.h>
49 #include <stdlib.h>
50 #include <sys/times.h>
51 #include <sys/resource.h>
52 #include <assert.h>
53 #include <sys/syscall.h>
54 #include <sys/ptrace.h>
55 #include <unistd.h>
56 #include <math.h>
57 #include <errno.h>
58
59 #include <sys/procfs.h> /* /proc PIOCUSAGE */
60 #include <stdio.h>
61 #include <fcntl.h> /* O_RDONLY */
62 /* #include <sigcontext.h> - included in signal.h */
63 #include <unistd.h> /* getpid() */
64 #include <limits.h>
65
66 #include "rtinst/h/rtinst.h"
67 #include "rtinst/h/trace.h"
68 #include "rtinst/h/RThwtimer-x86.h"
69
70 #if defined(SHM_SAMPLING) && defined(MT_THREAD)
71 #include <thread.h>
72 #endif
73
74 /*extern int    gettimeofday(struct timeval *, struct timezone *);*/
75 extern void perror(const char *);
76
77 /************************************************************************
78  * symbolic constants.
79 ************************************************************************/
80
81 static int procfd = -1;
82 #ifdef HRTIME
83 struct hrtime_struct *hr_cpu_map = NULL;
84 #endif
85
86 /* PARADYNos_init formerly "void DYNINSTgetCPUtimeInitialize(void)" */
87 void PARADYNos_init(int calledByFork, int calledByAttach) {
88 #ifdef HRTIME
89   if(isLibhrtimeAvail(&hr_cpu_map, (int)getpid()))
90     hintBestCpuTimerLevel  = HARDWARE_TIMER_LEVEL;
91   else
92 #endif
93     hintBestCpuTimerLevel  = SOFTWARE_TIMER_LEVEL;
94   if(isTSCAvail()) {
95     hintBestWallTimerLevel = HARDWARE_TIMER_LEVEL;   
96   }
97   else {
98     hintBestWallTimerLevel = SOFTWARE_TIMER_LEVEL;
99   }
100
101 #ifdef notdef   /* Has this ever been active on this platform? */
102    /* This must be done once for each process (including forked) children */
103     char str[20];
104     sprintf(str, "/proc/%d", (int)getpid());
105    /* have to use syscall here for applications that have their own
106       versions of open, poll...In these cases there is no guarentee that
107       things have been initialized so that the application's version of
108       open can be used when this open call occurs (in DYNINSTinit)
109    */
110     procfd = _syscall2(SYS_open,str, O_RDONLY);
111     if (procfd < 0) {
112       fprintf(stderr, "open of /proc failed in PARADYNos_init\n");
113       perror("open");
114       abort();
115     }
116 #endif
117 }
118
119 static unsigned long long mulMillion(unsigned long long in) {
120    unsigned long long result = in;
121
122    /* multiply by 125 by multiplying by 128 and subtracting 3x */
123    result = (result << 7) - result - result - result;
124
125    /* multiply by 125 again, for a total of 15625x */
126    result = (result << 7) - result - result - result;
127
128    /* multiply by 64, for a total of 1,000,000x */
129    result <<= 6;
130
131    /* cost was: 3 shifts and 6 subtracts
132     * cost of calling mul1000(mul1000()) would be: 6 shifts and 4 subtracts
133     *
134     * Another algorithm is to multiply by 2^6 and then 5^6.
135     * The former is super-cheap (one shift); the latter is more expensive.
136     * 5^6 = 15625 = 16384 - 512 - 256 + 8 + 1
137     * so multiplying by 5^6 means 4 shift operations and 4 add/sub ops
138     * so multiplying by 1000000 means 5 shift operations and 4 add/sub ops.
139     * That may or may not be cheaper than what we're doing (3 shifts; 6 subtracts);
140     * I'm not sure.  --ari
141     */
142
143    return result;
144 }
145
146 /*static int MaxRollbackReport = 0; /* don't report any rollbacks! */
147 /*static int MaxRollbackReport = 1; /* only report 1st rollback */
148 static int MaxRollbackReport = INT_MAX; /* report all rollbacks */
149
150
151 /* --- CPU time retrieval functions --- */
152 /* Hardware Level ---
153    method:      libhrtime get_hrvtime()
154    return unit: ticks
155 */
156 rawTime64 
157 DYNINSTgetCPUtime_hw(void) {
158   static rawTime64 cpuPrevious=0;
159   static int cpuRollbackOccurred=0;
160   rawTime64 now=0, tmp_cpuPrevious=cpuPrevious;
161
162 #ifdef HRTIME  
163   now = hrtimeGetVtime(hr_cpu_map);
164 #endif
165
166   if (now < tmp_cpuPrevious) {
167     if (cpuRollbackOccurred < MaxRollbackReport) {
168       rtUIMsg traceData;
169       sprintf(traceData.msgString, "CPU time rollback %lld with current time: "
170               "%lld ticks, using previous value %lld ticks.",
171               tmp_cpuPrevious-now, now, tmp_cpuPrevious);
172       traceData.errorNum = 112;
173       traceData.msgType = rtWarning;
174       DYNINSTgenerateTraceRecord(0, TR_ERROR, sizeof(traceData),
175                                  &traceData, 1, 1, 1);
176     }
177     cpuRollbackOccurred++;
178     now = cpuPrevious;
179   }
180   else  cpuPrevious = now;
181   
182   return now;
183 }
184
185 /* Software Level --- 
186    method:      times()
187    return unit: jiffies  
188 */
189 rawTime64
190 DYNINSTgetCPUtime_sw(void) {
191   static rawTime64 cpuPrevious=0;
192   static int cpuRollbackOccurred=0;
193   rawTime64 now=0, tmp_cpuPrevious=cpuPrevious;
194   struct tms tm;
195   
196   times( &tm );
197   now = (rawTime64)tm.tms_utime + (rawTime64)tm.tms_stime;
198   
199   if (now < tmp_cpuPrevious) {
200     if (cpuRollbackOccurred < MaxRollbackReport) {
201       rtUIMsg traceData;
202       sprintf(traceData.msgString, "CPU time rollback %lld with current time: "
203               "%lld jiffies, using previous value %lld jiffies.",
204               tmp_cpuPrevious-now, now, tmp_cpuPrevious);
205       traceData.errorNum = 112;
206       traceData.msgType = rtWarning;
207       DYNINSTgenerateTraceRecord(0, TR_ERROR, sizeof(traceData),
208                                  &traceData, 1, 1, 1);
209     }
210     cpuRollbackOccurred++;
211     now = cpuPrevious;
212   }
213   else  cpuPrevious = now;
214   
215   return now;
216 }
217
218
219 /* --- Wall time retrieval functions --- */
220 /* Hardware Level ---
221    method:      direct read of TSC (ie. time stamp counter) register
222    return unit: ticks
223 */
224 rawTime64
225 DYNINSTgetWalltime_hw(void) {
226   static rawTime64 wallPrevious=0;
227   static int wallRollbackOccurred=0;
228   rawTime64 now, tmp_wallPrevious=wallPrevious;
229   struct timeval tv;
230
231   now = getTSC();
232
233   if (now < tmp_wallPrevious) {
234     if (wallRollbackOccurred < MaxRollbackReport) {
235       rtUIMsg traceData;
236       sprintf(traceData.msgString,"Wall time rollback %lld with current time: "
237               "%lld ticks, using previous value %lld ticks.",
238                 tmp_wallPrevious-now, now, tmp_wallPrevious);
239       traceData.errorNum = 112;
240       traceData.msgType = rtWarning;
241       DYNINSTgenerateTraceRecord(0, TR_ERROR, sizeof(traceData), &traceData, 
242                                1, 1, 1);
243     }
244     wallRollbackOccurred++;
245     wallPrevious = now;
246   }
247   else  wallPrevious = now;
248
249   return now;
250 }
251
252 /* Software Level --- 
253    method:      gettimeofday()
254    return unit: microseconds
255 */
256 rawTime64
257 DYNINSTgetWalltime_sw(void) {
258   static rawTime64 wallPrevious=0;
259   static int wallRollbackOccurred=0;
260   rawTime64 now, tmp_wallPrevious=wallPrevious;
261   struct timeval tv;
262
263   if (gettimeofday(&tv,NULL) == -1) {
264     perror("gettimeofday");
265     assert(0);
266     abort();
267   }
268   
269   now = mulMillion( (rawTime64)tv.tv_sec );
270   now += (rawTime64)tv.tv_usec;
271
272   if (now < tmp_wallPrevious) {
273     if (wallRollbackOccurred < MaxRollbackReport) {
274       rtUIMsg traceData;
275       sprintf(traceData.msgString,"Wall time rollback %lld with current time: "
276               "%lld usecs, using previous value %lld usecs.",
277                 tmp_wallPrevious-now, now, tmp_wallPrevious);
278       traceData.errorNum = 112;
279       traceData.msgType = rtWarning;
280       DYNINSTgenerateTraceRecord(0, TR_ERROR, sizeof(traceData), &traceData, 
281                                1, 1, 1);
282     }
283     wallRollbackOccurred++;
284     wallPrevious = now;
285   }
286   else  wallPrevious = now;
287
288   return(now);
289 }
290
291
292 #if defined(SHM_SAMPLING) && defined(MT_THREAD)
293 extern unsigned DYNINST_hash_lookup(unsigned key);
294 extern unsigned DYNINST_initialize_done;
295 extern void DYNINST_initialize_hash(unsigned total);
296 extern void DYNINST_initialize_free(unsigned total);
297 extern unsigned DYNINST_hash_insert(unsigned k);
298
299 int DYNINSTthreadSelf(void) {
300   return(thr_self());
301 }
302
303 int DYNINSTthreadPos(void) {
304   if (initialize_done) {
305     return(DYNINST_hash_lookup(DYNINSTthreadSelf()));
306   } else {
307     DYNINST_initialize_free(MAX_NUMBER_OF_THREADS);
308     DYNINST_initialize_hash(MAX_NUMBER_OF_THREADS);
309     DYNINST_initialize_done=1;
310     return(DYNINST_hash_insert(DYNINSTthreadSelf()));
311   }
312 }
313 #endif
314
315