1 /* -*- mode: C; c-file-style: "k&r"; -*-
2  *---------------------------------------------------------------------------*
3  *
4  * Copyright (c) 2000, Johan Bengtsson
5  * Copyright (c) 2009, Michael Weber
6  *
7  * 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  *---------------------------------------------------------------------------*/
26 #define _USE_BSD
28 #include <sys/time.h>
29 #include <sys/resource.h>
30 #include <sys/wait.h>
31 #include <sys/types.h>
32 #include <unistd.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
38 #include <signal.h>
40 #include <errno.h>
41 #include <assert.h>
43 #include "machdep.h"
45 void
46 usage (FILE *ffile, const char *progname)
47 {
48      fprintf (ffile,
49               "Usage:\n%s [-t <interval>] [-e] [-m <maxkilobytes>] "
50               "[-c <maxcpuseconds>] [-o <filedescriptor>] <cmd> [<params>]\n", progname);
51 }
53 static int print_stats;
54 static pid_t kid;
55 int kid_signalled = 0;
56 static int outfd = 2;
57 static FILE* outf; /* initialized to stderr in main */
59 static int
60 xkill (pid_t kid, int signal)
61 {
62      int rc = -1;
63      if (kid != 0) {
64           rc = kill (kid, signal);
65           kid_signalled = 1;
66      }
67      return rc;
68 }
70 static const int MAX_SIGNAL = 64;       /* works for most OSes */
71 static void (*signal_prev[64]) (int);
72 static 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 };
83 static void
84 fwd_signal (int signal)
85 {
86      xkill (kid, signal);
87      assert (signal < MAX_SIGNAL);
88      if (signal_prev[signal] != NULL && signal_prev[signal] != fwd_signal)
89           signal_prev[signal] (signal);
90 }
92 static void (*sigprof_prev) (int);
93 static void
94 handle_sigprof (int signal)
95 {
96      print_stats = 1;
97      if (sigprof_prev != NULL && sigprof_prev != handle_sigprof)
98           sigprof_prev (signal);
99 }
102 int
103 main (int argc, char *argv[])
105      struct rusage kid_usage;
106      int    kid_status;
107      int    i, opt, echo_args = 0, exit_flag;
108      char   *endptr;
110      unsigned long sample_time = 0, time = 0;
111      unsigned long maxkbytes = 0; // kilobytes
112      unsigned long maxseconds = 0; // seconds
113      unsigned long maxmillis = 0;
115      unsigned long max_vsize = 0, max_rss = 0;
116      unsigned long start, end;
118      memtime_info_t info;
119      memset (&info, 0, sizeof info);
121      outf = stderr;
123      if (argc < 2) {
124           usage(stderr, argv[0]);
125           exit(EXIT_FAILURE);
126      }
128      while ((opt = getopt(argc, argv, "+eht:m:c:o:")) != -1) {
130           switch (opt) {
131           case 'e' :
132                echo_args = 1;
133                break;
135           case 't' :
136                errno = 0;
137                sample_time = strtoul(optarg, &endptr, 10);
138                if (errno) {
139                     perror("Illegal argument to t option");
140                     exit(EXIT_FAILURE);
141                } else if (endptr == optarg || *endptr != '\0') {
142                     fprintf(stderr, "Illegal argument to t option\n");
143                     exit(EXIT_FAILURE);
144                }
145                break;
146           case 'm' :
147                errno = 0;
148                maxkbytes =  strtoul(optarg, &endptr, 10);
149                if (errno) {
150                     perror("Illegal argument to m option");
151                     exit(EXIT_FAILURE);
152                } else if (endptr == optarg || *endptr != '\0') {
153                     fprintf(stderr, "Illegal argument to m option\n");
154                     exit(EXIT_FAILURE);
155                }
156                break;
158           case 'c' :
159                errno = 0;
160                maxseconds = strtoul(optarg, &endptr, 10);
161                if (errno) {
162                     perror("Illegal argument to c option");
163                     exit(EXIT_FAILURE);
164                } else if (endptr == optarg || *endptr != '\0') {
165                     fprintf(stderr, "Illegal argument to c option\n");
166                     exit(EXIT_FAILURE);
167                }
168                maxmillis = 1000*maxseconds;
169                break;
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;
187           case 'h':
188                usage (stdout, argv[0]);
189                exit(EXIT_SUCCESS);
191           default:
192                exit(EXIT_FAILURE);
193           }
194      }
196      if (optind == argc) {
197           fprintf(stderr, "Missing command\n");
198           exit(EXIT_FAILURE);
199      }
201      if (echo_args) {
202           fprintf(stderr,"Command line: ");
203           for (i = optind; i < argc; i++)
204                fprintf(outf,"%s ", argv[i]);
205           fprintf(outf,"\n");
206      }
208      start = get_time();
210      switch (kid = sampling_fork()) {
211      case -1 :
212           perror("sampling_fork failed");
213           exit(EXIT_FAILURE);
215      case 0 :
216 #if defined(CAN_USE_RLIMIT_VSIZE)
217           if (maxkbytes>0) {
218                set_mem_limit(maxkbytes*1024);
219           }
220 #endif
221 #if defined(CAN_USE_RLIMIT_CPU)
222           if (maxseconds>0) {
223                set_cpu_limit(maxseconds);
224           }
225 #endif
226           execvp(argv[optind], &(argv[optind]));
227           perror("exec failed");
228           exit(EXIT_FAILURE);
230      default :
231           break;
232      }
234      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      }
239      sigprof_prev = signal (SIGPROF, handle_sigprof);
241      do {
242           get_sample(&info);
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);
247           if (sample_time) {
248                time++;
249                if (time >= 10 * sample_time) {
250                     time = 0;
251                     print_stats = 1;
252                }
253           }
255           if (print_stats) {
256                end = get_time();
258                fprintf(outf,"%.2f user, %.2f system, %.2f elapsed"
259                        " -- 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);
264                fflush(outf);
265                print_stats = 0;               
266           }
268           usleep(100000);
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)));
278 #if !defined(CAN_USE_RLIMIT_VSIZE)
279           if ((maxkbytes>0) && (max_vsize>maxkbytes)) {
280                xkill(kid, SIGKILL);
281           }
282 #endif
283 #if !defined(CAN_USE_RLIMIT_CPU)
284           if ((maxmillis>0) && (info.utime_ms>maxmillis)) {
285                xkill(kid, SIGKILL);
286           }
287 #endif
288      } while (!exit_flag);
290      end = get_time();
292      for (i = 0; caught_signals[i] != 0; ++i)
293           signal (caught_signals[i], signal_prev[caught_signals[i]]);
294      signal (SIGPROF, sigprof_prev);
296      if (WIFEXITED(kid_status)) {
297           fprintf(outf, "Exit [%d]\n", WEXITSTATUS(kid_status));
298      } else {
299           fprintf(outf, "Killed [%d]\n", WTERMSIG(kid_status));
300      }
302      {
303           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);
310           fprintf(outf, "%.2f user, %.2f system, %.2f elapsed -- "
311                   "Max VSize = %luKB, Max RSS = %luKB\n",
312                   kid_utime, kid_stime, (double)(end - start) / 1000.0,
313                   max_vsize, max_rss);
314      }
316      if(WIFEXITED(kid_status))
317           exit(WEXITSTATUS(kid_status));
318      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:
323 #ifdef SIGXCPU
324           case SIGXCPU:
325 #endif
326 #ifdef SIGXFSZ
327           case SIGXFSZ:
328 #endif
329                raise (csig);
330           }
331           fprintf (outf, "%s: child died with signal %d, aborting.\n",
332                    argv[0], csig);
333           abort ();
334      }