Newsgroups: comp.sources.unix From: lefebvre@athens.dis.anl.gov (William LeFebvre) Subject: v29i013: top-3.3 - top users display, V3.3, Part13/16 References: <1.801816033.16454@gw.home.vix.com> Sender: unix-sources-moderator@gw.home.vix.com Approved: vixie@gw.home.vix.com Submitted-By: lefebvre@athens.dis.anl.gov (William LeFebvre) Posting-Number: Volume 29, Issue 13 Archive-Name: top-3.3/part13 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'display.c' <<'END_OF_FILE' X/* X * Top users/processes display for Unix X * Version 3 X * X * This program may be freely redistributed, X * but this entire comment MUST remain intact. X * X * Copyright (c) 1984, 1989, William LeFebvre, Rice University X * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University X */ X X/* X * This file contains the routines that display information on the screen. X * Each section of the screen has two routines: one for initially writing X * all constant and dynamic text, and one for only updating the text that X * changes. The prefix "i_" is used on all the "initial" routines and the X * prefix "u_" is used for all the "updating" routines. X * X * ASSUMPTIONS: X * None of the "i_" routines use any of the termcap capabilities. X * In this way, those routines can be safely used on terminals that X * have minimal (or nonexistant) terminal capabilities. X * X * The routines are called in this order: *_loadave, i_timeofday, X * *_procstates, *_cpustates, *_memory, *_message, *_header, X * *_process, u_endscreen. X */ X X#include "os.h" X#include X#include X X#include "screen.h" /* interface to screen package */ X#include "layout.h" /* defines for screen position layout */ X#include "display.h" X#include "top.h" X#include "top.local.h" X#include "boolean.h" X#include "machine.h" /* we should eliminate this!!! */ X#include "utils.h" X X#ifdef DEBUG XFILE *debug; X#endif X X/* imported from screen.c */ Xextern int overstrike; X Xstatic int lmpid = 0; Xstatic int last_hi = 0; /* used in u_process and u_endscreen */ Xstatic int lastline = 0; Xstatic int display_width = MAX_COLS; X X#define lineindex(l) ((l)*display_width) X Xchar *printable(); X X/* things initialized by display_init and used thruout */ X X/* buffer of proc information lines for display updating */ Xchar *screenbuf = NULL; X Xstatic char **procstate_names; Xstatic char **cpustate_names; Xstatic char **memory_names; X Xstatic int num_procstates; Xstatic int num_cpustates; Xstatic int num_memory; X Xstatic int *lprocstates; Xstatic int *lcpustates; Xstatic int *lmemory; X Xstatic enum { OFF, ON, ERASE } header_status = ON; X Xstatic int string_count(); Xstatic void summary_format(); Xstatic void line_update(); X Xint display_resize() X X{ X register int lines; X X /* first, deallocate any previous buffer that may have been there */ X if (screenbuf != NULL) X { X free(screenbuf); X } X X /* calculate the current dimensions */ X /* if operating in "dumb" mode, we only need one line */ X lines = smart_terminal ? screen_length - Header_lines : 1; X X /* we don't want more than MAX_COLS columns, since the machine-dependent X modules make static allocations based on MAX_COLS and we don't want X to run off the end of their buffers */ X display_width = screen_width; X if (display_width >= MAX_COLS) X { X display_width = MAX_COLS - 1; X } X X /* now, allocate space for the screen buffer */ X screenbuf = (char *)malloc(lines * display_width); X if (screenbuf == (char *)NULL) X { X /* oops! */ X return(-1); X } X X /* return number of lines available */ X /* for dumb terminals, pretend like we can show any amount */ X return(smart_terminal ? lines : Largest); X} X Xint display_init(statics) X Xstruct statics *statics; X X{ X register int lines; X X /* call resize to do the dirty work */ X lines = display_resize(); X X /* only do the rest if we need to */ X if (lines > -1) X { X /* save pointers and allocate space for procstate and cpustate names */ X procstate_names = statics->procstate_names; X num_procstates = string_count(procstate_names); X lprocstates = (int *)malloc(num_procstates * sizeof(int)); X X cpustate_names = statics->cpustate_names; X num_cpustates = string_count(cpustate_names); X lcpustates = (int *)malloc(num_cpustates * sizeof(int)); X X memory_names = statics->memory_names; X num_memory = string_count(memory_names); X lmemory = (int *)malloc(num_memory * sizeof(int)); X } X X /* return number of lines available */ X return(lines); X} X Xi_loadave(mpid, avenrun) X Xint mpid; Xdouble *avenrun; X X{ X register int i; X X /* i_loadave also clears the screen, since it is first */ X clear(); X X /* mpid == -1 implies this system doesn't have an _mpid */ X if (mpid != -1) X { X printf("last pid: %5d; ", mpid); X } X X printf("load averages"); X X for (i = 0; i < 3; i++) X { X printf("%c %5.2f", X i == 0 ? ':' : ',', X avenrun[i]); X } X lmpid = mpid; X} X Xu_loadave(mpid, avenrun) X Xint mpid; Xdouble *avenrun; X X{ X register int i; X X if (mpid != -1) X { X /* change screen only when value has really changed */ X if (mpid != lmpid) X { X Move_to(x_lastpid, y_lastpid); X printf("%5d", mpid); X lmpid = mpid; X } X X /* i remembers x coordinate to move to */ X i = x_loadave; X } X else X { X i = x_loadave_nompid; X } X X /* move into position for load averages */ X Move_to(i, y_loadave); X X /* display new load averages */ X /* we should optimize this and only display changes */ X for (i = 0; i < 3; i++) X { X printf("%s%5.2f", X i == 0 ? "" : ", ", X avenrun[i]); X } X} X Xi_timeofday(tod) X Xtime_t *tod; X X{ X /* X * Display the current time. X * "ctime" always returns a string that looks like this: X * X * Sun Sep 16 01:03:52 1973 X * 012345678901234567890123 X * 1 2 X * X * We want indices 11 thru 18 (length 8). X */ X X if (smart_terminal) X { X Move_to(screen_width - 8, 0); X } X else X { X fputs(" ", stdout); X } X#ifdef DEBUG X { X char *foo; X foo = ctime(tod); X fputs(foo, stdout); X } X#endif X printf("%-8.8s\n", &(ctime(tod)[11])); X lastline = 1; X} X Xstatic int ltotal = 0; Xstatic char procstates_buffer[128]; X X/* X * *_procstates(total, brkdn, names) - print the process summary line X * X * Assumptions: cursor is at the beginning of the line on entry X * lastline is valid X */ X Xi_procstates(total, brkdn) X Xint total; Xint *brkdn; X X{ X register int i; X X /* write current number of processes and remember the value */ X printf("%d processes:", total); X ltotal = total; X X /* put out enough spaces to get to column 15 */ X i = digits(total); X while (i++ < 4) X { X putchar(' '); X } X X /* format and print the process state summary */ X summary_format(procstates_buffer, brkdn, procstate_names); X fputs(procstates_buffer, stdout); X X /* save the numbers for next time */ X memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); X} X Xu_procstates(total, brkdn) X Xint total; Xint *brkdn; X X{ X static char new[128]; X register int i; X X /* update number of processes only if it has changed */ X if (ltotal != total) X { X /* move and overwrite */ X#if (x_procstate == 0) X Move_to(x_procstate, y_procstate); X#else X /* cursor is already there...no motion needed */ X /* assert(lastline == 1); */ X#endif X printf("%d", total); X X /* if number of digits differs, rewrite the label */ X if (digits(total) != digits(ltotal)) X { X fputs(" processes:", stdout); X /* put out enough spaces to get to column 15 */ X i = digits(total); X while (i++ < 4) X { X putchar(' '); X } X /* cursor may end up right where we want it!!! */ X } X X /* save new total */ X ltotal = total; X } X X /* see if any of the state numbers has changed */ X if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) X { X /* format and update the line */ X summary_format(new, brkdn, procstate_names); X line_update(procstates_buffer, new, x_brkdn, y_brkdn); X } X} X X/* X * *_cpustates(states, names) - print the cpu state percentages X * X * Assumptions: cursor is on the PREVIOUS line X */ X Xi_cpustates(states) X Xregister int *states; X X{ X register int i = 0; X register int value; X register char **names = cpustate_names; X register char *thisname; X X printf("\nCpu states: "); X lastline++; X while ((thisname = *names++) != NULL) X { X if (*thisname != '\0') X { X /* retrieve the value and remember it */ X value = *states++; X X /* if percentage is >= 1000, print it as 100% */ X printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"), X i++ == 0 ? "" : ", ", X ((float)value)/10., X thisname); X } X } X} X Xu_cpustates(states) X Xregister int *states; X X{ X register int i = 0; X register int value; X register char **names = cpustate_names; X register char *thisname; X X Move_to(12, y_cpustates); X lastline = y_cpustates; X X /* we could be much more optimal about this */ X while ((thisname = *names++) != NULL) X { X if (*thisname != '\0') X { X /* retrieve value and remember it */ X value = *states++; X X /* if percentage is >= 1000, print it as 100% */ X printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"), X i++ == 0 ? "" : ", ", X ((double)value)/10., X thisname); X } X } X} X Xz_cpustates() X X{ X register int i = 0; X register char **names = cpustate_names; X register char *thisname; X X printf("\nCpu states: "); X lastline++; X X while ((thisname = *names++) != NULL) X { X if (*thisname != '\0') X { X printf("%s %% %s", i++ == 0 ? "" : ", ", thisname); X } X } X} X X/* X * *_memory(stats) - print "Memory: " followed by the memory summary string X * X * Assumptions: cursor is on "lastline" X * for i_memory ONLY: cursor is on the previous line X */ X Xchar memory_buffer[MAX_COLS]; X Xi_memory(stats) X Xint *stats; X X{ X fputs("\nMemory: ", stdout); X lastline++; X X /* format and print the memory summary */ X summary_format(memory_buffer, stats, memory_names); X fputs(memory_buffer, stdout); X} X Xu_memory(stats) X Xint *stats; X X{ X static char new[MAX_COLS]; X X /* format the new line */ X summary_format(new, stats, memory_names); X line_update(memory_buffer, new, x_mem, y_mem); X} X X/* X * *_message() - print the next pending message line, or erase the one X * that is there. X * X * Note that u_message is (currently) the same as i_message. X * X * Assumptions: lastline is consistent X */ X X/* X * i_message is funny because it gets its message asynchronously (with X * respect to screen updates). X */ X Xstatic char next_msg[MAX_COLS + 5]; Xstatic int msglen = 0; X/* Invariant: msglen is always the length of the message currently displayed X on the screen (even when next_msg doesn't contain that message). */ X Xi_message() X X{ X while (lastline < y_message) X { X fputc('\n', stdout); X lastline++; X } X if (next_msg[0] != '\0') X { X standout(next_msg); X msglen = strlen(next_msg); X next_msg[0] = '\0'; X } X else if (msglen > 0) X { X (void) clear_eol(msglen); X msglen = 0; X } X} X Xu_message() X X{ X#ifdef notdef X putchar('\n'); X if (msglen > 0) X { X (void) clear_eol(msglen); X msglen = 0; X } X#endif X i_message(); X} X Xstatic int header_length; X X/* X * *_header(text) - print the header for the process area X * X * Assumptions: cursor is on the previous line and lastline is consistent X */ X Xi_header(text) X Xchar *text; X X{ X header_length = strlen(text); X if (header_status == ON) X { X putchar('\n'); X fputs(text, stdout); X lastline++; X } X else if (header_status == ERASE) X { X header_status = OFF; X } X} X X/*ARGSUSED*/ Xu_header(text) X Xchar *text; /* ignored */ X X{ X if (header_status == ERASE) X { X putchar('\n'); X lastline++; X clear_eol(header_length); X header_status = OFF; X } X} X X/* X * *_process(line, thisline) - print one process line X * X * Assumptions: lastline is consistent X */ X Xi_process(line, thisline) X Xint line; Xchar *thisline; X X{ X register char *p; X register char *base; X X /* make sure we are on the correct line */ X while (lastline < y_procs + line) X { X putchar('\n'); X lastline++; X } X X /* truncate the line to conform to our current screen width */ X thisline[display_width] = '\0'; X X /* write the line out */ X fputs(thisline, stdout); X X /* copy it in to our buffer */ X base = smart_terminal ? screenbuf + lineindex(line) : screenbuf; X p = strecpy(base, thisline); X X /* zero fill the rest of it */ X memzero(p, display_width - (p - base)); X} X Xu_process(line, newline) X Xint line; Xchar *newline; X X{ X register char *optr; X register int screen_line = line + Header_lines; X register char *bufferline; X X /* remember a pointer to the current line in the screen buffer */ X bufferline = &screenbuf[lineindex(line)]; X X /* truncate the line to conform to our current screen width */ X newline[display_width] = '\0'; X X /* is line higher than we went on the last display? */ X if (line >= last_hi) X { X /* yes, just ignore screenbuf and write it out directly */ X /* get positioned on the correct line */ X if (screen_line - lastline == 1) X { X putchar('\n'); X lastline++; X } X else X { X Move_to(0, screen_line); X lastline = screen_line; X } X X /* now write the line */ X fputs(newline, stdout); X X /* copy it in to the buffer */ X optr = strecpy(bufferline, newline); X X /* zero fill the rest of it */ X memzero(optr, display_width - (optr - bufferline)); X } X else X { X line_update(bufferline, newline, 0, line + Header_lines); X } X} X Xu_endscreen(hi) X Xregister int hi; X X{ X register int screen_line = hi + Header_lines; X register int i; X X if (smart_terminal) X { X if (hi < last_hi) X { X /* need to blank the remainder of the screen */ X /* but only if there is any screen left below this line */ X if (lastline + 1 < screen_length) X { X /* efficiently move to the end of currently displayed info */ X if (screen_line - lastline < 5) X { X while (lastline < screen_line) X { X putchar('\n'); X lastline++; X } X } X else X { X Move_to(0, screen_line); X lastline = screen_line; X } X X if (clear_to_end) X { X /* we can do this the easy way */ X putcap(clear_to_end); X } X else X { X /* use clear_eol on each line */ X i = hi; X while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi) X { X putchar('\n'); X } X } X } X } X last_hi = hi; X X /* move the cursor to a pleasant place */ X Move_to(x_idlecursor, y_idlecursor); X lastline = y_idlecursor; X } X else X { X /* separate this display from the next with some vertical room */ X fputs("\n\n", stdout); X } X} X Xdisplay_header(t) X Xint t; X X{ X if (t) X { X header_status = ON; X } X else if (header_status == ON) X { X header_status = ERASE; X } X} X X/*VARARGS2*/ Xnew_message(type, msgfmt, a1, a2, a3) X Xint type; Xchar *msgfmt; Xcaddr_t a1, a2, a3; X X{ X register int i; X X /* first, format the message */ X (void) sprintf(next_msg, msgfmt, a1, a2, a3); X X if (msglen > 0) X { X /* message there already -- can we clear it? */ X if (!overstrike) X { X /* yes -- write it and clear to end */ X i = strlen(next_msg); X if ((type & MT_delayed) == 0) X { X type & MT_standout ? standout(next_msg) : X fputs(next_msg, stdout); X (void) clear_eol(msglen - i); X msglen = i; X next_msg[0] = '\0'; X } X } X } X else X { X if ((type & MT_delayed) == 0) X { X type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout); X msglen = strlen(next_msg); X next_msg[0] = '\0'; X } X } X} X Xclear_message() X X{ X if (clear_eol(msglen) == 1) X { X putchar('\r'); X } X} X Xreadline(buffer, size, numeric) X Xchar *buffer; Xint size; Xint numeric; X X{ X register char *ptr = buffer; X register char ch; X register char cnt = 0; X register char maxcnt = 0; X X /* allow room for null terminator */ X size -= 1; X X /* read loop */ X while ((fflush(stdout), read(0, ptr, 1) > 0)) X { X /* newline means we are done */ X if ((ch = *ptr) == '\n') X { X break; X } X X /* handle special editing characters */ X if (ch == ch_kill) X { X /* kill line -- account for overstriking */ X if (overstrike) X { X msglen += maxcnt; X } X X /* return null string */ X *buffer = '\0'; X putchar('\r'); X return(-1); X } X else if (ch == ch_erase) X { X /* erase previous character */ X if (cnt <= 0) X { X /* none to erase! */ X putchar('\7'); X } X else X { X fputs("\b \b", stdout); X ptr--; X cnt--; X } X } X /* check for character validity and buffer overflow */ X else if (cnt == size || (numeric && !isdigit(ch)) || X !isprint(ch)) X { X /* not legal */ X putchar('\7'); X } X else X { X /* echo it and store it in the buffer */ X putchar(ch); X ptr++; X cnt++; X if (cnt > maxcnt) X { X maxcnt = cnt; X } X } X } X X /* all done -- null terminate the string */ X *ptr = '\0'; X X /* account for the extra characters in the message area */ X /* (if terminal overstrikes, remember the furthest they went) */ X msglen += overstrike ? maxcnt : cnt; X X /* return either inputted number or string length */ X putchar('\r'); X return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); X} X X/* internal support routines */ X Xstatic int string_count(pp) X Xregister char **pp; X X{ X register int cnt; X X cnt = 0; X while (*pp++ != NULL) X { X cnt++; X } X return(cnt); X} X Xstatic void summary_format(str, numbers, names) X Xchar *str; Xint *numbers; Xregister char **names; X X{ X register char *p; X register int num; X register char *thisname; X register int useM = No; X X /* format each number followed by its string */ X p = str; X while ((thisname = *names++) != NULL) X { X /* get the number to format */ X num = *numbers++; X X /* display only non-zero numbers */ X if (num > 0) X { X /* is this number in kilobytes and it is larger than 9999? */ X if (thisname[0] == 'K' && num >= 10000) X { X /* yes: format it as megabytes, rounded */ X p = strecpy(p, itoa((num+500)/1000)); X X /* replace 'K' with 'M' in string to display */ X *p++ = 'M'; X p = strecpy(p, thisname+1); X } X else X { X p = strecpy(p, itoa(num)); X p = strecpy(p, thisname); X } X } X X /* ignore negative numbers, but display corresponding string */ X else if (num < 0) X { X p = strecpy(p, thisname); X } X } X X /* if the last two characters in the string are ", ", delete them */ X p -= 2; X if (p >= str && p[0] == ',' && p[1] == ' ') X { X *p = '\0'; X } X} X Xstatic void line_update(old, new, start, line) X Xregister char *old; Xregister char *new; Xint start; Xint line; X X{ X register int ch; X register int diff; X register int newcol = start + 1; X register int lastcol = start; X char cursor_on_line = No; X char *current; X X /* compare the two strings and only rewrite what has changed */ X current = old; X#ifdef DEBUG X fprintf(debug, "line_update, starting at %d\n", start); X fputs(old, debug); X fputc('\n', debug); X fputs(new, debug); X fputs("\n-\n", debug); X#endif X X /* start things off on the right foot */ X /* this is to make sure the invariants get set up right */ X if ((ch = *new++) != *old) X { X if (line - lastline == 1 && start == 0) X { X putchar('\n'); X } X else X { X Move_to(start, line); X } X cursor_on_line = Yes; X putchar(ch); X *old = ch; X lastcol = 1; X } X old++; X X /* X * main loop -- check each character. If the old and new aren't the X * same, then update the display. When the distance from the X * current cursor position to the new change is small enough, X * the characters that belong there are written to move the X * cursor over. X * X * Invariants: X * lastcol is the column where the cursor currently is sitting X * (always one beyond the end of the last mismatch). X */ X do /* yes, a do...while */ X { X if ((ch = *new++) != *old) X { X /* new character is different from old */ X /* make sure the cursor is on top of this character */ X diff = newcol - lastcol; X if (diff > 0) X { X /* some motion is required--figure out which is shorter */ X if (diff < 6 && cursor_on_line) X { X /* overwrite old stuff--get it out of the old buffer */ X printf("%.*s", diff, ¤t[lastcol-start]); X } X else X { X /* use cursor addressing */ X Move_to(newcol, line); X cursor_on_line = Yes; X } X /* remember where the cursor is */ X lastcol = newcol + 1; X } X else X { X /* already there, update position */ X lastcol++; X } X X /* write what we need to */ X if (ch == '\0') X { X /* at the end--terminate with a clear-to-end-of-line */ X (void) clear_eol(strlen(old)); X } X else X { X /* write the new character */ X putchar(ch); X } X /* put the new character in the screen buffer */ X *old = ch; X } X X /* update working column and screen buffer pointer */ X newcol++; X old++; X X } while (ch != '\0'); X X /* zero out the rest of the line buffer -- MUST BE DONE! */ X diff = display_width - newcol; X if (diff > 0) X { X memzero(old, diff); X } X X /* remember where the current line is */ X if (cursor_on_line) X { X lastline = line; X } X} X X/* X * printable(str) - make the string pointed to by "str" into one that is X * printable (i.e.: all ascii), by converting all non-printable X * characters into '?'. Replacements are done in place and a pointer X * to the original buffer is returned. X */ X Xchar *printable(str) X Xchar *str; X X{ X register char *ptr; X register char ch; X X ptr = str; X while ((ch = *ptr) != '\0') X { X if (!isprint(ch)) X { X *ptr = '?'; X } X ptr++; X } X return(str); X} END_OF_FILE if test 21271 -ne `wc -c <'display.c'`; then echo shar: \"'display.c'\" unpacked with wrong size! fi # end of 'display.c' fi if test -f 'machine/m_sunos4.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'machine/m_sunos4.c'\" else echo shar: Extracting \"'machine/m_sunos4.c'\" \(20728 characters\) sed "s/^X//" >'machine/m_sunos4.c' <<'END_OF_FILE' X/* X * top - a top users display for Unix X * X * SYNOPSIS: any Sun running SunOS version 4.x X * X * DESCRIPTION: X * This is the machine-dependent module for SunOS 4.x. X * This makes top work on the following systems: X * SunOS 4.0 X * SunOS 4.0.1 X * SunOS 4.0.2 (including 386i architecture) X * SunOS 4.0.3 X * SunOS 4.1 X * SunOS 4.1.1 X * SunOS 4.1.2 (including MP architectures) X * SunOS 4.1.3 (including MP architectures) X * SunOS 4.1.4 (including MP architectures) X * Solbourne OS/MP PRIOR to 4.1A X * X * LIBS: -lkvm X * X * CFLAGS: -DHAVE_GETOPT X * X * AUTHOR: William LeFebvre X * Solbourne support by David MacKenzie X */ X X/* X * #ifdef MULTIPROCESSOR means Sun MP. X * #ifdef solbourne is for Solbourne. X */ X X#include X#include X X/* make sure param.h gets loaded with KERNEL defined to get PZERO & NZERO */ X#define KERNEL X#include X#undef KERNEL X X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X X#ifdef solbourne X#include X#endif X X/* Older versions of SunOS don't have a typedef for pid_t. X Hopefully this will catch all those cases without causing other problems. X */ X#ifndef __sys_stdtypes_h Xtypedef int pid_t; X#endif X X#include "top.h" X#include "machine.h" X#include "utils.h" X X/* declarations for load_avg */ X#include "loadavg.h" X X/* get_process_info passes back a handle. This is what it looks like: */ X Xstruct handle X{ X struct proc **next_proc; /* points to next valid proc pointer */ X int remaining; /* number of pointers remaining */ X}; X X/* define what weighted cpu is. */ X#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \ X ((pct) / (1.0 - exp((pp)->p_time * logcpu)))) X X/* what we consider to be process size: */ X#define PROCSIZE(pp) ((pp)->p_tsize + (pp)->p_dsize + (pp)->p_ssize) X X/* definitions for indices in the nlist array */ X#define X_AVENRUN 0 X#define X_CCPU 1 X#define X_MPID 2 X#define X_NPROC 3 X#define X_PROC 4 X#define X_TOTAL 5 X#define X_CP_TIME 6 X#define X_PAGES 7 X#define X_EPAGES 8 X Xstatic struct nlist nlst[] = { X#ifdef i386 X { "avenrun" }, /* 0 */ X { "ccpu" }, /* 1 */ X { "mpid" }, /* 2 */ X { "nproc" }, /* 3 */ X { "proc" }, /* 4 */ X { "total" }, /* 5 */ X { "cp_time" }, /* 6 */ X { "pages" }, /* 7 */ X { "epages" }, /* 8 */ X#else X { "_avenrun" }, /* 0 */ X { "_ccpu" }, /* 1 */ X { "_mpid" }, /* 2 */ X { "_nproc" }, /* 3 */ X { "_proc" }, /* 4 */ X { "_total" }, /* 5 */ X { "_cp_time" }, /* 6 */ X { "_pages" }, /* 7 */ X { "_epages" }, /* 8 */ X#ifdef MULTIPROCESSOR X { "_ncpu" }, X#define X_NCPU 9 X { "_xp_time" }, X#define X_XP_TIME 10 X#endif X#endif X { 0 } X}; X X/* X * These definitions control the format of the per-process area X */ X Xstatic char header[] = X " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND"; X/* 0123456 -- field to fill in starts at header+6 */ X#define UNAME_START 6 X X#define Proc_format \ X "%5d %-8.8s %3d %4d %5s %5s %-5s %-6s %5.2f%% %5.2f%% %s" X X X/* process state names for the "STATE" column of the display */ X/* the extra nulls in the string "run" are for adding a slash and X the processor number when needed */ X Xchar *state_abbrev[] = X{ X "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop" X}; X X/* values that we stash away in _init and use in later routines */ X Xstatic double logcpu; Xkvm_t *kd; X X/* these are retrieved from the kernel in _init */ X Xstatic unsigned long proc; Xstatic int nproc; Xstatic load_avg ccpu; Xstatic unsigned long pages; Xstatic unsigned long epages; Xstatic int ncpu = 0; X X/* these are offsets obtained via nlist and used in the get_ functions */ X Xstatic unsigned long mpid_offset; Xstatic unsigned long avenrun_offset; Xstatic unsigned long total_offset; Xstatic unsigned long cp_time_offset; X#ifdef MULTIPROCESSOR Xstatic unsigned long xp_time_offset; X#endif X X/* these are for calculating cpu state percentages */ X Xstatic long cp_time[CPUSTATES]; Xstatic long cp_old[CPUSTATES]; Xstatic long cp_diff[CPUSTATES]; X#ifdef MULTIPROCESSOR Xstatic long xp_time[NCPU][XPSTATES]; X/* for now we only accumulate spin time, but extending this to pick up X other stuff in xp_time is trivial. */ Xstatic long xp_old[NCPU]; X#endif X X/* these are for detailing the process states */ X Xint process_states[7]; Xchar *procstatenames[] = { X "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ", X " zombie, ", " stopped, ", X NULL X}; X X/* these are for detailing the cpu states */ X Xint cpu_states[5]; Xchar *cpustatenames[] = { X "user", "nice", "system", "idle", X#ifdef MULTIPROCESSOR X "spin", X#define XCP_SPIN 4 X#endif X NULL X}; X X/* these are for detailing the memory statistics */ X Xint memory_stats[4]; Xchar *memorynames[] = { X "K available, ", "K in use, ", "K free, ", "K locked", NULL X}; X X/* these are for keeping track of the proc array */ X Xstatic int bytes; Xstatic int pref_len; Xstatic struct proc *pbase; Xstatic struct proc **pref; X X/* these are for getting the memory statistics */ X Xstatic struct page *physpage; Xstatic int bytesize; Xstatic int count; Xstatic int pageshift; /* log base 2 of the pagesize */ X X/* define pagetok in terms of pageshift */ X X#define pagetok(size) ((size) << pageshift) X X/* useful externals */ Xextern int errno; Xextern char *sys_errlist[]; X Xlong lseek(); Xlong time(); X Xmachine_init(statics) X Xstruct statics *statics; X X{ X register int i; X register int pagesize; X X /* initialize the kernel interface */ X if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "top")) == NULL) X { X perror("kvm_open"); X return(-1); X } X X /* get the list of symbols we want to access in the kernel */ X if ((i = kvm_nlist(kd, nlst)) < 0) X { X fprintf(stderr, "top: nlist failed\n"); X return(-1); X } X X#ifdef MULTIPROCESSOR X /* were ncpu and xp_time not found in the nlist? */ X if (i > 0 && nlst[X_NCPU].n_type == 0 && nlst[X_XP_TIME].n_type == 0) X { X /* we were compiled on an MP system but we are not running on one */ X /* so we will pretend this didn't happen and set ncpu = 1 */ X i -= 2; X ncpu = 1; X } X#endif X X#ifdef solbourne X { X unsigned int status, type; X X /* Get the number of CPUs on this system. */ X syscall(SYS_getcpustatus, &status, &ncpu, &type); X } X#endif X X /* make sure they were all found */ X if (i > 0 && check_nlist(nlst) > 0) X { X return(-1); X } X X /* get the symbol values out of kmem */ X (void) getkval(nlst[X_PROC].n_value, (int *)(&proc), sizeof(proc), X nlst[X_PROC].n_name); X (void) getkval(nlst[X_NPROC].n_value, &nproc, sizeof(nproc), X nlst[X_NPROC].n_name); X (void) getkval(nlst[X_CCPU].n_value, (int *)(&ccpu), sizeof(ccpu), X nlst[X_CCPU].n_name); X (void) getkval(nlst[X_PAGES].n_value, (int *)(&pages), sizeof(pages), X nlst[X_PAGES].n_name); X (void) getkval(nlst[X_EPAGES].n_value, (int *)(&epages), sizeof(epages), X nlst[X_EPAGES].n_name); X#ifdef MULTIPROCESSOR X if (ncpu == 0) X { X /* if ncpu > 0 then we are not really on an MP system */ X (void) getkval(nlst[X_NCPU].n_value, (int *)(&ncpu), sizeof(ncpu), X nlst[X_NCPU].n_name); X } X#endif X X /* stash away certain offsets for later use */ X mpid_offset = nlst[X_MPID].n_value; X avenrun_offset = nlst[X_AVENRUN].n_value; X total_offset = nlst[X_TOTAL].n_value; X cp_time_offset = nlst[X_CP_TIME].n_value; X#ifdef MULTIPROCESSOR X xp_time_offset = nlst[X_XP_TIME].n_value; X#endif X X /* this is used in calculating WCPU -- calculate it ahead of time */ X logcpu = log(loaddouble(ccpu)); X X /* allocate space for proc structure array and array of pointers */ X bytes = nproc * sizeof(struct proc); X pbase = (struct proc *)malloc(bytes); X pref = (struct proc **)malloc(nproc * sizeof(struct proc *)); X X /* Just in case ... */ X if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL) X { X fprintf(stderr, "top: can't allocate sufficient memory\n"); X return(-1); X } X X /* allocate a table to hold all the page structs */ X bytesize = epages - pages; X count = bytesize / sizeof(struct page); X physpage = (struct page *)malloc(epages - pages); X if (physpage == NULL) X { X fprintf(stderr, "top: can't allocate sufficient memory\n"); X return(-1); X } X X /* get the page size with "getpagesize" and calculate pageshift from it */ X pagesize = getpagesize(); X pageshift = 0; X while (pagesize > 1) X { X pageshift++; X pagesize >>= 1; X } X X /* we only need the amount of log(2)1024 for our conversion */ X pageshift -= LOG1024; X X#if defined(MULTIPROCESSOR) || defined(solbourne) X /* add a slash to the "run" state abbreviation */ X if (ncpu > 1) X { X state_abbrev[SRUN][3] = '/'; X } X#endif X X /* fill in the statics information */ X statics->procstate_names = procstatenames; X statics->cpustate_names = cpustatenames; X statics->memory_names = memorynames; X X /* all done! */ X return(0); X} X Xchar *format_header(uname_field) X Xregister char *uname_field; X X{ X register char *ptr; X X ptr = header + UNAME_START; X while (*uname_field != '\0') X { X *ptr++ = *uname_field++; X } X X return(header); X} X Xget_system_info(si) X Xstruct system_info *si; X X{ X load_avg avenrun[3]; X long total; X#ifdef MULTIPROCESSOR X long half_total; X#endif X X /* get the cp_time array */ X (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time), X "_cp_time"); X X#ifdef MULTIPROCESSOR X /* get the xp_time array as well */ X if (ncpu > 1) X { X (void) getkval(xp_time_offset, (int *)xp_time, sizeof(xp_time), X "_xp_time"); X } X#endif X X /* get load average array */ X (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun), X "_avenrun"); X X /* get mpid -- process id of last process */ X (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), X "_mpid"); X X /* get the array of physpage descriptors */ X (void) getkval(pages, (int *)physpage, bytesize, "array _page"); X X /* convert load averages to doubles */ X { X register int i; X register double *infoloadp; X register load_avg *sysloadp; X X infoloadp = si->load_avg; X sysloadp = avenrun; X for (i = 0; i < 3; i++) X { X *infoloadp++ = loaddouble(*sysloadp++); X } X } X X /* convert cp_time counts to percentages */ X total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); X X#ifdef MULTIPROCESSOR X /* calculate spin time from all processors */ X if (ncpu > 1) X { X register int c; X register int i; X register long sum; X register long change; X X /* collect differences for each processor and add them */ X sum = 0; X for (i = 0; i < ncpu; i++) X { X c = xp_time[i][XP_SPIN]; X change = c - xp_old[i]; X if (change < 0) X { X /* counter wrapped */ X change = (long)((unsigned long)c - X (unsigned long)xp_old[i]); X } X sum += change; X xp_old[i] = c; X } X X /* X * NOTE: I am assuming that the ticks found in xp_time are X * already included in the ticks accumulated in cp_time. To X * get an accurate reflection, therefore, we have to subtract X * the spin time from the system time and recompute those two X * percentages. X */ X half_total = total / 2l; X cp_diff[CP_SYS] -= sum; X cpu_states[CP_SYS] = (int)((cp_diff[CP_SYS] * 1000 + half_total) / X total); X cpu_states[XCP_SPIN] = (int)((sum * 1000 + half_total) / total); X } X#endif X X /* sum memory statistics */ X { X register struct page *pp; X register int cnt; X register int inuse; X register int free; X register int locked; X X /* bop thru the array counting page types */ X pp = physpage; X inuse = free = locked = 0; X for (cnt = count; --cnt >= 0; pp++) X { X if (pp->p_free) X free++; X else if (pp->p_lock || pp->p_keepcnt > 0) X locked++; X else X inuse++; X } X X /* convert memory stats to Kbytes */ X memory_stats[0] = pagetok(inuse + free); X memory_stats[1] = pagetok(inuse); X memory_stats[2] = pagetok(free); X memory_stats[3] = pagetok(locked); X } X X /* set arrays and strings */ X si->cpustates = cpu_states; X si->memory = memory_stats; X} X Xstatic struct handle handle; X Xcaddr_t get_process_info(si, sel, compare) X Xstruct system_info *si; Xstruct process_select *sel; Xint (*compare)(); X X{ X register int i; X register int total_procs; X register int active_procs; X register struct proc **prefp; X register struct proc *pp; X X /* these are copied out of sel for speed */ X int show_idle; X int show_system; X int show_uid; X int show_command; X X /* read all the proc structures in one fell swoop */ X (void) getkval(proc, (int *)pbase, bytes, "proc array"); X X /* get a pointer to the states summary array */ X si->procstates = process_states; X X /* set up flags which define what we are going to select */ X show_idle = sel->idle; X show_system = sel->system; X show_uid = sel->uid != -1; X show_command = sel->command != NULL; X X /* count up process states and get pointers to interesting procs */ X total_procs = 0; X active_procs = 0; X bzero((char *)process_states, sizeof(process_states)); X prefp = pref; X for (pp = pbase, i = 0; i < nproc; pp++, i++) X { X /* X * Place pointers to each valid proc structure in pref[]. X * Process slots that are actually in use have a non-zero X * status field. Processes with SSYS set are system X * processes---these get ignored unless show_sysprocs is set. X */ X if (pp->p_stat != 0 && X (show_system || ((pp->p_flag & SSYS) == 0))) X { X total_procs++; X process_states[pp->p_stat]++; X if ((pp->p_stat != SZOMB) && X (show_idle || (pp->p_pctcpu != 0) || (pp->p_stat == SRUN)) && X (!show_uid || pp->p_uid == (uid_t)sel->uid)) X { X *prefp++ = pp; X active_procs++; X } X } X } X X /* if requested, sort the "interesting" processes */ X if (compare != NULL) X { X qsort((char *)pref, active_procs, sizeof(struct proc *), compare); X } X X /* remember active and total counts */ X si->p_total = total_procs; X si->p_active = pref_len = active_procs; X X /* pass back a handle */ X handle.next_proc = pref; X handle.remaining = active_procs; X return((caddr_t)&handle); X} X Xchar fmt[MAX_COLS]; /* static area where result is built */ X Xchar *format_next_process(handle, get_userid) X Xcaddr_t handle; Xchar *(*get_userid)(); X X{ X register struct proc *pp; X register long cputime; X register double pct; X struct user u; X struct handle *hp; X X /* find and remember the next proc structure */ X hp = (struct handle *)handle; X pp = *(hp->next_proc++); X hp->remaining--; X X /* get the process's user struct and set cputime */ X if (getu(pp, &u) == -1) X { X (void) strcpy(u.u_comm, ""); X cputime = 0; X } X else X { X /* set u_comm for system processes */ X if (u.u_comm[0] == '\0') X { X if (pp->p_pid == 0) X { X (void) strcpy(u.u_comm, "Swapper"); X } X else if (pp->p_pid == 2) X { X (void) strcpy(u.u_comm, "Pager"); X } X } X X cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec; X } X X /* calculate the base for cpu percentages */ X pct = pctdouble(pp->p_pctcpu); X X#ifdef MULTIPROCESSOR X /* X * If there is more than one cpu then add the processor number to X * the "run/" string. Note that this will only show up if the X * process is in the run state. Also note: when they X * start making Suns with more than 9 processors this will break X * since the string will then be more than 5 characters. X */ X if (ncpu > 1) X { X state_abbrev[SRUN][4] = (pp->p_cpuid & 0xf) + '0'; X } X#endif X#ifdef solbourne X if (ncpu > 1) X { X state_abbrev[SRUN][4] = (pp->p_lastcpu) + '0'; X } X#endif X X /* format this entry */ X sprintf(fmt, X Proc_format, X pp->p_pid, X (*get_userid)(pp->p_uid), X pp->p_pri - PZERO, X pp->p_nice - NZERO, X format_k(pagetok(PROCSIZE(pp))), X format_k(pagetok(pp->p_rssize)), X state_abbrev[pp->p_stat], X format_time(cputime), X 100.0 * weighted_cpu(pct, pp), X 100.0 * pct, X printable(u.u_comm)); X X /* return the result */ X return(fmt); X} X X/* X * getu(p, u) - get the user structure for the process whose proc structure X * is pointed to by p. The user structure is put in the buffer pointed X * to by u. Return 0 if successful, -1 on failure (such as the process X * being swapped out). X */ X Xgetu(p, u) X Xregister struct proc *p; Xstruct user *u; X X{ X register struct user *lu; X X lu = kvm_getu(kd, p); X if (lu == NULL) X { X return(-1); X } X else X { X *u = *lu; X return(0); X } X} X X/* X * check_nlist(nlst) - checks the nlist to see if any symbols were not X * found. For every symbol that was not found, a one-line X * message is printed to stderr. The routine returns the X * number of symbols NOT found. X */ X Xint check_nlist(nlst) X Xregister struct nlist *nlst; X X{ X register int i; X X /* check to see if we got ALL the symbols we requested */ X /* this will write one line to stderr for every symbol not found */ X X i = 0; X while (nlst->n_name != NULL) X { X#ifdef i386 X if (nlst->n_value == 0) X#else X if (nlst->n_type == 0) X#endif X { X /* this one wasn't found */ X fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name); X i = 1; X } X nlst++; X } X X return(i); X} X X X/* X * getkval(offset, ptr, size, refstr) - get a value out of the kernel. X * "offset" is the byte offset into the kernel for the desired value, X * "ptr" points to a buffer into which the value is retrieved, X * "size" is the size of the buffer (and the object to retrieve), X * "refstr" is a reference string used when printing error meessages, X * if "refstr" starts with a '!', then a failure on read will not X * be fatal (this may seem like a silly way to do things, but I X * really didn't want the overhead of another argument). X * X */ X Xgetkval(offset, ptr, size, refstr) X Xunsigned long offset; Xint *ptr; Xint size; Xchar *refstr; X X{ X if (kvm_read(kd, offset, ptr, size) != size) X { X if (*refstr == '!') X { X return(0); X } X else X { X fprintf(stderr, "top: kvm_read for %s: %s\n", X refstr, sys_errlist[errno]); X quit(23); X /*NOTREACHED*/ X } X } X return(1); X} X X/* comparison routine for qsort */ X X/* X * proc_compare - comparison function for "qsort" X * Compares the resource consumption of two processes using five X * distinct keys. The keys (in descending order of importance) are: X * percent cpu, cpu ticks, state, resident set size, total virtual X * memory usage. The process states are ordered as follows (from least X * to most important): WAIT, zombie, sleep, stop, start, run. The X * array declaration below maps a process state index into a number X * that reflects this ordering. X */ X Xstatic unsigned char sorted_state[] = X{ X 0, /* not used */ X 3, /* sleep */ X 1, /* ABANDONED (WAIT) */ X 6, /* run */ X 5, /* start */ X 2, /* zombie */ X 4 /* stop */ X}; X Xproc_compare(pp1, pp2) X Xstruct proc **pp1; Xstruct proc **pp2; X X{ X register struct proc *p1; X register struct proc *p2; X register int result; X register pctcpu lresult; X X /* remove one level of indirection */ X p1 = *pp1; X p2 = *pp2; X X /* compare percent cpu (pctcpu) */ X if ((lresult = p2->p_pctcpu - p1->p_pctcpu) == 0) X { X /* use cpticks to break the tie */ X if ((result = p2->p_cpticks - p1->p_cpticks) == 0) X { X /* use process state to break the tie */ X if ((result = sorted_state[p2->p_stat] - X sorted_state[p1->p_stat]) == 0) X { X /* use priority to break the tie */ X if ((result = p2->p_pri - p1->p_pri) == 0) X { X /* use resident set size (rssize) to break the tie */ X if ((result = p2->p_rssize - p1->p_rssize) == 0) X { X /* use total memory to break the tie */ X result = PROCSIZE(p2) - PROCSIZE(p1); X } X } X } X } X } X else X { X result = lresult < 0 ? -1 : 1; X } X X return(result); X} X X/* X * proc_owner(pid) - returns the uid that owns process "pid", or -1 if X * the process does not exist. X * It is EXTREMLY IMPORTANT that this function work correctly. X * If top runs setuid root (as in SVR4), then this function X * is the only thing that stands in the way of a serious X * security problem. It validates requests for the "kill" X * and "renice" commands. X */ X Xint proc_owner(pid) X Xint pid; X X{ X register int cnt; X register struct proc **prefp; X register struct proc *pp; X X prefp = pref; X cnt = pref_len; X while (--cnt >= 0) X { X if ((pp = *prefp++)->p_pid == (pid_t)pid) X { X return((int)pp->p_uid); X } X } X return(-1); X} END_OF_FILE if test 20728 -ne `wc -c <'machine/m_sunos4.c'`; then echo shar: \"'machine/m_sunos4.c'\" unpacked with wrong size! fi # end of 'machine/m_sunos4.c' fi echo shar: End of archive 13 \(of 16\). cp /dev/null ark13isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 16 archives. echo "Now read README and INSTALL, then run Configure" rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0