/* * 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. */ #define _GNU_SOURCE #include "config.h" #include #include #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 { RB_ENTRY(top_proc) entry; pid_t pid; /* index for which element is current in data arrays */ int index; /* Data from /proc//stat. */ char *name; char *usename; unsigned long size, rss; /* in k */ int state; int pgstate; unsigned long time; unsigned long start_time; unsigned long xtime; unsigned long qtime; unsigned int locks; double pcpu; /* Data from /proc//io. */ long long iops[2]; /* syscr + syscw */ long long syscr[2]; long long syscw[2]; long long read_bytes[2]; long long write_bytes[2]; /* Replication data */ char *application_name; char *client_addr; char *repstate; char *primary; char *sent; char *write; char *flush; char *replay; long long sent_lag; long long write_lag; long long flush_lag; long long replay_lag; }; int topproccmp(struct top_proc *, struct top_proc *); RB_HEAD(pgproc, top_proc) head_proc = RB_INITIALIZER(&head_proc); RB_PROTOTYPE(pgproc, top_proc, entry, topproccmp) RB_GENERATE(pgproc, top_proc, entry, topproccmp) double timediff; /*=STATE IDENT STRINGS==================================================*/ #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 }; enum swap { SWAPUSED, SWAPFREE, SWAPCACHED, SWAPIN, SWAPOUT, NSWAPSTATS }; static char *swapnames[NSWAPSTATS + 1] = { "K used, ", "K free, ", "K cached, ", "K in, ", "K out", NULL }; struct swap_t { int index; long long in[2]; long long out[2]; } swap_activity; static char fmt_header[] = " PID X SIZE RES STATE XTIME QTIME %CPU LOCKS COMMAND"; char fmt_header_io[] = " PID IOPS IORPS IOWPS READS WRITES COMMAND"; /* these are names given to allowed sorting orders -- first is default */ static char *ordernames[] = { "cpu", "size", "res", "xtime", "qtime", "iops", "iorps", "iowps", "reads", "writes", "locks", "command", "flag", "rlag", "slag", "wlag", NULL }; /* forward definitions for comparison functions */ static int compare_cmd(const void *, const void *); static int compare_cpu(const void *, const void *); static int compare_iops(const void *, const void *); static int compare_lag_flush(const void *, const void *); static int compare_lag_replay(const void *, const void *); static int compare_lag_sent(const void *, const void *); static int compare_lag_write(const void *, const void *); static int compare_locks(const void *, const void *); static int compare_qtime(const void *, const void *); static int compare_reads(const void *, const void *); static int compare_res(const void *, const void *); static int compare_size(const void *, const void *); static int compare_syscr(const void *, const void *); static int compare_syscw(const void *, const void *); static int compare_writes(const void *, const void *); static int compare_xtime(const void *, const void *); int (*proc_compares[]) () = { compare_cpu, compare_size, compare_res, compare_xtime, compare_qtime, compare_iops, compare_syscr, compare_syscw, compare_reads, compare_writes, compare_locks, compare_cmd, compare_lag_flush, compare_lag_replay, compare_lag_sent, compare_lag_write, 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 INITIAL_ACTIVE_SIZE (256) #define PROCBLOCK_SIZE (32) static struct top_proc *pgtable; static int proc_index; 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) /*======================================================================*/ static inline long long diff_stat(long long value[2], int index) { return value[index] - value[(index + 1) % 2]; } 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; } int topproccmp(struct top_proc *e1, struct top_proc *e2) { return (e1->pid < e2->pid ? -1 : e1->pid > e2->pid); } static void xfrm_cmdline(char *p, int len) { while (--len > 0) { if (*p == '\0') { *p = ' '; } p++; } } 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 = 0; 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->boottime = boottime; statics->flags.fullcmds = 1; statics->flags.warmup = 1; /* 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); } /* get swap activity */ if ((fd = open("vmstat", O_RDONLY)) != -1) { unsigned long swpin = -1; unsigned long swpout = -1; if ((len = read(fd, buffer, sizeof(buffer) - 1)) > 0) { buffer[len] = '\0'; p = buffer - 1; while (p != NULL) { p++; if (swpin == -1 && strncmp(p, "pswpin", 6) == 0) { p = skip_token(p); swpin = strtoul(p, &p, 10); } else if (swpout == -1 && strncmp(p, "pswpout", 7) == 0) { p = skip_token(p); swpout = strtoul(p, &p, 10); } if (swpin != -1 && swpout != -1) { swap_activity.in[swap_activity.index] = swpin; swap_activity.out[swap_activity.index] = swpout; swap_stats[SWAPIN] = diff_stat(swap_activity.in, swap_activity.index); swap_stats[SWAPOUT] = diff_stat(swap_activity.out, swap_activity.index); swap_activity.index = (swap_activity.index + 1) % 2; break; } /* move to the next line */ p = strchr(p, '\n'); } } close(fd); } else { swap_activity.in[swap_activity.index] = -1; swap_activity.out[swap_activity.index] = -1; } /* set arrays and strings */ info->cpustates = cpu_states; info->memory = memory_stats; info->swap = swap_stats; } static void read_one_proc_stat(struct top_proc *proc, struct process_select *sel) { char buffer[4096], *p, *q; int fd, len; int fullcmd; char value[BUFFERLEN + 1]; long long tmp; /* 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", proc->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_str(&proc->name, buffer); printable(proc->name); } else { fullcmd = 0; } close(fd); } else { fullcmd = 0; } } /* grab the proc stat info in one go */ sprintf(buffer, "%d/stat", proc->pid); fd = open(buffer, O_RDONLY); len = read(fd, buffer, sizeof(buffer) - 1); close(fd); buffer[len] = '\0'; /* 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_str(&proc->name, p); printable(proc->name); } /* 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 */ p = skip_token(p); /* skip priority */ p = skip_token(p); /* skip 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", proc->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. */ return; } len = read(fd, buffer, sizeof(buffer) - 1); close(fd); buffer[len] = '\0'; p = buffer; p = skip_token(p); /* rchar */ p = skip_token(p); /* wchar */ GET_VALUE(tmp); /* syscr */ proc->syscr[proc->index] = tmp; proc->iops[proc->index] = tmp; GET_VALUE(tmp); /* syscw */ proc->syscw[proc->index] = tmp; proc->iops[proc->index] += tmp; GET_VALUE(tmp); /* read_bytes */ proc->read_bytes[proc->index] = tmp; GET_VALUE(tmp); /* write_bytes */ proc->write_bytes[proc->index] = tmp; GET_VALUE(tmp); /* cancelled_write_bytes */ proc->write_bytes[proc->index] -= tmp; } caddr_t get_process_info(struct system_info *si, struct process_select *sel, int compare_index, struct pg_conninfo_ctx *conninfo, int mode) { struct timeval thistime; double tickdiff; /* 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; tickdiff = timediff * HZ; /* convert to ticks */ /* read the process information */ { int total_procs = 0; int active_procs = 0; int show_idle = sel->idle; int i; int rows; PGresult *pgresult = NULL; struct top_proc *n, *p; memset(process_states, 0, sizeof(process_states)); connect_to_db(conninfo); if (conninfo->connection != NULL) { if (mode == MODE_REPLICATION) { pgresult = pg_replication(conninfo->connection); } else { pgresult = pg_processes(conninfo->connection); } rows = PQntuples(pgresult); } else { rows = 0; } if (rows > 0) { p = realloc(pgtable, sizeof(struct top_proc) * rows); if (p == NULL) { fprintf(stderr, "realloc error\n"); if (pgresult != NULL) PQclear(pgresult); disconnect_from_db(conninfo); exit(1); } pgtable = p; } for (i = 0; i < rows; i++) { unsigned long otime; n = malloc(sizeof(struct top_proc)); if (n == NULL) { fprintf(stderr, "malloc error\n"); if (pgresult != NULL) PQclear(pgresult); disconnect_from_db(conninfo); exit(1); } memset(n, 0, sizeof(struct top_proc)); n->pid = atoi(PQgetvalue(pgresult, i, 0)); p = RB_INSERT(pgproc, &head_proc, n); if (p != NULL) { free(n); n = p; } else { n->time = 0; } otime = n->time; if (mode == MODE_REPLICATION) { update_str(&n->usename, PQgetvalue(pgresult, i, REP_USENAME)); update_str(&n->application_name, PQgetvalue(pgresult, i, REP_APPLICATION_NAME)); update_str(&n->client_addr, PQgetvalue(pgresult, i, REP_CLIENT_ADDR)); update_str(&n->repstate, PQgetvalue(pgresult, i, REP_STATE)); update_str(&n->primary, PQgetvalue(pgresult, i, REP_WAL_INSERT)); update_str(&n->sent, PQgetvalue(pgresult, i, REP_SENT)); update_str(&n->write, PQgetvalue(pgresult, i, REP_WRITE)); update_str(&n->flush, PQgetvalue(pgresult, i, REP_FLUSH)); update_str(&n->replay, PQgetvalue(pgresult, i, REP_REPLAY)); n->sent_lag = atol(PQgetvalue(pgresult, i, REP_SENT_LAG)); n->write_lag = atol(PQgetvalue(pgresult, i, REP_WRITE_LAG)); n->flush_lag = atol(PQgetvalue(pgresult, i, REP_FLUSH_LAG)); n->replay_lag = atol(PQgetvalue(pgresult, i, REP_REPLAY_LAG)); memcpy(&pgtable[active_procs++], n, sizeof(struct top_proc)); } else { read_one_proc_stat(n, sel); if (sel->fullcmd == 2) { update_str(&n->name, PQgetvalue(pgresult, i, PROC_QUERY)); printable(n->name); } update_state(&n->pgstate, PQgetvalue(pgresult, i, PROC_STATE)); update_str(&n->usename, PQgetvalue(pgresult, i, PROC_USENAME)); n->xtime = atol(PQgetvalue(pgresult, i, PROC_XSTART)); n->qtime = atol(PQgetvalue(pgresult, i, PROC_QSTART)); n->locks = atoi(PQgetvalue(pgresult, i, PROC_LOCKS)); process_states[n->pgstate]++; if (tickdiff > 0.0) { if ((n->pcpu = (n->time - otime) / tickdiff) < 0.0001) { n->pcpu = 0; } } if ((show_idle || n->pgstate != STATE_IDLE) && (sel->usename[0] == '\0' || strcmp(n->usename, sel->usename) == 0)) memcpy(&pgtable[active_procs++], n, sizeof(struct top_proc)); } n->index = (n->index + 1) % 2; total_procs++; } if (pgresult != NULL) PQclear(pgresult); disconnect_from_db(conninfo); si->p_active = active_procs; si->p_total = total_procs; si->procstates = process_states; } /* if requested, sort the "active" procs */ if (compare_index >= 0 && si->p_active) { qsort(pgtable, si->p_active, sizeof(struct top_proc), proc_compares[compare_index]); } /* don't even pretend that the return value thing here isn't bogus */ proc_index = 0; 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) { static char fmt[MAX_COLS]; /* static area where result is built */ struct top_proc *p = &pgtable[proc_index++]; snprintf(fmt, sizeof(fmt), "%7d %7.0f %7.0f %7.0f %5s %6s %s", p->pid, diff_stat(p->iops, p->index) / timediff, diff_stat(p->syscr, p->index) / timediff, diff_stat(p->syscw, p->index) / timediff, format_b(diff_stat(p->read_bytes, p->index) / timediff), format_b(diff_stat(p->write_bytes, p->index) / timediff), p->name); return (fmt); } char * format_next_process(caddr_t handle) { static char fmt[MAX_COLS]; /* static area where result is built */ struct top_proc *p = &pgtable[proc_index++]; snprintf(fmt, sizeof(fmt), "%7d %-10.8s %5s %5s %-6s %5s %5s %5.1f %5d %s", p->pid, p->usename, format_k(p->size), format_k(p->rss), backendstatenames[p->pgstate], format_time(p->xtime), format_time(p->qtime), p->pcpu * 100.0, p->locks, p->name); /* return the result */ return (fmt); } char * format_next_replication(caddr_t handle) { static char fmt[MAX_COLS]; /* static area where result is built */ struct top_proc *p = &pgtable[proc_index++]; snprintf(fmt, sizeof(fmt), "%7d %-8.8s %-11.11s %15s %-9.9s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %5s %5s %5s %5s", p->pid, p->usename, p->application_name, p->client_addr, p->repstate, p->primary, p->sent, p->write, p->flush, p->replay, format_b(p->sent_lag), format_b(p->write_lag), format_b(p->flush_lag), format_b(p->replay_lag)); /* 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_IOPS if ((result = diff_stat(p2->iops, p2->index) - \ diff_stat(p1->iops, p1->index)) == 0) #define ORDERKEY_LAG_FLUSH if ((result = p2->flush_lag - p1->flush_lag) == 0) #define ORDERKEY_LAG_REPLAY if ((result = p2->replay_lag - \ p1->replay_lag) == 0) #define ORDERKEY_LAG_SENT if ((result = p2->sent_lag - p1->sent_lag) == 0) #define ORDERKEY_LAG_WRITE if ((result = p2->write_lag - p1->write_lag) == 0) #define ORDERKEY_LOCKS if ((result = p2->locks - p1->locks) == 0) #define ORDERKEY_MEM if ((result = p2->size - p1->size) == 0) #define ORDERKEY_NAME if ((result = strcmp(p1->name, p2->name)) == 0) #define ORDERKEY_PCTCPU if ((result = (int)(p2->pcpu - p1->pcpu)) == 0) #define ORDERKEY_QTIME if ((result = p2->qtime - p1->qtime) == 0) #define ORDERKEY_READS if ((result = diff_stat(p2->read_bytes, p2->index) - \ diff_stat(p1->read_bytes, p1->index)) == 0) #define ORDERKEY_RSSIZE if ((result = p2->rss - p1->rss) == 0) #define ORDERKEY_STATE if ((result = p1->pgstate < p2->pgstate)) #define ORDERKEY_SYSCR if ((result = diff_stat(p2->syscr, p2->index) - \ diff_stat(p1->syscr, p1->index)) == 0) #define ORDERKEY_SYSCW if ((result = diff_stat(p2->syscw, p2->index) - \ diff_stat(p1->syscw, p1->index)) == 0) #define ORDERKEY_WRITES if ((result = diff_stat(p2->write_bytes, p2->index) - \ diff_stat(p1->write_bytes, p1->index)) == 0) #define ORDERKEY_XTIME if ((result = p2->xtime - p1->xtime) == 0) /* compare_cmd - the comparison function for sorting by command name */ static int compare_cmd(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_NAME ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_RSSIZE ORDERKEY_MEM ; return (result); } /* compare_cpu - the comparison function for sorting by cpu percentage */ static int compare_cpu(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_RSSIZE ORDERKEY_MEM ; return (result); } /* compare_iops - the comparison function for sorting by iops */ static int compare_iops(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_IOPS ORDERKEY_SYSCW ORDERKEY_SYSCR ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_NAME ; return (result); } static int compare_lag_flush(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_LAG_FLUSH ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_MEM ORDERKEY_RSSIZE ; return (result); } static int compare_lag_replay(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_LAG_REPLAY ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_MEM ORDERKEY_RSSIZE ; return (result); } static int compare_lag_sent(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_LAG_SENT ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_MEM ORDERKEY_RSSIZE ; return (result); } static int compare_lag_write(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_LAG_WRITE ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_MEM ORDERKEY_RSSIZE ; return (result); } /* * compare_locks - the comparison function for sorting by total locks ancquired */ static int compare_locks(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_LOCKS ORDERKEY_QTIME ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_MEM ORDERKEY_RSSIZE ; return (result); } /* compare_qtime - the comparison function for sorting by total cpu qtime */ static int compare_qtime(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_QTIME ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_MEM ORDERKEY_RSSIZE ; return (result); } static int compare_reads(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_READS ORDERKEY_SYSCR ORDERKEY_IOPS ORDERKEY_SYSCW ORDERKEY_WRITES ORDERKEY_NAME ; return (result); } /* compare_res - the comparison function for sorting by resident set size */ static int compare_res(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_RSSIZE ORDERKEY_MEM ORDERKEY_PCTCPU ORDERKEY_STATE ; return (result); } /* compare_size - the comparison function for sorting by total memory usage */ static int compare_size(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_MEM ORDERKEY_RSSIZE ORDERKEY_PCTCPU ORDERKEY_STATE ; return (result); } static int compare_syscr(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_SYSCR ORDERKEY_IOPS ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_NAME ; return (result); } static int compare_syscw(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_SYSCW ORDERKEY_IOPS ORDERKEY_SYSCR ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_NAME ; return (result); } /* compare_xtime - the comparison function for sorting by total cpu xtime */ static int compare_xtime(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_XTIME ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_MEM ORDERKEY_RSSIZE ; return (result); } static int compare_writes(const void *v1, const void *v2) { struct top_proc *p1 = (struct top_proc *) v1; struct top_proc *p2 = (struct top_proc *) v2; int result; ORDERKEY_WRITES ORDERKEY_IOPS ORDERKEY_SYSCR ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_NAME ; return (result); } /* * 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; }