1 #include "cado.h" // IWYU pragma: keep
2 #include <utility> // pair
3 // IWYU pragma: no_include <bits/types/struct_rusage.h>
4 #include <pthread.h>
5 #include <cstdio>       // FILE // IWYU pragma: keep
6 #ifdef HAVE_RESOURCE_H
7 #include <sys/resource.h>	/* for cputime */
8 #endif
9 #include <sys/time.h>	/* for gettimeofday */
10 #include "timing.h"
11 #include "memusage.h"
12 
13 #ifdef HAVE_GETRUSAGE
14 /* I'm including some STL code for the timer info layer, but this could
15  * equally well be done in C */
16 #include <map>
17 #include <string>
18 #endif
19 
20 /* return total user time (all threads) */
21 uint64_t
microseconds(void)22 microseconds (void)
23 {
24 #ifdef HAVE_GETRUSAGE
25     struct rusage res[1];
26     getrusage(RUSAGE_SELF, res);
27     uint64_t r;
28     r = (uint64_t) res->ru_utime.tv_sec;
29     r *= (uint64_t) 1000000UL;
30     r += (uint64_t) res->ru_utime.tv_usec;
31     return r;
32 #else
33     return 0;
34 #endif
35 }
36 
37 /* only consider user time of the current thread */
38 uint64_t
microseconds_thread(void)39 microseconds_thread (void)
40 {
41 #ifdef HAVE_GETRUSAGE
42     struct rusage ru[1];
43     uint64_t r;
44 
45 #ifdef HAVE_RUSAGE_THREAD
46     getrusage (RUSAGE_THREAD, ru);
47 #else
48     getrusage (RUSAGE_SELF, ru);
49 #endif
50     r = (uint64_t) ru->ru_utime.tv_sec;
51     r *= (uint64_t) 1000000UL;
52     r += (uint64_t) ru->ru_utime.tv_usec;
53     return r;
54 #else
55     return 0;
56 #endif
57 }
58 
59 /* cputime */
60 unsigned long
milliseconds(void)61 milliseconds (void)
62 {
63     return (unsigned long) (microseconds() / (uint64_t) 1000);
64 }
65 
66 /* cputime */
67 unsigned long
milliseconds_thread(void)68 milliseconds_thread (void)
69 {
70     return (unsigned long) (microseconds_thread() / (uint64_t) 1000);
71 }
72 
73 double
seconds(void)74 seconds (void)
75 {
76     return (double) microseconds() / 1.0e6;
77 }
78 
79 double
seconds_thread(void)80 seconds_thread (void)
81 {
82 #ifdef HAVE_GETRUSAGE
83     struct rusage ru[1];
84 #ifdef HAVE_RUSAGE_THREAD
85     getrusage (RUSAGE_THREAD, ru);
86 #else
87     getrusage (RUSAGE_SELF, ru);
88 #endif
89     double r = 1.0e-6 * ru->ru_utime.tv_usec;
90     r += ru->ru_utime.tv_sec;
91     return r;
92 #else
93     return 0;
94 #endif
95 }
96 
97 void
seconds_user_sys(double * res)98 seconds_user_sys (double * res)
99 {
100 #ifdef HAVE_GETRUSAGE
101     struct rusage ru[1];
102 
103     getrusage (RUSAGE_SELF, ru);
104     res[0] = (double)ru->ru_utime.tv_sec + (double)ru->ru_utime.tv_usec/1.0e6;
105     res[1] = (double)ru->ru_stime.tv_sec + (double)ru->ru_stime.tv_usec/1.0e6;
106 #else
107     res[0] = res[1] = 0.;
108 #endif
109 }
110 
111 /* returns the number of seconds since the Epoch (1970-01-01 00:00:00 +0000).
112    Thus we have to call it twice and subtract to get the wall clock time of
113    a given program. */
114 double
wct_seconds(void)115 wct_seconds (void)
116 {
117     struct timeval tv[1];
118     gettimeofday (tv, NULL);
119     return (double)tv->tv_sec + (double)tv->tv_usec*1.0e-6;
120 }
121 
122 /* Print timings (cpu/wct) and memory usage since cpu0/wct0.
123    The memory is expressed in MiB (2^20 bytes).
124 */
125 void
print_timing_and_memory(FILE * fp,double cpu0,double wct0)126 print_timing_and_memory (FILE*fp, double cpu0, double wct0)
127 {
128   fprintf (fp, "Total usage: time %1.0fs (cpu), %1.0fs (wct) ; "
129            "memory %luMiB, peak %luMiB\n",
130            seconds () - cpu0, wct_seconds () - wct0,
131            Memusage () >> 10, PeakMemusage () >> 10);
132 }
133 
134 /* We need some way to detect the time spent by threads. Unfortunately,
135  * this is something not defined by POSIX.
136  */
137 
138 #if defined(HAVE_RUSAGE_THREAD)
thread_seconds_user_sys(double * res)139 void thread_seconds_user_sys(double * res)
140 {
141     struct rusage ru[1];
142     getrusage(RUSAGE_THREAD, ru);
143     res[0] = (double)ru->ru_utime.tv_sec + (double)ru->ru_utime.tv_usec/1.0e6;
144     res[1] = (double)ru->ru_stime.tv_sec + (double)ru->ru_stime.tv_usec/1.0e6;
145 }
146 #elif defined(__linux)
147 #include <unistd.h>
148 #include <sys/syscall.h>
149 #include <stdarg.h>
150 #include <string.h>
151 #include <stdlib.h>
152 #include "portability.h"
153 
gettid()154 static inline pid_t gettid() { return syscall(SYS_gettid); }
155 
156 /* On linux (well, using nptl and not linuxthreads, but we don't care
157  * much), that's doable by parsing /proc/<tid>/stat
158  */
159 
160 /* statfields is fairly crude, for sure. ... has to be terminated by -1.
161  * Arguments must come in groups of three, first the field index, then
162  * its parsing format, then the adress of the pointer.
163  */
statfields(pid_t t,...)164 static int statfields(pid_t t, ...)
165 {
166     int nparsed = 0;
167     va_list ap;
168     va_start(ap, t);
169     char * tmp;
170     int rc = asprintf(&tmp,"/proc/%d/stat",t);
171     if (rc < 0) return 0;
172     char buf[1024];
173     FILE * f = fopen(tmp,"r");
174     char * s = fgets(buf, sizeof(buf), f);
175     if (s == NULL) return 0;
176     int j = va_arg(ap, int);
177     for(int i = 0 ; j != -1 ; i++) {
178         char * next;
179         next = strchr(s, ' ');
180         if (next == NULL)
181             break;
182         *next++='\0';
183         if (i == j) {
184             const char * fmt = va_arg(ap, const char *);
185             void * ptr = va_arg(ap, void *);
186             nparsed += sscanf(s, fmt, ptr);
187             j = va_arg(ap, int);
188         }
189         s = next;
190     }
191 
192     fclose(f);
193     free(tmp);
194     va_end(ap);
195 
196     return nparsed;
197 }
198 
thread_seconds_user_sys(double * res)199 void thread_seconds_user_sys(double * res)
200 {
201     unsigned long utime = 0;
202     unsigned long stime = 0;
203     statfields(gettid(), 13, "%lu", &utime, 14, "%lu", &stime, -1);
204     res[0] = (double) utime / sysconf(_SC_CLK_TCK);
205     res[1] = (double) stime / sysconf(_SC_CLK_TCK);
206 }
207 
208 #else   /* Otherwise we'll do something stupid */
thread_seconds_user_sys(double * res)209 void thread_seconds_user_sys(double * res)
210 {
211     seconds_user_sys(res); /* really stupid */
212 }
213 #endif
214 
215 #ifdef HAVE_GETRUSAGE
216 typedef std::multimap<std::string, struct rusage> real_timingstats_dict_t;
217 
timingstats_dict_init(timingstats_dict_ptr p)218 void timingstats_dict_init(timingstats_dict_ptr p)
219 {
220     *p = static_cast<void*>(new real_timingstats_dict_t());
221 }
222 
timingstats_dict_clear(timingstats_dict_ptr p)223 void timingstats_dict_clear(timingstats_dict_ptr p)
224 {
225     delete static_cast<real_timingstats_dict_t*>(*p);
226 }
227 
timingstats_dict_add(timingstats_dict_ptr p,const char * key,struct rusage * r)228 void timingstats_dict_add(timingstats_dict_ptr p, const char * key, struct rusage * r)
229 {
230     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
231     pthread_mutex_lock(&lock);
232     real_timingstats_dict_t& s(*static_cast<real_timingstats_dict_t*>(*p));
233     s.insert(std::make_pair(std::string(key), *r));
234     pthread_mutex_unlock(&lock);
235 }
236 
timingstats_dict_add_mythread(timingstats_dict_ptr p,const char * key)237 void timingstats_dict_add_mythread(timingstats_dict_ptr p, const char * key)
238 {
239     struct rusage ru[1];
240 #ifdef HAVE_RUSAGE_THREAD
241     getrusage (RUSAGE_THREAD, ru);
242 #else
243     /* this will be plain bogus, but that's life */
244     getrusage (RUSAGE_SELF, ru);
245 #endif
246     timingstats_dict_add(p, key, ru);
247 }
248 
timingstats_dict_add_myprocess(timingstats_dict_ptr p,const char * key)249 void timingstats_dict_add_myprocess(timingstats_dict_ptr p, const char * key)
250 {
251     struct rusage ru[1];
252     getrusage (RUSAGE_SELF, ru);
253     timingstats_dict_add(p, key, ru);
254 }
255 
timingstats_dict_disp(timingstats_dict_ptr p)256 void timingstats_dict_disp(timingstats_dict_ptr p)
257 {
258     real_timingstats_dict_t const& s(*static_cast<real_timingstats_dict_t*>(*p));
259     /* multimap is sorted */
260     typedef real_timingstats_dict_t::const_iterator it_t;
261     for(it_t i = s.begin(), j ; i != s.end() ; i = j) {
262         double tu = 0;
263         double ts = 0;
264         int n = 0;
265         for(j = i ; j != s.end() && j->first == i->first ; j++, n++) {
266             tu += (double)j->second.ru_utime.tv_sec
267                 + (double)j->second.ru_utime.tv_usec / 1.0e6;
268             ts += (double)j->second.ru_stime.tv_sec
269                 + (double)j->second.ru_stime.tv_usec / 1.0e6;
270         }
271         printf("%s: %d process%s, total %.2fs+%.2fs on cpu\n",
272                 i->first.c_str(), n, n>1 ? "es" : "", tu, ts);
273     }
274 }
275 #endif
276