blob: 65be23cd8060646a421a8c61299d1f661df7a4a0 [file] [log] [blame]
//===-- watchdog - Watch Dog Tool -----------------------------------------===//
//
// The SAFECode Project
//
// This file was developed by the LLVM research group and is distributed
// under the University of Illinois Open Source License. See LICENSE.TXT for
// details.
//
// This file is based on code developed by Terry Lambert's mailing list post:
// http://lists.apple.com/archives/unix-porting/2005/Jun/msg00115.html.
//===----------------------------------------------------------------------===//
//
// This program executes another program and monitors its memory usage. If the
// executed program consumes too much memory, then this program will terminate
// it.
//
// This program is needed on Mac OS X because memory limits set by setrlimit()
// are ignored by the kernel. Instead, we must have a program carefully watch
// a program and terminate it if it uses too much memory.
//
//===----------------------------------------------------------------------===//
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
// Process ID to watch. This is global because it is needed by the signal
// handler.
static int pid_to_watch = 0;
// Number of seconds to wait before checking up on the child process
static const unsigned check_interval = 10;
// Maximum amount for the resident set size in kilobytes (i.e., how much
// physical memory is in use).
static const int rss_max_allowable = 4 * 1024 * 1024;
//
// Function: create_process()
//
// Description:
// Create a new child process running the specified program.
//
// Return value:
// 0 - The program was not successfully created.
// Otherwise, the process ID (PID) of the new process is returned.
//
static pid_t
create_process (int argc, char ** argv) {
// Child process ID
pid_t child;
//
// Create a new process.
//
switch (child = fork()) {
// Parent
default:
return child;
// Error
case -1:
return 0;
// Child
case 0:
break;
}
//
// We are now executing in the child process. Do an exec()!
//
//
// The exec() failed.
//
execvp (argv[1], &(argv[1]));
fprintf (stderr, "Failed to execute program\n");
exit (1);
}
//
// Function: check_process()
//
// Description:
// This function is a signal handler. It is called on receipt of an alarm
// signal to check the memory usage of a child process.
//
static void
check_process (int sig) {
char cmd[256];
int rss;
FILE *fp;
//
// If we caught a signal, check on the child process's memory usage.
//
snprintf(cmd, 255, "ps -p %d -o rss | tail -1", pid_to_watch);
if ((fp = popen(cmd, "r")) != NULL) {
if (fscanf(fp, "%d", &rss) == 1) {
if (rss > rss_max_allowable) {
fprintf (stderr, "Terminating bad process %d!\n", pid_to_watch);
kill (pid_to_watch, SIGKILL);
return;
}
}
pclose(fp);
}
//
// Reset the alarm.
//
signal (SIGALRM, check_process);
alarm (check_interval);
return;
}
int
main (int argc, char ** argv) {
//
// Verify that we have sufficient command line arguments.
//
if (argc < 2) {
fprintf (stderr, "Usage: %s <command> [args ...]\n", argv[0]);
exit (1);
}
//
// Execute the program to watch.
//
pid_to_watch = create_process (argc, argv);
if (!pid_to_watch) {
fprintf (stderr, "Failed to create child process\n");
exit (1);
}
//
// Set up an alarm to wake us up.
//
#if defined(__APPLE__)
signal (SIGALRM, check_process);
alarm (check_interval);
#endif
//
// Keep looping until the child terminates or we terminate it.
//
int status;
waitpid (pid_to_watch, &status, 0);
//
// The program has terminated. Get its exit status and use that as our
// exit status.
//
exit (WEXITSTATUS (status));
}