1 /* timer.c -- Timer support
2 * Created: Sat Oct 7 13:05:31 1995 by faith@dict.org
3 * Copyright 1995-1998, 2002 Rickard E. Faith (faith@dict.org)
4 * Copyright 2002-2008 Aleksey Cheusov (vle@gmx.net)
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 * \section{Timer Support}
26 *
27 * \intro These routines provide access to a microsecond-resolution time
28 * that can be used for profiling.
29 *
30 */
31
32 #include "maaP.h"
33
34 static hsh_HashTable _tim_Hash;
35
36 typedef struct tim_Entry {
37 double real; /* wall time in sec */
38 double self_user; /* user time in sec */
39 double self_system; /* system time in sec */
40 double children_user; /* user time in sec */
41 double children_system; /* system time in sec */
42 struct timeval real_mark;
43 struct rusage self_mark;
44 struct rusage children_mark;
45 } *tim_Entry;
46
_tim_check(void)47 static void _tim_check(void)
48 {
49 if (!_tim_Hash) _tim_Hash = hsh_create(NULL, NULL);
50 }
51
52 /* \doc Start the named timer. */
53
tim_start(const char * name)54 void tim_start(const char *name)
55 {
56 tim_Entry entry;
57
58 _tim_check();
59 if (!(entry = (tim_Entry)__UNCONST(hsh_retrieve(_tim_Hash, name)))) {
60 entry = xmalloc(sizeof(struct tim_Entry ));
61 entry->real = 0.0;
62 entry->self_user = 0.0;
63 entry->self_system = 0.0;
64 entry->children_user = 0.0;
65 entry->children_system = 0.0;
66 hsh_insert(_tim_Hash, name, entry);
67 }
68
69 gettimeofday(&entry->real_mark, NULL);
70
71 getrusage(RUSAGE_SELF, &entry->self_mark);
72 getrusage(RUSAGE_CHILDREN, &entry->children_mark);
73 }
74
75 /* \doc Stop the named timer. */
76
tim_stop(const char * name)77 void tim_stop(const char *name)
78 {
79 tim_Entry entry;
80 struct timeval real;
81 struct rusage rusage;
82
83 #define DIFFTIME(now,then) \
84 (((now).tv_sec - (then).tv_sec) \
85 + ((now).tv_usec - (then).tv_usec)/1000000)
86
87 _tim_check();
88 gettimeofday(&real, NULL);
89
90 if (!(entry = (tim_Entry)__UNCONST(hsh_retrieve(_tim_Hash, name))))
91 err_internal (__func__, "No timer: %s",
92 (name ? name : "<null>"));
93
94 entry->real = DIFFTIME(real, entry->real_mark);
95 getrusage(RUSAGE_SELF, &rusage);
96 entry->self_user = DIFFTIME(rusage.ru_utime, entry->self_mark.ru_utime);
97 entry->self_system = DIFFTIME(rusage.ru_stime, entry->self_mark.ru_stime);
98
99 getrusage(RUSAGE_CHILDREN, &rusage);
100 entry->children_user
101 = DIFFTIME(rusage.ru_utime, entry->children_mark.ru_utime);
102 entry->children_system
103 = DIFFTIME(rusage.ru_stime, entry->children_mark.ru_stime);
104 }
105
106 /* \doc Reset the named timer to zero elapsed time. Use |tim_start| to reset
107 the start time. */
108
tim_reset(const char * name)109 void tim_reset(const char *name)
110 {
111 tim_Entry entry;
112
113 _tim_check();
114 if (!(entry = (tim_Entry)__UNCONST(hsh_retrieve(_tim_Hash, name))))
115 err_internal (__func__, "No timer: %s",
116 (name ? name : "<null>"));
117
118 entry->real = 0.0;
119 entry->self_user = 0.0;
120 entry->self_system = 0.0;
121 entry->children_user = 0.0;
122 entry->children_system = 0.0;
123 }
124
125 /* \doc Get the wall time in seconds from the named timer. The return
126 value is a |double| and has microsecond resolution if the current system
127 provides that accuracy (most don't). */
128
tim_get_real(const char * name)129 double tim_get_real(const char *name)
130 {
131 tim_Entry entry;
132
133 _tim_check();
134 if (!(entry = (tim_Entry)__UNCONST(hsh_retrieve(_tim_Hash, name))))
135 err_internal (__func__, "No timer: %s",
136 (name ? name : "<null>"));
137
138 return entry->real;
139 }
140
141 /* \doc Get the number of seconds of user CPU time. */
142
tim_get_user(const char * name)143 double tim_get_user(const char *name)
144 {
145 tim_Entry entry;
146
147 _tim_check();
148 if (!(entry = (tim_Entry)__UNCONST(hsh_retrieve(_tim_Hash, name))))
149 err_internal (__func__, "No timer: %s",
150 (name ? name : "<null>"));
151
152 #if 0
153 printf("self: maxrss %ld ixrss %ld idrss %ld isrss %ld minflt %ld"
154 " majflt %ld nswap %ld inblock %ld outblock %ld msgsnd %ld"
155 " msgrcv %ld nsignals %ld nvcwm %ld nivcsm %ld\n",
156 entry->self_mark.ru_maxrss,
157 entry->self_mark.ru_ixrss,
158 entry->self_mark.ru_idrss,
159 entry->self_mark.ru_isrss,
160 entry->self_mark.ru_minflt,
161 entry->self_mark.ru_majflt,
162 entry->self_mark.ru_nswap,
163 entry->self_mark.ru_inblock,
164 entry->self_mark.ru_oublock,
165 entry->self_mark.ru_msgsnd,
166 entry->self_mark.ru_msgrcv,
167 entry->self_mark.ru_nsignals,
168 entry->self_mark.ru_nvcsw,
169 entry->self_mark.ru_nivcsw);
170 printf("chld: maxrss %ld ixrss %ld idrss %ld isrss %ld minflt %ld"
171 " majflt %ld nswap %ld inblock %ld outblock %ld msgsnd %ld"
172 " msgrcv %ld nsignals %ld nvcwm %ld nivcsm %ld\n",
173 entry->children_mark.ru_maxrss,
174 entry->children_mark.ru_ixrss,
175 entry->children_mark.ru_idrss,
176 entry->children_mark.ru_isrss,
177 entry->children_mark.ru_minflt,
178 entry->children_mark.ru_majflt,
179 entry->children_mark.ru_nswap,
180 entry->children_mark.ru_inblock,
181 entry->children_mark.ru_oublock,
182 entry->children_mark.ru_msgsnd,
183 entry->children_mark.ru_msgrcv,
184 entry->children_mark.ru_nsignals,
185 entry->children_mark.ru_nvcsw,
186 entry->children_mark.ru_nivcsw);
187 #endif
188
189 return (entry->self_user + entry->children_user);
190 }
191
192 /* \doc Get the number of seconds of system CPU time. */
193
tim_get_system(const char * name)194 double tim_get_system(const char *name)
195 {
196 tim_Entry entry;
197
198 _tim_check();
199 if (!(entry = (tim_Entry)__UNCONST(hsh_retrieve(_tim_Hash, name))))
200 err_internal (__func__, "No timer: %s",
201 (name ? name : "<null>"));
202
203 return (entry->self_system + entry->children_system);
204 }
205
206 /* \doc Print the named timer values to |str|. The format is similar to
207 "time(1)". */
208
tim_print_timer(FILE * str,const char * name)209 void tim_print_timer(FILE *str, const char *name)
210 {
211 fprintf(str, "%-20s %0.3fr %0.3fu %0.3fs\n",
212 name,
213 tim_get_real(name),
214 tim_get_user(name),
215 tim_get_system(name));
216 }
217
_tim_iterator(const void * key,const void * datum,void * arg)218 static int _tim_iterator(const void *key, const void *datum, void *arg)
219 {
220 FILE *str = (FILE *)arg;
221
222 tim_print_timer(str, key);
223 return 0;
224 }
225
226 /* \doc Print all the timers to |str|. The order is arbitary. */
227
tim_print_timers(FILE * str)228 void tim_print_timers(FILE *str)
229 {
230 if (_tim_Hash) hsh_iterate_arg(_tim_Hash, _tim_iterator, str);
231 }
232
_tim_freer(const void * key,const void * datum)233 static int _tim_freer(const void *key, const void *datum)
234 {
235 xfree(__UNCONST(datum)); /* Discard const */
236 return 0;
237 }
238
239 /* \doc Free all memory associated with the timers. This function is
240 called automatically at program termination. There should never be a
241 need to call this function in user-level code. */
242
_tim_shutdown(void)243 void _tim_shutdown(void)
244 {
245 if (_tim_Hash) {
246 hsh_iterate(_tim_Hash, _tim_freer);
247 hsh_destroy(_tim_Hash);
248 }
249 _tim_Hash = NULL;
250 }
251