sssimulator / proc-time.c @ master
History | View | Annotate | Download (8.26 KB)
1 | 6b38058b | luca | /*
|
---|---|---|---|
2 | proc-time.c -- Get resource usage from /proc for a command (GNU/Linux).
|
||
3 | |||
4 | awaiting PC's linux-mm patch for getrusage
|
||
5 | |||
6 | source file of the GNU LilyPond music typesetter
|
||
7 | Licence: GNU GPL
|
||
8 |
|
||
9 | (c) 2000 Jan Nieuwenhuizen <janneke@gnu.org>
|
||
10 | |||
11 | Translated from Python prototype
|
||
12 | |||
13 | GNU time doesn't report memory stuff on FreeBSD either,
|
||
14 | but I couldn't find an easy way like to get this info (like /proc).
|
||
15 | */
|
||
16 | |||
17 | #include <getopt.h> |
||
18 | #include <stdio.h> |
||
19 | |||
20 | char const* name = "proc-time"; |
||
21 | char const* version = "1.3.55"; |
||
22 | |||
23 | /* + : stop parsing options when non-option is found */
|
||
24 | char const* short_opts = "+bhi:ntvV"; |
||
25 | struct option long_opts[] =
|
||
26 | { |
||
27 | {"heartbeat", no_argument, 0, 'b'}, |
||
28 | {"help", no_argument, 0, 'h'}, |
||
29 | {"interval", required_argument, 0, 'i'}, |
||
30 | {"no-fork", no_argument, 0, 'n'}, |
||
31 | {"test", no_argument, 0, 't'}, |
||
32 | {"version", no_argument, 0, 'v'}, |
||
33 | {"verbose", no_argument, 0, 'V'}, |
||
34 | {0, no_argument, 0, 0} |
||
35 | }; |
||
36 | |||
37 | int test = 0; |
||
38 | int nofork = 0; |
||
39 | int heartbeat = 0; |
||
40 | float interval = 0.5; |
||
41 | int status = 0; |
||
42 | int verbose = 0; |
||
43 | |||
44 | void
|
||
45 | identify (FILE* f) |
||
46 | { |
||
47 | fprintf (f, "%s from LilyPond %s\n", name, version);
|
||
48 | } |
||
49 | |||
50 | void
|
||
51 | print_usage () |
||
52 | { |
||
53 | identify (stdout); |
||
54 | printf ("\n"
|
||
55 | "Usage: %s [OPTION]... COMMAND\n"
|
||
56 | "\n"
|
||
57 | "Get resource usage from /proc for COMMAND (GNU/Linux).\n"
|
||
58 | "\n"
|
||
59 | "Options:\n"
|
||
60 | " -b, --heartbeat show memory info at every heartbeat\n"
|
||
61 | " -h, --help this help\n"
|
||
62 | " -i, --interval=TIME set heartbeat to TIME seconds\n"
|
||
63 | " -n, --no-fork don't fork\n"
|
||
64 | " -t, --test test mode\n"
|
||
65 | " -V, --verbose be verbose\n"
|
||
66 | " -v, --version version information\n"
|
||
67 | "\n", name);
|
||
68 | } |
||
69 | |||
70 | void
|
||
71 | print_version () |
||
72 | { |
||
73 | printf ("%s (GNU LilyPond) %s", name, version);
|
||
74 | } |
||
75 | |||
76 | #include <sys/types.h> |
||
77 | #include <errno.h> |
||
78 | #include <signal.h> |
||
79 | #include <sys/wait.h> |
||
80 | #include <sys/time.h> |
||
81 | #include <unistd.h> |
||
82 | |||
83 | #define JIF_TO_SEC 0.01 |
||
84 | #define PAGE_SIZE 4096 |
||
85 | #define PAGE_TO_MEG PAGE_SIZE / (1024 * 1024) |
||
86 | #define MAX(a, b) (a < b ? b : a)
|
||
87 | |||
88 | enum stat_enum
|
||
89 | { |
||
90 | STAT_USER_JIFFIES = 13,
|
||
91 | STAT_SYSTEM_JIFFIES, |
||
92 | STAT_COUNTER = 17,
|
||
93 | STAT_MAX |
||
94 | }; |
||
95 | |||
96 | enum statm_enum
|
||
97 | { |
||
98 | STATM_SIZE = 0,
|
||
99 | STATM_RSS, |
||
100 | STATM_SHARED, |
||
101 | STATM_CODE, |
||
102 | STATM_DATA, |
||
103 | STATM_LIB, |
||
104 | STATM_DIRTY, |
||
105 | STATM_MAX |
||
106 | }; |
||
107 | |||
108 | struct process_info
|
||
109 | { |
||
110 | pid_t id; |
||
111 | int t;
|
||
112 | char stat_name[81]; |
||
113 | char statm_name[81]; |
||
114 | int stat_i[STAT_MAX];
|
||
115 | int max_size;
|
||
116 | int cum_size;
|
||
117 | int max_rss;
|
||
118 | int cum_rss;
|
||
119 | struct timeval start_tv;
|
||
120 | struct timeval stop_tv;
|
||
121 | }; |
||
122 | |||
123 | struct process_info child_pi;
|
||
124 | void
|
||
125 | init (struct process_info* pi, pid_t pid)
|
||
126 | { |
||
127 | pi->id = pid; |
||
128 | pi->t = 0;
|
||
129 | snprintf (pi->statm_name, sizeof (pi->statm_name) - 1, |
||
130 | "/proc/%d/statm", pi->id);
|
||
131 | snprintf (pi->stat_name, sizeof (pi->stat_name) - 1, |
||
132 | "/proc/%d/stat", pi->id);
|
||
133 | gettimeofday (&pi->start_tv, 0);
|
||
134 | } |
||
135 | |||
136 | void
|
||
137 | update_mem_stats (struct process_info* pi)
|
||
138 | { |
||
139 | FILE* f = fopen (pi->statm_name, "r");
|
||
140 | if (f)
|
||
141 | { |
||
142 | int statm_i[STATM_MAX];
|
||
143 | if (fscanf (f, "%d %d %d %d %d %d %d", |
||
144 | &statm_i[0],
|
||
145 | &statm_i[1],
|
||
146 | &statm_i[2],
|
||
147 | &statm_i[3],
|
||
148 | &statm_i[4],
|
||
149 | &statm_i[5],
|
||
150 | &statm_i[6])
|
||
151 | >= STATM_MAX - 1)
|
||
152 | { |
||
153 | pi->t++; |
||
154 | pi->max_size = MAX (statm_i[STATM_SIZE], pi->max_size); |
||
155 | pi->cum_size += statm_i[STATM_SIZE]; |
||
156 | pi->max_rss = MAX (statm_i[STATM_RSS], pi->max_rss); |
||
157 | pi->cum_rss += statm_i[STATM_RSS]; |
||
158 | } |
||
159 | else
|
||
160 | fprintf (stderr, "%s: scanf failed: %s", pi->statm_name, strerror (errno));
|
||
161 | fclose (f); |
||
162 | } |
||
163 | else
|
||
164 | fprintf (stderr, "%s: reading failed: %s", pi->statm_name, strerror (errno));
|
||
165 | } |
||
166 | |||
167 | void
|
||
168 | print_mem_stats (struct process_info* pi)
|
||
169 | { |
||
170 | if (pi->t)
|
||
171 | { |
||
172 | int avg_size = pi->cum_size / pi->t;
|
||
173 | int avg_rss = pi->cum_rss / pi->t;
|
||
174 | fprintf (stderr, "MAXSIZE: %6.3fM(%d), MAXRSS: %6.3fM(%d)\n",
|
||
175 | (float)pi->max_size * PAGE_TO_MEG, pi->max_size,
|
||
176 | (float)pi->max_rss * PAGE_TO_MEG, pi->max_rss);
|
||
177 | fprintf (stderr, "AVGSIZE: %6.3fM(%d), AVGRSS: %6.3fM(%d)\n",
|
||
178 | (float)avg_size * PAGE_TO_MEG, avg_size,
|
||
179 | (float)avg_rss * PAGE_TO_MEG, avg_rss);
|
||
180 | fflush (stdout); |
||
181 | fflush (stderr); |
||
182 | } |
||
183 | } |
||
184 | |||
185 | void
|
||
186 | update_time_stats (struct process_info* pi)
|
||
187 | { |
||
188 | FILE* f = fopen (pi->stat_name, "r");
|
||
189 | if (f)
|
||
190 | { |
||
191 | char name[80]; |
||
192 | char s[80]; |
||
193 | if (fscanf (f,
|
||
194 | "%d %s %s %d %d "
|
||
195 | "%d %d %d %d %d "
|
||
196 | "%d %d %d %d %d "
|
||
197 | "%d %d %d",
|
||
198 | &pi->stat_i[0],
|
||
199 | &name, |
||
200 | &s, |
||
201 | &pi->stat_i[3],
|
||
202 | &pi->stat_i[4],
|
||
203 | &pi->stat_i[5],
|
||
204 | &pi->stat_i[6],
|
||
205 | &pi->stat_i[7],
|
||
206 | &pi->stat_i[8],
|
||
207 | &pi->stat_i[9],
|
||
208 | &pi->stat_i[10],
|
||
209 | &pi->stat_i[11],
|
||
210 | &pi->stat_i[12],
|
||
211 | &pi->stat_i[13],
|
||
212 | &pi->stat_i[14],
|
||
213 | &pi->stat_i[15],
|
||
214 | &pi->stat_i[16],
|
||
215 | &pi->stat_i[17])
|
||
216 | >= STAT_MAX - 1)
|
||
217 | ; |
||
218 | else
|
||
219 | fprintf (stderr, "%s: scanf failed: %s", pi->stat_name, strerror (errno));
|
||
220 | fclose (f); |
||
221 | } |
||
222 | gettimeofday (&pi->stop_tv, 0);
|
||
223 | } |
||
224 | |||
225 | void
|
||
226 | print_time_stats (struct process_info* pi)
|
||
227 | { |
||
228 | fprintf (stderr, "user: %6.2f(%d) system: %6.2f(%d)\n",
|
||
229 | (float)pi->stat_i[STAT_USER_JIFFIES] * JIF_TO_SEC,
|
||
230 | pi->stat_i[STAT_USER_JIFFIES], |
||
231 | (float)pi->stat_i[STAT_SYSTEM_JIFFIES] * JIF_TO_SEC,
|
||
232 | pi->stat_i[STAT_SYSTEM_JIFFIES]); |
||
233 | fprintf (stderr, "elapsed: %6.2f\n",
|
||
234 | (float)(pi->stop_tv.tv_sec - pi->start_tv.tv_sec)
|
||
235 | + (pi->stop_tv.tv_usec - pi->start_tv.tv_usec) * 1e-6); |
||
236 | } |
||
237 | |||
238 | void
|
||
239 | handler (int arg)
|
||
240 | { |
||
241 | (void)arg;
|
||
242 | /* urg: child_pi */
|
||
243 | update_time_stats (&child_pi); |
||
244 | update_mem_stats (&child_pi); |
||
245 | fprintf (stderr, "\n");
|
||
246 | print_time_stats (&child_pi); |
||
247 | print_mem_stats (&child_pi); |
||
248 | if (test)
|
||
249 | fprintf (stderr, "handler\n");
|
||
250 | } |
||
251 | |||
252 | char**
|
||
253 | getargs (int argc, char** argv) |
||
254 | { |
||
255 | int c;
|
||
256 | while ((c = getopt_long (argc, argv, short_opts, long_opts, (int *) 0)) |
||
257 | != EOF)
|
||
258 | { |
||
259 | switch (c)
|
||
260 | { |
||
261 | case 'b': |
||
262 | heartbeat = 1;
|
||
263 | break;
|
||
264 | case 'h': |
||
265 | print_usage (); |
||
266 | exit (0);
|
||
267 | break;
|
||
268 | case 'i': |
||
269 | if (optarg)
|
||
270 | { |
||
271 | float f;
|
||
272 | if (sscanf (optarg, "%f", &f)) |
||
273 | { |
||
274 | interval = f; |
||
275 | } |
||
276 | } |
||
277 | break;
|
||
278 | case 'n': |
||
279 | nofork = 1;
|
||
280 | break;
|
||
281 | case 't': |
||
282 | test = 1;
|
||
283 | break;
|
||
284 | case 'v': |
||
285 | identify (stdout); |
||
286 | exit (0);
|
||
287 | break;
|
||
288 | case 'V': |
||
289 | verbose = 1;
|
||
290 | break;
|
||
291 | default:
|
||
292 | print_usage (); |
||
293 | exit (2);
|
||
294 | break;
|
||
295 | } |
||
296 | } |
||
297 | |||
298 | if (optind == argc)
|
||
299 | { |
||
300 | print_usage (); |
||
301 | exit (2);
|
||
302 | } |
||
303 | |||
304 | return &argv[optind];
|
||
305 | } |
||
306 | |||
307 | int
|
||
308 | run (char** command)
|
||
309 | { |
||
310 | if (nofork)
|
||
311 | init (&child_pi, 1);
|
||
312 | else
|
||
313 | init (&child_pi, fork ()); |
||
314 | |||
315 | if (child_pi.id)
|
||
316 | { |
||
317 | /* Parent */
|
||
318 | |||
319 | int input = 0; |
||
320 | fd_set rfds; |
||
321 | struct timeval tv;
|
||
322 | /* Watch stdin (fd 0) to see when it has input. */
|
||
323 | FD_ZERO (&rfds); |
||
324 | FD_SET (0, &rfds);
|
||
325 | |||
326 | update_mem_stats (&child_pi); |
||
327 | if (interval <= 0) |
||
328 | { |
||
329 | waitpid (child_pi.id, &status, 0);
|
||
330 | exit (status); |
||
331 | } |
||
332 | while (1) |
||
333 | { |
||
334 | if (waitpid (child_pi.id, &status, WNOHANG))
|
||
335 | break;
|
||
336 | |||
337 | /*
|
||
338 | should we use pause/alarm?
|
||
339 | when using the sleeptime of select, we don't see any input??
|
||
340 | */
|
||
341 | #if 0
|
||
342 | /* man page says we should reset these */
|
||
343 | tv.tv_sec = 0;
|
||
344 | tv.tv_usec = (unsigned long)(interval*1e6);
|
||
345 | #else
|
||
346 | usleep ((unsigned long)(interval*1e6)); |
||
347 | /* man page says we should reset these */
|
||
348 | tv.tv_sec = 0;
|
||
349 | tv.tv_usec = 0;
|
||
350 | #endif
|
||
351 | input = select(1, &rfds, 0, 0, &tv); |
||
352 | update_mem_stats (&child_pi); |
||
353 | |||
354 | //if (input)
|
||
355 | // {
|
||
356 | // fprintf (stderr, "\n");
|
||
357 | // print_mem_stats (&child_pi);
|
||
358 | // getc (stdin);
|
||
359 | // }
|
||
360 | //else
|
||
361 | //if (heartbeat)
|
||
362 | // {
|
||
363 | // fprintf (stderr, "\n");
|
||
364 | // print_mem_stats (&child_pi);
|
||
365 | // }
|
||
366 | } |
||
367 | } |
||
368 | else
|
||
369 | { |
||
370 | /* Child */
|
||
371 | execvp (command[0], command);
|
||
372 | } |
||
373 | |||
374 | return status;
|
||
375 | } |
||
376 | |||
377 | |||
378 | void
|
||
379 | check_file (char const* name, char const* message) |
||
380 | { |
||
381 | FILE* f; |
||
382 | f = fopen (name, "r");
|
||
383 | if (f)
|
||
384 | { |
||
385 | fclose (f); |
||
386 | } |
||
387 | else
|
||
388 | { |
||
389 | fprintf (stderr, "%s: can't open: %s\n", name, message);
|
||
390 | } |
||
391 | } |
||
392 | |||
393 | void
|
||
394 | check_sanity () |
||
395 | { |
||
396 | struct process_info init_pi;
|
||
397 | init (&init_pi, 1);
|
||
398 | |||
399 | check_file (init_pi.statm_name, "memory statistics unavailable");
|
||
400 | check_file (init_pi.stat_name, "detailed time statistics unavailable");
|
||
401 | } |
||
402 | |||
403 | int
|
||
404 | main (int argc, char** argv) |
||
405 | { |
||
406 | int status = 0; |
||
407 | char** command = getargs (argc, argv);
|
||
408 | identify (stderr); |
||
409 | |||
410 | check_sanity (); |
||
411 | |||
412 | signal (SIGINT, handler); |
||
413 | signal (SIGCHLD, handler); |
||
414 | signal (SIGTERM, handler); |
||
415 | |||
416 | status = run (command); |
||
417 | |||
418 | if (!status)
|
||
419 | waitpid (child_pi.id, &status, 0);
|
||
420 | |||
421 | if (status)
|
||
422 | fprintf (stderr, "Command exited with non-zero exit status: %d\n", status);
|
||
423 | |||
424 | return status;
|
||
425 | } |