Changing PIOCUSAGE by PIOCPSINFO in the /proc call to compute the CPU time
[dyninst.git] / rtinst / src / RTetc-solaris.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  * RTsolaris.c: clock access functions for solaris-2.
44 ************************************************************************/
45
46 #include <signal.h>
47 #include <sys/ucontext.h>
48 #include <sys/time.h>
49 #include <assert.h>
50 #include <sys/syscall.h>
51
52 #include <sys/procfs.h> /* /proc PIOCUSAGE */
53 #include <stdio.h>
54 #include <fcntl.h> /* O_RDONLY */
55 #include <unistd.h> /* getpid() */
56
57 #include "rtinst/h/rtinst.h"
58
59 #if defined(SHM_SAMPLING) && defined(MT_THREAD)
60 #include <thread.h>
61 #endif
62
63 /*extern int    gettimeofday(struct timeval *, struct timezone *);*/
64 extern void perror(const char *);
65
66 \f
67
68
69 /************************************************************************
70  * symbolic constants.
71 ************************************************************************/
72
73 static const double NANO_PER_USEC = 1.0e3;
74 static const double MILLION       = 1.0e6;
75
76
77 \f
78
79
80 /************************************************************************
81  * void DYNINSTos_init(void)
82  *
83  * os initialization function---currently null.
84 ************************************************************************/
85
86 void
87 DYNINSTos_init(int calledByFork, int calledByAttach) {
88
89     /*
90        Install trap handler.
91        This is currently being used only on the x86 platform.
92     */
93 #ifdef i386_unknown_solaris2_5
94     void DYNINSTtrapHandler(int sig, siginfo_t *info, ucontext_t *uap);
95     struct sigaction act;
96     act.sa_handler = DYNINSTtrapHandler;
97     act.sa_flags = 0;
98     sigfillset(&act.sa_mask);
99     if (sigaction(SIGTRAP, &act, 0) != 0) {
100         perror("sigaction(SIGTRAP)");
101         assert(0);
102         abort();
103     }
104 #endif
105
106
107 }
108
109
110 \f
111
112
113 /************************************************************************
114  * time64 DYNINSTgetCPUtime(void)
115  *
116  * get the total CPU time used for "an" LWP of the monitored process.
117  * this functions needs to be rewritten if a per-thread CPU time is
118  * required.  time for a specific LWP can be obtained via the "/proc"
119  * filesystem.
120  * return value is in usec units.
121  *
122  * XXXX - This should really return time in native units and use normalize.
123  *      conversion to float and division are way too expensive to
124  *      do everytime we want to read a clock (slows this down 2x) -
125  *      jkh 3/9/95
126 ************************************************************************/
127
128
129 static unsigned long long div1000(unsigned long long in) {
130    /* Divides by 1000 without an integer division instruction or library call, both of
131     * which are slow.
132     * We do only shifts, adds, and subtracts.
133     *
134     * We divide by 1000 in this way:
135     * multiply by 1/1000, or multiply by (1/1000)*2^30 and then right-shift by 30.
136     * So what is 1/1000 * 2^30?
137     * It is 1,073,742.   (actually this is rounded)
138     * So we can multiply by 1,073,742 and then right-shift by 30 (neat, eh?)
139     *
140     * Now for multiplying by 1,073,742...
141     * 1,073,742 = (1,048,576 + 16384 + 8192 + 512 + 64 + 8 + 4 + 2)
142     * or, slightly optimized:
143     * = (1,048,576 + 16384 + 8192 + 512 + 64 + 16 - 2)
144     * for a total of 8 shifts and 6 add/subs, or 14 operations.
145     *
146     */
147
148    unsigned long long temp = in << 20; // multiply by 1,048,576
149       // beware of overflow; left shift by 20 is quite a lot.
150       // If you know that the input fits in 32 bits (4 billion) then
151       // no problem.  But if it's much bigger then start worrying...
152
153    temp += in << 14; // 16384
154    temp += in << 13; // 8192
155    temp += in << 9;  // 512
156    temp += in << 6;  // 64
157    temp += in << 4;  // 16
158    temp -= in >> 2;  // 2
159
160    return (temp >> 30); // divide by 2^30
161 }
162
163 static unsigned long long divMillion(unsigned long long in) {
164    /* Divides by 1,000,000 without an integer division instruction or library call,
165     * both of which are slow.
166     * We do only shifts, adds, and subtracts.
167     *
168     * We divide by 1,000,000 in this way:
169     * multiply by 1/1,000,000, or multiply by (1/1,000,000)*2^30 and then right-shift
170     * by 30.  So what is 1/1,000,000 * 2^30?
171     * It is 1,074.   (actually this is rounded)
172     * So we can multiply by 1,074 and then right-shift by 30 (neat, eh?)
173     *
174     * Now for multiplying by 1,074
175     * 1,074 = (1024 + 32 + 16 + 2)
176     * for a total of 4 shifts and 4 add/subs, or 8 operations.
177     *
178     * Note: compare with div1000 -- it's cheaper to divide by a million than
179     *       by a thousand (!)
180     *
181     */
182
183    unsigned long long temp = in << 10; // multiply by 1024
184       // beware of overflow...if the input arg uses more than 52 bits
185       // than start worrying about whether (in << 10) plus the smaller additions
186       // we're gonna do next will fit in 64...
187
188    temp += in << 5; // 32
189    temp += in << 4; // 16
190    temp += in << 1; // 2
191
192    return (temp >> 30); // divide by 2^30
193 }
194
195 static unsigned long long mulMillion(unsigned long long in) {
196    unsigned long long result = in;
197
198    /* multiply by 125 by multiplying by 128 and subtracting 3x */
199    result = (result << 7) - result - result - result;
200
201    /* multiply by 125 again, for a total of 15625x */
202    result = (result << 7) - result - result - result;
203
204    /* multiply by 64, for a total of 1,000,000x */
205    result <<= 6;
206
207    /* cost was: 3 shifts and 6 subtracts
208     * cost of calling mul1000(mul1000()) would be: 6 shifts and 4 subtracts
209     *
210     * Another algorithm is to multiply by 2^6 and then 5^6.
211     * The former is super-cheap (one shift); the latter is more expensive.
212     * 5^6 = 15625 = 16384 - 512 - 256 + 8 + 1
213     * so multiplying by 5^6 means 4 shift operations and 4 add/sub ops
214     * so multiplying by 1000000 means 5 shift operations and 4 add/sub ops.
215     * That may or may not be cheaper than what we're doing (3 shifts; 6 subtracts);
216     * I'm not sure.  --ari
217     */
218
219    return result;
220 }
221
222 static int firstTime = 1; /* boolean */
223 static int procfd = -1;
224
225 void DYNINSTgetCPUtimeInitialize(void) {
226    /* This stuff is done just once */
227    char str[20];
228
229    sprintf(str, "/proc/%d", (int)getpid());
230    // have to use syscall here for applications that have their own
231    // versions of open, poll...In these cases there is no guarentee that
232    // things have been initialized so that the application's version of
233    // open can be used when this open call occurs (in DYNINSTinit)
234    procfd = syscall(SYS_open,str, O_RDONLY);
235    if (procfd < 0) {
236       fprintf(stderr, "open of /proc failed in DYNINSTgetCPUtimeInitialize\n");
237       perror("open");
238       abort();
239    }
240 }
241
242 time64
243 DYNINSTgetCPUtime(void) {
244   static time64 previous=0;
245
246   while (1) {
247 /* gethrvtime()/1000 doesn't work right any more with shm sampling because it
248  * returns values that are out of sync with /proc's PIOCUSAGE, so when a fudge
249  * factor needs to be added by paradynd's shm sampling of an active timer,
250  * things don't work.  getrusage() does seem to work okay, but we'd like to not use
251  * getrusage() because it's obsolete in solaris and slower; so we use /proc PIOCUSAGE...
252  *
253  * This is too bad; we'd prefer the (presumably fast) gethrvtime().  But, again,
254  * it simply won't work with shm sampling.  If you are thinking of changing things
255  * back to gethrvtime(), please check with me first. --ari
256  *
257  * Some day........in an ideal world, we'll use the %TICK register......
258  */
259
260 /*     time64 now = (time64)gethrvtime()/(time64)1000; */
261
262      struct prpsinfo theUsage; /* for /proc PIOCPSINFO call */
263      time64 now;
264
265      if (firstTime) {
266         DYNINSTgetCPUtimeInitialize();
267         firstTime = 0;
268      }
269
270      if (ioctl(procfd, PIOCPSINFO, &theUsage) < 0) {
271         perror("rtinst get-cpu-time PIOCPSINFO");
272         abort();
273      }
274
275      now = mulMillion(theUsage.pr_time.tv_sec); /* sec to usec */
276      now += div1000(theUsage.pr_time.tv_nsec);  /* nsec to usec */
277
278      if (now < previous)
279         /* I don't think that this ever happens for solaris, thankfully */
280         /* ...well, it can happen if we use PIOCRUSAGE! - naim 5/29/97 */
281         continue;
282
283      previous = now;
284      return (now);
285   }
286 }
287
288
289 \f
290
291
292 /************************************************************************
293  * time64 DYNINSTgetWalltime(void)
294  *
295  * get the total walltime used by the monitored process.
296  * return value is in usec units.
297 ************************************************************************/
298
299 time64
300 DYNINSTgetWalltime(void) {
301   static time64 previous=0;
302   time64 now;
303
304   while (1) {
305     struct timeval tv;
306     if (gettimeofday(&tv,NULL) == -1) {
307         perror("gettimeofday");
308         assert(0);
309         abort();
310     }
311
312     now = mulMillion(tv.tv_sec) + tv.tv_usec;
313 //    now = (time64)tv.tv_sec*(time64)1000000 + (time64)tv.tv_usec;
314
315     if (now < previous) continue;
316     previous = now;
317     return(now);
318   }
319 }
320
321 #if defined(SHM_SAMPLING) && defined(MT_THREAD)
322 extern unsigned DYNINST_hash_lookup(unsigned key);
323 extern unsigned DYNINST_initialize_done;
324 extern void DYNINST_initialize_hash(unsigned total);
325 extern void DYNINST_initialize_free(unsigned total);
326 extern unsigned DYNINST_hash_insert(unsigned k);
327
328 int DYNINSTthreadSelf(void) {
329   return(thr_self());
330 }
331
332 int DYNINSTthreadPos(void) {
333   if (initialize_done) {
334     return(DYNINST_hash_lookup(DYNINSTthreadSelf()));
335   } else {
336     DYNINST_initialize_free(MAX_NUMBER_OF_THREADS);
337     DYNINST_initialize_hash(MAX_NUMBER_OF_THREADS);
338     DYNINST_initialize_done=1;
339     return(DYNINST_hash_insert(DYNINSTthreadSelf()));
340   }
341 }
342 #endif
343
344
345 /****************************************************************************
346    The trap handler. Currently being used only on x86 platform.
347
348    Traps are used when we can't insert a jump at a point. The trap
349    handler looks up the address of the base tramp for the point that
350    uses the trap, and set the pc to this base tramp.
351    The paradynd is responsible for updating the tramp table when it
352    inserts instrumentation.
353 *****************************************************************************/
354
355 #ifdef i386_unknown_solaris2_5
356 trampTableEntry DYNINSTtrampTable[TRAMPTABLESZ];
357 unsigned DYNINSTtotalTraps = 0;
358
359 static unsigned lookup(unsigned key) {
360     unsigned u;
361     unsigned k;
362     for (u = HASH1(key); 1; u = (u + HASH2(key)) % TRAMPTABLESZ) {
363       k = DYNINSTtrampTable[u].key;
364       if (k == 0)
365         return 0;
366       else if (k == key)
367         return DYNINSTtrampTable[u].val;
368     }
369     /* not reached */
370     assert(0);
371     abort();
372 }
373
374 void DYNINSTtrapHandler(int sig, siginfo_t *info, ucontext_t *uap) {
375     unsigned pc = uap->uc_mcontext.gregs[PC];
376     unsigned nextpc = lookup(pc);
377
378     if (!nextpc) {
379       /* kludge: maybe the PC was not automatically adjusted after the trap */
380       /* this happens for a forked process */
381       pc--;
382       nextpc = lookup(pc);
383     }
384
385     if (nextpc) {
386       uap->uc_mcontext.gregs[PC] = nextpc;
387     } else {
388       assert(0);
389       abort();
390     }
391     DYNINSTtotalTraps++;
392 }
393 #endif
394
395
396