Commit [+] Author Date Line Data
57c2f1c6 Michael Weber2009-03-12 18:13:11 +01001/* -*- mode: C; c-file-style: "k&r"; -*-
2 *---------------------------------------------------------------------------*
3 *
4 * Copyright (c) 2000, Johan Bengtsson
e9bdaf40 Michael Weber2009-03-12 18:43:30 +01005 * Copyright (c) 2009, Michael Weber
95eca0ff Michael Weber2009-10-27 12:29:26 +01006 *
57c2f1c6 Michael Weber2009-03-12 18:13:11 +01007 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *
24 *---------------------------------------------------------------------------*/
25
26#define _USE_BSD
27
28#include <sys/time.h>
29#include <sys/resource.h>
30#include <sys/wait.h>
31#include <sys/types.h>
32#include <unistd.h>
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37
38#include <signal.h>
39
40#include <errno.h>
2d233322 Michael Weber2009-11-20 11:04:47 +010041#include <assert.h>
57c2f1c6 Michael Weber2009-03-12 18:13:11 +010042
43#include "machdep.h"
44
859b2ee1 Michael Weber2009-03-12 18:20:44 +010045void
df63a13f Jeroen Ketema2009-11-26 12:06:13 +010046usage (FILE *ffile, const char *progname)
859b2ee1 Michael Weber2009-03-12 18:20:44 +010047{
48 fprintf (ffile,
df63a13f Jeroen Ketema2009-11-26 12:06:13 +010049 "Usage:\n%s [-t <interval>] [-e] [-m <maxkilobytes>] "
421e0819 Axel Belinfante2013-04-10 19:34:40 +020050 "[-c <maxcpuseconds>] [-o <filedescriptor>] <cmd> [<params>]\n", progname);
859b2ee1 Michael Weber2009-03-12 18:20:44 +010051}
52
95eca0ff Michael Weber2009-10-27 12:29:26 +010053static int print_stats;
54static pid_t kid;
5a6f6b5d Michael Weber2009-11-20 11:05:41 +010055int kid_signalled = 0;
421e0819 Axel Belinfante2013-04-10 19:34:40 +020056static int outfd = 2;
57static FILE* outf; /* initialized to stderr in main */
95eca0ff Michael Weber2009-10-27 12:29:26 +010058
2d233322 Michael Weber2009-11-20 11:04:47 +010059static int
60xkill (pid_t kid, int signal)
95eca0ff Michael Weber2009-10-27 12:29:26 +010061{
2d233322 Michael Weber2009-11-20 11:04:47 +010062 int rc = -1;
63 if (kid != 0) {
64 rc = kill (kid, signal);
5a6f6b5d Michael Weber2009-11-20 11:05:41 +010065 kid_signalled = 1;
2d233322 Michael Weber2009-11-20 11:04:47 +010066 }
67 return rc;
95eca0ff Michael Weber2009-10-27 12:29:26 +010068}
69
2d233322 Michael Weber2009-11-20 11:04:47 +010070static const int MAX_SIGNAL = 64; /* works for most OSes */
71static void (*signal_prev[64]) (int);
72static int caught_signals[] = {
73 SIGINT, SIGTERM, SIGHUP, SIGUSR1, SIGUSR2,
74#ifdef SIGXCPU
75 SIGXCPU,
76#endif
77#ifdef SIGXFSZ
78 SIGXFSZ,
79#endif
80 0
81};
95eca0ff Michael Weber2009-10-27 12:29:26 +010082
95eca0ff Michael Weber2009-10-27 12:29:26 +010083static void
2d233322 Michael Weber2009-11-20 11:04:47 +010084fwd_signal (int signal)
95eca0ff Michael Weber2009-10-27 12:29:26 +010085{
2d233322 Michael Weber2009-11-20 11:04:47 +010086 xkill (kid, signal);
87 assert (signal < MAX_SIGNAL);
88 if (signal_prev[signal] != NULL && signal_prev[signal] != fwd_signal)
89 signal_prev[signal] (signal);
95eca0ff Michael Weber2009-10-27 12:29:26 +010090}
91
92static void (*sigprof_prev) (int);
93static void
94handle_sigprof (int signal)
95{
96 print_stats = 1;
97 if (sigprof_prev != NULL && sigprof_prev != handle_sigprof)
98 sigprof_prev (signal);
99}
100
101
859b2ee1 Michael Weber2009-03-12 18:20:44 +0100102int
103main (int argc, char *argv[])
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100104{
105 struct rusage kid_usage;
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100106 int kid_status;
107 int i, opt, echo_args = 0, exit_flag;
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100108 char *endptr;
95eca0ff Michael Weber2009-10-27 12:29:26 +0100109
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100110 unsigned long sample_time = 0, time = 0;
95eca0ff Michael Weber2009-10-27 12:29:26 +0100111 unsigned long maxkbytes = 0; // kilobytes
112 unsigned long maxseconds = 0; // seconds
dc08d4b5 Jeroen Ketema2009-11-19 13:18:14 +0100113 unsigned long maxmillis = 0;
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100114
859b2ee1 Michael Weber2009-03-12 18:20:44 +0100115 unsigned long max_vsize = 0, max_rss = 0;
116 unsigned long start, end;
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100117
859b2ee1 Michael Weber2009-03-12 18:20:44 +0100118 memtime_info_t info;
95eca0ff Michael Weber2009-10-27 12:29:26 +0100119 memset (&info, 0, sizeof info);
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100120
421e0819 Axel Belinfante2013-04-10 19:34:40 +0200121 outf = stderr;
122
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100123 if (argc < 2) {
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100124 usage(stderr, argv[0]);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100125 exit(EXIT_FAILURE);
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100126 }
127
421e0819 Axel Belinfante2013-04-10 19:34:40 +0200128 while ((opt = getopt(argc, argv, "+eht:m:c:o:")) != -1) {
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100129
95eca0ff Michael Weber2009-10-27 12:29:26 +0100130 switch (opt) {
131 case 'e' :
132 echo_args = 1;
133 break;
134
135 case 't' :
136 errno = 0;
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100137 sample_time = strtoul(optarg, &endptr, 10);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100138 if (errno) {
139 perror("Illegal argument to t option");
140 exit(EXIT_FAILURE);
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100141 } else if (endptr == optarg || *endptr != '\0') {
142 fprintf(stderr, "Illegal argument to t option\n");
143 exit(EXIT_FAILURE);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100144 }
145 break;
146 case 'm' :
147 errno = 0;
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100148 maxkbytes = strtoul(optarg, &endptr, 10);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100149 if (errno) {
150 perror("Illegal argument to m option");
151 exit(EXIT_FAILURE);
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100152 } else if (endptr == optarg || *endptr != '\0') {
153 fprintf(stderr, "Illegal argument to m option\n");
154 exit(EXIT_FAILURE);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100155 }
156 break;
157
158 case 'c' :
159 errno = 0;
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100160 maxseconds = strtoul(optarg, &endptr, 10);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100161 if (errno) {
162 perror("Illegal argument to c option");
163 exit(EXIT_FAILURE);
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100164 } else if (endptr == optarg || *endptr != '\0') {
165 fprintf(stderr, "Illegal argument to c option\n");
166 exit(EXIT_FAILURE);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100167 }
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100168 maxmillis = 1000*maxseconds;
95eca0ff Michael Weber2009-10-27 12:29:26 +0100169 break;
421e0819 Axel Belinfante2013-04-10 19:34:40 +0200170
171 case 'o' :
172 outfd = strtoul(optarg, &endptr, 10);
173 if (errno) {
174 perror("Illegal argument to o option");
175 exit(EXIT_FAILURE);
176 } else if (endptr == optarg || *endptr != '\0') {
177 fprintf(stderr, "Illegal argument to o option\n");
178 exit(EXIT_FAILURE);
179 }
180 outf = fdopen(outfd, "w");
181 if (errno) {
182 perror("Cannot open file descriptor given as argument to o option");
183 exit(EXIT_FAILURE);
184 }
185 break;
186
859b2ee1 Michael Weber2009-03-12 18:20:44 +0100187 case 'h':
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100188 usage (stdout, argv[0]);
859b2ee1 Michael Weber2009-03-12 18:20:44 +0100189 exit(EXIT_SUCCESS);
5e52c81c Michael Weber2009-03-12 19:05:19 +0100190
191 default:
192 exit(EXIT_FAILURE);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100193 }
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100194 }
195
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100196 if (optind == argc) {
197 fprintf(stderr, "Missing command\n");
198 exit(EXIT_FAILURE);
199 }
200
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100201 if (echo_args) {
95eca0ff Michael Weber2009-10-27 12:29:26 +0100202 fprintf(stderr,"Command line: ");
203 for (i = optind; i < argc; i++)
421e0819 Axel Belinfante2013-04-10 19:34:40 +0200204 fprintf(outf,"%s ", argv[i]);
205 fprintf(outf,"\n");
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100206 }
207
208 start = get_time();
95eca0ff Michael Weber2009-10-27 12:29:26 +0100209
b6f0e376 Michael Weber2009-03-13 04:48:16 +0100210 switch (kid = sampling_fork()) {
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100211 case -1 :
95eca0ff Michael Weber2009-10-27 12:29:26 +0100212 perror("sampling_fork failed");
213 exit(EXIT_FAILURE);
214
215 case 0 :
eec8cbb1 Jeroen Ketema2009-11-19 20:34:51 +0100216#if defined(CAN_USE_RLIMIT_VSIZE)
95eca0ff Michael Weber2009-10-27 12:29:26 +0100217 if (maxkbytes>0) {
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100218 set_mem_limit(maxkbytes*1024);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100219 }
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100220#endif
95eca0ff Michael Weber2009-10-27 12:29:26 +0100221#if defined(CAN_USE_RLIMIT_CPU)
222 if (maxseconds>0) {
df63a13f Jeroen Ketema2009-11-26 12:06:13 +0100223 set_cpu_limit(maxseconds);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100224 }
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100225#endif
95eca0ff Michael Weber2009-10-27 12:29:26 +0100226 execvp(argv[optind], &(argv[optind]));
227 perror("exec failed");
228 exit(EXIT_FAILURE);
229
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100230 default :
95eca0ff Michael Weber2009-10-27 12:29:26 +0100231 break;
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100232 }
233
2d233322 Michael Weber2009-11-20 11:04:47 +0100234 for (i = 0; caught_signals[i] != 0; ++i) {
235 assert (caught_signals[i] < MAX_SIGNAL);
236 signal_prev[caught_signals[i]] =
237 signal (caught_signals[i], fwd_signal);
238 }
95eca0ff Michael Weber2009-10-27 12:29:26 +0100239 sigprof_prev = signal (SIGPROF, handle_sigprof);
240
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100241 do {
95eca0ff Michael Weber2009-10-27 12:29:26 +0100242 get_sample(&info);
243
244 max_vsize = (info.vsize_kb > max_vsize ? info.vsize_kb : max_vsize);
245 max_rss = (info.rss_kb > max_rss ? info.rss_kb : max_rss);
246
247 if (sample_time) {
248 time++;
249 if (time >= 10 * sample_time) {
250 time = 0;
251 print_stats = 1;
252 }
253 }
254
255 if (print_stats) {
256 end = get_time();
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100257
421e0819 Axel Belinfante2013-04-10 19:34:40 +0200258 fprintf(outf,"%.2f user, %.2f system, %.2f elapsed"
95eca0ff Michael Weber2009-10-27 12:29:26 +0100259 " -- VSize = %luKB, RSS = %luKB\n",
260 (double)info.utime_ms/1000.0,
261 (double)info.stime_ms/1000.0,
262 (double)(end - start)/1000.0,
263 info.vsize_kb, info.rss_kb);
421e0819 Axel Belinfante2013-04-10 19:34:40 +0200264 fflush(outf);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100265 print_stats = 0;
266 }
267
268 usleep(100000);
269
270 int wait_for = wait4(kid, &kid_status, WNOHANG, &kid_usage);
271 if (wait_for == -1 && errno != EINTR) {
272 perror ("wait4");
273 abort ();
274 }
275 exit_flag = (wait_for == kid
276 && (WIFEXITED(kid_status) || WIFSIGNALED(kid_status)));
277
eec8cbb1 Jeroen Ketema2009-11-19 20:34:51 +0100278#if !defined(CAN_USE_RLIMIT_VSIZE)
95eca0ff Michael Weber2009-10-27 12:29:26 +0100279 if ((maxkbytes>0) && (max_vsize>maxkbytes)) {
2d233322 Michael Weber2009-11-20 11:04:47 +0100280 xkill(kid, SIGKILL);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100281 }
282#endif
283#if !defined(CAN_USE_RLIMIT_CPU)
284 if ((maxmillis>0) && (info.utime_ms>maxmillis)) {
2d233322 Michael Weber2009-11-20 11:04:47 +0100285 xkill(kid, SIGKILL);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100286 }
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100287#endif
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100288 } while (!exit_flag);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100289
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100290 end = get_time();
95eca0ff Michael Weber2009-10-27 12:29:26 +0100291
2d233322 Michael Weber2009-11-20 11:04:47 +0100292 for (i = 0; caught_signals[i] != 0; ++i)
293 signal (caught_signals[i], signal_prev[caught_signals[i]]);
95eca0ff Michael Weber2009-10-27 12:29:26 +0100294 signal (SIGPROF, sigprof_prev);
295
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100296 if (WIFEXITED(kid_status)) {
421e0819 Axel Belinfante2013-04-10 19:34:40 +0200297 fprintf(outf, "Exit [%d]\n", WEXITSTATUS(kid_status));
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100298 } else {
421e0819 Axel Belinfante2013-04-10 19:34:40 +0200299 fprintf(outf, "Killed [%d]\n", WTERMSIG(kid_status));
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100300 }
301
302 {
95eca0ff Michael Weber2009-10-27 12:29:26 +0100303 max_vsize = (info.vsize_kb > max_vsize ? info.vsize_kb : max_vsize);
304 max_rss = (info.rss_kb > max_rss ? info.rss_kb : max_rss);
305 double kid_utime = ((double)kid_usage.ru_utime.tv_sec
306 + (double)kid_usage.ru_utime.tv_usec / 1E6);
307 double kid_stime = ((double)kid_usage.ru_stime.tv_sec
308 + (double)kid_usage.ru_stime.tv_usec / 1E6);
309
421e0819 Axel Belinfante2013-04-10 19:34:40 +0200310 fprintf(outf, "%.2f user, %.2f system, %.2f elapsed -- "
95eca0ff Michael Weber2009-10-27 12:29:26 +0100311 "Max VSize = %luKB, Max RSS = %luKB\n",
312 kid_utime, kid_stime, (double)(end - start) / 1000.0,
313 max_vsize, max_rss);
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100314 }
315
859b2ee1 Michael Weber2009-03-12 18:20:44 +0100316 if(WIFEXITED(kid_status))
95eca0ff Michael Weber2009-10-27 12:29:26 +0100317 exit(WEXITSTATUS(kid_status));
a6c2e564 Michael Weber2009-04-08 16:31:58 +0200318 else {
319 int csig = WTERMSIG(kid_status);
320 switch (csig) {
321 case SIGHUP: case SIGINT: case SIGUSR1: case SIGUSR2:
322 case SIGKILL: case SIGALRM: case SIGTERM: case SIGPIPE:
2d233322 Michael Weber2009-11-20 11:04:47 +0100323#ifdef SIGXCPU
324 case SIGXCPU:
325#endif
326#ifdef SIGXFSZ
327 case SIGXFSZ:
328#endif
3b56914c Michael Weber2009-07-24 14:32:24 +0200329 raise (csig);
a6c2e564 Michael Weber2009-04-08 16:31:58 +0200330 }
421e0819 Axel Belinfante2013-04-10 19:34:40 +0200331 fprintf (outf, "%s: child died with signal %d, aborting.\n",
3b56914c Michael Weber2009-07-24 14:32:24 +0200332 argv[0], csig);
333 abort ();
a6c2e564 Michael Weber2009-04-08 16:31:58 +0200334 }
57c2f1c6 Michael Weber2009-03-12 18:13:11 +0100335}