/* * pg_top - a top PostgreSQL users display for Unix * * SYNOPSIS: PowerPC running AIX 4.2 or higher * * DESCRIPTION: * This is the machine-dependent module for AIX 4.2 and higher * It is currenlty only tested on PowerPC architectures. * * TERMCAP: -lcurses * * CFLAGS: -DORDER -DHAVE_GETOPT * * LIBS: -bD:0x18000000 * * AUTHOR: Joep Vesseur * * PATCHES: Antoine Tabary */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "pg_top.h" #include "machine.h" #include "utils.h" #define PROCRESS(p) (((p)->pi_trss + (p)->pi_drss)*4) #define PROCSIZE(p) (((p)->pi_tsize/1024+(p)->pi_dvm)*4) #define PROCTIME(pi) (pi->pi_ru.ru_utime.tv_sec + pi->pi_ru.ru_stime.tv_sec) /* * structure definition taken from 'monitor' by Jussi Maki (jmaki@hut.fi) */ struct vmker { uint n0, n1, n2, n3, n4, n5, n6, n7, n8; uint totalmem; uint badmem; /* this is used in RS/6000 model 220 */ uint freemem; uint n12; uint numperm; /* this seems to keep other than text and data * segment usage; name taken from * /usr/lpp/bos/samples/vmtune.c */ uint totalvmem, freevmem; uint n15, n16, n17, n18, n19; }; #define KMEM "/dev/kmem" /* Indices in the nlist array */ #define X_AVENRUN 0 #define X_SYSINFO 1 #define X_VMKER 2 #define X_PROC 3 #define X_V 4 static struct nlist nlst[] = { {"avenrun", 0, 0, 0, 0, 0}, /* 0 */ {"sysinfo", 0, 0, 0, 0, 0}, /* 1 */ {"vmker", 0, 0, 0, 0, 0}, /* 2 */ {"proc", 0, 0, 0, 0, 0}, /* 3 */ {"v", 0, 0, 0, 0, 0}, /* 4 */ {NULL, 0, 0, 0, 0, 0} }; /* get_process_info returns handle. definition is here */ struct handle { struct procsinfo **next_proc; int remaining; }; /* * These definitions control the format of the per-process area */ static char header[] = " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND"; /* 0123456 -- field to fill in starts at header+6 */ #define UNAME_START 7 #define Proc_format \ "%6d %-8.8s %3d %4d %5d%c %4d%c %-5s %6s %5.2f%% %5.2f%% %.14s%s" /* these are for detailing the process states */ int process_states[9]; char *procstatenames[] = { " none, ", " sleeping, ", " state2, ", " runnable, ", " idle, ", " zombie, ", " stopped, ", " running, ", " swapped, ", NULL }; /* these are for detailing the cpu states */ int cpu_states[4]; char *cpustatenames[] = { "idle", "user", "kernel", "wait", NULL }; /* these are for detailing the memory statistics */ long memory_stats[4]; char *memorynames[] = { "K Total, ", "K Free, ", "K Buffers", NULL }; #define M_REAL 0 #define M_REALFREE 1 #define M_BUFFERS 2 long swap_stats[3]; char *swapnames[] = { "K Total, ", "K Free", NULL }; #define M_VIRTUAL 0 #define M_VIRTFREE 1 char *state_abbrev[] = { "", "sleep", "", "", "sleep", "zomb", "stop", "run", "swap" }; /* sorting orders. first is default */ char *ordernames[] = {"cpu", "size", "res", "time", "pri", NULL}; /* compare routines */ int compare_cpu(), compare_size(), compare_res(), compare_time(), compare_prio(); int (*proc_compares[]) () = { compare_cpu, compare_size, compare_res, compare_time, compare_prio, NULL }; /* useful externals */ extern int errno; extern char *sys_errlist[]; long lseek(); long time(); long percentages(); /* useful globals */ int kmem; /* file descriptor */ /* offsets in kernel */ static unsigned long avenrun_offset; static unsigned long sysinfo_offset; static unsigned long vmker_offset; static unsigned long proc_offset; static unsigned long v_offset; /* used for calculating cpu state percentages */ static long cp_time[CPU_NTIMES]; static long cp_old[CPU_NTIMES]; static long cp_diff[CPU_NTIMES]; /* the runqueue length is a cumulative value. keep old value */ long old_runque; /* process info */ struct var v_info; /* to determine nprocs */ int nprocs; /* maximum nr of procs in proctab */ int ncpus; /* nr of cpus installed */ int ptsize; /* size of process table in bytes */ struct proc *p_proc; /* a copy of the process table */ struct procsinfo *p_info; /* needed for vm and ru info */ struct procsinfo **pref; /* processes selected for display */ int pref_len; /* number of processes selected */ /* needed to calculate WCPU */ unsigned long curtime; /* * Initialize globals, get kernel offsets and stuff... */ machine_init(struct statics * statics) { time_t uptime, now; struct tms tbuf; if ((kmem = open(KMEM, O_RDONLY)) == -1) { perror(KMEM); return -1; } /* get kernel symbol offsets */ if (knlist(nlst, 5, sizeof(struct nlist)) != 0) { perror("knlist"); return -1; } avenrun_offset = nlst[X_AVENRUN].n_value; sysinfo_offset = nlst[X_SYSINFO].n_value; vmker_offset = nlst[X_VMKER].n_value; proc_offset = nlst[X_PROC].n_value; v_offset = nlst[X_V].n_value; getkval(v_offset, (caddr_t) & v_info, sizeof v_info, "v"); ncpus = v_info.v_ncpus; /* number of cpus */ nprocs = PROCMASK(PIDMAX); if (nprocs > 1024) nprocs = 1024; ptsize = nprocs * sizeof(struct proc); p_proc = (struct proc *) malloc(ptsize); p_info = (struct procsinfo *) malloc(nprocs * sizeof(struct procsinfo)); pref = (struct procsinfo **) malloc(nprocs * sizeof(struct procsinfo *)); if (!p_proc || !p_info || !pref) { fprintf(stderr, "pg_top: not enough memory\n"); return -1; } /* set boot time */ now = time(NULL); uptime = times(&tbuf) / HZ; statics->boottime = now - uptime; statics->procstate_names = procstatenames; statics->cpustate_names = cpustatenames; statics->memory_names = memorynames; statics->order_names = ordernames; statics->swap_names = swapnames; return (0); } char * format_header(char *uname_field) { register char *ptr; ptr = header + UNAME_START; while (*uname_field != '\0') { *ptr++ = *uname_field++; } return (header); } void get_system_info(struct system_info * si) { int load_avg[3]; struct sysinfo s_info; struct vmker m_info; int i; double total = 0; /* get the load avarage array */ getkval(avenrun_offset, (caddr_t) load_avg, sizeof load_avg, "avenrun"); /* get the sysinfo structure */ getkval(sysinfo_offset, (caddr_t) & s_info, sizeof s_info, "sysinfo"); /* get vmker structure */ getkval(vmker_offset, (caddr_t) & m_info, sizeof m_info, "vmker"); /* convert load avarages to doubles */ for (i = 0; i < 3; i++) si->load_avg[i] = (double) load_avg[i] / 65536.0; /* calculate cpu state in percentages */ for (i = 0; i < CPU_NTIMES; i++) { cp_old[i] = cp_time[i]; cp_time[i] = s_info.cpu[i]; cp_diff[i] = cp_time[i] - cp_old[i]; total += cp_diff[i]; } total = total / 1000.0; /* pg_top itself will correct this */ for (i = 0; i < CPU_NTIMES; i++) { cpu_states[i] = cp_diff[i] / total; } /* calculate memory statistics, scale 4K pages to megabytes */ #define PAGE_TO_MB(a) ((a)*4/1024) memory_stats[M_REAL] = PAGE_TO_MB(m_info.totalmem); memory_stats[M_REALFREE] = PAGE_TO_MB(m_info.freemem); memory_stats[M_BUFFERS] = PAGE_TO_MB(m_info.numperm); swap_stats[M_VIRTUAL] = PAGE_TO_MB(m_info.totalvmem); swap_stats[M_VIRTFREE] = PAGE_TO_MB(m_info.freevmem); /* runnable processes */ process_states[0] = s_info.runque - old_runque; old_runque = s_info.runque; si->cpustates = cpu_states; si->memory = memory_stats; si->swap = swap_stats; } static struct handle handle; caddr_t get_process_info(struct system_info * si, struct process_select * sel, int compare_index) { int i, nproc; int ptsize_util; int active_procs = 0, total_procs = 0; struct procsinfo *pp, **p_pref = pref; unsigned long pctcpu; pid_t procsindex = 0; struct proc *p; si->procstates = process_states; curtime = time(0); /* get the procsinfo structures of all running processes */ nproc = getprocs(p_info, sizeof(struct procsinfo), NULL, 0, &procsindex, nprocs); if (nproc < 0) { perror("getprocs"); quit(1); } /* the swapper has no cmd-line attached */ strcpy(p_info[0].pi_comm, "swapper"); /* get proc table */ ptsize_util = (PROCMASK(p_info[nproc - 1].pi_pid) + 1) * sizeof(struct proc); getkval(proc_offset, (caddr_t) p_proc, ptsize_util, "proc"); memset(process_states, 0, sizeof process_states); /* * build a list of pointers to processes to show. walk through the list of * procsinfo structures instead of the proc table since the mapping of * procsinfo -> proctable is easy, the other way around is cumbersome */ for (pp = p_info, i = 0; i < nproc; pp++, i++) { p = &p_proc[PROCMASK(pp->pi_pid)]; /* * AIX marks all runnable processes as ACTIVE. We want to know which * processes are sleeping, so check used cpu ticks and adjust status * field accordingly */ if (p->p_stat == SACTIVE && p->p_cpticks == 0) p->p_stat = SIDL; if (pp->pi_state && (sel->system || ((pp->pi_flags & SKPROC) == 0))) { total_procs++; process_states[p->p_stat]++; if ((pp->pi_state != SZOMB) && (sel->idle || p->p_cpticks != 0 || (p->p_stat == SACTIVE)) && (sel->uid == -1 || pp->pi_uid == (uid_t) sel->uid)) { *p_pref++ = pp; active_procs++; } } } /* * the pref array now holds pointers to the procsinfo structures in the * p_info array that were selected for display */ /* sort if requested */ if (si->p_active) qsort((char *) pref, active_procs, sizeof(struct procsinfo *), proc_compares[compare_index]); si->last_pid = -1; /* no way to figure out last used pid */ si->p_total = total_procs; si->p_active = pref_len = active_procs; handle.next_proc = pref; handle.remaining = active_procs; return ((caddr_t) & handle); } char fmt[MAX_COLS]; /* static area where result is built */ /* define what weighted cpu is. use definition of %CPU from 'man ps(1)' */ #define weighted_cpu(pp) (PROCTIME(pp) == 0 ? 0.0 : \ (((PROCTIME(pp)*100.0)/(curtime-pi->pi_start)/ncpus))) #define double_pctcpu(p) ((double)p->p_pctcpu/(double)FLT_MODULO) char * format_next_process(caddr_t handle, char *(*get_userid) ()) { register struct handle *hp; register struct procsinfo *pi; register struct proc *p; char *uname; long cpu_time; int proc_size, proc_ress; char size_unit = 'K'; char ress_unit = 'K'; hp = (struct handle *) handle; if (hp->remaining == 0) { /* safe guard */ fmt[0] = '\0'; return fmt; } pi = *(hp->next_proc++); hp->remaining--; p = &p_proc[PROCMASK(pi->pi_pid)]; cpu_time = PROCTIME(pi); /* we disply sizes up to 10M in KiloBytes, beyond 10M in MegaBytes */ if ((proc_size = (pi->pi_tsize / 1024 + pi->pi_dvm) * 4) > 10240) { proc_size /= 1024; size_unit = 'M'; } if ((proc_ress = (pi->pi_trss + pi->pi_drss) * 4) > 10240) { proc_ress /= 1024; ress_unit = 'M'; } sprintf(fmt, Proc_format, pi->pi_pid, /* PID */ (*get_userid) (pi->pi_uid), /* login name */ getpriority(PRIO_PROCESS, pi->pi_pid), EXTRACT_NICE(p), /* fixed or vari */ proc_size, /* size */ size_unit, /* K or M */ proc_ress, /* resident */ ress_unit, /* K or M */ state_abbrev[p->p_stat], /* process state */ format_time(cpu_time), /* time used */ weighted_cpu(pi), /* WCPU */ 100.0 * double_pctcpu(p), /* CPU */ printable(pi->pi_comm), /* COMM */ (pi->pi_flags & SKPROC) == 0 ? "" : " (sys)" /* kernel process? */ ); return (fmt); } /* * getkval(offset, ptr, size, refstr) - get a value out of the kernel. * "offset" is the byte offset into the kernel for the desired value, * "ptr" points to a buffer into which the value is retrieved, * "size" is the size of the buffer (and the object to retrieve), * "refstr" is a reference string used when printing error meessages, * if "refstr" starts with a '!', then a failure on read will not * be fatal (this may seem like a silly way to do things, but I * really didn't want the overhead of another argument). * */ int getkval(unsigned long offset, caddr_t ptr, int size, char *refstr) { int upper_2gb = 0; /* * reads above 2Gb are done by seeking to offset%2Gb, and supplying 1 * (opposed to 0) as fourth parameter to readx (see 'man kmem') */ if (offset > 1 << 31) { upper_2gb = 1; offset &= 0x7fffffff; } if (lseek(kmem, offset, SEEK_SET) != offset) { fprintf(stderr, "pg_top: lseek failed\n"); quit(2); } if (readx(kmem, ptr, size, upper_2gb) != size) { if (*refstr == '!') return 0; else { fprintf(stderr, "pg_top: kvm_read for %s: %s\n", refstr, sys_errlist[errno]); quit(2); } } return 1; } /* comparison routine for qsort */ /* * The following code is taken from the solaris module and adjusted * for AIX -- JV . */ #define ORDERKEY_PCTCPU \ if (lresult = p2->p_pctcpu - p1->p_pctcpu, \ (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0) #define ORDERKEY_CPTICKS \ if ((result = PROCTIME(pi2) - PROCTIME(pi1)) == 0) #define ORDERKEY_STATE \ if ((result = sorted_state[p2->p_stat] \ - sorted_state[p1->p_stat]) == 0) /* Nice values directly reflect the process' priority, and are always >0 ;-) */ #define ORDERKEY_PRIO \ if ((result = EXTRACT_NICE(p1) - EXTRACT_NICE(p2)) == 0) #define ORDERKEY_RSSIZE \ if ((result = PROCRESS(pi2) - PROCRESS(pi1)) == 0) #define ORDERKEY_MEM \ if ((result = PROCSIZE(pi2) - PROCSIZE(pi1)) == 0) static unsigned char sorted_state[] = { 0, /* not used */ 0, 0, 0, 3, /* sleep */ 1, /* zombie */ 4, /* stop */ 6, /* run */ 2, /* swap */ }; /* compare_cpu - the comparison function for sorting by cpu percentage */ int compare_cpu(struct procsinfo ** ppi1, struct procsinfo ** ppi2) { register struct procsinfo *pi1 = *ppi1, *pi2 = *ppi2; register struct proc *p1; register struct proc *p2; register int result; register long lresult; p1 = &p_proc[PROCMASK(pi1->pi_pid)]; p2 = &p_proc[PROCMASK(pi2->pi_pid)]; ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_RSSIZE ORDERKEY_MEM ; return result; } /* compare_size - the comparison function for sorting by total memory usage */ int compare_size(struct procsinfo ** ppi1, struct procsinfo ** ppi2) { register struct procsinfo *pi1 = *ppi1, *pi2 = *ppi2; register struct proc *p1; register struct proc *p2; register int result; register long lresult; p1 = &p_proc[PROCMASK(pi1->pi_pid)]; p2 = &p_proc[PROCMASK(pi2->pi_pid)]; ORDERKEY_MEM ORDERKEY_RSSIZE ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ; return result; } /* compare_res - the comparison function for sorting by resident set size */ int compare_res(struct procsinfo ** ppi1, struct procsinfo ** ppi2) { register struct procsinfo *pi1 = *ppi1, *pi2 = *ppi2; register struct proc *p1; register struct proc *p2; register int result; register long lresult; p1 = &p_proc[PROCMASK(pi1->pi_pid)]; p2 = &p_proc[PROCMASK(pi2->pi_pid)]; ORDERKEY_RSSIZE ORDERKEY_MEM ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ; return result; } /* compare_time - the comparison function for sorting by total cpu time */ int compare_time(struct procsinfo ** ppi1, struct procsinfo ** ppi2) { register struct procsinfo *pi1 = *ppi1, *pi2 = *ppi2; register struct proc *p1; register struct proc *p2; register int result; register long lresult; p1 = &p_proc[PROCMASK(pi1->pi_pid)]; p2 = &p_proc[PROCMASK(pi2->pi_pid)]; ORDERKEY_CPTICKS ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_MEM ORDERKEY_RSSIZE ; return result; } /* compare_prio - the comparison function for sorting by cpu percentage */ int compare_prio(struct procsinfo ** ppi1, struct procsinfo ** ppi2) { register struct procsinfo *pi1 = *ppi1, *pi2 = *ppi2; register struct proc *p1; register struct proc *p2; register int result; register long lresult; p1 = &p_proc[PROCMASK(pi1->pi_pid)]; p2 = &p_proc[PROCMASK(pi2->pi_pid)]; ORDERKEY_PRIO ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_RSSIZE ORDERKEY_MEM ; return result; } int proc_owner(int pid) { int uid; register struct procsinfo **prefp = pref; register int cnt = pref_len; while (--cnt >= 0) { if ((*prefp)->pi_pid == pid) return (*prefp)->pi_uid; prefp++; } return (-1); } void get_io_info(struct io_info *io_info) { /* Not supported yet */ memset(io_info, 0, sizeof(*io_info)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); }