Xref: utzoo comp.unix.xenix:6167 alt.sources:623 Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!rutgers!apple!ames!elroy!ucla-cs!uci-ics!zardoz!conexch!sandy From: sandy@conexch.UUCP (Sandford Zelkovitz) Newsgroups: comp.unix.xenix,alt.sources Subject: lload for xenix Keywords: lload xenix Lars_Pensj Message-ID: <30067@conexch.UUCP> Date: 27 May 89 06:13:07 GMT Organization: The Consultants' Exchange, Orange County, CA Lines: 270 Below, is my ported version of lload.c for Xenix. The original code, by Lars Pensj from Myab Gothenburg, Sweden, was distributed in alt.sources. --------------------------- cut for lload.c ---------------------------- /* * Load Average deamon. * * The load average is updated every constant time interval, and the result * written to a file as 3 double values. * * The load average is for the last 1, 5 and 15 minutes (3 values). * * A second argument (-v) will set the program in a verbose mode, writing * the load average to the standard output and not to the file. * * Preferably start this process from the inittab. It needs special * priviledges to read from /dev/kmem. * * The following processes are regarded as "runnning": * A process that has the SRUN status (simply running). * A process that is being created (SIDLE). * A process that is being swapped out (SXBRK). * A process that waits for disk I/O. * * A process is regarded as waiting for disk I/O if it is SSLEEP and * has wchan set to a buf in the buffer pool. * * The sleep is implemented using poll on a stream device, not the * more usual sleep() call. Why ? * Because you do not want to wake up simultaneosly with other programs * doing sleep(), which might give wrong load average. * Of course, if you do not have the stream pipe device, use the normal * sleep(). */ /* For Xenix: compile as follows: cc -Ox lload.c -lm -lx -o lload */ /* Modified for Xenix by Sanford Zelkovitz XBBS 714-898-8634 */ #include #ifdef M_XENIX #include #else #include #endif #include #include #include #include #include #include #include #ifdef M_XENIX #include #else #include #include #endif #include #include /* #define DEBUG */ /* Will append all values in a debug file */ #define LOADAV "/etc/loadav" /* Where to write the load avarge */ #define STREAM_PIPE "/dev/spx" /* Used for polling with timeout */ /* * You may or may not need the '_' in the following names. */ #define VAR_NAME "_v" #ifdef M_I386 #define BUF_NAME "_pbuf" #else #define BUF_NAME "_buf" #endif #define PROC_NAME "_proc" struct nlist nl[] = { {VAR_NAME}, {BUF_NAME}, {PROC_NAME}, {0}, }; int loadfile; /* file descr to result file */ struct proc *p; struct var v; int kmem; struct nlist *v_p, *proc_p, *buf_p; int size; int first_buf, last_buf; int sleeping = 1; /* Poll frequency in seconds */ int verbose = 0; double av[3] = { 0.0, 0.0, 0.0 }; /* The loadaverage */ double apa[3]; /* Holding constants */ main(argc, argv) char **argv; { int i, n, n_run, n_disk; #ifdef DEBUG int debug_fd; char buff[100]; debug_fd = open("/tmp/loadavdebug", O_CREAT|O_WRONLY); #endif if (argc == 2 && strcmp(argv[1], "-v") == 0) { verbose = 1; printf("Verbose\n"); } kmem = open("/dev/kmem", 0); if (kmem == -1) { perror("/dev/kmem"); exit(1); } if (!verbose) { loadfile = open(LOADAV, 1|O_CREAT,0664); if (loadfile == -1) { fprintf(stderr, "%s:", argv[0]); perror(LOADAV); exit(1); } } #ifdef M_XENIX if (nlist("/xenix", nl) == -1) { #else if (nlist("/unix", nl) == -1) { #endif perror("nlist"); exit(1); } for (i=0; nl[i].n_name; i++) { #ifdef M_XENIX if(nl[i].n_name[0] == '\0') break; #endif #ifdef DEBUG fprintf(stderr, "nl[%d] = %s\n", i, nl[i].n_name); #endif if (nl[i].n_value == 0) { fprintf(stderr, "Could not get address for %s\n", nl[i].n_name); exit(1); } if (strcmp(nl[i].n_name, VAR_NAME) == 0) v_p = &nl[i]; if (strcmp(nl[i].n_name, PROC_NAME) == 0) proc_p = &nl[i]; if (strcmp(nl[i].n_name, BUF_NAME) == 0) buf_p = &nl[i]; } /* * Setup the constants used for computing load average. */ apa[0] = exp(-sleeping/60.0); apa[1] = exp(-sleeping/300.0); apa[2] = exp(-sleeping/900.0); /* * Start looping */ while(1) { /* * Read the 'v' structure every time. It says how * many procs are used. */ if (lseek(kmem, v_p->n_value, 0) == -1) { perror("lseek v"); exit(1); } if (read(kmem, &v, sizeof v) == -1) { perror("read v"); exit(1); } size = (struct proc *)v.ve_proc - (struct proc *)proc_p->n_value; first_buf = buf_p->n_value; last_buf = first_buf + v.v_buf * sizeof (struct buf); if (lseek(kmem, proc_p->n_value, 0) == -1) { perror("lseek proc"); exit(1); } p = (struct proc *)malloc(size * sizeof (struct proc)); if (p == 0) { fprintf(stderr, "Could not malloc %d bytes\n", size * sizeof (struct proc)); exit(1); } n = read(kmem, p, size * sizeof (struct proc)); if (n != size * sizeof (struct proc)) { if (n == -1) { perror("read procs"); exit(1); } fprintf(stderr, "Could only read %d (%d) procs\n", n, size); size = n / sizeof (struct proc); } n_run = 0; n_disk = 0; for (i=0; i= first_buf && (unsigned int)p[i].p_wchan < last_buf) n_disk++; } /* * Update the load average using a decay filter. */ for (i = 0; i < 3; i++) av[i] = apa[i] * av[i] + (n_run + n_disk) * (1.0 - apa[i]); if (!verbose) { if (lseek(loadfile, 0L, 0) == -1) { fprintf(stderr, "Couldn't seek in %s\n", LOADAV); exit(1); } if (write(loadfile, (char *)av, sizeof av) != sizeof av) { perror(argv[0]); exit(1); } } else printf("(%d %d) %f %f %f\n", n_run, n_disk, av[0], av[1], av[2]); #ifdef DEBUG sprintf(buff, "(%d %d) %4.2f\n", n_run, n_disk, av[0]); write(debug_fd, buff, strlen(buff)); #endif (void)nap(sleeping * 1000); free(p); } } #ifndef M_XENIX /* * Use a stream pipe to implement a sleep. * We have a stream pipe for ourselves, so we know noone will write * on it. */ nap(milli) { static int fd = 0; static struct pollfd pollfd; if (fd == 0) { fd = open(STREAM_PIPE, 0); if (fd == -1) { perror(STREAM_PIPE); exit(1); } pollfd.fd = fd; pollfd.events = POLLIN; } if (poll(&pollfd, 1, milli) == -1) { perror("nap: poll"); exit(1); } if (pollfd.revents != 0) { fprintf(stderr, "nap: poll: got something\n"); exit(1); } } #endif