Hi List,
For all you memory resource watchers with larger scores than bank-accounts,
here's a little something to keep track of it all. Use it to complain to
us about Lily's phenomenal resource appetite.
Greetings,
Jan.
PS: I'm not a system-level programmer, don't flame me (rather, send a patch :-)
/*
proc-time.c -- Get resource usage from /proc for a command (GNU/Linux).
awaiting PC's linux-mm patch for getrusage
source file of the GNU LilyPond music typesetter
Licence: GNU GPL
(c) 2000 Jan Nieuwenhuizen <[EMAIL PROTECTED]>
Translated from Python prototype
GNU time doesn't report memory stuff on FreeBSD either,
but I couldn't find an easy way like to get this info (like /proc).
*/
#include <getopt.h>
#include <stdio.h>
char const* name = "proc-time";
char const* version = "1.3.55";
char const* short_opts = "bhi:ntvV";
struct option long_opts[] =
{
{"heartbeat", no_argument, 0, 'b'},
{"help", no_argument, 0, 'h'},
{"interval", required_argument, 0, 'i'},
{"no-fork", no_argument, 0, 'n'},
{"test", no_argument, 0, 't'},
{"version", no_argument, 0, 'v'},
{"verbose", no_argument, 0, 'V'},
{0, no_argument, 0, 0}
};
int test = 0;
int nofork = 0;
int heartbeat = 0;
float interval = 0.5;
int status = 0;
int verbose = 0;
void
identify (FILE* f)
{
fprintf (f, "%s from LilyPond %s\n", name, version);
}
void
print_usage ()
{
identify (stdout);
printf ("\n"
"Usage: %s [OPTION]... COMMAND\n"
"\n"
"Get resource usage from /proc for COMMAND (GNU/Linux).\n"
"\n"
"Options:\n"
" -b, --heartbeat show memory info at every heartbeat\n"
" -h, --help this help\n"
" -i, --interval=TIME set heartbeat to TIME seconds\n"
" -n, --no-fork don't fork\n"
" -t, --test test mode\n"
" -V, --verbose be verbose\n"
" -v, --version version information\n"
"\n", name);
}
void
print_version ()
{
printf ("%s (GNU LilyPond) %s", name, version);
}
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/unistd.h>
#define JIF_TO_SEC 0.01
#define PAGE_SIZE 4096
#define PAGE_TO_MEG PAGE_SIZE / (1024 * 1024)
#define MAX(a, b) (a < b ? b : a)
enum stat_enum
{
STAT_USER_JIFFIES = 13,
STAT_SYSTEM_JIFFIES,
STAT_COUNTER = 17,
STAT_MAX
};
enum statm_enum
{
STATM_SIZE = 0,
STATM_RSS,
STATM_SHARED,
STATM_CODE,
STATM_DATA,
STATM_LIB,
STATM_DIRTY,
STATM_MAX
};
struct process_info
{
pid_t id;
int t;
char stat_name[81];
char statm_name[81];
int stat_i[STAT_MAX];
int max_size;
int cum_size;
int max_rss;
int cum_rss;
struct timeval start_tv;
struct timeval stop_tv;
};
struct process_info child_pi;
void
init (struct process_info* pi, pid_t pid)
{
pi->id = pid;
pi->t = 0;
snprintf (pi->statm_name, sizeof (pi->statm_name) - 1,
"/proc/%d/statm", pi->id);
snprintf (pi->stat_name, sizeof (pi->stat_name) - 1,
"/proc/%d/stat", pi->id);
gettimeofday (&pi->start_tv, 0);
}
void
update_mem_stats (struct process_info* pi)
{
FILE* f = fopen (pi->statm_name, "r");
if (f)
{
int statm_i[STATM_MAX];
if (fscanf (f, "%d %d %d %d %d %d %d",
&statm_i[0],
&statm_i[1],
&statm_i[2],
&statm_i[3],
&statm_i[4],
&statm_i[5],
&statm_i[6])
>= STATM_MAX - 1)
{
pi->t++;
pi->max_size = MAX (statm_i[STATM_SIZE], pi->max_size);
pi->cum_size += statm_i[STATM_SIZE];
pi->max_rss = MAX (statm_i[STATM_RSS], pi->max_rss);
pi->cum_rss += statm_i[STATM_RSS];
}
else
fprintf (stderr, "%s: scanf failed: %s", strerror (errno));
fclose (f);
}
else
fprintf (stderr, "%s: reading failed: %s", strerror (errno));
}
void
print_mem_stats (struct process_info* pi)
{
if (pi->t)
{
int avg_size = pi->cum_size / pi->t;
int avg_rss = pi->cum_rss / pi->t;
fprintf (stderr, "MAXSIZE: %6.3fM(%d), MAXRSS: %6.3fM(%d)\n",
(float)pi->max_size * PAGE_TO_MEG, pi->max_size,
(float)pi->max_rss * PAGE_TO_MEG, pi->max_rss);
fprintf (stderr, "AVGSIZE: %6.3fM(%d), AVGRSS: %6.3fM(%d)\n",
(float)avg_size * PAGE_TO_MEG, avg_size,
(float)avg_rss * PAGE_TO_MEG, avg_rss);
fflush (stdout);
fflush (stderr);
}
}
void
update_time_stats (struct process_info* pi)
{
FILE* f = fopen (pi->stat_name, "r");
if (f)
{
char name[80];
char s[80];
if (fscanf (f,
"%d %s %s %d %d "
"%d %d %d %d %d "
"%d %d %d %d %d "
"%d %d",
&pi->stat_i[0],
&name,
&s,
&pi->stat_i[3],
&pi->stat_i[4],
&pi->stat_i[5],
&pi->stat_i[6],
&pi->stat_i[7],
&pi->stat_i[8],
&pi->stat_i[9],
&pi->stat_i[10],
&pi->stat_i[11],
&pi->stat_i[12],
&pi->stat_i[13],
&pi->stat_i[14],
&pi->stat_i[15])
>= STAT_MAX - 1)
;
fclose (f);
}
gettimeofday (&pi->stop_tv, 0);
}
void
print_time_stats (struct process_info* pi)
{
fprintf (stderr, "user: %6.2f(%d) system: %6.2f(%d)\n",
(float)pi->stat_i[STAT_USER_JIFFIES] * JIF_TO_SEC,
pi->stat_i[STAT_USER_JIFFIES],
(float)pi->stat_i[STAT_SYSTEM_JIFFIES] * JIF_TO_SEC,
pi->stat_i[STAT_SYSTEM_JIFFIES]);
fprintf (stderr, "elapsed: %6.2f\n",
(float)(pi->stop_tv.tv_sec - pi->start_tv.tv_sec)
+ (pi->stop_tv.tv_usec - pi->start_tv.tv_usec) * 1e-6);
}
void
handler (int arg)
{
(void)arg;
/* urg: child_pi */
update_time_stats (&child_pi);
update_mem_stats (&child_pi);
fprintf (stderr, "\n");
print_time_stats (&child_pi);
print_mem_stats (&child_pi);
if (test)
fprintf (stderr, "handler\n");
exit (status);
}
char**
getargs (int argc, char** argv)
{
int c;
while ((c = getopt_long (argc, argv, short_opts, long_opts, (int *) 0))
!= EOF)
{
switch (c)
{
case 'b':
heartbeat = 1;
break;
case 'h':
print_usage ();
exit (0);
break;
case 'i':
if (optarg)
{
float f;
if (sscanf (optarg, "%f", &f))
{
interval = f;
}
}
break;
case 'n':
nofork = 1;
break;
case 't':
test = 1;
break;
case 'v':
identify (stdout);
exit (0);
break;
case 'V':
verbose = 1;
break;
default:
print_usage ();
exit (2);
break;
}
}
if (optind == argc)
{
print_usage ();
exit (2);
}
return &argv[optind];
}
int
run (char** command)
{
if (nofork)
init (&child_pi, 1);
else
init (&child_pi, fork ());
if (child_pi.id)
{
/* Parent */
int input = 0;
fd_set rfds;
struct timeval tv;
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO (&rfds);
FD_SET (0, &rfds);
update_mem_stats (&child_pi);
if (interval <= 0)
{
waitpid (child_pi.id, &status, 0);
exit (status);
}
while (1)
{
if (waitpid (child_pi.id, &status, WNOHANG))
break;
/*
should we use pause/alarm?
when using the sleeptime of select, we don't see any input??
*/
#if 0
/* man page says we should reset these */
tv.tv_sec = 0;
tv.tv_usec = (unsigned long)(interval*1e6);
#else
usleep ((unsigned long)(interval*1e6));
/* man page says we should reset these */
tv.tv_sec = 0;
tv.tv_usec = 0;
#endif
input = select(1, &rfds, 0, 0, &tv);
update_mem_stats (&child_pi);
if (input)
{
fprintf (stderr, "\n");
print_mem_stats (&child_pi);
getc (stdin);
}
else if (heartbeat)
{
fprintf (stderr, "\n");
print_mem_stats (&child_pi);
}
}
}
else
{
/* Child */
execvp (command[0], command);
}
return status;
}
void
check_file (char const* name, char const* message)
{
FILE* f;
f = fopen (name, "r");
if (f)
{
fclose (f);
}
else
{
fprintf (stderr, "%s: can't open: %s\n", name, message);
}
}
void
check_sanity ()
{
struct process_info init_pi;
init (&init_pi, 1);
check_file (init_pi.statm_name, "memory statistics unavailable");
check_file (init_pi.stat_name, "detailed time statistics unavailable");
}
int
main (int argc, char** argv)
{
int status = 0;
char** command = getargs (argc, argv);
identify (stderr);
check_sanity ();
signal (SIGINT, handler);
signal (SIGCHLD, handler);
signal (SIGTERM, handler);
status = run (command);
if (status)
fprintf (stderr, "Non zero exit status: %d\n", status);
return status;
}
--
Jan Nieuwenhuizen <[EMAIL PROTECTED]> | GNU LilyPond - The music typesetter
http://www.xs4all.nl/~jantien | http://www.lilypond.org