Index: head/usr.bin/top/display.c =================================================================== --- head/usr.bin/top/display.c (revision 352557) +++ head/usr.bin/top/display.c (revision 352558) @@ -1,1379 +1,1354 @@ /* * Top users/processes display for Unix * Version 3 * * This program may be freely redistributed, * but this entire comment MUST remain intact. * * Copyright (c) 1984, 1989, William LeFebvre, Rice University * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University * * $FreeBSD$ */ /* * This file contains the routines that display information on the screen. * Each section of the screen has two routines: one for initially writing * all constant and dynamic text, and one for only updating the text that * changes. The prefix "i_" is used on all the "initial" routines and the * prefix "u_" is used for all the "updating" routines. * * ASSUMPTIONS: * None of the "i_" routines use any of the termcap capabilities. * In this way, those routines can be safely used on terminals that * have minimal (or nonexistant) terminal capabilities. * * The routines are called in this order: *_loadave, i_timeofday, * *_procstates, *_cpustates, *_memory, *_message, *_header, * *_process, u_endscreen. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "screen.h" /* interface to screen package */ #include "layout.h" /* defines for screen position layout */ #include "display.h" #include "top.h" #include "machine.h" /* we should eliminate this!!! */ #include "utils.h" #ifdef DEBUG FILE *debug; #endif static int lmpid = 0; static int last_hi = 0; /* used in u_process and u_endscreen */ static int lastline = 0; #define lineindex(l) ((l)*screen_width) /* things initialized by display_init and used thruout */ /* buffer of proc information lines for display updating */ static char *screenbuf = NULL; static const char * const *procstate_names; static const char * const *cpustate_names; static const char * const *memory_names; static const char * const *arc_names; static const char * const *carc_names; static const char * const *swap_names; static int num_procstates; static int num_cpustates; static int num_memory; static int num_swap; static int *lprocstates; static int *lcpustates; static int *lmemory; static int *lswap; static int num_cpus; static int *cpustate_columns; static int cpustate_total_length; static int cpustates_column; static enum { OFF, ON, ERASE } header_status = ON; static void summary_format(char *, int *, const char * const *); static void line_update(char *, char *, int, int); static int setup_buffer_bufsiz = 0; static char * setup_buffer(char *, int); int x_lastpid = 10; int y_lastpid = 0; int x_loadave = 33; int x_loadave_nompid = 15; int y_loadave = 0; int x_procstate = 0; int y_procstate = 1; int x_brkdn = 15; int y_brkdn = 1; int x_mem = 5; int y_mem = 3; int x_arc = 5; int y_arc = 4; int x_carc = 5; int y_carc = 5; int x_swap = 6; int y_swap = 4; int y_message = 5; int x_header = 0; int y_header = 6; int x_idlecursor = 0; int y_idlecursor = 5; int y_procs = 7; int y_cpustates = 2; int Header_lines = 7; int display_resize(void) { int lines; /* first, deallocate any previous buffer that may have been there */ if (screenbuf != NULL) { free(screenbuf); } /* calculate the current dimensions */ /* if operating in "dumb" mode, we only need one line */ lines = smart_terminal ? screen_length - Header_lines : 1; if (lines < 0) lines = 0; /* now, allocate space for the screen buffer */ screenbuf = calloc(lines, screen_width); if (screenbuf == NULL) { /* oops! */ return(-1); } /* return number of lines available */ /* for dumb terminals, pretend like we can show any amount */ return(smart_terminal ? lines : Largest); } int display_updatecpus(struct statics *statics) { int lines; int i; /* call resize to do the dirty work */ lines = display_resize(); if (pcpu_stats) num_cpus = statics->ncpus; else num_cpus = 1; cpustates_column = 5; /* CPU: */ if (num_cpus > 1) { cpustates_column += 1 + digits(num_cpus); /* CPU #: */ } /* fill the "last" array with all -1s, to insure correct updating */ for (i = 0; i < num_cpustates * num_cpus; ++i) { lcpustates[i] = -1; } return(lines); } int display_init(struct statics * statics) { int lines; const char * const *pp; int *ip; int i; lines = display_updatecpus(statics); /* only do the rest if we need to */ if (lines > -1) { /* save pointers and allocate space for names */ procstate_names = statics->procstate_names; num_procstates = 8; assert(num_procstates > 0); lprocstates = calloc(num_procstates, sizeof(int)); cpustate_names = statics->cpustate_names; swap_names = statics->swap_names; num_swap = 7; assert(num_swap > 0); lswap = calloc(num_swap, sizeof(int)); num_cpustates = CPUSTATES; assert(num_cpustates > 0); lcpustates = calloc(num_cpustates * sizeof(int), statics->ncpus); cpustate_columns = calloc(num_cpustates, sizeof(int)); memory_names = statics->memory_names; num_memory = 7; assert(num_memory > 0); lmemory = calloc(num_memory, sizeof(int)); arc_names = statics->arc_names; carc_names = statics->carc_names; /* calculate starting columns where needed */ cpustate_total_length = 0; pp = cpustate_names; ip = cpustate_columns; while (*pp != NULL) { *ip++ = cpustate_total_length; if ((i = strlen(*pp++)) > 0) { cpustate_total_length += i + 8; } } } /* return number of lines available */ return(lines); } void i_loadave(int mpid, double avenrun[]) { int i; /* i_loadave also clears the screen, since it is first */ top_clear(); /* mpid == -1 implies this system doesn't have an _mpid */ if (mpid != -1) { printf("last pid: %5d; ", mpid); } printf("load averages"); for (i = 0; i < 3; i++) { printf("%c %5.2f", i == 0 ? ':' : ',', avenrun[i]); } lmpid = mpid; } void u_loadave(int mpid, double *avenrun) { int i; if (mpid != -1) { /* change screen only when value has really changed */ if (mpid != lmpid) { Move_to(x_lastpid, y_lastpid); printf("%5d", mpid); lmpid = mpid; } /* i remembers x coordinate to move to */ i = x_loadave; } else { i = x_loadave_nompid; } /* move into position for load averages */ Move_to(i, y_loadave); /* display new load averages */ /* we should optimize this and only display changes */ for (i = 0; i < 3; i++) { printf("%s%5.2f", i == 0 ? "" : ", ", avenrun[i]); } } void i_timeofday(time_t *tod) { /* * Display the current time. * "ctime" always returns a string that looks like this: * * Sun Sep 16 01:03:52 1973 * 012345678901234567890123 * 1 2 * * We want indices 11 thru 18 (length 8). */ if (smart_terminal) { Move_to(screen_width - 8, 0); } else { fputs(" ", stdout); } #ifdef DEBUG { char *foo; foo = ctime(tod); fputs(foo, stdout); } #endif printf("%-8.8s\n", &(ctime(tod)[11])); lastline = 1; } static int ltotal = 0; static char *procstates_buffer = NULL; /* * *_procstates(total, brkdn, names) - print the process summary line * * Assumptions: cursor is at the beginning of the line on entry * lastline is valid */ void i_procstates(int total, int *brkdn) { int i; procstates_buffer = setup_buffer(procstates_buffer, 0); /* write current number of processes and remember the value */ printf("%d %s:", total, ps.thread ? "threads" : "processes"); ltotal = total; /* put out enough spaces to get to column 15 */ i = digits(total); while (i++ < (ps.thread ? 6 : 4)) { putchar(' '); } /* format and print the process state summary */ summary_format(procstates_buffer, brkdn, procstate_names); fputs(procstates_buffer, stdout); /* save the numbers for next time */ memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); } void u_procstates(int total, int *brkdn) { static char *new = NULL; int i; new = setup_buffer(new, 0); /* update number of processes only if it has changed */ if (ltotal != total) { /* move and overwrite */ if (x_procstate == 0) { Move_to(x_procstate, y_procstate); } else { /* cursor is already there...no motion needed */ assert(lastline == 1); } printf("%d", total); /* if number of digits differs, rewrite the label */ if (digits(total) != digits(ltotal)) { printf(" %s:", ps.thread ? "threads" : "processes"); /* put out enough spaces to get to column 15 */ i = digits(total); while (i++ < (ps.thread ? 6 : 4)) { putchar(' '); } /* cursor may end up right where we want it!!! */ } /* save new total */ ltotal = total; } /* see if any of the state numbers has changed */ if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) { /* format and update the line */ summary_format(new, brkdn, procstate_names); line_update(procstates_buffer, new, x_brkdn, y_brkdn); memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); } } void i_cpustates(int *states) { int i = 0; int value; const char * const *names; const char *thisname; int *hstates = states; int cpu; for (cpu = 0; cpu < num_cpus; cpu++) { names = cpustate_names; /* print tag and bump lastline */ if (num_cpus == 1) printf("\nCPU: "); else { value = printf("\nCPU %d: ", cpu); while (value++ <= cpustates_column) printf(" "); } lastline++; /* now walk thru the names and print the line */ while ((thisname = *names++) != NULL) { if (*thisname != '\0') { /* retrieve the value and remember it */ value = *states++; /* if percentage is >= 1000, print it as 100% */ printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"), (i++ % num_cpustates) == 0 ? "" : ", ", ((float)value)/10., thisname); } } } /* copy over values into "last" array */ states = hstates; memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus); } void u_cpustates(int *states) { int value; const char * const *names; const char *thisname; int *hstates = states; int *lp; int *colp; int cpu; for (cpu = 0; cpu < num_cpus; cpu++) { names = cpustate_names; Move_to(cpustates_column, y_cpustates + cpu); lastline = y_cpustates + cpu; lp = lcpustates + (cpu * num_cpustates); colp = cpustate_columns; /* we could be much more optimal about this */ while ((thisname = *names++) != NULL) { if (*thisname != '\0') { /* did the value change since last time? */ if (*lp != *states) { /* yes, move and change */ Move_to(cpustates_column + *colp, y_cpustates + cpu); lastline = y_cpustates + cpu; /* retrieve value and remember it */ value = *states; /* if percentage is >= 1000, print it as 100% */ printf((value >= 1000 ? "%4.0f" : "%4.1f"), ((double)value)/10.); /* remember it for next time */ *lp = value; } } /* increment and move on */ lp++; states++; colp++; } } states = hstates; } void z_cpustates(void) { int i = 0; const char * const *names; const char *thisname; int cpu, value; for (cpu = 0; cpu < num_cpus; cpu++) { names = cpustate_names; /* show tag and bump lastline */ if (num_cpus == 1) printf("\nCPU: "); else { value = printf("\nCPU %d: ", cpu); while (value++ <= cpustates_column) printf(" "); } lastline++; while ((thisname = *names++) != NULL) { if (*thisname != '\0') { printf("%s %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname); } } } /* fill the "last" array with all -1s, to insure correct updating */ for (i = 0; i < num_cpustates * num_cpus; ++i) { lcpustates[i] = -1; } } /* * *_memory(stats) - print "Memory: " followed by the memory summary string * * Assumptions: cursor is on "lastline" * for i_memory ONLY: cursor is on the previous line */ static char *memory_buffer = NULL; void i_memory(int *stats) { memory_buffer = setup_buffer(memory_buffer, 0); fputs("\nMem: ", stdout); lastline++; /* format and print the memory summary */ summary_format(memory_buffer, stats, memory_names); fputs(memory_buffer, stdout); } void u_memory(int *stats) { static char *new = NULL; new = setup_buffer(new, 0); /* format the new line */ summary_format(new, stats, memory_names); line_update(memory_buffer, new, x_mem, y_mem); } /* * *_arc(stats) - print "ARC: " followed by the ARC summary string * * Assumptions: cursor is on "lastline" * for i_arc ONLY: cursor is on the previous line */ static char *arc_buffer = NULL; void i_arc(int *stats) { arc_buffer = setup_buffer(arc_buffer, 0); if (arc_names == NULL) return; fputs("\nARC: ", stdout); lastline++; /* format and print the memory summary */ summary_format(arc_buffer, stats, arc_names); fputs(arc_buffer, stdout); } void u_arc(int *stats) { static char *new = NULL; new = setup_buffer(new, 0); if (arc_names == NULL) return; /* format the new line */ summary_format(new, stats, arc_names); line_update(arc_buffer, new, x_arc, y_arc); } /* * *_carc(stats) - print "Compressed ARC: " followed by the summary string * * Assumptions: cursor is on "lastline" * for i_carc ONLY: cursor is on the previous line */ static char *carc_buffer = NULL; void i_carc(int *stats) { carc_buffer = setup_buffer(carc_buffer, 0); if (carc_names == NULL) return; fputs("\n ", stdout); lastline++; /* format and print the memory summary */ summary_format(carc_buffer, stats, carc_names); fputs(carc_buffer, stdout); } void u_carc(int *stats) { static char *new = NULL; new = setup_buffer(new, 0); if (carc_names == NULL) return; /* format the new line */ summary_format(new, stats, carc_names); line_update(carc_buffer, new, x_carc, y_carc); } /* * *_swap(stats) - print "Swap: " followed by the swap summary string * * Assumptions: cursor is on "lastline" * for i_swap ONLY: cursor is on the previous line */ static char *swap_buffer = NULL; void i_swap(int *stats) { swap_buffer = setup_buffer(swap_buffer, 0); if (swap_names == NULL) return; fputs("\nSwap: ", stdout); lastline++; /* format and print the swap summary */ summary_format(swap_buffer, stats, swap_names); fputs(swap_buffer, stdout); } void u_swap(int *stats) { static char *new = NULL; new = setup_buffer(new, 0); if (swap_names == NULL) return; /* format the new line */ summary_format(new, stats, swap_names); line_update(swap_buffer, new, x_swap, y_swap); } /* * *_message() - print the next pending message line, or erase the one * that is there. * * Note that u_message is (currently) the same as i_message. * * Assumptions: lastline is consistent */ /* * i_message is funny because it gets its message asynchronously (with * respect to screen updates). */ #define NEXT_MSG_ADDLEN 5 static char *next_msg = NULL; static int msglen = 0; /* Invariant: msglen is always the length of the message currently displayed on the screen (even when next_msg doesn't contain that message). */ void i_message(void) { next_msg = setup_buffer(next_msg, NEXT_MSG_ADDLEN); while (lastline < y_message) { fputc('\n', stdout); lastline++; } if (next_msg[0] != '\0') { top_standout(next_msg); msglen = strlen(next_msg); next_msg[0] = '\0'; } else if (msglen > 0) { (void) clear_eol(msglen); msglen = 0; } } void u_message(void) { i_message(); } static int header_length; /* * Trim a header string to the current display width and return a newly * allocated area with the trimmed header. */ char * trim_header(const char *text) { char *s; int width; s = NULL; width = screen_width; header_length = strlen(text); if (header_length >= width) { s = strndup(text, width); if (s == NULL) return (NULL); } return (s); } /* * *_header(text) - print the header for the process area * * Assumptions: cursor is on the previous line and lastline is consistent */ void i_header(const char *text) { char *s; s = trim_header(text); if (s != NULL) text = s; if (header_status == ON) { putchar('\n'); fputs(text, stdout); lastline++; } else if (header_status == ERASE) { header_status = OFF; } free(s); } void u_header(const char *text __unused) { if (header_status == ERASE) { putchar('\n'); lastline++; clear_eol(header_length); header_status = OFF; } } /* * *_process(line, thisline) - print one process line * * Assumptions: lastline is consistent */ void i_process(int line, char *thisline) { char *p; char *base; /* make sure we are on the correct line */ while (lastline < y_procs + line) { putchar('\n'); lastline++; } /* truncate the line to conform to our current screen width */ int len = strlen(thisline); if (screen_width < len) { thisline[screen_width] = '\0'; } /* write the line out */ fputs(thisline, stdout); /* copy it in to our buffer */ base = smart_terminal ? screenbuf + lineindex(line) : screenbuf; p = stpcpy(base, thisline); /* zero fill the rest of it */ if (p - base < screen_width) { memset(p, 0, screen_width - (p - base)); } } void u_process(int line, char *newline) { char *optr; int screen_line = line + Header_lines; char *bufferline; /* remember a pointer to the current line in the screen buffer */ bufferline = &screenbuf[lineindex(line)]; /* truncate the line to conform to our current screen width */ int len = strlen(newline); if (screen_width < len) { newline[screen_width] = '\0'; } /* is line higher than we went on the last display? */ if (line >= last_hi) { /* yes, just ignore screenbuf and write it out directly */ /* get positioned on the correct line */ if (screen_line - lastline == 1) { putchar('\n'); lastline++; } else { Move_to(0, screen_line); lastline = screen_line; } /* now write the line */ fputs(newline, stdout); /* copy it in to the buffer */ optr = stpcpy(bufferline, newline); /* zero fill the rest of it */ if (optr - bufferline < screen_width) { memset(optr, 0, screen_width - (optr - bufferline)); } } else { line_update(bufferline, newline, 0, line + Header_lines); } } void u_endscreen(int hi) { int screen_line = hi + Header_lines; int i; if (smart_terminal) { if (hi < last_hi) { /* need to blank the remainder of the screen */ /* but only if there is any screen left below this line */ if (lastline + 1 < screen_length) { /* efficiently move to the end of currently displayed info */ if (screen_line - lastline < 5) { while (lastline < screen_line) { putchar('\n'); lastline++; } } else { Move_to(0, screen_line); lastline = screen_line; } if (clear_to_end) { /* we can do this the easy way */ putcap(clear_to_end); } else { /* use clear_eol on each line */ i = hi; while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi) { putchar('\n'); } } } } last_hi = hi; /* move the cursor to a pleasant place */ Move_to(x_idlecursor, y_idlecursor); lastline = y_idlecursor; } else { /* separate this display from the next with some vertical room */ fputs("\n\n", stdout); } } void display_header(int t) { if (t) { header_status = ON; } else if (header_status == ON) { header_status = ERASE; } } void new_message(int type, const char *msgfmt, ...) { va_list args; size_t i; va_start(args, msgfmt); /* first, format the message */ vsnprintf(next_msg, setup_buffer_bufsiz + NEXT_MSG_ADDLEN, msgfmt, args); va_end(args); if (msglen > 0) { /* message there already -- can we clear it? */ if (!overstrike) { /* yes -- write it and clear to end */ i = strlen(next_msg); if ((type & MT_delayed) == 0) { if (type & MT_standout) { top_standout(next_msg); } else { fputs(next_msg, stdout); } clear_eol(msglen - i); msglen = i; next_msg[0] = '\0'; } } } else { if ((type & MT_delayed) == 0) { if (type & MT_standout) { top_standout(next_msg); } else { fputs(next_msg, stdout); } msglen = strlen(next_msg); next_msg[0] = '\0'; } } } void clear_message(void) { if (clear_eol(msglen) == 1) { putchar('\r'); } } int readline(char *buffer, int size, int numeric) { char *ptr = buffer; char ch; char cnt = 0; char maxcnt = 0; /* allow room for null terminator */ size -= 1; /* read loop */ while ((fflush(stdout), read(0, ptr, 1) > 0)) { /* newline means we are done */ if ((ch = *ptr) == '\n' || ch == '\r') { break; } /* handle special editing characters */ if (ch == ch_kill) { /* kill line -- account for overstriking */ if (overstrike) { msglen += maxcnt; } /* return null string */ *buffer = '\0'; putchar('\r'); return(-1); } else if (ch == ch_erase) { /* erase previous character */ if (cnt <= 0) { /* none to erase! */ putchar('\7'); } else { fputs("\b \b", stdout); ptr--; cnt--; } } /* check for character validity and buffer overflow */ else if (cnt == size || (numeric && !isdigit(ch)) || !isprint(ch)) { /* not legal */ putchar('\7'); } else { /* echo it and store it in the buffer */ putchar(ch); ptr++; cnt++; if (cnt > maxcnt) { maxcnt = cnt; } } } /* all done -- null terminate the string */ *ptr = '\0'; /* account for the extra characters in the message area */ /* (if terminal overstrikes, remember the furthest they went) */ msglen += overstrike ? maxcnt : cnt; /* return either inputted number or string length */ putchar('\r'); return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); } /* internal support routines */ static void summary_format(char *str, int *numbers, const char * const *names) { char *p; int num; const char *thisname; char rbuf[6]; /* format each number followed by its string */ p = str; while ((thisname = *names++) != NULL) { /* get the number to format */ num = *numbers++; /* display only non-zero numbers */ if (num > 0) { /* is this number in kilobytes? */ if (thisname[0] == 'K') { /* yes: format it as a memory value */ p = stpcpy(p, format_k(num)); /* skip over the K, since it was included by format_k */ p = stpcpy(p, thisname+1); } /* is this number a ratio? */ else if (thisname[0] == ':') { (void) snprintf(rbuf, sizeof(rbuf), "%.2f", (float)*(numbers - 2) / (float)num); p = stpcpy(p, rbuf); p = stpcpy(p, thisname); } else { p = stpcpy(p, itoa(num)); p = stpcpy(p, thisname); } } /* ignore negative numbers, but display corresponding string */ else if (num < 0) { p = stpcpy(p, thisname); } } /* if the last two characters in the string are ", ", delete them */ p -= 2; if (p >= str && p[0] == ',' && p[1] == ' ') { *p = '\0'; } } static void line_update(char *old, char *new, int start, int line) { int ch; int diff; int newcol = start + 1; int lastcol = start; char cursor_on_line = false; char *current; /* compare the two strings and only rewrite what has changed */ current = old; #ifdef DEBUG fprintf(debug, "line_update, starting at %d\n", start); fputs(old, debug); fputc('\n', debug); fputs(new, debug); fputs("\n-\n", debug); #endif /* start things off on the right foot */ /* this is to make sure the invariants get set up right */ if ((ch = *new++) != *old) { if (line - lastline == 1 && start == 0) { putchar('\n'); } else { Move_to(start, line); } cursor_on_line = true; putchar(ch); *old = ch; lastcol = start + 1; } old++; /* * main loop -- check each character. If the old and new aren't the * same, then update the display. When the distance from the * current cursor position to the new change is small enough, * the characters that belong there are written to move the * cursor over. * * Invariants: * lastcol is the column where the cursor currently is sitting * (always one beyond the end of the last mismatch). */ do /* yes, a do...while */ { if ((ch = *new++) != *old) { /* new character is different from old */ /* make sure the cursor is on top of this character */ diff = newcol - lastcol; if (diff > 0) { /* some motion is required--figure out which is shorter */ if (diff < 6 && cursor_on_line) { /* overwrite old stuff--get it out of the old buffer */ printf("%.*s", diff, ¤t[lastcol-start]); } else { /* use cursor addressing */ Move_to(newcol, line); cursor_on_line = true; } /* remember where the cursor is */ lastcol = newcol + 1; } else { /* already there, update position */ lastcol++; } /* write what we need to */ if (ch == '\0') { /* at the end--terminate with a clear-to-end-of-line */ (void) clear_eol(strlen(old)); } else { /* write the new character */ putchar(ch); } /* put the new character in the screen buffer */ *old = ch; } /* update working column and screen buffer pointer */ newcol++; old++; } while (ch != '\0'); /* zero out the rest of the line buffer -- MUST BE DONE! */ diff = screen_width - newcol; if (diff > 0) { memset(old, 0, diff); } /* remember where the current line is */ if (cursor_on_line) { lastline = line; } } -/* - * printable(str) - make the string pointed to by "str" into one that is - * printable (i.e.: all ascii), by converting all non-printable - * characters into '?'. Replacements are done in place and a pointer - * to the original buffer is returned. - */ - -char * -printable(char str[]) -{ - char *ptr; - char ch; - - ptr = str; - while ((ch = *ptr) != '\0') - { - if (!isprint(ch)) - { - *ptr = '?'; - } - ptr++; - } - return(str); -} - void i_uptime(struct timeval *bt, time_t *tod) { time_t uptime; int days, hrs, mins, secs; if (bt->tv_sec != -1) { uptime = *tod - bt->tv_sec; days = uptime / 86400; uptime %= 86400; hrs = uptime / 3600; uptime %= 3600; mins = uptime / 60; secs = uptime % 60; /* * Display the uptime. */ if (smart_terminal) { Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0); } else { fputs(" ", stdout); } printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs); } } #define SETUPBUFFER_MIN_SCREENWIDTH 80 #define SETUPBUFFER_REQUIRED_ADDBUFSIZ 2 static char * setup_buffer(char *buffer, int addlen) { size_t len, old_len; char *new_buffer; setup_buffer_bufsiz = screen_width; if (setup_buffer_bufsiz < SETUPBUFFER_MIN_SCREENWIDTH) { setup_buffer_bufsiz = SETUPBUFFER_MIN_SCREENWIDTH; } len = setup_buffer_bufsiz + addlen + SETUPBUFFER_REQUIRED_ADDBUFSIZ; new_buffer = calloc(len, sizeof(char)); if (new_buffer == NULL) { errx(4, "can't allocate sufficient memory"); } if (buffer != NULL) { old_len = strlen(buffer); memcpy(new_buffer, buffer, old_len < len - 1 ? old_len : len - 1); free(buffer); } return new_buffer; } Index: head/usr.bin/top/display.h =================================================================== --- head/usr.bin/top/display.h (revision 352557) +++ head/usr.bin/top/display.h (revision 352558) @@ -1,42 +1,41 @@ /* $FreeBSD$ */ /* constants needed for display.c */ #define MT_standout 1 #define MT_delayed 2 #include struct statics; int display_updatecpus(struct statics *statics); void clear_message(void); int display_resize(void); void i_header(const char *text); -char *printable(char *string); void display_header(int t); int display_init(struct statics *statics); void i_arc(int *stats); void i_carc(int *stats); void i_cpustates(int *states); void i_loadave(int mpid, double *avenrun); void i_memory(int *stats); void i_message(void); void i_process(int line, char *thisline); void i_procstates(int total, int *brkdn); void i_swap(int *stats); void i_timeofday(time_t *tod); void i_uptime(struct timeval *bt, time_t *tod); void new_message(int type, const char *msgfmt, ...); int readline(char *buffer, int size, int numeric); char *trim_header(const char *text); void u_arc(int *stats); void u_carc(int *stats); void u_cpustates(int *states); void u_endscreen(int hi); void u_header(const char *text); void u_loadave(int mpid, double *avenrun); void u_memory(int *stats); void u_message(void); void u_process(int line, char *newline); void u_procstates(int total, int *brkdn); void u_swap(int *stats); void z_cpustates(void); Index: head/usr.bin/top/machine.c =================================================================== --- head/usr.bin/top/machine.c (revision 352557) +++ head/usr.bin/top/machine.c (revision 352558) @@ -1,1561 +1,1561 @@ /* * top - a top users display for Unix * * DESCRIPTION: * Originally written for BSD4.4 system by Christos Zoulas. * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider * Order support hacked in from top-3.5beta6/machine/m_aix41.c * by Monte Mitzelfelt (for latest top see http://www.groupsys.com/topinfo/) * * AUTHOR: Christos Zoulas * Steven Wallace * Wolfram Schneider * Thomas Moestl * Eitan Adler * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "top.h" #include "display.h" #include "machine.h" #include "loadavg.h" #include "screen.h" #include "utils.h" #include "layout.h" #define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var)) extern struct timeval timeout; static int smpmode; enum displaymodes displaymode; static const int namelength = 10; /* TOP_JID_LEN based on max of 999999 */ #define TOP_JID_LEN 6 #define TOP_SWAP_LEN 5 /* get_process_info passes back a handle. This is what it looks like: */ struct handle { struct kinfo_proc **next_proc; /* points to next valid proc pointer */ int remaining; /* number of pointers remaining */ }; /* define what weighted cpu is. */ #define weighted_cpu(pct, pp) ((pp)->ki_swtime == 0 ? 0.0 : \ ((pct) / (1.0 - exp((pp)->ki_swtime * logcpu)))) /* what we consider to be process size: */ #define PROCSIZE(pp) ((pp)->ki_size / 1024) #define RU(pp) (&(pp)->ki_rusage) #define PCTCPU(pp) (pcpu[pp - pbase]) /* process state names for the "STATE" column of the display */ /* the extra nulls in the string "run" are for adding a slash and the processor number when needed */ static const char *state_abbrev[] = { "", "START", "RUN\0\0\0", "SLEEP", "STOP", "ZOMB", "WAIT", "LOCK" }; static kvm_t *kd; /* values that we stash away in _init and use in later routines */ static double logcpu; /* these are retrieved from the kernel in _init */ static load_avg ccpu; /* these are used in the get_ functions */ static int lastpid; /* these are for calculating cpu state percentages */ static long cp_time[CPUSTATES]; static long cp_old[CPUSTATES]; static long cp_diff[CPUSTATES]; /* these are for detailing the process states */ static const char *procstatenames[] = { "", " starting, ", " running, ", " sleeping, ", " stopped, ", " zombie, ", " waiting, ", " lock, ", NULL }; static int process_states[nitems(procstatenames)]; /* these are for detailing the cpu states */ static int cpu_states[CPUSTATES]; static const char *cpustatenames[] = { "user", "nice", "system", "interrupt", "idle", NULL }; /* these are for detailing the memory statistics */ static const char *memorynames[] = { "K Active, ", "K Inact, ", "K Laundry, ", "K Wired, ", "K Buf, ", "K Free", NULL }; static int memory_stats[nitems(memorynames)]; static const char *arcnames[] = { "K Total, ", "K MFU, ", "K MRU, ", "K Anon, ", "K Header, ", "K Other", NULL }; static int arc_stats[nitems(arcnames)]; static const char *carcnames[] = { "K Compressed, ", "K Uncompressed, ", ":1 Ratio, ", NULL }; static int carc_stats[nitems(carcnames)]; static const char *swapnames[] = { "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out", NULL }; static int swap_stats[nitems(swapnames)]; static int has_swap; /* these are for keeping track of the proc array */ static int nproc; static int onproc = -1; static int pref_len; static struct kinfo_proc *pbase; static struct kinfo_proc **pref; static struct kinfo_proc *previous_procs; static struct kinfo_proc **previous_pref; static int previous_proc_count = 0; static int previous_proc_count_max = 0; static int previous_thread; /* data used for recalculating pctcpu */ static double *pcpu; static struct timespec proc_uptime; static struct timeval proc_wall_time; static struct timeval previous_wall_time; static uint64_t previous_interval = 0; /* total number of io operations */ static long total_inblock; static long total_oublock; static long total_majflt; /* these are for getting the memory statistics */ static int arc_enabled; static int carc_enabled; static int pageshift; /* log base 2 of the pagesize */ /* define pagetok in terms of pageshift */ #define pagetok(size) ((size) << pageshift) /* swap usage */ #define ki_swap(kip) \ ((kip)->ki_swrss > (kip)->ki_rssize ? (kip)->ki_swrss - (kip)->ki_rssize : 0) /* * Sorting orders. The first element is the default. */ static const char *ordernames[] = { "cpu", "size", "res", "time", "pri", "threads", "total", "read", "write", "fault", "vcsw", "ivcsw", "jid", "swap", "pid", NULL }; /* Per-cpu time states */ static int maxcpu; static int maxid; static int ncpus; static unsigned long cpumask; static long *times; static long *pcpu_cp_time; static long *pcpu_cp_old; static long *pcpu_cp_diff; static int *pcpu_cpu_states; static int compare_swap(const void *a, const void *b); static int compare_jid(const void *a, const void *b); static int compare_pid(const void *a, const void *b); static int compare_tid(const void *a, const void *b); static const char *format_nice(const struct kinfo_proc *pp); static void getsysctl(const char *name, void *ptr, size_t len); static int swapmode(int *retavail, int *retfree); static void update_layout(void); static int find_uid(uid_t needle, int *haystack); static int find_uid(uid_t needle, int *haystack) { size_t i = 0; for (; i < TOP_MAX_UIDS; ++i) if ((uid_t)haystack[i] == needle) return 1; return (0); } void toggle_pcpustats(void) { if (ncpus == 1) return; update_layout(); } /* Adjust display based on ncpus and the ARC state. */ static void update_layout(void) { y_mem = 3; y_arc = 4; y_carc = 5; y_swap = 3 + arc_enabled + carc_enabled + has_swap; y_idlecursor = 4 + arc_enabled + carc_enabled + has_swap; y_message = 4 + arc_enabled + carc_enabled + has_swap; y_header = 5 + arc_enabled + carc_enabled + has_swap; y_procs = 6 + arc_enabled + carc_enabled + has_swap; Header_lines = 6 + arc_enabled + carc_enabled + has_swap; if (pcpu_stats) { y_mem += ncpus - 1; y_arc += ncpus - 1; y_carc += ncpus - 1; y_swap += ncpus - 1; y_idlecursor += ncpus - 1; y_message += ncpus - 1; y_header += ncpus - 1; y_procs += ncpus - 1; Header_lines += ncpus - 1; } } int machine_init(struct statics *statics) { int i, j, empty, pagesize; uint64_t arc_size; int carc_en, nswapdev; size_t size; size = sizeof(smpmode); if ((sysctlbyname("machdep.smp_active", &smpmode, &size, NULL, 0) != 0 && sysctlbyname("kern.smp.active", &smpmode, &size, NULL, 0) != 0) || size != sizeof(smpmode)) smpmode = 0; size = sizeof(arc_size); if (sysctlbyname("kstat.zfs.misc.arcstats.size", &arc_size, &size, NULL, 0) == 0 && arc_size != 0) arc_enabled = 1; size = sizeof(carc_en); if (arc_enabled && sysctlbyname("vfs.zfs.compressed_arc_enabled", &carc_en, &size, NULL, 0) == 0 && carc_en == 1) carc_enabled = 1; kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); if (kd == NULL) return (-1); size = sizeof(nswapdev); if (sysctlbyname("vm.nswapdev", &nswapdev, &size, NULL, 0) == 0 && nswapdev != 0) has_swap = 1; GETSYSCTL("kern.ccpu", ccpu); /* this is used in calculating WCPU -- calculate it ahead of time */ logcpu = log(loaddouble(ccpu)); pbase = NULL; pref = NULL; pcpu = NULL; nproc = 0; onproc = -1; /* get the page size and calculate pageshift from it */ pagesize = getpagesize(); pageshift = 0; while (pagesize > 1) { pageshift++; pagesize >>= 1; } /* we only need the amount of log(2)1024 for our conversion */ pageshift -= LOG1024; /* fill in the statics information */ statics->procstate_names = procstatenames; statics->cpustate_names = cpustatenames; statics->memory_names = memorynames; if (arc_enabled) statics->arc_names = arcnames; else statics->arc_names = NULL; if (carc_enabled) statics->carc_names = carcnames; else statics->carc_names = NULL; if (has_swap) statics->swap_names = swapnames; else statics->swap_names = NULL; statics->order_names = ordernames; /* Allocate state for per-CPU stats. */ cpumask = 0; ncpus = 0; GETSYSCTL("kern.smp.maxcpus", maxcpu); times = calloc(maxcpu * CPUSTATES, sizeof(long)); if (times == NULL) err(1, "calloc for kern.smp.maxcpus"); size = sizeof(long) * maxcpu * CPUSTATES; if (sysctlbyname("kern.cp_times", times, &size, NULL, 0) == -1) err(1, "sysctlbyname kern.cp_times"); pcpu_cp_time = calloc(1, size); maxid = (size / CPUSTATES / sizeof(long)) - 1; for (i = 0; i <= maxid; i++) { empty = 1; for (j = 0; empty && j < CPUSTATES; j++) { if (times[i * CPUSTATES + j] != 0) empty = 0; } if (!empty) { cpumask |= (1ul << i); ncpus++; } } assert(ncpus > 0); pcpu_cp_old = calloc(ncpus * CPUSTATES, sizeof(long)); pcpu_cp_diff = calloc(ncpus * CPUSTATES, sizeof(long)); pcpu_cpu_states = calloc(ncpus * CPUSTATES, sizeof(int)); statics->ncpus = ncpus; update_layout(); /* all done! */ return (0); } char * format_header(const char *uname_field) { static struct sbuf* header = NULL; /* clean up from last time. */ if (header != NULL) { sbuf_clear(header); } else { header = sbuf_new_auto(); } switch (displaymode) { case DISP_CPU: { sbuf_printf(header, " %s", ps.thread_id ? " THR" : "PID"); sbuf_printf(header, "%*s", ps.jail ? TOP_JID_LEN : 0, ps.jail ? " JID" : ""); sbuf_printf(header, " %-*.*s ", namelength, namelength, uname_field); if (!ps.thread) { sbuf_cat(header, "THR "); } sbuf_cat(header, "PRI NICE SIZE RES "); if (ps.swap) { sbuf_printf(header, "%*s ", TOP_SWAP_LEN - 1, "SWAP"); } sbuf_cat(header, "STATE "); if (smpmode) { sbuf_cat(header, "C "); } sbuf_cat(header, "TIME "); sbuf_printf(header, " %6s ", ps.wcpu ? "WCPU" : "CPU"); sbuf_cat(header, "COMMAND"); sbuf_finish(header); break; } case DISP_IO: { sbuf_printf(header, " %s%*s %-*.*s", ps.thread_id ? " THR" : "PID", ps.jail ? TOP_JID_LEN : 0, ps.jail ? " JID" : "", namelength, namelength, uname_field); sbuf_cat(header, " VCSW IVCSW READ WRITE FAULT TOTAL PERCENT COMMAND"); sbuf_finish(header); break; } case DISP_MAX: assert("displaymode must not be set to DISP_MAX"); } return sbuf_data(header); } static int swappgsin = -1; static int swappgsout = -1; void get_system_info(struct system_info *si) { struct loadavg sysload; int mib[2]; struct timeval boottime; uint64_t arc_stat, arc_stat2; int i, j; size_t size; /* get the CPU stats */ size = (maxid + 1) * CPUSTATES * sizeof(long); if (sysctlbyname("kern.cp_times", pcpu_cp_time, &size, NULL, 0) == -1) err(1, "sysctlbyname kern.cp_times"); GETSYSCTL("kern.cp_time", cp_time); GETSYSCTL("vm.loadavg", sysload); GETSYSCTL("kern.lastpid", lastpid); /* convert load averages to doubles */ for (i = 0; i < 3; i++) si->load_avg[i] = (double)sysload.ldavg[i] / sysload.fscale; /* convert cp_time counts to percentages */ for (i = j = 0; i <= maxid; i++) { if ((cpumask & (1ul << i)) == 0) continue; percentages(CPUSTATES, &pcpu_cpu_states[j * CPUSTATES], &pcpu_cp_time[j * CPUSTATES], &pcpu_cp_old[j * CPUSTATES], &pcpu_cp_diff[j * CPUSTATES]); j++; } percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); /* sum memory & swap statistics */ { static unsigned int swap_delay = 0; static int swapavail = 0; static int swapfree = 0; static long bufspace = 0; static uint64_t nspgsin, nspgsout; GETSYSCTL("vfs.bufspace", bufspace); GETSYSCTL("vm.stats.vm.v_active_count", memory_stats[0]); GETSYSCTL("vm.stats.vm.v_inactive_count", memory_stats[1]); GETSYSCTL("vm.stats.vm.v_laundry_count", memory_stats[2]); GETSYSCTL("vm.stats.vm.v_wire_count", memory_stats[3]); GETSYSCTL("vm.stats.vm.v_free_count", memory_stats[5]); GETSYSCTL("vm.stats.vm.v_swappgsin", nspgsin); GETSYSCTL("vm.stats.vm.v_swappgsout", nspgsout); /* convert memory stats to Kbytes */ memory_stats[0] = pagetok(memory_stats[0]); memory_stats[1] = pagetok(memory_stats[1]); memory_stats[2] = pagetok(memory_stats[2]); memory_stats[3] = pagetok(memory_stats[3]); memory_stats[4] = bufspace / 1024; memory_stats[5] = pagetok(memory_stats[5]); memory_stats[6] = -1; /* first interval */ if (swappgsin < 0) { swap_stats[4] = 0; swap_stats[5] = 0; } /* compute differences between old and new swap statistic */ else { swap_stats[4] = pagetok(((nspgsin - swappgsin))); swap_stats[5] = pagetok(((nspgsout - swappgsout))); } swappgsin = nspgsin; swappgsout = nspgsout; /* call CPU heavy swapmode() only for changes */ if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) { swap_stats[3] = swapmode(&swapavail, &swapfree); swap_stats[0] = swapavail; swap_stats[1] = swapavail - swapfree; swap_stats[2] = swapfree; } swap_delay = 1; swap_stats[6] = -1; } if (arc_enabled) { GETSYSCTL("kstat.zfs.misc.arcstats.size", arc_stat); arc_stats[0] = arc_stat >> 10; GETSYSCTL("vfs.zfs.mfu_size", arc_stat); arc_stats[1] = arc_stat >> 10; GETSYSCTL("vfs.zfs.mru_size", arc_stat); arc_stats[2] = arc_stat >> 10; GETSYSCTL("vfs.zfs.anon_size", arc_stat); arc_stats[3] = arc_stat >> 10; GETSYSCTL("kstat.zfs.misc.arcstats.hdr_size", arc_stat); GETSYSCTL("kstat.zfs.misc.arcstats.l2_hdr_size", arc_stat2); arc_stats[4] = (arc_stat + arc_stat2) >> 10; GETSYSCTL("kstat.zfs.misc.arcstats.bonus_size", arc_stat); arc_stats[5] = arc_stat >> 10; GETSYSCTL("kstat.zfs.misc.arcstats.dnode_size", arc_stat); arc_stats[5] += arc_stat >> 10; GETSYSCTL("kstat.zfs.misc.arcstats.dbuf_size", arc_stat); arc_stats[5] += arc_stat >> 10; si->arc = arc_stats; } if (carc_enabled) { GETSYSCTL("kstat.zfs.misc.arcstats.compressed_size", arc_stat); carc_stats[0] = arc_stat >> 10; carc_stats[2] = arc_stat >> 10; /* For ratio */ GETSYSCTL("kstat.zfs.misc.arcstats.uncompressed_size", arc_stat); carc_stats[1] = arc_stat >> 10; si->carc = carc_stats; } /* set arrays and strings */ if (pcpu_stats) { si->cpustates = pcpu_cpu_states; si->ncpus = ncpus; } else { si->cpustates = cpu_states; si->ncpus = 1; } si->memory = memory_stats; si->swap = swap_stats; if (lastpid > 0) { si->last_pid = lastpid; } else { si->last_pid = -1; } /* * Print how long system has been up. * (Found by looking getting "boottime" from the kernel) */ mib[0] = CTL_KERN; mib[1] = KERN_BOOTTIME; size = sizeof(boottime); if (sysctl(mib, nitems(mib), &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) { si->boottime = boottime; } else { si->boottime.tv_sec = -1; } } #define NOPROC ((void *)-1) /* * We need to compare data from the old process entry with the new * process entry. * To facilitate doing this quickly we stash a pointer in the kinfo_proc * structure to cache the mapping. We also use a negative cache pointer * of NOPROC to avoid duplicate lookups. * XXX: this could be done when the actual processes are fetched, we do * it here out of laziness. */ static const struct kinfo_proc * get_old_proc(struct kinfo_proc *pp) { const struct kinfo_proc * const *oldpp, *oldp; /* * If this is the first fetch of the kinfo_procs then we don't have * any previous entries. */ if (previous_proc_count == 0) return (NULL); /* negative cache? */ if (pp->ki_udata == NOPROC) return (NULL); /* cached? */ if (pp->ki_udata != NULL) return (pp->ki_udata); /* * Not cached, * 1) look up based on pid. * 2) compare process start. * If we fail here, then setup a negative cache entry, otherwise * cache it. */ oldpp = bsearch(&pp, previous_pref, previous_proc_count, sizeof(*previous_pref), ps.thread ? compare_tid : compare_pid); if (oldpp == NULL) { pp->ki_udata = NOPROC; return (NULL); } oldp = *oldpp; if (memcmp(&oldp->ki_start, &pp->ki_start, sizeof(pp->ki_start)) != 0) { pp->ki_udata = NOPROC; return (NULL); } pp->ki_udata = __DECONST(void *, oldp); return (oldp); } /* * Return the total amount of IO done in blocks in/out and faults. * store the values individually in the pointers passed in. */ static long get_io_stats(const struct kinfo_proc *pp, long *inp, long *oup, long *flp, long *vcsw, long *ivcsw) { const struct kinfo_proc *oldp; static struct kinfo_proc dummy; long ret; oldp = get_old_proc(__DECONST(struct kinfo_proc *, pp)); if (oldp == NULL) { memset(&dummy, 0, sizeof(dummy)); oldp = &dummy; } *inp = RU(pp)->ru_inblock - RU(oldp)->ru_inblock; *oup = RU(pp)->ru_oublock - RU(oldp)->ru_oublock; *flp = RU(pp)->ru_majflt - RU(oldp)->ru_majflt; *vcsw = RU(pp)->ru_nvcsw - RU(oldp)->ru_nvcsw; *ivcsw = RU(pp)->ru_nivcsw - RU(oldp)->ru_nivcsw; ret = (RU(pp)->ru_inblock - RU(oldp)->ru_inblock) + (RU(pp)->ru_oublock - RU(oldp)->ru_oublock) + (RU(pp)->ru_majflt - RU(oldp)->ru_majflt); return (ret); } /* * If there was a previous update, use the delta in ki_runtime over * the previous interval to calculate pctcpu. Otherwise, fall back * to using the kernel's ki_pctcpu. */ static double proc_calc_pctcpu(struct kinfo_proc *pp) { const struct kinfo_proc *oldp; if (previous_interval != 0) { oldp = get_old_proc(pp); if (oldp != NULL) return ((double)(pp->ki_runtime - oldp->ki_runtime) / previous_interval); /* * If this process/thread was created during the previous * interval, charge it's total runtime to the previous * interval. */ else if (pp->ki_start.tv_sec > previous_wall_time.tv_sec || (pp->ki_start.tv_sec == previous_wall_time.tv_sec && pp->ki_start.tv_usec >= previous_wall_time.tv_usec)) return ((double)pp->ki_runtime / previous_interval); } return (pctdouble(pp->ki_pctcpu)); } /* * Return true if this process has used any CPU time since the * previous update. */ static int proc_used_cpu(struct kinfo_proc *pp) { const struct kinfo_proc *oldp; oldp = get_old_proc(pp); if (oldp == NULL) return (PCTCPU(pp) != 0); return (pp->ki_runtime != oldp->ki_runtime || RU(pp)->ru_nvcsw != RU(oldp)->ru_nvcsw || RU(pp)->ru_nivcsw != RU(oldp)->ru_nivcsw); } /* * Return the total number of block in/out and faults by a process. */ static long get_io_total(const struct kinfo_proc *pp) { long dummy; return (get_io_stats(pp, &dummy, &dummy, &dummy, &dummy, &dummy)); } static struct handle handle; void * get_process_info(struct system_info *si, struct process_select *sel, int (*compare)(const void *, const void *)) { int i; int total_procs; long p_io; long p_inblock, p_oublock, p_majflt, p_vcsw, p_ivcsw; long nsec; int active_procs; struct kinfo_proc **prefp; struct kinfo_proc *pp; struct timespec previous_proc_uptime; /* * If thread state was toggled, don't cache the previous processes. */ if (previous_thread != sel->thread) nproc = 0; previous_thread = sel->thread; /* * Save the previous process info. */ if (previous_proc_count_max < nproc) { free(previous_procs); previous_procs = calloc(nproc, sizeof(*previous_procs)); free(previous_pref); previous_pref = calloc(nproc, sizeof(*previous_pref)); if (previous_procs == NULL || previous_pref == NULL) { fprintf(stderr, "top: Out of memory.\n"); quit(TOP_EX_SYS_ERROR); } previous_proc_count_max = nproc; } if (nproc) { for (i = 0; i < nproc; i++) previous_pref[i] = &previous_procs[i]; memcpy(previous_procs, pbase, nproc * sizeof(*previous_procs)); qsort(previous_pref, nproc, sizeof(*previous_pref), ps.thread ? compare_tid : compare_pid); } previous_proc_count = nproc; previous_proc_uptime = proc_uptime; previous_wall_time = proc_wall_time; previous_interval = 0; pbase = kvm_getprocs(kd, sel->thread ? KERN_PROC_ALL : KERN_PROC_PROC, 0, &nproc); gettimeofday(&proc_wall_time, NULL); if (clock_gettime(CLOCK_UPTIME, &proc_uptime) != 0) memset(&proc_uptime, 0, sizeof(proc_uptime)); else if (previous_proc_uptime.tv_sec != 0 && previous_proc_uptime.tv_nsec != 0) { previous_interval = (proc_uptime.tv_sec - previous_proc_uptime.tv_sec) * 1000000; nsec = proc_uptime.tv_nsec - previous_proc_uptime.tv_nsec; if (nsec < 0) { previous_interval -= 1000000; nsec += 1000000000; } previous_interval += nsec / 1000; } if (nproc > onproc) { pref = realloc(pref, sizeof(*pref) * nproc); pcpu = realloc(pcpu, sizeof(*pcpu) * nproc); onproc = nproc; } if (pref == NULL || pbase == NULL || pcpu == NULL) { fprintf(stderr, "top: Out of memory.\n"); quit(TOP_EX_SYS_ERROR); } /* get a pointer to the states summary array */ si->procstates = process_states; /* count up process states and get pointers to interesting procs */ total_procs = 0; active_procs = 0; total_inblock = 0; total_oublock = 0; total_majflt = 0; memset(process_states, 0, sizeof(process_states)); prefp = pref; for (pp = pbase, i = 0; i < nproc; pp++, i++) { if (pp->ki_stat == 0) /* not in use */ continue; if (!sel->self && pp->ki_pid == mypid && sel->pid == -1) /* skip self */ continue; if (!sel->system && (pp->ki_flag & P_SYSTEM) && sel->pid == -1) /* skip system process */ continue; p_io = get_io_stats(pp, &p_inblock, &p_oublock, &p_majflt, &p_vcsw, &p_ivcsw); total_inblock += p_inblock; total_oublock += p_oublock; total_majflt += p_majflt; total_procs++; process_states[(unsigned char)pp->ki_stat]++; if (pp->ki_stat == SZOMB) /* skip zombies */ continue; if (!sel->kidle && pp->ki_tdflags & TDF_IDLETD && sel->pid == -1) /* skip kernel idle process */ continue; PCTCPU(pp) = proc_calc_pctcpu(pp); if (sel->thread && PCTCPU(pp) > 1.0) PCTCPU(pp) = 1.0; if (displaymode == DISP_CPU && !sel->idle && (!proc_used_cpu(pp) || pp->ki_stat == SSTOP || pp->ki_stat == SIDL)) /* skip idle or non-running processes */ continue; if (displaymode == DISP_IO && !sel->idle && p_io == 0) /* skip processes that aren't doing I/O */ continue; if (sel->jid != -1 && pp->ki_jid != sel->jid) /* skip proc. that don't belong to the selected JID */ continue; if (sel->uid[0] != -1 && !find_uid(pp->ki_ruid, sel->uid)) /* skip proc. that don't belong to the selected UID */ continue; if (sel->pid != -1 && pp->ki_pid != sel->pid) continue; *prefp++ = pp; active_procs++; } /* if requested, sort the "interesting" processes */ if (compare != NULL) qsort(pref, active_procs, sizeof(*pref), compare); /* remember active and total counts */ si->p_total = total_procs; si->p_pactive = pref_len = active_procs; /* pass back a handle */ handle.next_proc = pref; handle.remaining = active_procs; return (&handle); } char * format_next_process(struct handle * xhandle, char *(*get_userid)(int), int flags) { struct kinfo_proc *pp; const struct kinfo_proc *oldp; long cputime; char status[22]; size_t state; struct rusage ru, *rup; long p_tot, s_tot; char *cmdbuf = NULL; char **args; static struct sbuf* procbuf = NULL; /* clean up from last time. */ if (procbuf != NULL) { sbuf_clear(procbuf); } else { procbuf = sbuf_new_auto(); } /* find and remember the next proc structure */ pp = *(xhandle->next_proc++); xhandle->remaining--; /* get the process's command name */ if ((pp->ki_flag & P_INMEM) == 0) { /* * Print swapped processes as */ size_t len; len = strlen(pp->ki_comm); if (len > sizeof(pp->ki_comm) - 3) len = sizeof(pp->ki_comm) - 3; memmove(pp->ki_comm + 1, pp->ki_comm, len); pp->ki_comm[0] = '<'; pp->ki_comm[len + 1] = '>'; pp->ki_comm[len + 2] = '\0'; } /* * Convert the process's runtime from microseconds to seconds. This * time includes the interrupt time although that is not wanted here. * ps(1) is similarly sloppy. */ cputime = (pp->ki_runtime + 500000) / 1000000; /* generate "STATE" field */ switch (state = pp->ki_stat) { case SRUN: if (smpmode && pp->ki_oncpu != NOCPU) sprintf(status, "CPU%d", pp->ki_oncpu); else strcpy(status, "RUN"); break; case SLOCK: if (pp->ki_kiflag & KI_LOCKBLOCK) { sprintf(status, "*%.6s", pp->ki_lockname); break; } /* fall through */ case SSLEEP: sprintf(status, "%.6s", pp->ki_wmesg); break; default: if (state < nitems(state_abbrev)) { sprintf(status, "%.6s", state_abbrev[state]); } else { sprintf(status, "?%5zu", state); } break; } cmdbuf = calloc(screen_width + 1, 1); if (cmdbuf == NULL) { warn("calloc(%d)", screen_width + 1); return NULL; } if (!(flags & FMT_SHOWARGS)) { if (ps.thread && pp->ki_flag & P_HADTHREADS && pp->ki_tdname[0]) { snprintf(cmdbuf, screen_width, "%s{%s%s}", pp->ki_comm, pp->ki_tdname, pp->ki_moretdname); } else { snprintf(cmdbuf, screen_width, "%s", pp->ki_comm); } } else { if (pp->ki_flag & P_SYSTEM || (args = kvm_getargv(kd, pp, screen_width)) == NULL || !(*args)) { if (ps.thread && pp->ki_flag & P_HADTHREADS && pp->ki_tdname[0]) { snprintf(cmdbuf, screen_width, "[%s{%s%s}]", pp->ki_comm, pp->ki_tdname, pp->ki_moretdname); } else { snprintf(cmdbuf, screen_width, "[%s]", pp->ki_comm); } } else { const char *src; char *dst, *argbuf; const char *cmd; size_t argbuflen; size_t len; argbuflen = screen_width * 4; argbuf = calloc(argbuflen + 1, 1); if (argbuf == NULL) { warn("calloc(%zu)", argbuflen + 1); free(cmdbuf); return NULL; } dst = argbuf; /* Extract cmd name from argv */ cmd = basename(*args); for (; (src = *args++) != NULL; ) { if (*src == '\0') continue; len = (argbuflen - (dst - argbuf) - 1) / 4; strvisx(dst, src, MIN(strlen(src), len), - VIS_NL | VIS_CSTYLE); + VIS_NL | VIS_CSTYLE | VIS_OCTAL | VIS_SAFE); while (*dst != '\0') dst++; if ((argbuflen - (dst - argbuf) - 1) / 4 > 0) *dst++ = ' '; /* add delimiting space */ } if (dst != argbuf && dst[-1] == ' ') dst--; *dst = '\0'; if (strcmp(cmd, pp->ki_comm) != 0) { if (ps.thread && pp->ki_flag & P_HADTHREADS && pp->ki_tdname[0]) snprintf(cmdbuf, screen_width, "%s (%s){%s%s}", argbuf, pp->ki_comm, pp->ki_tdname, pp->ki_moretdname); else snprintf(cmdbuf, screen_width, "%s (%s)", argbuf, pp->ki_comm); } else { if (ps.thread && pp->ki_flag & P_HADTHREADS && pp->ki_tdname[0]) snprintf(cmdbuf, screen_width, "%s{%s%s}", argbuf, pp->ki_tdname, pp->ki_moretdname); else strlcpy(cmdbuf, argbuf, screen_width); } free(argbuf); } } if (displaymode == DISP_IO) { oldp = get_old_proc(pp); if (oldp != NULL) { ru.ru_inblock = RU(pp)->ru_inblock - RU(oldp)->ru_inblock; ru.ru_oublock = RU(pp)->ru_oublock - RU(oldp)->ru_oublock; ru.ru_majflt = RU(pp)->ru_majflt - RU(oldp)->ru_majflt; ru.ru_nvcsw = RU(pp)->ru_nvcsw - RU(oldp)->ru_nvcsw; ru.ru_nivcsw = RU(pp)->ru_nivcsw - RU(oldp)->ru_nivcsw; rup = &ru; } else { rup = RU(pp); } p_tot = rup->ru_inblock + rup->ru_oublock + rup->ru_majflt; s_tot = total_inblock + total_oublock + total_majflt; sbuf_printf(procbuf, "%5d ", (ps.thread_id) ? pp->ki_tid : pp->ki_pid); if (ps.jail) { sbuf_printf(procbuf, "%*d ", TOP_JID_LEN - 1, pp->ki_jid); } sbuf_printf(procbuf, "%-*.*s", namelength, namelength, (*get_userid)(pp->ki_ruid)); sbuf_printf(procbuf, "%6ld ", rup->ru_nvcsw); sbuf_printf(procbuf, "%6ld ", rup->ru_nivcsw); sbuf_printf(procbuf, "%6ld ", rup->ru_inblock); sbuf_printf(procbuf, "%6ld ", rup->ru_oublock); sbuf_printf(procbuf, "%6ld ", rup->ru_majflt); sbuf_printf(procbuf, "%6ld ", p_tot); sbuf_printf(procbuf, "%6.2f%% ", s_tot == 0 ? 0.0 : (p_tot * 100.0 / s_tot)); } else { sbuf_printf(procbuf, "%5d ", (ps.thread_id) ? pp->ki_tid : pp->ki_pid); if (ps.jail) { sbuf_printf(procbuf, "%*d ", TOP_JID_LEN - 1, pp->ki_jid); } sbuf_printf(procbuf, "%-*.*s ", namelength, namelength, (*get_userid)(pp->ki_ruid)); if (!ps.thread) { sbuf_printf(procbuf, "%4d ", pp->ki_numthreads); } else { sbuf_printf(procbuf, " "); } sbuf_printf(procbuf, "%3d ", pp->ki_pri.pri_level - PZERO); sbuf_printf(procbuf, "%4s", format_nice(pp)); sbuf_printf(procbuf, "%7s ", format_k(PROCSIZE(pp))); sbuf_printf(procbuf, "%6s ", format_k(pagetok(pp->ki_rssize))); if (ps.swap) { sbuf_printf(procbuf, "%*s ", TOP_SWAP_LEN - 1, format_k(pagetok(ki_swap(pp)))); } sbuf_printf(procbuf, "%-6.6s ", status); if (smpmode) { int cpu; if (state == SRUN && pp->ki_oncpu != NOCPU) { cpu = pp->ki_oncpu; } else { cpu = pp->ki_lastcpu; } sbuf_printf(procbuf, "%3d ", cpu); } sbuf_printf(procbuf, "%6s ", format_time(cputime)); sbuf_printf(procbuf, "%6.2f%% ", ps.wcpu ? 100.0 * weighted_cpu(PCTCPU(pp), pp) : 100.0 * PCTCPU(pp)); } - sbuf_printf(procbuf, "%s", printable(cmdbuf)); + sbuf_printf(procbuf, "%s", cmdbuf); free(cmdbuf); return (sbuf_data(procbuf)); } static void getsysctl(const char *name, void *ptr, size_t len) { size_t nlen = len; if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) { fprintf(stderr, "top: sysctl(%s...) failed: %s\n", name, strerror(errno)); quit(TOP_EX_SYS_ERROR); } if (nlen != len) { fprintf(stderr, "top: sysctl(%s...) expected %lu, got %lu\n", name, (unsigned long)len, (unsigned long)nlen); quit(TOP_EX_SYS_ERROR); } } static const char * format_nice(const struct kinfo_proc *pp) { const char *fifo, *kproc; int rtpri; static char nicebuf[4 + 1]; fifo = PRI_NEED_RR(pp->ki_pri.pri_class) ? "" : "F"; kproc = (pp->ki_flag & P_KPROC) ? "k" : ""; switch (PRI_BASE(pp->ki_pri.pri_class)) { case PRI_ITHD: return ("-"); case PRI_REALTIME: /* * XXX: the kernel doesn't tell us the original rtprio and * doesn't really know what it was, so to recover it we * must be more chummy with the implementation than the * implementation is with itself. pri_user gives a * constant "base" priority, but is only initialized * properly for user threads. pri_native gives what the * kernel calls the "base" priority, but it isn't constant * since it is changed by priority propagation. pri_native * also isn't properly initialized for all threads, but it * is properly initialized for kernel realtime and idletime * threads. Thus we use pri_user for the base priority of * user threads (it is always correct) and pri_native for * the base priority of kernel realtime and idletime threads * (there is nothing better, and it is usually correct). * * The field width and thus the buffer are too small for * values like "kr31F", but such values shouldn't occur, * and if they do then the tailing "F" is not displayed. */ rtpri = ((pp->ki_flag & P_KPROC) ? pp->ki_pri.pri_native : pp->ki_pri.pri_user) - PRI_MIN_REALTIME; snprintf(nicebuf, sizeof(nicebuf), "%sr%d%s", kproc, rtpri, fifo); break; case PRI_TIMESHARE: if (pp->ki_flag & P_KPROC) return ("-"); snprintf(nicebuf, sizeof(nicebuf), "%d", pp->ki_nice - NZERO); break; case PRI_IDLE: /* XXX: as above. */ rtpri = ((pp->ki_flag & P_KPROC) ? pp->ki_pri.pri_native : pp->ki_pri.pri_user) - PRI_MIN_IDLE; snprintf(nicebuf, sizeof(nicebuf), "%si%d%s", kproc, rtpri, fifo); break; default: return ("?"); } return (nicebuf); } /* comparison routines for qsort */ static int compare_pid(const void *p1, const void *p2) { const struct kinfo_proc * const *pp1 = p1; const struct kinfo_proc * const *pp2 = p2; assert((*pp2)->ki_pid >= 0 && (*pp1)->ki_pid >= 0); return ((*pp1)->ki_pid - (*pp2)->ki_pid); } static int compare_tid(const void *p1, const void *p2) { const struct kinfo_proc * const *pp1 = p1; const struct kinfo_proc * const *pp2 = p2; assert((*pp2)->ki_tid >= 0 && (*pp1)->ki_tid >= 0); return ((*pp1)->ki_tid - (*pp2)->ki_tid); } /* * proc_compare - comparison function for "qsort" * Compares the resource consumption of two processes using five * distinct keys. The keys (in descending order of importance) are: * percent cpu, cpu ticks, state, resident set size, total virtual * memory usage. The process states are ordered as follows (from least * to most important): WAIT, zombie, sleep, stop, start, run. The * array declaration below maps a process state index into a number * that reflects this ordering. */ static int sorted_state[] = { 0, /* not used */ 3, /* sleep */ 1, /* ABANDONED (WAIT) */ 6, /* run */ 5, /* start */ 2, /* zombie */ 4 /* stop */ }; #define ORDERKEY_PCTCPU(a, b) do { \ double diff; \ if (ps.wcpu) \ diff = weighted_cpu(PCTCPU((b)), (b)) - \ weighted_cpu(PCTCPU((a)), (a)); \ else \ diff = PCTCPU((b)) - PCTCPU((a)); \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_CPTICKS(a, b) do { \ int64_t diff = (int64_t)(b)->ki_runtime - (int64_t)(a)->ki_runtime; \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_STATE(a, b) do { \ int diff = sorted_state[(unsigned char)(b)->ki_stat] - sorted_state[(unsigned char)(a)->ki_stat]; \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_PRIO(a, b) do { \ int diff = (int)(b)->ki_pri.pri_level - (int)(a)->ki_pri.pri_level; \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_THREADS(a, b) do { \ int diff = (int)(b)->ki_numthreads - (int)(a)->ki_numthreads; \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_RSSIZE(a, b) do { \ long diff = (long)(b)->ki_rssize - (long)(a)->ki_rssize; \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_MEM(a, b) do { \ long diff = (long)PROCSIZE((b)) - (long)PROCSIZE((a)); \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_JID(a, b) do { \ int diff = (int)(b)->ki_jid - (int)(a)->ki_jid; \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_SWAP(a, b) do { \ int diff = (int)ki_swap(b) - (int)ki_swap(a); \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) /* compare_cpu - the comparison function for sorting by cpu percentage */ static int compare_cpu(const void *arg1, const void *arg2) { const struct kinfo_proc *p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc *p2 = *(const struct kinfo_proc * const *)arg2; ORDERKEY_PCTCPU(p1, p2); ORDERKEY_CPTICKS(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_PRIO(p1, p2); ORDERKEY_RSSIZE(p1, p2); ORDERKEY_MEM(p1, p2); return (0); } /* compare_size - the comparison function for sorting by total memory usage */ static int compare_size(const void *arg1, const void *arg2) { const struct kinfo_proc *p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc *p2 = *(const struct kinfo_proc * const *)arg2; ORDERKEY_MEM(p1, p2); ORDERKEY_RSSIZE(p1, p2); ORDERKEY_PCTCPU(p1, p2); ORDERKEY_CPTICKS(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_PRIO(p1, p2); return (0); } /* compare_res - the comparison function for sorting by resident set size */ static int compare_res(const void *arg1, const void *arg2) { const struct kinfo_proc *p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc *p2 = *(const struct kinfo_proc * const *)arg2; ORDERKEY_RSSIZE(p1, p2); ORDERKEY_MEM(p1, p2); ORDERKEY_PCTCPU(p1, p2); ORDERKEY_CPTICKS(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_PRIO(p1, p2); return (0); } /* compare_time - the comparison function for sorting by total cpu time */ static int compare_time(const void *arg1, const void *arg2) { const struct kinfo_proc *p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc *p2 = *(const struct kinfo_proc * const *) arg2; ORDERKEY_CPTICKS(p1, p2); ORDERKEY_PCTCPU(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_PRIO(p1, p2); ORDERKEY_RSSIZE(p1, p2); ORDERKEY_MEM(p1, p2); return (0); } /* compare_prio - the comparison function for sorting by priority */ static int compare_prio(const void *arg1, const void *arg2) { const struct kinfo_proc *p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc *p2 = *(const struct kinfo_proc * const *)arg2; ORDERKEY_PRIO(p1, p2); ORDERKEY_CPTICKS(p1, p2); ORDERKEY_PCTCPU(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_RSSIZE(p1, p2); ORDERKEY_MEM(p1, p2); return (0); } /* compare_threads - the comparison function for sorting by threads */ static int compare_threads(const void *arg1, const void *arg2) { const struct kinfo_proc *p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc *p2 = *(const struct kinfo_proc * const *)arg2; ORDERKEY_THREADS(p1, p2); ORDERKEY_PCTCPU(p1, p2); ORDERKEY_CPTICKS(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_PRIO(p1, p2); ORDERKEY_RSSIZE(p1, p2); ORDERKEY_MEM(p1, p2); return (0); } /* compare_jid - the comparison function for sorting by jid */ static int compare_jid(const void *arg1, const void *arg2) { const struct kinfo_proc *p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc *p2 = *(const struct kinfo_proc * const *)arg2; ORDERKEY_JID(p1, p2); ORDERKEY_PCTCPU(p1, p2); ORDERKEY_CPTICKS(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_PRIO(p1, p2); ORDERKEY_RSSIZE(p1, p2); ORDERKEY_MEM(p1, p2); return (0); } /* compare_swap - the comparison function for sorting by swap */ static int compare_swap(const void *arg1, const void *arg2) { const struct kinfo_proc *p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc *p2 = *(const struct kinfo_proc * const *)arg2; ORDERKEY_SWAP(p1, p2); ORDERKEY_PCTCPU(p1, p2); ORDERKEY_CPTICKS(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_PRIO(p1, p2); ORDERKEY_RSSIZE(p1, p2); ORDERKEY_MEM(p1, p2); return (0); } /* assorted comparison functions for sorting by i/o */ static int compare_iototal(const void *arg1, const void *arg2) { const struct kinfo_proc * const p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc * const p2 = *(const struct kinfo_proc * const *)arg2; return (get_io_total(p2) - get_io_total(p1)); } static int compare_ioread(const void *arg1, const void *arg2) { const struct kinfo_proc *p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc *p2 = *(const struct kinfo_proc * const *)arg2; long dummy, inp1, inp2; (void) get_io_stats(p1, &inp1, &dummy, &dummy, &dummy, &dummy); (void) get_io_stats(p2, &inp2, &dummy, &dummy, &dummy, &dummy); return (inp2 - inp1); } static int compare_iowrite(const void *arg1, const void *arg2) { const struct kinfo_proc *p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc *p2 = *(const struct kinfo_proc * const *)arg2; long dummy, oup1, oup2; (void) get_io_stats(p1, &dummy, &oup1, &dummy, &dummy, &dummy); (void) get_io_stats(p2, &dummy, &oup2, &dummy, &dummy, &dummy); return (oup2 - oup1); } static int compare_iofault(const void *arg1, const void *arg2) { const struct kinfo_proc *p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc *p2 = *(const struct kinfo_proc * const *)arg2; long dummy, flp1, flp2; (void) get_io_stats(p1, &dummy, &dummy, &flp1, &dummy, &dummy); (void) get_io_stats(p2, &dummy, &dummy, &flp2, &dummy, &dummy); return (flp2 - flp1); } static int compare_vcsw(const void *arg1, const void *arg2) { const struct kinfo_proc *p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc *p2 = *(const struct kinfo_proc * const *)arg2; long dummy, flp1, flp2; (void) get_io_stats(p1, &dummy, &dummy, &dummy, &flp1, &dummy); (void) get_io_stats(p2, &dummy, &dummy, &dummy, &flp2, &dummy); return (flp2 - flp1); } static int compare_ivcsw(const void *arg1, const void *arg2) { const struct kinfo_proc *p1 = *(const struct kinfo_proc * const *)arg1; const struct kinfo_proc *p2 = *(const struct kinfo_proc * const *)arg2; long dummy, flp1, flp2; (void) get_io_stats(p1, &dummy, &dummy, &dummy, &dummy, &flp1); (void) get_io_stats(p2, &dummy, &dummy, &dummy, &dummy, &flp2); return (flp2 - flp1); } int (*compares[])(const void *arg1, const void *arg2) = { compare_cpu, compare_size, compare_res, compare_time, compare_prio, compare_threads, compare_iototal, compare_ioread, compare_iowrite, compare_iofault, compare_vcsw, compare_ivcsw, compare_jid, compare_swap, NULL }; static int swapmode(int *retavail, int *retfree) { int n; struct kvm_swap swapary[1]; static int pagesize = 0; static unsigned long swap_maxpages = 0; *retavail = 0; *retfree = 0; #define CONVERT(v) ((quad_t)(v) * pagesize / 1024) n = kvm_getswapinfo(kd, swapary, 1, 0); if (n < 0 || swapary[0].ksw_total == 0) return (0); if (pagesize == 0) pagesize = getpagesize(); if (swap_maxpages == 0) GETSYSCTL("vm.swap_maxpages", swap_maxpages); /* ksw_total contains the total size of swap all devices which may exceed the maximum swap size allocatable in the system */ if ( swapary[0].ksw_total > swap_maxpages ) swapary[0].ksw_total = swap_maxpages; *retavail = CONVERT(swapary[0].ksw_total); *retfree = CONVERT(swapary[0].ksw_total - swapary[0].ksw_used); #undef CONVERT n = (int)(swapary[0].ksw_used * 100.0 / swapary[0].ksw_total); return (n); } Index: head/usr.bin/top/top.1 =================================================================== --- head/usr.bin/top/top.1 (revision 352557) +++ head/usr.bin/top/top.1 (revision 352558) @@ -1,445 +1,449 @@ .\" $FreeBSD$ -.Dd October 2, 2018 +.Dd September 20, 2019 .Dt TOP 1 .Os .Sh NAME .Nm top .Nd display and update information about the top cpu processes .Sh SYNOPSIS .Nm .Op Fl CHIPSTabijnpqtuvxz .Op Fl J Ar jail .Op Fl U Ar uid .Op Fl d Ar count .Op Fl m Ar cpu|io .Op Fl s Ar time .Op Fl o Ar field .Op Fl p Ar pid .Op Ar count .Sh DESCRIPTION .Nm displays the top processes on the system and periodically updates this information. If standard output is an intelligent terminal (see below) then as many processes as will fit on the terminal screen are displayed by default. Otherwise, a good number of them are shown (around 20). Raw cpu percentage is used to rank the processes. If .Ar number is given, then the top .Ar number processes will be displayed instead of the default. .Pp .Nm makes a distinction between terminals that support advanced capabilities and those that do not. This distinction affects the choice of defaults for certain options. In the remainder of this document, an \*(lqintelligent\*(rq terminal is one that supports cursor addressing, clear screen, and clear to end of line. Conversely, a \*(lqdumb\*(rq terminal is one that does not support such features. If the output of .Nm is redirected to a file, it acts as if it were being run on a dumb terminal. .Bl -tag -width indent -compact .It Fl C Toggle CPU display mode. By default top displays the weighted CPU percentage in the WCPU column (this is the same value that .Xr ps 1 displays as CPU). Each time .Fl C flag is passed it toggles between \*(lqraw cpu\*(rq mode and \*(lqweighted cpu\*(rq mode, showing the \*(lqCPU\*(rq or the \*(lqWCPU\*(rq column respectively. .It Fl S Show system processes in the display. Normally, system processes such as the pager and the swapper are not shown. This option makes them visible. .It Fl a Display command names derived from the argv[] vector, rather than real executable name. It it useful when you want to watch applications, that puts their status information there. If the real name differs from argv[0], it will be displayed in parenthesis. .It Fl b Use \*(lqbatch\*(rq mode. In this mode, all input from the terminal is ignored. Interrupt characters (such as ^C and ^\e) still have an effect. This is the default on a dumb terminal, or when the output is not a terminal. .It Fl H Display each thread for a multithreaded process individually. By default a single summary line is displayed for each process. .It Fl i Use \*(lqinteractive\*(rq mode. In this mode, any input is immediately read for processing. See the section on \*(lqInteractive Mode\*(rq for an explanation of which keys perform what functions. After the command is processed, the screen will immediately be updated, even if the command was not understood. This mode is the default when standard output is an intelligent terminal. .It Fl I Do not display idle processes. By default, top displays both active and idle processes. .It Fl j Display the .Xr jail 8 ID. .It Fl T Toggle displaying thread ID (tid) instead of process id (pid). .It Fl t Do not display the .Nm process itself. .It Fl display Display either 'cpu' or 'io' statistics. Default is 'cpu'. .It Fl n Use \*(lqnon-interactive\*(rq mode. This is identical to \*(lqbatch\*(rq mode. .It Fl P Display per-cpu CPU usage statistics. .It Fl q Renice .Nm to -20 so that it will run faster. This can be used when the system is being very sluggish to improve the possibility of discovering the problem. This option can only be used by root. .It Fl u Do not map uid numbers to usernames. Normally, .Nm will read as much of the file \*(lq/etc/passwd\*(rq as is necessary to map all the user id numbers it encounters into login names. This option disables all that, while possibly decreasing execution time. The uid numbers are displayed instead of the names. .It Fl v Write version number information to stderr then exit immediately. .It Fl w Display approximate swap usage for each process. .It Fl z Do not display the system idle process. .It Fl d Ar count Show only .Ar count displays, then exit. A display is considered to be one update of the screen. The default is 1 for dumb terminals. Note that for .Ar count = 1 no information is available about the percentage of time spent by the CPU in every state. .It Fl s Ar time Set the delay between screen updates to .Ar time seconds. The default delay between updates is 1 second. .It Fl o Ar field Sort the process display area on the specified field. The field name is the name of the column as seen in the output, but in lower case: \*(lqcpu\*(lq, \*(rqsize\*(lq, \*(rqres\*(lq, \*(rqtime\*(lq, \*(rqpri\*(lq, \*(rqthreads\*(lq, \*(lqtotal\*(lq, \*(rqread\*(lq, \*(rqwrite\*(lq, \*(rqfault\*(lq, \*(rqvcsw\*(lq, \*(rqivcsw\*(lq, \*(lqjid\*(lq, \*(rqswap\*(lq or \*(rqpid\*(lq. .It Fl p Ar pid Show only the process .Ar pid . .It Fl J Ar jail Show only those processes owned by .Ar jail . This may be either the .Ar jid or .Ar name of the jail. Use 0 to limit to host processes. Using this option implies .Fl j . .Pp .It Fl U Ar username Show only those processes owned by .Ar username . This option currently only accepts usernames and will not understand uid numbers. .El .Pp Both .Ar count and .Ar number fields can be specified as \*(lqinfinite\*(rq, indicating that they can stretch as far as possible. This is accomplished by using any proper prefix of the keywords \*(lqinfinity\*(rq, \*(lqmaximum\*(rq, or \*(lqall\*(rq. Boolean flags are toggles. A second specification of any of these options will negate the first. +.Pp +The display of command names changes according to the locale. +If command names displayed in the locale settings are recognized as +non-display characters, they are displayed in three digit octal sequence. .Sh "INTERACTIVE MODE" When .Nm is running in \*(lqinteractive mode\*(rq, it reads commands from the terminal and acts upon them accordingly. In this mode, the terminal is put in \*(lqCBREAK\*(rq, so that a character will be processed as soon as it is typed. Almost always, a key will be pressed when .Nm is between displays; that is, while it is waiting for .Ar time seconds to elapse. If this is the case, the command will be processed and the display will be updated immediately thereafter (reflecting any changes that the command may have specified). This happens even if the command was incorrect. If a key is pressed while .Nm is in the middle of updating the display, it will finish the update and then process the command. Some commands require additional information, and the user will be prompted accordingly. While typing this information in, the user's erase and kill keys (as set up by the command .Xr stty 1 ) are recognized, and a newline terminates the input. .Pp These commands are currently recognized (^L refers to control-L): .Bl -tag -width indent .It ^L Redraw the screen. .It h Display a summary of the commands (help screen). Version information is included in this display. .It q Quit .Nm .It d Change the number of displays to show (prompt for new number). Remember that the next display counts as one, so typing .It d1 will make .Nm show one final display and then immediately exit. .It m Toggle the display between 'cpu' and 'io' modes. .It n or # Change the number of processes to display (prompt for new number). .It s Change the number of seconds to delay between displays (prompt for new number). .It S Toggle the display of system processes. .It a Toggle the display of process titles. .It k Send a signal (\*(lqkill\*(rq by default) to a list of processes. This acts similarly to the command .Xr kill 1 . .It r Change the priority (the \*(lqnice\*(rq) of a list of processes. This acts similarly to .Xr renice 8 . .It u Display only processes owned by a specific set of usernames (prompt for username). If the username specified is simply \*(lq+\*(rq or \*(lq-\*(rq, then processes belonging to all users will be displayed. Usernames can be added to and removed from the set by prepending them with \*(lq+\*(rq and \*(lq-\*(rq, respectively. .It o Change the order in which the display is sorted. The sort key names include \*(lqcpu\*(rq, \*(lqres\*(rq, \*(lqsize\*(rq, \*(lqtime\*(rq. The default is cpu. .It p Display a specific process (prompt for pid). If the pid specified is simply \*(lq+\*(rq, then show all processes. .It e Display a list of system errors (if any) generated by the last command. .It B H Toggle the display of threads. .It i or I Toggle the display of idle processes. .It j Toggle the display of .Xr jail 8 ID. .It J Display only processes owned by a specific jail (prompt for jail). If the jail specified is simply \*(lq+\*(rq, then processes belonging to all jails and the host will be displayed. This will also enable the display of JID. .It P Toggle the display of per-CPU statistics. .It T Toggle display of TID and PID .It t Toggle the display of the .Nm process. .It w Toggle the display of swap usage. .It z Toggle the display of the system idle process. .El .Sh "THE DISPLAY" The top few lines of the display show general information about the state of the system, including the last process id assigned to a process (on most systems), the three load averages, the current time, the number of existing processes, the number of processes in each state (sleeping, running, starting, zombies, and stopped), and a percentage of time spent in each of the processor states (user, nice, system, and idle). It also includes information about physical and virtual memory allocation. .Pp The remainder of the screen displays information about individual processes. This display is similar in spirit to .Xr ps 1 but it is not exactly the same. PID is the process id, JID, when displayed, is the .Xr jail 8 ID corresponding to the process, USERNAME is the name of the process's owner (if .Fl u is specified, a UID column will be substituted for USERNAME), PRI is the current priority of the process, NICE is the .Xr nice 1 amount, SIZE is the total size of the process (text, data, and stack), RES is the current amount of resident memory, SWAP is the approximate amount of swap, if enabled (SIZE, RES and SWAP are given in kilobytes), STATE is the current state (one of \*(lqSTART\*(rq, \*(lqRUN\*(rq (shown as \*(lqCPUn\*(rq on SMP systems), \*(lqSLEEP\*(rq, \*(lqSTOP\*(rq, \*(lqZOMB\*(rq, \*(lqWAIT\*(rq, \*(lqLOCK\*(rq or the event on which the process waits), C is the processor number on which the process is executing (visible only on SMP systems), TIME is the number of system and user cpu seconds that the process has used, WCPU, when displayed, is the weighted cpu percentage (this is the same value that .Xr ps 1 displays as CPU), CPU is the raw percentage and is the field that is sorted to determine the order of the processes, and COMMAND is the name of the command that the process is currently running (if the process is swapped out, this column is marked \*(lq\*(rq). .Pp If a process is in the \*(lqSLEEP\*(rq or \*(lqLOCK\*(rq state, the state column will report the name of the event or lock on which the process is waiting. Lock names are prefixed with an asterisk \*(lq*\*(rq while sleep events are not. .Sh DESCRIPTION OF MEMORY .Bd -literal Mem: 61M Active, 86M Inact, 368K Laundry, 22G Wired, 102G Free ARC: 15G Total, 9303M MFU, 6155M MRU, 1464K Anon, 98M Header, 35M Other 15G Compressed, 27G Uncompressed, 1.75:1 Ratio, 174M Overhead Swap: 4096M Total, 532M Free, 13% Inuse, 80K In, 104K Out .Ed .Ss Physical Memory Stats .Bl -tag -width "Uncompressed" -compact .It Em Active number of bytes active .It Em Inact number of clean bytes inactive .It Em Laundry number of dirty bytes queued for laundering .It Em Wired number of bytes wired down, including IO-level cached file data pages .It Em Buf number of bytes used for IO-level disk caching .It Em Free number of bytes free .El .Ss ZFS ARC Stats These stats are only displayed when the ARC is in use. .Pp .Bl -tag -width "Uncompressed" -compact .It Em Total number of wired bytes used for the ZFS ARC .It Em MRU number of ARC bytes holding most recently used data .It Em MFU number of ARC bytes holding most frequently used data .It Em Anon number of ARC bytes holding in flight data .It Em Header number of ARC bytes holding headers .It Em Other miscellaneous ARC bytes .It Em Compressed bytes of memory used by ARC caches .It Em Uncompressed bytes of data stored in ARC caches before compression .It Em Ratio compression ratio of data cached in the ARC .El .Ss Swap Stats .Bl -tag -width "Uncompressed" -compact .It Em Total total available swap usage .It Em Free total free swap usage .It Em Inuse swap usage .It Em \&In bytes paged in from swap devices (last interval) .It Em Out bytes paged out to swap devices (last interval) .El .Sh ENVIRONMENT .Bl -tag -width "Uncompressed" .It Ev TOP Default set of arguments to .Nm . .El .Sh SEE ALSO .Xr kill 1 , .Xr ps 1 , .Xr stty 1 , .Xr getrusage 2 , .Xr humanize_number 3 , .Xr mem 4 , .Xr renice 8 .Sh AUTHORS .An William LeFebvre, EECS Department, Northwestern University .Sh BUGS The command name for swapped processes should be tracked down, but this would make the program run slower. .Pp As with .Xr ps 1 , things can change while .Nm is collecting information for an update. The picture it gives is only a close approximation to reality. Index: head/usr.bin/top/top.c =================================================================== --- head/usr.bin/top/top.c (revision 352557) +++ head/usr.bin/top/top.c (revision 352558) @@ -1,1198 +1,1204 @@ /*- * Top users/processes display for Unix * * This program may be freely redistributed, * but this entire comment MUST remain intact. * * Copyright (c) 1984, 1989, William LeFebvre, Rice University * Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University * Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory * Copyright (c) 1996, William LeFebvre, Group sys Consulting * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include "commands.h" #include "display.h" /* interface to display package */ #include "screen.h" /* interface to screen package */ #include "top.h" #include "machine.h" #include "utils.h" #include "username.h" /* Size of the stdio buffer given to stdout */ #define Buffersize 2048 char copyright[] = "Copyright (c) 1984 through 1996, William LeFebvre"; typedef void sigret_t; /* The buffer that stdio will use */ static char stdoutbuf[Buffersize]; static int fmt_flags = 0; int pcpu_stats = false; /* signal handling routines */ static sigret_t leave(int); static sigret_t tstop(int); static sigret_t top_winch(int); static volatile sig_atomic_t leaveflag; static volatile sig_atomic_t tstopflag; static volatile sig_atomic_t winchflag; /* values which need to be accessed by signal handlers */ static int max_topn; /* maximum displayable processes */ /* miscellaneous things */ struct process_select ps; pid_t mypid; /* pointers to display routines */ static void (*d_loadave)(int mpid, double *avenrun) = i_loadave; static void (*d_procstates)(int total, int *brkdn) = i_procstates; static void (*d_cpustates)(int *states) = i_cpustates; static void (*d_memory)(int *stats) = i_memory; static void (*d_arc)(int *stats) = i_arc; static void (*d_carc)(int *stats) = i_carc; static void (*d_swap)(int *stats) = i_swap; static void (*d_message)(void) = i_message; static void (*d_header)(const char *text) = i_header; static void (*d_process)(int line, char *thisline) = i_process; static void reset_display(void); static const struct option longopts[] = { { "cpu-display-mode", no_argument, NULL, 'C' }, /* differs from orignal */ /* D reserved */ { "thread", no_argument, NULL, 'H' }, { "idle-procs", no_argument, NULL, 'I' }, { "jail", required_argument, NULL, 'J' }, { "per-cpu", no_argument, NULL, 'P' }, { "system-procs", no_argument, NULL, 'S' }, { "thread-id", no_argument, NULL, 'T' }, /* differs from orignal */ { "user", required_argument, NULL, 'U' }, { "all", no_argument, NULL, 'a' }, { "batch", no_argument, NULL, 'b' }, /* c reserved */ { "displays", required_argument, NULL, 'd' }, { "interactive", no_argument, NULL, 'i' }, { "jail-id", no_argument, NULL, 'j' }, { "display-mode", required_argument, NULL, 'm' }, /* n is identical to batch */ { "sort-order", required_argument, NULL, 'o' }, { "pid", required_argument, NULL, 'p' }, { "quick", no_argument, NULL, 'q' }, { "delay", required_argument, NULL, 's' }, { "threads", no_argument, NULL, 't' }, { "uids", no_argument, NULL, 'u' }, { "version", no_argument, NULL, 'v' }, { "swap", no_argument, NULL, 'w' }, { "system-idle-procs", no_argument, NULL, 'z' }, { NULL, 0, NULL, 0 } }; static void reset_uids(void) { for (size_t i = 0; i < TOP_MAX_UIDS; ++i) ps.uid[i] = -1; } static int add_uid(int uid) { size_t i = 0; /* Add the uid if there's room */ for (; i < TOP_MAX_UIDS; ++i) { if (ps.uid[i] == -1 || ps.uid[i] == uid) { ps.uid[i] = uid; break; } } return (i == TOP_MAX_UIDS); } static void rem_uid(int uid) { size_t i = 0; size_t where = TOP_MAX_UIDS; /* Look for the user to remove - no problem if it's not there */ for (; i < TOP_MAX_UIDS; ++i) { if (ps.uid[i] == -1) break; if (ps.uid[i] == uid) where = i; } /* Make sure we don't leave a hole in the middle */ if (where != TOP_MAX_UIDS) { ps.uid[where] = ps.uid[i-1]; ps.uid[i-1] = -1; } } static int handle_user(char *buf, size_t buflen) { int rc = 0; int uid = -1; char *buf2 = buf; new_message(MT_standout, "Username to show (+ for all): "); if (readline(buf, buflen, false) <= 0) { clear_message(); return (rc); } if (buf[0] == '+' || buf[0] == '-') { if (buf[1] == '\0') { reset_uids(); goto end; } else ++buf2; } if ((uid = userid(buf2)) == -1) { new_message(MT_standout, " %s: unknown user", buf2); rc = 1; goto end; } if (buf2 == buf) { reset_uids(); ps.uid[0] = uid; goto end; } if (buf[0] == '+') { if (add_uid(uid)) { new_message(MT_standout, " too many users, reset with '+'"); rc = 1; goto end; } } else rem_uid(uid); end: putchar('\r'); return (rc); } int main(int argc, const char *argv[]) { int i; int active_procs; struct system_info system_info; struct statics statics; void * processes; static char tempbuf1[50]; static char tempbuf2[50]; sigset_t old_sigmask, new_sigmask; int topn = Infinity; double delay = 2; int displays = 0; /* indicates unspecified */ int sel_ret = 0; time_t curr_time; char *(*get_userid)(int) = username; const char *uname_field = "USERNAME"; const char *header_text; char *env_top; const char **preset_argv; int preset_argc = 0; const char **av = NULL; int ac = -1; bool do_unames = true; char interactive = 2; char warnings = 0; char topn_specified = false; char ch; char no_command = 1; struct timeval timeout; char *order_name = NULL; int order_index = 0; fd_set readfds; char *nptr; /* set the buffer for stdout */ #ifdef DEBUG extern FILE *debug; debug = fopen("debug.run", "w"); setbuffer(stdout, NULL, 0); #else setbuffer(stdout, stdoutbuf, Buffersize); #endif + + if (setlocale(LC_ALL, "") == NULL) { + fprintf(stderr, "invalid locale.\n"); + exit(1); + } mypid = getpid(); /* get our name */ /* initialize some selection options */ ps.idle = true; ps.self = true; ps.system = false; reset_uids(); ps.thread = false; ps.wcpu = 1; ps.jid = -1; ps.jail = false; ps.swap = false; ps.kidle = true; ps.pid = -1; ps.command = NULL; ps.thread_id = false; /* get preset options from the environment */ if ((env_top = getenv("TOP")) != NULL) { av = preset_argv = argparse(env_top, &preset_argc); ac = preset_argc; /* set the dummy argument to an explanatory message, in case getopt encounters a bad argument */ preset_argv[0] = "while processing environment"; } /* process options */ do { /* if we're done doing the presets, then process the real arguments */ if (preset_argc == 0) { ac = argc; av = argv; /* this should keep getopt happy... */ optind = 1; } while ((i = getopt_long(ac, __DECONST(char * const *, av), "CSIHPabijJ:nquvzs:d:U:m:o:p:Ttw", longopts, NULL)) != EOF) { switch(i) { case 'v': /* show version number */ errx(0, "version FreeBSD"); break; case 'u': /* toggle uid/username display */ do_unames = !do_unames; break; case 'U': /* display only username's processes */ if ((ps.uid[0] = userid(optarg)) == -1) { errx(1, "%s: unknown user\n", optarg); } break; case 'S': /* show system processes */ ps.system = true; break; case 'I': /* show idle processes */ ps.idle = !ps.idle; break; case 'i': /* go interactive regardless */ interactive = 1; break; case 'n': /* batch, or non-interactive */ case 'b': interactive = 0; break; case 'a': fmt_flags ^= FMT_SHOWARGS; break; case 'd': /* number of displays to show */ if ((i = atoiwi(optarg)) == Invalid || i == 0) { warnx("warning: display count should be positive -- option ignored"); warnings++; } else { displays = i; } break; case 'p': { unsigned long long num; const char *errstr; num = strtonum(optarg, 0, INT_MAX, &errstr); if (errstr != NULL || !find_pid(num)) { fprintf(stderr, "%s: unknown pid\n", optarg); exit(1); } ps.pid = (pid_t)num; ps.system = true; break; } case 's': delay = strtod(optarg, &nptr); if (nptr == optarg) { warnx("warning: invalid delay"); delay = 2; warnings++; } if (delay < 0) { warnx("warning: seconds delay should be positive -- using default"); delay = 2; warnings++; } break; case 'q': /* be quick about it */ errno = 0; i = setpriority(PRIO_PROCESS, 0, PRIO_MIN); if (i == -1 && errno != 0) { warnx("warning: `-q' option failed (%m)"); warnings++; } break; case 'm': /* select display mode */ if (strcmp(optarg, "io") == 0) { displaymode = DISP_IO; } else if (strcmp(optarg, "cpu") == 0) { displaymode = DISP_CPU; } else { errx(1, "warning: `-m' option can only take args 'io' or 'cpu'"); } break; case 'o': /* select sort order */ order_name = optarg; break; case 't': ps.self = !ps.self; break; case 'C': ps.wcpu = !ps.wcpu; break; case 'H': ps.thread = !ps.thread; break; case 'T': ps.thread_id = !ps.thread_id; break; case 'j': ps.jail = !ps.jail; break; case 'J': /* display only jail's processes */ if ((ps.jid = jail_getid(optarg)) == -1) { fprintf(stderr, "%s: unknown jail\n", optarg); exit(1); } ps.jail = 1; break; case 'P': pcpu_stats = !pcpu_stats; break; case 'w': ps.swap = 1; break; case 'z': ps.kidle = !ps.kidle; break; default: errx(1, "[-abCHIijnPqStuvwz] [-d count] [-m io | cpu] [-o field] [-p pid]\n" " [-s time] [-J jail] [-U username] [number]"); } } /* get count of top processes to display (if any) */ if (optind < ac) { if ((topn = atoiwi(av[optind])) == Invalid) { warnx("warning: process display count should be non-negative -- using default"); warnings++; } else { topn_specified = true; } } /* tricky: remember old value of preset_argc & set preset_argc = 0 */ i = preset_argc; preset_argc = 0; /* repeat only if we really did the preset arguments */ } while (i != 0); /* set constants for username/uid display correctly */ if (!do_unames) { uname_field = " UID "; get_userid = itoa7; } /* initialize the kernel memory interface */ if (machine_init(&statics) == -1) { exit(1); } /* determine sorting order index, if necessary */ if (order_name != NULL) { if ((order_index = string_index(order_name, statics.order_names)) == -1) { const char * const *pp; warnx("'%s' is not a recognized sorting order.", order_name); fprintf(stderr, "\tTry one of these:"); pp = statics.order_names; while (*pp != NULL) { fprintf(stderr, " %s", *pp++); } fputc('\n', stderr); exit(1); } } /* initialize termcap */ init_termcap(interactive); /* get the string to use for the process area header */ header_text = format_header(uname_field); /* initialize display interface */ if ((max_topn = display_init(&statics)) == -1) { errx(4, "can't allocate sufficient memory"); } /* print warning if user requested more processes than we can display */ if (topn > max_topn) { warnx("warning: this terminal can only display %d processes.", max_topn); warnings++; } /* adjust for topn == Infinity */ if (topn == Infinity) { /* * For smart terminals, infinity really means everything that can * be displayed, or Largest. * On dumb terminals, infinity means every process in the system! * We only really want to do that if it was explicitly specified. * This is always the case when "Default_TOPN != Infinity". But if * topn wasn't explicitly specified and we are on a dumb terminal * and the default is Infinity, then (and only then) we use * "Nominal_TOPN" instead. */ topn = smart_terminal ? Largest : (topn_specified ? Largest : Nominal_TOPN); } /* set header display accordingly */ display_header(topn > 0); /* determine interactive state */ if (interactive == 2) { interactive = smart_terminal; } /* if # of displays not specified, fill it in */ if (displays == 0) { displays = smart_terminal ? Infinity : 1; } /* hold interrupt signals while setting up the screen and the handlers */ sigemptyset(&new_sigmask); sigaddset(&new_sigmask, SIGINT); sigaddset(&new_sigmask, SIGQUIT); sigaddset(&new_sigmask, SIGTSTP); sigprocmask(SIG_BLOCK, &new_sigmask, &old_sigmask); init_screen(); signal(SIGINT, leave); signal(SIGQUIT, leave); signal(SIGTSTP, tstop); signal(SIGWINCH, top_winch); sigprocmask(SIG_SETMASK, &old_sigmask, NULL); if (warnings) { fputs("....", stderr); fflush(stderr); sleep(3 * warnings); fputc('\n', stderr); } restart: /* * main loop -- repeat while display count is positive or while it * indicates infinity (by being -1) */ while ((displays == -1) || (displays-- > 0)) { int (*compare)(const void * const, const void * const); /* get the current stats */ get_system_info(&system_info); compare = compares[order_index]; /* get the current set of processes */ processes = get_process_info(&system_info, &ps, compare); /* display the load averages */ (*d_loadave)(system_info.last_pid, system_info.load_avg); /* display the current time */ /* this method of getting the time SHOULD be fairly portable */ time(&curr_time); i_uptime(&system_info.boottime, &curr_time); i_timeofday(&curr_time); /* display process state breakdown */ (*d_procstates)(system_info.p_total, system_info.procstates); (*d_cpustates)(system_info.cpustates); /* display memory stats */ (*d_memory)(system_info.memory); (*d_arc)(system_info.arc); (*d_carc)(system_info.carc); /* display swap stats */ (*d_swap)(system_info.swap); /* handle message area */ (*d_message)(); /* update the header area */ (*d_header)(header_text); if (topn > 0) { /* determine number of processes to actually display */ /* this number will be the smallest of: active processes, number user requested, number current screen accomodates */ active_procs = system_info.p_pactive; if (active_procs > topn) { active_procs = topn; } if (active_procs > max_topn) { active_procs = max_topn; } /* now show the top "n" processes. */ for (i = 0; i < active_procs; i++) { (*d_process)(i, format_next_process(processes, get_userid, fmt_flags)); } } else { i = 0; } /* do end-screen processing */ u_endscreen(i); /* now, flush the output buffer */ if (fflush(stdout) != 0) { new_message(MT_standout, " Write error on stdout"); putchar('\r'); quit(1); } /* only do the rest if we have more displays to show */ if (displays) { /* switch out for new display on smart terminals */ if (smart_terminal) { if (overstrike) { reset_display(); } else { d_loadave = u_loadave; d_procstates = u_procstates; d_cpustates = u_cpustates; d_memory = u_memory; d_arc = u_arc; d_carc = u_carc; d_swap = u_swap; d_message = u_message; d_header = u_header; d_process = u_process; } } no_command = true; if (!interactive) { usleep(delay * 1e6); if (leaveflag) { end_screen(); exit(0); } } else while (no_command) { /* assume valid command unless told otherwise */ no_command = false; /* set up arguments for select with timeout */ FD_ZERO(&readfds); FD_SET(0, &readfds); /* for standard input */ timeout.tv_sec = delay; timeout.tv_usec = 0; if (leaveflag) { end_screen(); exit(0); } if (tstopflag) { /* move to the lower left */ end_screen(); fflush(stdout); /* default the signal handler action */ signal(SIGTSTP, SIG_DFL); /* unblock the signal and send ourselves one */ sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1))); kill(0, SIGTSTP); /* reset the signal handler */ signal(SIGTSTP, tstop); /* reinit screen */ reinit_screen(); reset_display(); tstopflag = 0; goto restart; } if (winchflag) { /* reascertain the screen dimensions */ get_screensize(); /* tell display to resize */ max_topn = display_resize(); /* reset the signal handler */ signal(SIGWINCH, top_winch); reset_display(); winchflag = 0; goto restart; } /* wait for either input or the end of the delay period */ sel_ret = select(2, &readfds, NULL, NULL, &timeout); if (sel_ret < 0 && errno != EINTR) quit(0); if (sel_ret > 0) { int newval; const char *errmsg; const struct command *cptr; /* something to read -- clear the message area first */ clear_message(); /* now read it and convert to command strchr */ /* (use "change" as a temporary to hold strchr) */ if (read(0, &ch, 1) != 1) { /* read error: either 0 or -1 */ new_message(MT_standout, " Read error on stdin"); putchar('\r'); quit(1); } if (ch == '\r' || ch == '\n') { continue; } cptr = all_commands; while (cptr->c != '\0') { if (cptr->c == ch) { break; } cptr++; } if (cptr->c == '\0') { new_message(MT_standout, " Command not understood"); putchar('\r'); no_command = true; } if (overstrike && !cptr->available_to_dumb) { new_message(MT_standout, " Command cannot be handled by this terminal"); putchar('\r'); no_command = true; } if (!no_command) { switch(cptr->id) { case CMD_redraw: /* redraw screen */ reset_display(); break; case CMD_update: /* merely update display */ break; case CMD_quit: quit(0); break; case CMD_help: reset_display(); top_clear(); show_help(); top_standout("Hit any key to continue: "); fflush(stdout); read(0, &ch, 1); break; case CMD_errors: /* show errors */ if (error_count() == 0) { new_message(MT_standout, " Currently no errors to report."); putchar('\r'); no_command = true; } else { reset_display(); top_clear(); show_errors(); top_standout("Hit any key to continue: "); fflush(stdout); read(0, &ch, 1); } break; case CMD_number: new_message(MT_standout, "Number of processes to show: "); newval = readline(tempbuf1, 8, true); if (newval > -1) { if (newval > max_topn) { new_message(MT_standout | MT_delayed, " This terminal can only display %d processes.", max_topn); putchar('\r'); } if (newval == 0) { /* inhibit the header */ display_header(false); } else if (newval > topn && topn == 0) { /* redraw the header */ display_header(true); d_header = i_header; } topn = newval; } break; case CMD_delay: /* new seconds delay */ new_message(MT_standout, "Seconds to delay: "); if ((i = readline(tempbuf1, 8, true)) > -1) { if ((delay = i) == 0) { delay = 1; } } clear_message(); break; case CMD_displays: /* change display count */ new_message(MT_standout, "Displays to show (currently %s): ", displays == -1 ? "infinite" : itoa(displays)); if ((i = readline(tempbuf1, 10, true)) > 0) { displays = i; } else if (i == 0) { quit(0); } clear_message(); break; case CMD_kill: /* kill program */ new_message(0, "kill "); if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) { if ((errmsg = kill_procs(tempbuf2)) != NULL) { new_message(MT_standout, "%s", errmsg); putchar('\r'); no_command = true; } } else { clear_message(); } break; case CMD_renice: /* renice program */ new_message(0, "renice "); if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) { if ((errmsg = renice_procs(tempbuf2)) != NULL) { new_message(MT_standout, "%s", errmsg); putchar('\r'); no_command = true; } } else { clear_message(); } break; case CMD_idletog: ps.idle = !ps.idle; new_message(MT_standout | MT_delayed, " %sisplaying idle processes.", ps.idle ? "D" : "Not d"); putchar('\r'); break; case CMD_selftog: ps.self = !ps.self; new_message(MT_standout | MT_delayed, " %sisplaying self.", (ps.self) ? "D" : "Not d"); putchar('\r'); break; case CMD_user: if (handle_user(tempbuf2, sizeof(tempbuf2))) no_command = true; break; case CMD_thrtog: ps.thread = !ps.thread; new_message(MT_standout | MT_delayed, " Displaying threads %s", ps.thread ? "separately" : "as a count"); header_text = format_header(uname_field); reset_display(); putchar('\r'); break; case CMD_toggletid: ps.thread_id = !ps.thread_id; new_message(MT_standout | MT_delayed, " Displaying %s", ps.thread_id ? "tid" : "pid"); header_text = format_header(uname_field); reset_display(); putchar('\r'); break; case CMD_wcputog: ps.wcpu = !ps.wcpu; new_message(MT_standout | MT_delayed, " Displaying %s CPU", ps.wcpu ? "weighted" : "raw"); header_text = format_header(uname_field); reset_display(); putchar('\r'); break; case CMD_viewtog: displaymode = displaymode == DISP_IO ? DISP_CPU : DISP_IO; new_message(MT_standout | MT_delayed, " Displaying %s statistics.", displaymode == DISP_IO ? "IO" : "CPU"); header_text = format_header(uname_field); display_header(true); d_header = i_header; reset_display(); break; case CMD_viewsys: ps.system = !ps.system; new_message(MT_standout | MT_delayed, " %sisplaying system processes.", ps.system ? "D" : "Not d"); break; case CMD_showargs: fmt_flags ^= FMT_SHOWARGS; new_message(MT_standout | MT_delayed, " %sisplaying process arguments.", fmt_flags & FMT_SHOWARGS ? "D" : "Not d"); break; case CMD_order: new_message(MT_standout, "Order to sort: "); if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) { if ((i = string_index(tempbuf2, statics.order_names)) == -1) { new_message(MT_standout, " %s: unrecognized sorting order", tempbuf2); no_command = true; } else { order_index = i; } putchar('\r'); } else { clear_message(); } break; case CMD_jidtog: ps.jail = !ps.jail; new_message(MT_standout | MT_delayed, " %sisplaying jail ID.", ps.jail ? "D" : "Not d"); header_text = format_header(uname_field); reset_display(); putchar('\r'); break; case CMD_jail: new_message(MT_standout, "Jail to show (+ for all): "); if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) { if (tempbuf2[0] == '+' && tempbuf2[1] == '\0') { ps.jid = -1; } else if ((i = jail_getid(tempbuf2)) == -1) { new_message(MT_standout, " %s: unknown jail", tempbuf2); no_command = true; } else { ps.jid = i; } if (ps.jail == 0) { ps.jail = 1; new_message(MT_standout | MT_delayed, " Displaying jail " "ID."); header_text = format_header(uname_field); reset_display(); } putchar('\r'); } else { clear_message(); } break; case CMD_kidletog: ps.kidle = !ps.kidle; new_message(MT_standout | MT_delayed, " %sisplaying system idle process.", ps.kidle ? "D" : "Not d"); putchar('\r'); break; case CMD_pcputog: pcpu_stats = !pcpu_stats; new_message(MT_standout | MT_delayed, " Displaying %sCPU statistics.", pcpu_stats ? "per-" : "global "); toggle_pcpustats(); max_topn = display_updatecpus(&statics); reset_display(); putchar('\r'); break; case CMD_swaptog: ps.swap = !ps.swap; new_message(MT_standout | MT_delayed, " %sisplaying per-process swap usage.", ps.swap ? "D" : "Not d"); header_text = format_header(uname_field); reset_display(); putchar('\r'); break; case CMD_pid: new_message(MT_standout, "Process id to show (+ for all): "); if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) { if (tempbuf2[0] == '+' && tempbuf2[1] == '\0') { ps.pid = (pid_t)-1; } else { unsigned long long num; const char *errstr; num = strtonum(tempbuf2, 0, INT_MAX, &errstr); if (errstr != NULL || !find_pid(num)) { new_message(MT_standout, " %s: unknown pid", tempbuf2); no_command = true; } else { ps.pid = (pid_t)num; } } putchar('\r'); } else clear_message(); break; case CMD_NONE: assert(false && "reached switch without command"); } } } /* flush out stuff that may have been written */ fflush(stdout); } } } #ifdef DEBUG fclose(debug); #endif quit(0); } /* * reset_display() - reset all the display routine pointers so that entire * screen will get redrawn. */ static void reset_display(void) { d_loadave = i_loadave; d_procstates = i_procstates; d_cpustates = i_cpustates; d_memory = i_memory; d_arc = i_arc; d_carc = i_carc; d_swap = i_swap; d_message = i_message; d_header = i_header; d_process = i_process; } /* * signal handlers */ static sigret_t leave(int i __unused) /* exit under normal conditions -- INT handler */ { leaveflag = 1; } static sigret_t tstop(int i __unused) /* SIGTSTP handler */ { tstopflag = 1; } static sigret_t top_winch(int i __unused) /* SIGWINCH handler */ { winchflag = 1; } void __dead2 quit(int status) /* exit under duress */ { end_screen(); exit(status); }