/* * pg_top - a top PostgreSQL users display for Unix * * SYNOPSIS: Linux 1.2.x, 1.3.x, 2.x, using the /proc filesystem * * DESCRIPTION: * This is the machine-dependent module for Linux 1.2.x, 1.3.x or 2.x. * * LIBS: * * CFLAGS: -DHAVE_GETOPT -DHAVE_STRERROR -DORDER * * TERMCAP: -lcurses * * AUTHOR: Richard Henderson * Order support added by Alexey Klimkin * Ported to 2.4 by William LeFebvre * * Portions Copyright (c) 2013 VMware, Inc. All Rights Reserved. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for HZ */ #if 0 #include /* for PROC_SUPER_MAGIC */ #else #define PROC_SUPER_MAGIC 0x9fa0 #endif #define BUFFERLEN 255 #define GET_VALUE(v) \ p = strchr(p, ':'); \ ++p; \ ++p; \ q = strchr(p, '\n'); \ len = q - p; \ if (len >= BUFFERLEN) \ { \ printf("ERROR - value is larger than the buffer: %d\n", __LINE__); \ exit(1); \ } \ strncpy(value, p, len); \ value[len] = '\0'; \ v = atoll(value); #include "machine.h" #include "utils.h" #define PROCFS "/proc" extern char *myname; /*=PROCESS INFORMATION==================================================*/ struct top_proc { pid_t pid; /* Data from /proc//stat. */ uid_t uid; char *name; int pri, nice; unsigned long size, rss; /* in k */ int state; unsigned long time; unsigned long start_time; double pcpu, wcpu; /* Data from /proc//io. */ long long rchar; long long wchar; long long syscr; long long syscw; long long read_bytes; long long write_bytes; long long cancelled_write_bytes; struct top_proc *next; }; struct io_node { pid_t pid; /* The change in the previous values and current values. */ long long diff_rchar; long long diff_wchar; long long diff_syscr; long long diff_syscw; long long diff_read_bytes; long long diff_write_bytes; long long diff_cancelled_write_bytes; /* The previous values. */ long long old_rchar; long long old_wchar; long long old_syscr; long long old_syscw; long long old_read_bytes; long long old_write_bytes; long long old_cancelled_write_bytes; struct io_node *next; }; /*=STATE IDENT STRINGS==================================================*/ #define NPROCSTATES 7 static char *state_abbrev[NPROCSTATES + 1] = { "", "run", "sleep", "disk", "zomb", "stop", "swap", NULL }; static char *procstatenames[NPROCSTATES + 1] = { "", " running, ", " sleeping, ", " uninterruptable, ", " zombie, ", " stopped, ", " swapping, ", NULL }; #define NCPUSTATES 5 static char *cpustatenames[NCPUSTATES + 1] = { "user", "nice", "system", "idle", "iowait", NULL }; static int show_iowait = 0; #define MEMUSED 0 #define MEMFREE 1 #define MEMSHARED 2 #define MEMBUFFERS 3 #define MEMCACHED 4 #define NMEMSTATS 5 static char *memorynames[NMEMSTATS + 1] = { "K used, ", "K free, ", "K shared, ", "K buffers, ", "K cached", NULL }; #define SWAPUSED 0 #define SWAPFREE 1 #define SWAPCACHED 2 #define NSWAPSTATS 3 static char *swapnames[NSWAPSTATS + 1] = { "K used, ", "K free, ", "K cached", NULL }; static char fmt_header[] = " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND"; /* these are names given to allowed sorting orders -- first is default */ static char *ordernames[] = {"cpu", "size", "res", "time", "command", NULL}; static char *ordernames_io[] = { "pid", "rchar", "wchar", "syscr", "syscw", "reads", "writes", "cwrites", "command", NULL }; /* forward definitions for comparison functions */ int compare_cpu(); int compare_size(); int compare_res(); int compare_time(); int compare_cmd(); int compare_pid(); int compare_rchar(); int compare_wchar(); int compare_syscr(); int compare_syscw(); int compare_reads(); int compare_writes(); int compare_cwrites(); int (*proc_compares[]) () = { compare_cpu, compare_size, compare_res, compare_time, compare_cmd, NULL }; int (*io_compares[]) () = { compare_pid, compare_rchar, compare_wchar, compare_syscr, compare_syscw, compare_reads, compare_writes, compare_cwrites, compare_cmd, NULL }; /*=SYSTEM STATE INFO====================================================*/ /* these are for calculating cpu state percentages */ static int64_t cp_time[NCPUSTATES]; static int64_t cp_old[NCPUSTATES]; static int64_t cp_diff[NCPUSTATES]; /* for calculating the exponential average */ static struct timeval lasttime; /* these are for keeping track of processes */ #define HASH_SIZE (1003) #define INITIAL_ACTIVE_SIZE (256) #define PROCBLOCK_SIZE (32) static struct top_proc *ptable[HASH_SIZE]; static struct top_proc **pactive; static struct top_proc **nextactive; static unsigned int activesize = 0; static time_t boottime = -1; /* these are for passing data back to the machine independant portion */ static int64_t cpu_states[NCPUSTATES]; static int process_states[NPROCSTATES]; static long memory_stats[NMEMSTATS]; static long swap_stats[NSWAPSTATS]; /* usefull macros */ #define bytetok(x) (((x) + 512) >> 10) #define pagetok(x) ((x) * sysconf(_SC_PAGESIZE) >> 10) #define HASH(x) (((x) * 1686629713U) % HASH_SIZE) /*======================================================================*/ struct io_node * new_io_node(pid_t); struct io_node * get_io_stats(struct io_node *, pid_t); struct io_node * insert_io_stats(struct io_node *, struct io_node *); void update_io_stats(struct io_node *, pid_t, long long, long long, long long, long long, long long, long long, long long); struct io_node * upsert_io_stats(struct io_node *, pid_t, long long, long long, long long, long long, long long, long long, long long); static inline char * skip_ws(const char *p) { while (isspace(*p)) p++; return (char *) p; } static inline char * skip_token(const char *p) { while (isspace(*p)) p++; while (*p && !isspace(*p)) p++; return (char *) p; } static void xfrm_cmdline(char *p, int len) { while (--len > 0) { if (*p == '\0') { *p = ' '; } p++; } } static void update_procname(struct top_proc * proc, char *cmd) { printable(cmd); if (proc->name == NULL) { proc->name = strdup(cmd); } else if (strcmp(proc->name, cmd) != 0) { free(proc->name); proc->name = strdup(cmd); } } /* * Process structures are allocated and freed as needed. Here we * keep big pools of them, adding more pool as needed. When a * top_proc structure is freed, it is added to a freelist and reused. */ static struct top_proc *freelist = NULL; static struct top_proc *procblock = NULL; static struct top_proc *procmax = NULL; static struct top_proc * new_proc() { struct top_proc *p; if (freelist) { p = freelist; freelist = freelist->next; } else if (procblock) { p = procblock; if (++procblock >= procmax) { procblock = NULL; } } else { p = procblock = (struct top_proc *) calloc(PROCBLOCK_SIZE, sizeof(struct top_proc)); procmax = procblock++ + PROCBLOCK_SIZE; } /* initialization */ if (p->name != NULL) { free(p->name); p->name = NULL; } return p; } static void free_proc(struct top_proc * proc) { proc->next = freelist; freelist = proc; } int machine_init(struct statics * statics) { /* make sure the proc filesystem is mounted */ { struct statfs sb; if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC) { fprintf(stderr, "%s: proc filesystem not mounted on " PROCFS "\n", myname); return -1; } } /* chdir to the proc filesystem to make things easier */ chdir(PROCFS); /* a few preliminary checks */ { int fd; char buff[128]; char *p; int cnt; unsigned long uptime; struct timeval tv; /* get a boottime */ if ((fd = open("uptime", 0)) != -1) { if (read(fd, buff, sizeof(buff)) > 0) { uptime = strtoul(buff, &p, 10); gettimeofday(&tv, 0); boottime = tv.tv_sec - uptime; } close(fd); } /* see how many states we get from stat */ if ((fd = open("stat", 0)) != -1) { if (read(fd, buff, sizeof(buff)) > 0) { if ((p = strchr(buff, '\n')) != NULL) { *p = '\0'; p = buff; cnt = 0; while (*p != '\0') { if (*p++ == ' ') { cnt++; } } } } close(fd); } if (cnt > 5) { /* we have iowait */ show_iowait = 1; } } /* if we aren't showing iowait, then we have to tweak cpustatenames */ if (!show_iowait) { cpustatenames[4] = NULL; } /* fill in the statics information */ statics->procstate_names = procstatenames; statics->cpustate_names = cpustatenames; statics->memory_names = memorynames; statics->swap_names = swapnames; statics->order_names = ordernames; statics->order_names_io = ordernames_io; statics->boottime = boottime; statics->flags.fullcmds = 1; statics->flags.warmup = 1; /* allocate needed space */ pactive = (struct top_proc **) malloc(sizeof(struct top_proc *) * INITIAL_ACTIVE_SIZE); activesize = INITIAL_ACTIVE_SIZE; /* make sure the hash table is empty */ memset(ptable, 0, HASH_SIZE * sizeof(struct top_proc *)); /* all done! */ return 0; } void get_system_info(struct system_info * info) { char buffer[4096 + 1]; int fd, len; char *p; /* get load averages */ if ((fd = open("loadavg", O_RDONLY)) != -1) { if ((len = read(fd, buffer, sizeof(buffer) - 1)) > 0) { buffer[len] = '\0'; info->load_avg[0] = strtod(buffer, &p); info->load_avg[1] = strtod(p, &p); info->load_avg[2] = strtod(p, &p); p = skip_token(p); /* skip running/tasks */ p = skip_ws(p); if (*p) { info->last_pid = atoi(p); } else { info->last_pid = -1; } } close(fd); } /* get the cpu time info */ if ((fd = open("stat", O_RDONLY)) != -1) { if ((len = read(fd, buffer, sizeof(buffer) - 1)) > 0) { buffer[len] = '\0'; p = skip_token(buffer); /* "cpu" */ cp_time[0] = strtoul(p, &p, 0); cp_time[1] = strtoul(p, &p, 0); cp_time[2] = strtoul(p, &p, 0); cp_time[3] = strtoul(p, &p, 0); if (show_iowait) { cp_time[4] = strtoul(p, &p, 0); } /* convert cp_time counts to percentages */ percentages(NCPUSTATES, cpu_states, cp_time, cp_old, cp_diff); } close(fd); } /* get system wide memory usage */ if ((fd = open("meminfo", O_RDONLY)) != -1) { char *p; int mem = 0; int swap = 0; unsigned long memtotal = 0; unsigned long memfree = 0; unsigned long swaptotal = 0; if ((len = read(fd, buffer, sizeof(buffer) - 1)) > 0) { buffer[len] = '\0'; p = buffer - 1; /* iterate thru the lines */ while (p != NULL) { p++; if (p[0] == ' ' || p[0] == '\t') { /* skip */ } else if (strncmp(p, "Mem:", 4) == 0) { p = skip_token(p); /* "Mem:" */ p = skip_token(p); /* total memory */ memory_stats[MEMUSED] = strtoul(p, &p, 10); memory_stats[MEMFREE] = strtoul(p, &p, 10); memory_stats[MEMSHARED] = strtoul(p, &p, 10); memory_stats[MEMBUFFERS] = strtoul(p, &p, 10); memory_stats[MEMCACHED] = strtoul(p, &p, 10); memory_stats[MEMUSED] = bytetok(memory_stats[MEMUSED]); memory_stats[MEMFREE] = bytetok(memory_stats[MEMFREE]); memory_stats[MEMSHARED] = bytetok(memory_stats[MEMSHARED]); memory_stats[MEMBUFFERS] = bytetok(memory_stats[MEMBUFFERS]); memory_stats[MEMCACHED] = bytetok(memory_stats[MEMCACHED]); mem = 1; } else if (strncmp(p, "Swap:", 5) == 0) { p = skip_token(p); /* "Swap:" */ p = skip_token(p); /* total swap */ swap_stats[SWAPUSED] = strtoul(p, &p, 10); swap_stats[SWAPFREE] = strtoul(p, &p, 10); swap_stats[SWAPUSED] = bytetok(swap_stats[SWAPUSED]); swap_stats[SWAPFREE] = bytetok(swap_stats[SWAPFREE]); swap = 1; } else if (!mem && strncmp(p, "MemTotal:", 9) == 0) { p = skip_token(p); memtotal = strtoul(p, &p, 10); } else if (!mem && memtotal > 0 && strncmp(p, "MemFree:", 8) == 0) { p = skip_token(p); memfree = strtoul(p, &p, 10); memory_stats[MEMUSED] = memtotal - memfree; memory_stats[MEMFREE] = memfree; } else if (!mem && strncmp(p, "MemShared:", 10) == 0) { p = skip_token(p); memory_stats[MEMSHARED] = strtoul(p, &p, 10); } else if (!mem && strncmp(p, "Buffers:", 8) == 0) { p = skip_token(p); memory_stats[MEMBUFFERS] = strtoul(p, &p, 10); } else if (!mem && strncmp(p, "Cached:", 7) == 0) { p = skip_token(p); memory_stats[MEMCACHED] = strtoul(p, &p, 10); } else if (!swap && strncmp(p, "SwapTotal:", 10) == 0) { p = skip_token(p); swaptotal = strtoul(p, &p, 10); } else if (!swap && swaptotal > 0 && strncmp(p, "SwapFree:", 9) == 0) { p = skip_token(p); memfree = strtoul(p, &p, 10); swap_stats[SWAPUSED] = swaptotal - memfree; swap_stats[SWAPFREE] = memfree; } else if (!mem && strncmp(p, "SwapCached:", 11) == 0) { p = skip_token(p); swap_stats[SWAPCACHED] = strtoul(p, &p, 10); } /* move to the next line */ p = strchr(p, '\n'); } } close(fd); } /* set arrays and strings */ info->cpustates = cpu_states; info->memory = memory_stats; info->swap = swap_stats; } static void read_one_proc_stat(pid_t pid, struct top_proc * proc, struct process_select * sel) { char buffer[4096], *p, *q; int fd, len; int fullcmd; char value[BUFFERLEN + 1]; /* if anything goes wrong, we return with proc->state == 0 */ proc->state = 0; /* full cmd handling */ fullcmd = sel->fullcmd; if (fullcmd == 1) { sprintf(buffer, "%d/cmdline", pid); if ((fd = open(buffer, O_RDONLY)) != -1) { /* read command line data */ /* (theres no sense in reading more than we can fit) */ if ((len = read(fd, buffer, MAX_COLS)) > 1) { buffer[len] = '\0'; xfrm_cmdline(buffer, len); update_procname(proc, buffer); } else { fullcmd = 0; } close(fd); } else { fullcmd = 0; } } /* grab the proc stat info in one go */ sprintf(buffer, "%d/stat", pid); fd = open(buffer, O_RDONLY); len = read(fd, buffer, sizeof(buffer) - 1); close(fd); buffer[len] = '\0'; proc->uid = (uid_t) proc_owner((int) pid); /* parse out the status, described in 'man proc' */ /* skip pid and locate command, which is in parentheses */ if ((p = strchr(buffer, '(')) == NULL) { return; } if ((q = strrchr(++p, ')')) == NULL) { return; } /* set the procname */ *q = '\0'; if (!fullcmd) { update_procname(proc, p); } /* scan the rest of the line */ p = q + 1; p = skip_ws(p); switch (*p++) /* state */ { case 'R': proc->state = 1; break; case 'S': proc->state = 2; break; case 'D': proc->state = 3; break; case 'Z': proc->state = 4; break; case 'T': proc->state = 5; break; case 'W': proc->state = 6; break; case '\0': return; } p = skip_token(p); /* skip ppid */ p = skip_token(p); /* skip pgrp */ p = skip_token(p); /* skip session */ p = skip_token(p); /* skip tty nr */ p = skip_token(p); /* skip tty pgrp */ p = skip_token(p); /* skip flags */ p = skip_token(p); /* skip min flt */ p = skip_token(p); /* skip cmin flt */ p = skip_token(p); /* skip maj flt */ p = skip_token(p); /* skip cmaj flt */ proc->time = strtoul(p, &p, 10); /* utime */ proc->time += strtoul(p, &p, 10); /* stime */ p = skip_token(p); /* skip cutime */ p = skip_token(p); /* skip cstime */ proc->pri = strtol(p, &p, 10); /* priority */ proc->nice = strtol(p, &p, 10); /* nice */ p = skip_token(p); /* skip num_threads */ p = skip_token(p); /* skip itrealvalue, 0 */ proc->start_time = strtoul(p, &p, 10); /* start_time */ proc->size = bytetok(strtoul(p, &p, 10)); /* vsize */ proc->rss = pagetok(strtoul(p, &p, 10)); /* rss */ #if 0 /* for the record, here are the rest of the fields */ p = skip_token(p); /* skip rlim */ p = skip_token(p); /* skip start_code */ p = skip_token(p); /* skip end_code */ p = skip_token(p); /* skip start_stack */ p = skip_token(p); /* skip esp */ p = skip_token(p); /* skip eip */ p = skip_token(p); /* skip signal */ p = skip_token(p); /* skip sigblocked */ p = skip_token(p); /* skip sigignore */ p = skip_token(p); /* skip sigcatch */ p = skip_token(p); /* skip wchan */ p = skip_token(p); /* skip nswap, not maintained */ p = skip_token(p); /* exit signal */ p = skip_token(p); /* processor */ p = skip_token(p); /* rt_priority */ p = skip_token(p); /* policy */ p = skip_token(p); /* delayacct_blkio_ticks */ #endif /* Get the io stats. */ sprintf(buffer, "%d/io", pid); fd = open(buffer, O_RDONLY); if (fd == -1) { /* * CONFIG_TASK_IO_ACCOUNTING is not enabled in the Linux kernel or * this version of Linux may not support collecting i/o statistics * per pid. Report 0's. */ proc->rchar = 0; proc->wchar = 0; proc->syscr = 0; proc->syscw = 0; proc->read_bytes = 0; proc->write_bytes = 0; proc->cancelled_write_bytes = 0; return; } len = read(fd, buffer, sizeof(buffer) - 1); close(fd); buffer[len] = '\0'; p = buffer; GET_VALUE(proc->rchar); GET_VALUE(proc->wchar); GET_VALUE(proc->syscr); GET_VALUE(proc->syscw); GET_VALUE(proc->read_bytes); GET_VALUE(proc->write_bytes); GET_VALUE(proc->cancelled_write_bytes); } caddr_t get_process_info(struct system_info * si, struct process_select * sel, int compare_index, char *conninfo, int mode) { struct timeval thistime; double timediff, alpha, beta; struct top_proc *proc; pid_t pid; unsigned long now; unsigned long elapsed; int i; /* calculate the time difference since our last check */ gettimeofday(&thistime, 0); if (lasttime.tv_sec) { timediff = ((thistime.tv_sec - lasttime.tv_sec) + (thistime.tv_usec - lasttime.tv_usec) * 1e-6); } else { timediff = 0; } lasttime = thistime; /* round current time to a second */ now = (unsigned long) thistime.tv_sec; if (thistime.tv_usec >= 500000) { now++; } /* calculate constants for the exponental average */ if (timediff > 0.0 && timediff < 30.0) { alpha = 0.5 * (timediff / 30.0); beta = 1.0 - alpha; } else { alpha = beta = 0.5; } timediff *= HZ; /* convert to ticks */ /* mark all hash table entries as not seen */ for (i = 0; i < HASH_SIZE; ++i) { for (proc = ptable[i]; proc; proc = proc->next) { proc->state = 0; } } /* read the process information */ { int total_procs = 0; struct top_proc **active; int show_idle = sel->idle; int show_uid = sel->uid != -1; int i; int rows; PGconn *pgconn; PGresult *pgresult = NULL; memset(process_states, 0, sizeof(process_states)); pgconn = connect_to_db(conninfo); if (pgconn != NULL) { pgresult = pg_processes(pgconn); rows = PQntuples(pgresult); } else { rows = 0; } for (i = 0; i < rows; i++) { char *procpid = PQgetvalue(pgresult, i, 0); struct top_proc *pp; unsigned long otime; pid = atoi(procpid); /* look up hash table entry */ proc = pp = ptable[HASH(pid)]; while (proc && proc->pid != pid) { proc = proc->next; } /* if we came up empty, create a new entry */ if (proc == NULL) { proc = new_proc(); proc->pid = pid; proc->next = pp; ptable[HASH(pid)] = proc; proc->time = 0; proc->wcpu = 0; } otime = proc->time; read_one_proc_stat(pid, proc, sel); if (sel->fullcmd == 2) update_procname(proc, PQgetvalue(pgresult, i, 1)); if (proc->state == 0) continue; total_procs++; process_states[proc->state]++; if (timediff > 0.0) { if ((proc->pcpu = (proc->time - otime) / timediff) < 0.0001) { proc->pcpu = 0; } proc->wcpu = proc->pcpu * alpha + proc->wcpu * beta; } else if ((elapsed = (now - boottime) * HZ - proc->start_time) > 0) { /* * What's with the noop statement? if ((proc->pcpu = * (double)proc->time / (double)elapsed) < 0.0001) { * proc->pcpu; } */ proc->wcpu = proc->pcpu; } else { proc->wcpu = proc->pcpu = 0.0; } } if (pgresult != NULL) PQclear(pgresult); PQfinish(pgconn); /* make sure we have enough slots for the active procs */ if (activesize < total_procs) { pactive = (struct top_proc **) realloc(pactive, sizeof(struct top_proc *) * total_procs); activesize = total_procs; } /* set up the active procs and flush dead entries */ active = pactive; for (i = 0; i < HASH_SIZE; i++) { struct top_proc *last; struct top_proc *ptmp; last = NULL; proc = ptable[i]; while (proc != NULL) { if (proc->state == 0) { ptmp = proc; if (last) { proc = last->next = proc->next; } else { proc = ptable[i] = proc->next; } free_proc(ptmp); } else { if ((show_idle || proc->state == 1 || proc->pcpu) && (!show_uid || proc->uid == sel->uid)) { *active++ = proc; last = proc; } proc = proc->next; } } } si->p_active = active - pactive; si->p_total = total_procs; si->procstates = process_states; } /* if requested, sort the "active" procs */ if (si->p_active) { if (mode == MODE_IO_STATS) { qsort(pactive, si->p_active, sizeof(struct top_proc *), io_compares[compare_index]); } else { qsort(pactive, si->p_active, sizeof(struct top_proc *), proc_compares[compare_index]); } } /* don't even pretend that the return value thing here isn't bogus */ nextactive = pactive; return (caddr_t) 0; } char * format_header(char *uname_field) { int uname_len = strlen(uname_field); if (uname_len > 8) uname_len = 8; memcpy(strchr(fmt_header, 'X'), uname_field, uname_len); return fmt_header; } char * format_next_io(caddr_t handle, char *(*get_userid) (uid_t)) { static char fmt[MAX_COLS]; /* static area where result is built */ static struct io_node *head = NULL; struct top_proc *p = *nextactive++; struct io_node *node = NULL; head = upsert_io_stats(head, p->pid, p->rchar, p->wchar, p->syscr, p->syscw, p->read_bytes, p->write_bytes, p->cancelled_write_bytes); if (mode_stats == STATS_DIFF) { node = get_io_stats(head, p->pid); if (node == NULL) { snprintf(fmt, sizeof(fmt), "%5d %5d %5d %7d %7d %5d %6d %7d %s", p->pid, 0, 0, 0, 0, 0, 0, 0, p->name); } else { snprintf(fmt, sizeof(fmt), "%5d %5s %5s %7lld %7lld %5s %6s %7s %s", p->pid, format_b(node->diff_rchar), format_b(node->diff_wchar), node->diff_syscr, node->diff_syscw, format_b(node->diff_read_bytes), format_b(node->diff_write_bytes), format_b(node->diff_cancelled_write_bytes), p->name); } } else { snprintf(fmt, sizeof(fmt), "%5d %5s %5s %7lld %7lld %5s %6s %7s %s", p->pid, format_b(p->rchar), format_b(p->wchar), p->syscr, p->syscw, format_b(p->read_bytes), format_b(p->write_bytes), format_b(p->cancelled_write_bytes), p->name); } return (fmt); } char * format_next_process(caddr_t handle, char *(*get_userid) (uid_t)) { static char fmt[MAX_COLS]; /* static area where result is built */ struct top_proc *p = *nextactive++; snprintf(fmt, sizeof(fmt), "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %s", p->pid, (*get_userid) (p->uid), p->pri < -99 ? -99 : p->pri, p->nice, format_k(p->size), format_k(p->rss), state_abbrev[p->state], format_time(p->time / HZ), p->wcpu * 100.0, p->pcpu * 100.0, p->name); /* return the result */ return (fmt); } /* comparison routines for qsort */ /* * There are currently four possible comparison routines. main selects * one of these by indexing in to the array proc_compares. * * Possible keys are defined as macros below. Currently these keys are * defined: percent cpu, cpu ticks, process 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. */ /* First, the possible comparison keys. These are defined in such a way that they can be merely listed in the source code to define the actual desired ordering. */ #define ORDERKEY_PCTCPU if (dresult = p2->pcpu - p1->pcpu,\ (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0) #define ORDERKEY_CPTICKS if ((result = (long)p2->time - (long)p1->time) == 0) #define ORDERKEY_STATE if ((result = (sort_state[p2->state] - \ sort_state[p1->state])) == 0) #define ORDERKEY_PRIO if ((result = p2->pri - p1->pri) == 0) #define ORDERKEY_RSSIZE if ((result = p2->rss - p1->rss) == 0) #define ORDERKEY_MEM if ((result = p2->size - p1->size) == 0) #define ORDERKEY_NAME if ((result = strcmp(p1->name, p2->name)) == 0) #define ORDERKEY_PID if ((result = p1->pid - p2->pid) == 0) #define ORDERKEY_RCHAR if ((result = p1->rchar - p2->rchar) == 0) #define ORDERKEY_WCHAR if ((result = p1->wchar - p2->wchar) == 0) #define ORDERKEY_SYSCR if ((result = p1->syscr - p2->syscr) == 0) #define ORDERKEY_SYSCW if ((result = p1->syscw - p2->syscw) == 0) #define ORDERKEY_READS if ((result = p1->read_bytes - p2->read_bytes) == 0) #define ORDERKEY_WRITES if ((result = p1->write_bytes - p2->write_bytes) == 0) #define ORDERKEY_CWRITES if ((result = p1->cancelled_write_bytes - p2->cancelled_write_bytes) == 0) /* Now the array that maps process state to a weight */ unsigned char sort_state[] = { 0, /* empty */ 6, /* run */ 3, /* sleep */ 5, /* disk wait */ 1, /* zombie */ 2, /* stop */ 4 /* swap */ }; /* compare_cpu - the comparison function for sorting by cpu percentage */ int compare_cpu( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_RSSIZE ORDERKEY_MEM ; return result == 0 ? 0 : result < 0 ? -1 : 1; } /* compare_size - the comparison function for sorting by total memory usage */ int compare_size( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_MEM ORDERKEY_RSSIZE ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ; return result == 0 ? 0 : result < 0 ? -1 : 1; } /* compare_res - the comparison function for sorting by resident set size */ int compare_res( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_RSSIZE ORDERKEY_MEM ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ; return result == 0 ? 0 : result < 0 ? -1 : 1; } /* compare_time - the comparison function for sorting by total cpu time */ int compare_time( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_CPTICKS ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_MEM ORDERKEY_RSSIZE ; return result == 0 ? 0 : result < 0 ? -1 : 1; } /* compare_cmd - the comparison function for sorting by command name */ int compare_cmd( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_NAME ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_RSSIZE ORDERKEY_MEM ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_pid(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_WCHAR ORDERKEY_SYSCR ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_rchar(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_RCHAR ORDERKEY_PID ORDERKEY_WCHAR ORDERKEY_SYSCR ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_wchar(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_WCHAR ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_SYSCR ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_syscr(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_SYSCR ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_WCHAR ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_syscw(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_SYSCW ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_WCHAR ORDERKEY_SYSCR ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_reads(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_READS ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_WCHAR ORDERKEY_SYSCR ORDERKEY_SYSCW ORDERKEY_WRITES ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_writes(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_WRITES ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_WCHAR ORDERKEY_SYSCR ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_cwrites(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_CWRITES ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_WCHAR ORDERKEY_SYSCR ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } /* * proc_owner(pid) - returns the uid that owns process "pid", or -1 if * the process does not exist. * It is EXTREMLY IMPORTANT that this function work correctly. * If pg_top runs setuid root (as in SVR4), then this function * is the only thing that stands in the way of a serious * security problem. It validates requests for the "kill" * and "renice" commands. */ uid_t proc_owner(pid_t pid) { struct stat sb; char buffer[32]; sprintf(buffer, "%d", pid); if (stat(buffer, &sb) < 0) return -1; else return (int) sb.st_uid; } struct io_node * new_io_node(pid_t pid) { struct io_node *node; node = (struct io_node *) malloc(sizeof(struct io_node)); bzero(node, sizeof(struct io_node)); node->pid = pid; node->next = NULL; return node; } struct io_node * get_io_stats(struct io_node *head, pid_t pid) { struct io_node *node = head; while (node != NULL) { if (node->pid == pid) { return node; } node = node->next; } return node; } struct io_node * insert_io_stats(struct io_node *head, struct io_node *node) { struct io_node *c = head; struct io_node *p = NULL; if (node->pid < head->pid) { node->next = head; head = node; return head; } c = head->next; p = head; while (c != NULL) { if (node->pid < c->pid) { node->next = c; p->next = node; return head; } p = c; c = c->next; } if (c == NULL) { p->next = node; } return head; } void update_io_stats(struct io_node *node, pid_t pid, long long rchar, long long wchar, long long syscr, long long syscw, long long read_bytes, long long write_bytes, long long cancelled_write_bytes) { /* Calculate difference between previous and current values. */ node->diff_rchar = rchar - node->old_rchar; node->diff_wchar = wchar - node->old_wchar; node->diff_syscr = syscr - node->old_syscr; node->diff_syscw = syscw - node->old_syscw; node->diff_read_bytes = read_bytes - node->old_read_bytes; node->diff_write_bytes = write_bytes - node->old_write_bytes; node->diff_cancelled_write_bytes = cancelled_write_bytes - node->old_cancelled_write_bytes; /* Save the current values as previous values. */ node->old_rchar = rchar; node->old_wchar = wchar; node->old_syscr = syscr; node->old_syscw = syscw; node->old_read_bytes = read_bytes; node->old_write_bytes = write_bytes; node->old_cancelled_write_bytes = cancelled_write_bytes; } struct io_node * upsert_io_stats(struct io_node *head, pid_t pid, long long rchar, long long wchar, long long syscr, long long syscw, long long read_bytes, long long write_bytes, long long cancelled_write_bytes) { struct io_node *c = head; /* List is empty, create a new node. */ if (head == NULL) { head = new_io_node(pid); update_io_stats(head, pid, rchar, wchar, syscr, syscw, read_bytes, write_bytes, cancelled_write_bytes); return head; } /* Check if this pid exists already. */ while (c != NULL) { if (c->pid == pid) { /* Found an existing node with same pid, update it. */ update_io_stats(c, pid, rchar, wchar, syscr, syscw, read_bytes, write_bytes, cancelled_write_bytes); return head; } c = c->next; } /* Didn't find pig. Create a new node, save the data and insert it. */ c = new_io_node(pid); update_io_stats(c, pid, rchar, wchar, syscr, syscw, read_bytes, write_bytes, cancelled_write_bytes); head = insert_io_stats(head, c); return head; } /* * Get IO information for the SCSI devices in the system. Returns * read/write IOs per second and bandwidth by comparing current values * with previous values. */ void get_io_info(struct io_info *io_info) { struct timeval thistime; double timediff; static struct timeval lasttime; struct io_info cur_info; static struct io_info last_io_info; FILE *fp; char line[256]; int major, minor; char dev_name[32]; int64_t reads, readsectors, skip, writes, writesectors; int i; /* calculate the time difference since our last check */ gettimeofday(&thistime, 0); if (lasttime.tv_sec) timediff = ((thistime.tv_sec - lasttime.tv_sec) + (thistime.tv_usec - lasttime.tv_usec) * 1e-6); else timediff = 0; lasttime = thistime; fp = fopen("/proc/diskstats", "r"); if (fp == NULL) { memset(io_info, 0, sizeof(*io_info)); return; } memset(&cur_info, 0, sizeof(cur_info)); while (fgets(line, 256, fp) != NULL) { i = sscanf(line, "%d %d %31s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", &major, &minor, dev_name, &reads, &skip, &readsectors, &skip, &writes, &skip, &writesectors, &skip, &skip, &skip, &skip); if (i != 14) continue; /* Total up full scsi devices (not partitions) */ if (major == 8 && (minor % 16) == 0) { cur_info.reads += reads; cur_info.readsectors += readsectors; cur_info.writes += writes; cur_info.writesectors += writesectors; } } fclose(fp); if (timediff == 0) { last_io_info = cur_info; memset(io_info, 0, sizeof(*io_info)); return; } /* Compute the rate information */ io_info->reads = (double)(cur_info.reads - last_io_info.reads) / timediff; io_info->readsectors = (double)(cur_info.readsectors - last_io_info.readsectors) / timediff; io_info->writes = (double)(cur_info.writes - last_io_info.writes) / timediff; io_info->writesectors = (double)(cur_info.writesectors - last_io_info.writesectors) / timediff; last_io_info = cur_info; } /* * Get disk space information for the disk that contains the data * directory. */ void get_disk_info(struct disk_info *disk_info, char *data_directory) { struct statfs buf; /* If no data directory found, return nothing */ if (!data_directory) { memset(disk_info, 0, sizeof(*disk_info)); return; } /* Get statistics */ if (statfs(data_directory, &buf) == 0) { disk_info->size = (int64_t)buf.f_bsize * (int64_t)buf.f_blocks; disk_info->avail = (int64_t)buf.f_bsize * (int64_t)buf.f_bavail; } }