Changeset View
Changeset View
Standalone View
Standalone View
contrib/top/m_freebsd.c
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/* | |||||
* Copyright (c) 1984 through 2008, William LeFebvre | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions are met: | |||||
* | |||||
* * Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* | |||||
* * Redistributions in binary form must reproduce the above | |||||
* copyright notice, this list of conditions and the following disclaimer | |||||
* in the documentation and/or other materials provided with the | |||||
* distribution. | |||||
* | |||||
* * Neither the name of William LeFebvre nor the names of other | |||||
* contributors may be used to endorse or promote products derived from | |||||
* this software without specific prior written permission. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
* | |||||
* $FreeBSD$ | |||||
*/ | |||||
/* | |||||
* top - a top users display for Unix | |||||
* | |||||
* SYNOPSIS: For FreeBSD 5.x, 6.x, 7.x, 8.x | |||||
* | |||||
* 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 | |||||
* Ported to FreeBSD 5.x and higher by William LeFebvre | |||||
* | |||||
* AUTHOR: Christos Zoulas <christos@ee.cornell.edu> | |||||
* Steven Wallace <swallace@freebsd.org> | |||||
* Wolfram Schneider <wosch@FreeBSD.org> | |||||
*/ | |||||
#include <sys/time.h> | |||||
#include <sys/types.h> | |||||
#include <sys/signal.h> | |||||
#include <sys/param.h> | |||||
#include "config.h" | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include <nlist.h> | |||||
#include <math.h> | |||||
#include <kvm.h> | |||||
#include <pwd.h> | |||||
#include <paths.h> | |||||
#include <sys/errno.h> | |||||
#include <sys/sysctl.h> | |||||
#include <sys/dkstat.h> | |||||
#include <sys/file.h> | |||||
#include <sys/time.h> | |||||
#include <sys/proc.h> | |||||
#include <sys/user.h> | |||||
#include <sys/vmmeter.h> | |||||
#include <sys/resource.h> | |||||
#include <sys/rtprio.h> | |||||
#ifdef HAVE_UNISTD_H | |||||
#include <unistd.h> | |||||
#endif | |||||
/* Swap */ | |||||
#include <stdlib.h> | |||||
#include <sys/conf.h> | |||||
#include <osreldate.h> /* for changes in kernel structures */ | |||||
#include "top.h" | |||||
#include "machine.h" | |||||
#include "utils.h" | |||||
#include "username.h" | |||||
#include "hash.h" | |||||
#include "display.h" | |||||
extern char* printable __P((char *)); | |||||
int swapmode __P((int *retavail, int *retfree)); | |||||
static int smpmode; | |||||
static int namelength; | |||||
/* | |||||
* Versions prior to 5.x do not track threads in kinfo_proc, so we | |||||
* simply do not display any information about them. | |||||
* Versions 5.x, 6.x, and 7.x track threads but the data reported | |||||
* as runtime for each thread is actually per-process and is just | |||||
* duplicated across all threads. It would be very wrong to show | |||||
* this data individually for each thread. Therefore we will show | |||||
* a THR column (number of threads) but not provide any sort of | |||||
* per-thread display. We distinguish between these three ways of | |||||
* handling threads as follows: HAS_THREADS indicates that the | |||||
* system has and tracks kernel threads (a THR column will appear | |||||
* in the display). HAS_SHOWTHREADS indicates that the system | |||||
* reports correct per-thread information and we will provide a | |||||
* per-thread display (the 'H' and 't' command) upon request. | |||||
* HAS_SHOWTHREADS implies HAS_THREADS. | |||||
*/ | |||||
/* HAS_THREADS for anything 5.x and up */ | |||||
#if OSMAJOR >= 5 | |||||
#define HAS_THREADS | |||||
#endif | |||||
/* HAS_SHOWTHREADS for anything 8.x and up */ | |||||
#if OSMAJOR >=8 | |||||
#define HAS_SHOWTHREADS | |||||
#endif | |||||
/* 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 */ | |||||
}; | |||||
/* declarations for load_avg */ | |||||
#include "loadavg.h" | |||||
/* | |||||
* Macros to access process information: | |||||
* In versions 4.x and earlier the kinfo_proc structure was a collection of | |||||
* substructures (kp_proc and kp_eproc). Starting with 5.0 kinfo_proc was | |||||
* redesigned and "flattene" so that most of the information was available | |||||
* in a single structure. We use macros to access the various types of | |||||
* information and define these macros according to the OS revision. The | |||||
* names PP, EP, and VP are due to the fact that information was originally | |||||
* contained in the different substructures. We retain these names in the | |||||
* code for backward compatibility. These macros use ANSI concatenation. | |||||
* PP: proc | |||||
* EP: extented proc | |||||
* VP: vm (virtual memory information) | |||||
* PRUID: Real uid | |||||
* RP: rusage | |||||
* PPCPU: where we store calculated cpu% data | |||||
* SPPTR: where we store pointer to extra calculated data | |||||
* SP: access to the extra calculated data pointed to by SPPTR | |||||
*/ | |||||
#if OSMAJOR <= 4 | |||||
#define PP(pp, field) ((pp)->kp_proc . p_##field) | |||||
#define EP(pp, field) ((pp)->kp_eproc . e_##field) | |||||
#define VP(pp, field) ((pp)->kp_eproc.e_vm . vm_##field) | |||||
#define PRUID(pp) ((pp)->kp_eproc.e_pcred.p_ruid) | |||||
#else | |||||
#define PP(pp, field) ((pp)->ki_##field) | |||||
#define EP(pp, field) ((pp)->ki_##field) | |||||
#define VP(pp, field) ((pp)->ki_##field) | |||||
#define PRUID(pp) ((pp)->ki_ruid) | |||||
#define RP(pp, field) ((pp)->ki_rusage.ru_##field) | |||||
#define PPCPU(pp) ((pp)->ki_sparelongs[0]) | |||||
#define SPPTR(pp) ((pp)->ki_spareptrs[0]) | |||||
#define SP(pp, field) (((struct save_proc *)((pp)->ki_spareptrs[0]))->sp_##field) | |||||
#endif | |||||
/* what we consider to be process size: */ | |||||
#if OSMAJOR <= 4 | |||||
#define PROCSIZE(pp) (VP((pp), map.size) / 1024) | |||||
#else | |||||
#define PROCSIZE(pp) (((pp)->ki_size) / 1024) | |||||
#endif | |||||
/* calculate a per-second rate using milliseconds */ | |||||
#define per_second(n, msec) (((n) * 1000) / (msec)) | |||||
/* 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 */ | |||||
char *state_abbrev[] = | |||||
{ | |||||
"?", "START", "RUN", "SLEEP", "STOP", "ZOMB", "WAIT", "LOCK" | |||||
}; | |||||
#define NUM_STATES 8 | |||||
/* kernel access */ | |||||
static kvm_t *kd; | |||||
/* these are for dealing with sysctl-based data */ | |||||
#define MAXMIBLEN 8 | |||||
struct sysctl_mib { | |||||
char *name; | |||||
int mib[MAXMIBLEN]; | |||||
size_t miblen; | |||||
}; | |||||
static struct sysctl_mib mibs[] = { | |||||
{ "vm.stats.sys.v_swtch" }, | |||||
#define V_SWTCH 0 | |||||
{ "vm.stats.sys.v_trap" }, | |||||
#define V_TRAP 1 | |||||
{ "vm.stats.sys.v_intr" }, | |||||
#define V_INTR 2 | |||||
{ "vm.stats.sys.v_soft" }, | |||||
#define V_SOFT 3 | |||||
{ "vm.stats.vm.v_forks" }, | |||||
#define V_FORKS 4 | |||||
{ "vm.stats.vm.v_vforks" }, | |||||
#define V_VFORKS 5 | |||||
{ "vm.stats.vm.v_rforks" }, | |||||
#define V_RFORKS 6 | |||||
{ "vm.stats.vm.v_vm_faults" }, | |||||
#define V_VM_FAULTS 7 | |||||
{ "vm.stats.vm.v_swapin" }, | |||||
#define V_SWAPIN 8 | |||||
{ "vm.stats.vm.v_swapout" }, | |||||
#define V_SWAPOUT 9 | |||||
{ "vm.stats.vm.v_tfree" }, | |||||
#define V_TFREE 10 | |||||
{ "vm.stats.vm.v_vnodein" }, | |||||
#define V_VNODEIN 11 | |||||
{ "vm.stats.vm.v_vnodeout" }, | |||||
#define V_VNODEOUT 12 | |||||
{ "vm.stats.vm.v_active_count" }, | |||||
#define V_ACTIVE_COUNT 13 | |||||
{ "vm.stats.vm.v_inactive_count" }, | |||||
#define V_INACTIVE_COUNT 14 | |||||
{ "vm.stats.vm.v_wire_count" }, | |||||
#define V_WIRE_COUNT 15 | |||||
{ "vm.stats.vm.v_cache_count" }, | |||||
#define V_CACHE_COUNT 16 | |||||
{ "vm.stats.vm.v_free_count" }, | |||||
#define V_FREE_COUNT 17 | |||||
{ "vm.stats.vm.v_swappgsin" }, | |||||
#define V_SWAPPGSIN 18 | |||||
{ "vm.stats.vm.v_swappgsout" }, | |||||
#define V_SWAPPGSOUT 19 | |||||
{ "vfs.bufspace" }, | |||||
#define VFS_BUFSPACE 20 | |||||
{ "kern.cp_time" }, | |||||
#define K_CP_TIME 21 | |||||
#ifdef HAS_SHOWTHREADS | |||||
{ "kern.proc.all" }, | |||||
#else | |||||
{ "kern.proc.proc" }, | |||||
#endif | |||||
#define K_PROC 22 | |||||
{ "kern.lastpid" }, | |||||
#define K_LASTPID 23 | |||||
/* ARC sysctls. */ | |||||
{ "kstat.zfs.misc.arcstats.size" }, | |||||
#define ZFS_ARC_SIZE 24 | |||||
{ "vfs.zfs.mfu_size" }, | |||||
#define ZFS_MFU_SIZE 25 | |||||
{ "vfs.zfs.mru_size" }, | |||||
#define ZFS_MRU_SIZE 26 | |||||
{ "vfs.zfs.anon_size" }, | |||||
#define ZFS_ANON_SIZE 27 | |||||
{ "kstat.zfs.misc.arcstats.hdr_size" }, | |||||
#define ZFS_HDR_SIZE 28 | |||||
{ "kstat.zfs.misc.arcstats.l2_hdr_size" }, | |||||
#define ZFS_L2_HDR_SIZE 29 | |||||
{ "kstat.zfs.misc.arcstats.other_size" }, | |||||
#define ZFS_OTHER_SIZE 30 | |||||
{ NULL } | |||||
}; | |||||
/* 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 */ | |||||
int process_states[8]; | |||||
char *procstatenames[] = { | |||||
"", " starting, ", " running, ", " sleeping, ", " stopped, ", " zombie, ", | |||||
" waiting, ", " locked, ", | |||||
NULL | |||||
}; | |||||
/* these are for detailing the cpu states */ | |||||
int cpu_states[CPUSTATES]; | |||||
char *cpustatenames[] = { | |||||
"user", "nice", "system", "interrupt", "idle", NULL | |||||
}; | |||||
/* these are for detailing the kernel information */ | |||||
int kernel_stats[9]; | |||||
char *kernelnames[] = { | |||||
" ctxsw, ", " trap, ", " intr, ", " soft, ", " fork, ", | |||||
" flt, ", " pgin, ", " pgout, ", " fr", | |||||
NULL | |||||
}; | |||||
/* these are for detailing the memory statistics */ | |||||
long memory_stats[7]; | |||||
char *memorynames[] = { | |||||
"K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free", | |||||
NULL | |||||
}; | |||||
static int arc_enabled; | |||||
long arc_stats[7]; | |||||
char *arcnames[] = { | |||||
"K Total, ", "K MFU, ", "K MRU, ", "K Anon, ", "K Header, ", "K Other", | |||||
NULL | |||||
}; | |||||
long swap_stats[7]; | |||||
char *swapnames[] = { | |||||
/* 0 1 2 3 4 5 */ | |||||
"K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out", | |||||
NULL | |||||
}; | |||||
/* | |||||
* pbase points to the array that holds the kinfo_proc structures. pref | |||||
* (pronounced p-ref) points to an array of kinfo_proc pointers and is where | |||||
* we build up a list of processes we wish to display. Both pbase and pref are | |||||
* potentially resized on every call to get_process_info. psize is the number | |||||
* of procs for which we currently have space allocated. pref_len is the number | |||||
* of valid pointers in pref (this is used by proc_owner). We start psize off | |||||
* at -1 to ensure that space gets allocated on the first call to | |||||
* get_process_info. | |||||
*/ | |||||
static int psize = -1; | |||||
static int pref_len; | |||||
static struct kinfo_proc *pbase = NULL; | |||||
static struct kinfo_proc **pref = NULL; | |||||
/* this structure retains information from the proc array between samples */ | |||||
struct save_proc { | |||||
pid_t sp_pid; | |||||
u_int64_t sp_runtime; | |||||
long sp_vcsw; | |||||
long sp_ivcsw; | |||||
long sp_inblock; | |||||
long sp_oublock; | |||||
long sp_majflt; | |||||
long sp_totalio; | |||||
long sp_old_nvcsw; | |||||
long sp_old_nivcsw; | |||||
long sp_old_inblock; | |||||
long sp_old_oublock; | |||||
long sp_old_majflt; | |||||
}; | |||||
hash_table *procs; | |||||
struct proc_field { | |||||
char *name; | |||||
int width; | |||||
int rjust; | |||||
int min_screenwidth; | |||||
int (*format)(char *, int, struct kinfo_proc *); | |||||
}; | |||||
/* these are for getting the memory statistics */ | |||||
static int pagesize; /* kept from getpagesize */ | |||||
static int pageshift; /* log base 2 of the pagesize */ | |||||
/* define pagetok in terms of pageshift */ | |||||
#define pagetok(size) ((size) << pageshift) | |||||
/* things that we track between updates */ | |||||
static u_int ctxsws = 0; | |||||
static u_int traps = 0; | |||||
static u_int intrs = 0; | |||||
static u_int softs = 0; | |||||
static u_int64_t forks = 0; | |||||
static u_int pfaults; | |||||
static u_int pagein; | |||||
static u_int pageout; | |||||
static u_int tfreed; | |||||
static int swappgsin = -1; | |||||
static int swappgsout = -1; | |||||
extern struct timeval timeout; | |||||
static struct timeval lasttime = { 0, 0 }; | |||||
static long elapsed_time; | |||||
static long elapsed_msecs; | |||||
static int lastpid; | |||||
/* things that we track during an update */ | |||||
static long total_io; | |||||
static int show_fullcmd; | |||||
static struct handle handle; | |||||
static int username_length; | |||||
static int show_usernames; | |||||
static int display_mode; | |||||
static int *display_fields; | |||||
#ifdef HAS_SHOWTHREADS | |||||
static int show_threads = 0; | |||||
#endif | |||||
/* sorting orders. first is default */ | |||||
char *ordernames[] = { | |||||
"cpu", "size", "res", "time", "pri", "io", "pid", NULL | |||||
}; | |||||
/* compare routines */ | |||||
int proc_compare(), compare_size(), compare_res(), compare_time(), | |||||
compare_prio(), compare_io(), compare_pid(); | |||||
int (*proc_compares[])() = { | |||||
proc_compare, | |||||
compare_size, | |||||
compare_res, | |||||
compare_time, | |||||
compare_prio, | |||||
compare_io, | |||||
compare_pid, | |||||
NULL | |||||
}; | |||||
/* swap related calculations */ | |||||
static int mib_swapinfo[16]; | |||||
static int *mib_swapinfo_idx; | |||||
static int mib_swapinfo_size = 0; | |||||
void | |||||
swap_init() | |||||
{ | |||||
size_t m; | |||||
m = sizeof(mib_swapinfo) / sizeof(mib_swapinfo[0]); | |||||
if (sysctlnametomib("vm.swap_info", mib_swapinfo, &m) != -1) | |||||
{ | |||||
mib_swapinfo_size = m + 1; | |||||
mib_swapinfo_idx = &(mib_swapinfo[m]); | |||||
} | |||||
} | |||||
int | |||||
swap_getdata(long long *retavail, long long *retfree) | |||||
{ | |||||
int n; | |||||
size_t size; | |||||
long long total = 0; | |||||
long long used = 0; | |||||
struct xswdev xsw; | |||||
n = 0; | |||||
if (mib_swapinfo_size > 0) | |||||
{ | |||||
*mib_swapinfo_idx = 0; | |||||
while (size = sizeof(xsw), | |||||
sysctl(mib_swapinfo, mib_swapinfo_size, &xsw, &size, NULL, 0) != -1) | |||||
{ | |||||
dprintf("swap_getdata: swaparea %d: nblks %d, used %d\n", | |||||
n, xsw.xsw_nblks, xsw.xsw_used); | |||||
total += (long long)xsw.xsw_nblks; | |||||
used += (long long)xsw.xsw_used; | |||||
*mib_swapinfo_idx = ++n; | |||||
} | |||||
*retavail = pagetok(total); | |||||
*retfree = pagetok(total) - pagetok(used); | |||||
if (total > 0) | |||||
{ | |||||
n = (int)((double)used * 100.0 / (double)total); | |||||
} | |||||
else | |||||
{ | |||||
n = 0; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
*retavail = 0; | |||||
*retfree = 0; | |||||
} | |||||
dprintf("swap_getdata: avail %lld, free %lld, %d%%\n", | |||||
*retavail, *retfree, n); | |||||
return(n); | |||||
} | |||||
/* | |||||
* getkval(offset, ptr, size) - 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). | |||||
* Return 0 on success, -1 on any kind of failure. | |||||
*/ | |||||
static int | |||||
getkval(unsigned long offset, int *ptr, int size) | |||||
{ | |||||
if (kd != NULL) | |||||
{ | |||||
if (kvm_read(kd, offset, (char *) ptr, size) == size) | |||||
{ | |||||
return(0); | |||||
} | |||||
} | |||||
return(-1); | |||||
} | |||||
int | |||||
get_sysctl_mibs() | |||||
{ | |||||
struct sysctl_mib *mp; | |||||
size_t len; | |||||
mp = mibs; | |||||
while (mp->name != NULL) | |||||
{ | |||||
len = MAXMIBLEN; | |||||
if (sysctlnametomib(mp->name, mp->mib, &len) == -1) | |||||
{ | |||||
message_error(" sysctlnametomib: %s", strerror(errno)); | |||||
return -1; | |||||
} | |||||
mp->miblen = len; | |||||
mp++; | |||||
} | |||||
return 0; | |||||
} | |||||
int | |||||
get_sysctl(int idx, void *v, size_t l) | |||||
{ | |||||
struct sysctl_mib *m; | |||||
size_t len; | |||||
m = &(mibs[idx]); | |||||
len = l; | |||||
if (sysctl(m->mib, m->miblen, v, &len, NULL, 0) == -1) | |||||
{ | |||||
message_error(" sysctl: %s", strerror(errno)); | |||||
return -1; | |||||
} | |||||
return len; | |||||
} | |||||
size_t | |||||
get_sysctlsize(int idx) | |||||
{ | |||||
size_t len; | |||||
struct sysctl_mib *m; | |||||
m = &(mibs[idx]); | |||||
if (sysctl(m->mib, m->miblen, NULL, &len, NULL, 0) == -1) | |||||
{ | |||||
message_error(" sysctl (size): %s", strerror(errno)); | |||||
len = 0; | |||||
} | |||||
return len; | |||||
} | |||||
int | |||||
fmt_pid(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%6d", PP(pp, pid)); | |||||
} | |||||
int | |||||
fmt_jid(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%6d", PP(pp, jid)); | |||||
} | |||||
int | |||||
fmt_username(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%-*.*s", | |||||
username_length, username_length, username(PRUID(pp))); | |||||
} | |||||
int | |||||
fmt_uid(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%6d", PRUID(pp)); | |||||
} | |||||
int | |||||
fmt_thr(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%3d", PP(pp, numthreads)); | |||||
} | |||||
int | |||||
fmt_pri(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
#if OSMAJOR <= 4 | |||||
return snprintf(buf, sz, "%3d", PP(pp, priority)); | |||||
#else | |||||
return snprintf(buf, sz, "%3d", PP(pp, pri.pri_level)); | |||||
#endif | |||||
} | |||||
int | |||||
fmt_nice(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%4d", PP(pp, nice) - NZERO); | |||||
} | |||||
int | |||||
fmt_size(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%5s", format_k(PROCSIZE(pp))); | |||||
} | |||||
int | |||||
fmt_res(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%5s", format_k(pagetok(VP(pp, rssize)))); | |||||
} | |||||
int | |||||
fmt_state(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
int state; | |||||
char status[16]; | |||||
state = PP(pp, stat); | |||||
switch(state) | |||||
{ | |||||
case SRUN: | |||||
if (smpmode && PP(pp, oncpu) != 0xff) | |||||
sprintf(status, "CPU%d", PP(pp, oncpu)); | |||||
else | |||||
strcpy(status, "RUN"); | |||||
break; | |||||
case SSLEEP: | |||||
if (EP(pp, wmesg) != NULL) { | |||||
sprintf(status, "%.6s", EP(pp, wmesg)); | |||||
break; | |||||
} | |||||
/* fall through */ | |||||
default: | |||||
if (state >= 0 && state < NUM_STATES) | |||||
sprintf(status, "%.6s", state_abbrev[(unsigned char) state]); | |||||
else | |||||
sprintf(status, "?%-5d", state); | |||||
break; | |||||
} | |||||
return snprintf(buf, sz, "%-6.6s", status); | |||||
} | |||||
int | |||||
fmt_flags(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
long flag; | |||||
char chrs[12]; | |||||
char *p; | |||||
flag = PP(pp, flag); | |||||
p = chrs; | |||||
if (PP(pp, nice) < NZERO) | |||||
*p++ = '<'; | |||||
else if (PP(pp, nice) > NZERO) | |||||
*p++ = 'N'; | |||||
if (flag & P_TRACED) | |||||
*p++ = 'X'; | |||||
if (flag & P_WEXIT && PP(pp, stat) != SZOMB) | |||||
*p++ = 'E'; | |||||
if (flag & P_PPWAIT) | |||||
*p++ = 'V'; | |||||
if (flag & P_SYSTEM || PP(pp, lock) > 0) | |||||
*p++ = 'L'; | |||||
if (PP(pp, kiflag) & KI_SLEADER) | |||||
*p++ = 's'; | |||||
if (flag & P_CONTROLT) | |||||
*p++ = '+'; | |||||
if (flag & P_JAILED) | |||||
*p++ = 'J'; | |||||
*p = '\0'; | |||||
return snprintf(buf, sz, "%-3.3s", chrs); | |||||
} | |||||
int | |||||
fmt_c(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%1x", PP(pp, lastcpu)); | |||||
} | |||||
int | |||||
fmt_time(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%6s", | |||||
format_time((PP(pp, runtime) + 500000) / 1000000)); | |||||
} | |||||
int | |||||
fmt_cpu(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%5.2f%%", (double)PPCPU(pp) / 100.0); | |||||
} | |||||
int | |||||
fmt_command(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
int inmem; | |||||
char cmd[MAX_COLS]; | |||||
char *bufp; | |||||
struct pargs pargs; | |||||
int len; | |||||
#if OSMAJOR <= 4 | |||||
inmem = (PP(pp, flag) & P_INMEM); | |||||
#else | |||||
inmem = (PP(pp, sflag) & PS_INMEM); | |||||
#endif | |||||
if (show_fullcmd && inmem) | |||||
{ | |||||
/* get the pargs structure */ | |||||
if (getkval((unsigned long)PP(pp, args), (int *)&pargs, sizeof(pargs)) != -1) | |||||
{ | |||||
/* determine workable length */ | |||||
if ((len = pargs.ar_length) >= MAX_COLS) | |||||
{ | |||||
len = MAX_COLS - 1; | |||||
} | |||||
/* get the string from that */ | |||||
if (len > 0 && getkval((unsigned long)PP(pp, args) + | |||||
sizeof(pargs.ar_ref) + | |||||
sizeof(pargs.ar_length), | |||||
(int *)cmd, len) != -1) | |||||
{ | |||||
/* successfull retrieval: now convert nulls in to spaces */ | |||||
bufp = cmd; | |||||
while (len-- > 0) | |||||
{ | |||||
if (*bufp == '\0') | |||||
{ | |||||
*bufp = ' '; | |||||
} | |||||
bufp++; | |||||
} | |||||
/* null terminate cmd */ | |||||
*--bufp = '\0'; | |||||
/* format cmd as our answer */ | |||||
return snprintf(buf, sz, "%s", cmd); | |||||
} | |||||
} | |||||
} | |||||
/* Show the thread name in {}. */ | |||||
if (show_threads) | |||||
return snprintf(buf, sz, inmem ? "%s{%s}" : "<%s{%s}>", | |||||
printable(PP(pp, comm)), printable(PP(pp,tdname))); | |||||
/* for anything else we just display comm */ | |||||
else | |||||
return snprintf(buf, sz, inmem ? "%s" : "<%s>", printable(PP(pp, comm))); | |||||
} | |||||
int | |||||
fmt_vcsw(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%6ld", per_second(SP(pp, vcsw), elapsed_msecs)); | |||||
} | |||||
int | |||||
fmt_ivcsw(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%6ld", per_second(SP(pp, ivcsw), elapsed_msecs)); | |||||
} | |||||
int | |||||
fmt_read(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%6ld", per_second(SP(pp, inblock), elapsed_msecs)); | |||||
} | |||||
int | |||||
fmt_write(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%6ld", per_second(SP(pp, oublock), elapsed_msecs)); | |||||
} | |||||
int | |||||
fmt_fault(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%6ld", per_second(SP(pp, majflt), elapsed_msecs)); | |||||
} | |||||
int | |||||
fmt_iototal(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%6ld", per_second(SP(pp, totalio), elapsed_msecs)); | |||||
} | |||||
int | |||||
fmt_iopct(char *buf, int sz, struct kinfo_proc *pp) | |||||
{ | |||||
return snprintf(buf, sz, "%6.2f", (SP(pp, totalio) * 100.) / total_io); | |||||
} | |||||
struct proc_field proc_field[] = { | |||||
{ "PID", 6, 1, 0, fmt_pid }, | |||||
{ "JID", 6, 1, 0, fmt_jid }, | |||||
{ "USERNAME", 8, 0, 0, fmt_username }, | |||||
#define FIELD_USERNAME 2 | |||||
{ "UID", 6, 1, 0, fmt_uid }, | |||||
#define FIELD_UID 3 | |||||
{ "THR", 3, 1, 0, fmt_thr }, | |||||
{ "PRI", 3, 1, 0, fmt_pri }, | |||||
{ "NICE", 4, 1, 0, fmt_nice }, | |||||
{ "SIZE", 5, 1, 0, fmt_size }, | |||||
{ "RES", 5, 1, 0, fmt_res }, | |||||
{ "STATE", 6, 0, 0, fmt_state }, | |||||
{ "FLG", 3, 0, 84, fmt_flags }, | |||||
{ "C", 1, 0, 0, fmt_c }, | |||||
{ "TIME", 6, 1, 0, fmt_time }, | |||||
{ "CPU", 6, 1, 0, fmt_cpu }, | |||||
{ "COMMAND", 7, 0, 0, fmt_command }, | |||||
{ "VCSW", 6, 1, 0, fmt_vcsw }, | |||||
{ "IVCSW", 6, 1, 0, fmt_ivcsw }, | |||||
{ "READ", 6, 1, 0, fmt_read }, | |||||
{ "WRITE", 6, 1, 0, fmt_write }, | |||||
{ "FAULT", 6, 1, 0, fmt_fault }, | |||||
{ "TOTAL", 6, 1, 0, fmt_iototal }, | |||||
{ "PERCENT", 7, 1, 0, fmt_iopct }, | |||||
{ NULL, 0, 0, 0, NULL } | |||||
}; | |||||
#define MAX_FIELDS 25 | |||||
static int mode0_display[MAX_FIELDS]; | |||||
static int mode0thr_display[MAX_FIELDS]; | |||||
static int mode1_display[MAX_FIELDS]; | |||||
int | |||||
field_index(char *col) | |||||
{ | |||||
struct proc_field *fp; | |||||
int i = 0; | |||||
fp = proc_field; | |||||
while (fp->name != NULL) | |||||
{ | |||||
if (strcmp(col, fp->name) == 0) | |||||
{ | |||||
return i; | |||||
} | |||||
fp++; | |||||
i++; | |||||
} | |||||
return -1; | |||||
} | |||||
void | |||||
field_subst(int *fp, int old, int new) | |||||
{ | |||||
while (*fp != -1) | |||||
{ | |||||
if (*fp == old) | |||||
{ | |||||
*fp = new; | |||||
} | |||||
fp++; | |||||
} | |||||
} | |||||
int | |||||
machine_init(struct statics *statics) | |||||
{ | |||||
int i = 0; | |||||
size_t len; | |||||
struct timeval boottime; | |||||
len = sizeof(smpmode); | |||||
if ((sysctlbyname("machdep.smp_active", &smpmode, &len, NULL, 0) < 0 && | |||||
sysctlbyname("kern.smp.active", &smpmode, &len, NULL, 0) < 0) || | |||||
len != sizeof(smpmode)) | |||||
{ | |||||
smpmode = 0; | |||||
} | |||||
smpmode = smpmode != 0; | |||||
/* kvm_open the active kernel: its okay if this fails */ | |||||
/* Second arg must be /dev/null to allow normal users. */ | |||||
kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL); | |||||
/* get boot time */ | |||||
len = sizeof(boottime); | |||||
if (sysctlbyname("kern.boottime", &boottime, &len, NULL, 0) == -1) | |||||
{ | |||||
/* we have no boottime to report */ | |||||
boottime.tv_sec = -1; | |||||
} | |||||
pbase = NULL; | |||||
pref = NULL; | |||||
/* get the page size with "getpagesize" and calculate pageshift from it */ | |||||
i = pagesize = getpagesize(); | |||||
pageshift = 0; | |||||
while (i > 1) | |||||
{ | |||||
pageshift++; | |||||
i >>= 1; | |||||
} | |||||
/* translate sysctl paths to mibs for faster access */ | |||||
get_sysctl_mibs(); | |||||
/* initialize swap stuff */ | |||||
swap_init(); | |||||
/* create the hash table that remembers proc data */ | |||||
procs = hash_create(2039); | |||||
/* 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; | |||||
statics->kernel_names = kernelnames; | |||||
statics->boottime = boottime.tv_sec; | |||||
statics->swap_names = swapnames; | |||||
statics->order_names = ordernames; | |||||
statics->flags.warmup = 1; | |||||
statics->modemax = 2; | |||||
#ifdef HAS_SHOWTHREADS | |||||
statics->flags.threads = 1; | |||||
#endif | |||||
/* Determine whether the system is running with ZFS. */ | |||||
uint64_t arc_size; | |||||
size_t size = sizeof(arc_size); | |||||
if (get_sysctl(ZFS_ARC_SIZE, &arc_size, sizeof(arc_size)) > 0 | |||||
&& arc_size != 0) | |||||
{ | |||||
statics->arc_names = arcnames; | |||||
arc_enabled = 1; | |||||
} | |||||
else | |||||
{ | |||||
statics->arc_names = NULL; | |||||
arc_enabled = 0; | |||||
} | |||||
/* we need kvm descriptor in order to show full commands */ | |||||
statics->flags.fullcmds = kd != NULL; | |||||
calculate_header_columns(statics); | |||||
/* all done! */ | |||||
return(0); | |||||
} | |||||
/* Recalculate which columns are used in each mode. This was moved out of | |||||
* machine_init because some commands need to change which columns are | |||||
* displayed and I don't want to create three additional modeN_display vars. | |||||
*/ | |||||
void | |||||
calculate_header_columns(struct statics *statics) | |||||
{ | |||||
int *ip; | |||||
/* set up the display indices for mode0 */ | |||||
ip = mode0_display; | |||||
*ip++ = field_index("PID"); | |||||
if (statics->flags.jids) | |||||
*ip++ = field_index("JID"); | |||||
*ip++ = field_index("USERNAME"); | |||||
#ifdef HAS_THREADS | |||||
*ip++ = field_index("THR"); | |||||
#endif | |||||
*ip++ = field_index("PRI"); | |||||
*ip++ = field_index("NICE"); | |||||
*ip++ = field_index("SIZE"); | |||||
*ip++ = field_index("RES"); | |||||
*ip++ = field_index("STATE"); | |||||
*ip++ = field_index("FLG"); | |||||
if (smpmode) | |||||
*ip++ = field_index("C"); | |||||
*ip++ = field_index("TIME"); | |||||
*ip++ = field_index("CPU"); | |||||
*ip++ = field_index("COMMAND"); | |||||
*ip = -1; | |||||
#ifdef HAS_SHOWTHREADS | |||||
/* set up the display indices for mode0 showing threads */ | |||||
ip = mode0thr_display; | |||||
*ip++ = field_index("PID"); | |||||
if (statics->flags.jids) | |||||
*ip++ = field_index("JID"); | |||||
*ip++ = field_index("USERNAME"); | |||||
*ip++ = field_index("PRI"); | |||||
*ip++ = field_index("NICE"); | |||||
*ip++ = field_index("SIZE"); | |||||
*ip++ = field_index("RES"); | |||||
*ip++ = field_index("STATE"); | |||||
*ip++ = field_index("FLG"); | |||||
if (smpmode) | |||||
*ip++ = field_index("C"); | |||||
*ip++ = field_index("TIME"); | |||||
*ip++ = field_index("CPU"); | |||||
*ip++ = field_index("COMMAND"); | |||||
*ip = -1; | |||||
#endif | |||||
/* set up the display indices for mode1 */ | |||||
ip = mode1_display; | |||||
*ip++ = field_index("PID"); | |||||
if (statics->flags.jids) | |||||
*ip++ = field_index("JID"); | |||||
*ip++ = field_index("USERNAME"); | |||||
*ip++ = field_index("VCSW"); | |||||
*ip++ = field_index("IVCSW"); | |||||
*ip++ = field_index("READ"); | |||||
*ip++ = field_index("WRITE"); | |||||
*ip++ = field_index("FAULT"); | |||||
*ip++ = field_index("TOTAL"); | |||||
*ip++ = field_index("PERCENT"); | |||||
*ip++ = field_index("COMMAND"); | |||||
*ip = -1; | |||||
} | |||||
char *format_header(char *uname_field) | |||||
{ | |||||
return ""; | |||||
} | |||||
void | |||||
get_vm_sum(struct vmmeter *sum) | |||||
{ | |||||
#define GET_VM_STAT(v, s) (void)get_sysctl(v, &(sum->s), sizeof(sum->s)) | |||||
GET_VM_STAT(V_SWTCH, v_swtch); | |||||
GET_VM_STAT(V_TRAP, v_trap); | |||||
GET_VM_STAT(V_INTR, v_intr); | |||||
GET_VM_STAT(V_SOFT, v_soft); | |||||
GET_VM_STAT(V_VFORKS, v_vforks); | |||||
GET_VM_STAT(V_FORKS, v_forks); | |||||
GET_VM_STAT(V_RFORKS, v_rforks); | |||||
GET_VM_STAT(V_VM_FAULTS, v_vm_faults); | |||||
GET_VM_STAT(V_SWAPIN, v_swapin); | |||||
GET_VM_STAT(V_SWAPOUT, v_swapout); | |||||
GET_VM_STAT(V_TFREE, v_tfree); | |||||
GET_VM_STAT(V_VNODEIN, v_vnodein); | |||||
GET_VM_STAT(V_VNODEOUT, v_vnodeout); | |||||
GET_VM_STAT(V_ACTIVE_COUNT, v_active_count); | |||||
GET_VM_STAT(V_INACTIVE_COUNT, v_inactive_count); | |||||
GET_VM_STAT(V_WIRE_COUNT, v_wire_count); | |||||
GET_VM_STAT(V_CACHE_COUNT, v_cache_count); | |||||
GET_VM_STAT(V_FREE_COUNT, v_free_count); | |||||
GET_VM_STAT(V_SWAPPGSIN, v_swappgsin); | |||||
GET_VM_STAT(V_SWAPPGSOUT, v_swappgsout); | |||||
} | |||||
void | |||||
get_system_info(struct system_info *si) | |||||
{ | |||||
long total; | |||||
struct timeval thistime; | |||||
struct timeval timediff; | |||||
/* timestamp and time difference */ | |||||
gettimeofday(&thistime, 0); | |||||
timersub(&thistime, &lasttime, &timediff); | |||||
elapsed_time = timediff.tv_sec * 1000000 + timediff.tv_usec; | |||||
elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000; | |||||
/* get the load averages */ | |||||
if (getloadavg(si->load_avg, NUM_AVERAGES) == -1) | |||||
{ | |||||
/* failed: fill in with zeroes */ | |||||
(void) memset(si->load_avg, 0, sizeof(si->load_avg)); | |||||
} | |||||
/* get the cp_time array */ | |||||
(void)get_sysctl(K_CP_TIME, &cp_time, sizeof(cp_time)); | |||||
/* convert cp_time counts to percentages */ | |||||
total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); | |||||
/* sum memory & swap statistics */ | |||||
{ | |||||
struct vmmeter sum; | |||||
static unsigned int swap_delay = 0; | |||||
static long long swapavail = 0; | |||||
static long long swapfree = 0; | |||||
static int bufspace = 0; | |||||
get_vm_sum(&sum); | |||||
/* get bufspace */ | |||||
bufspace = 0; | |||||
(void) get_sysctl(VFS_BUFSPACE, &bufspace, sizeof(bufspace)); | |||||
/* kernel stats */ | |||||
dprintf("kernel: swtch %d, trap %d, intr %d, soft %d, vforks %d\n", | |||||
sum.v_swtch, sum.v_trap, sum.v_intr, sum.v_soft, sum.v_vforks); | |||||
kernel_stats[0] = per_second(sum.v_swtch - ctxsws, elapsed_msecs); | |||||
kernel_stats[1] = per_second(sum.v_trap - traps, elapsed_msecs); | |||||
kernel_stats[2] = per_second(sum.v_intr - intrs, elapsed_msecs); | |||||
kernel_stats[3] = per_second(sum.v_soft - softs, elapsed_msecs); | |||||
kernel_stats[4] = per_second(sum.v_vforks + sum.v_forks + | |||||
sum.v_rforks - forks, elapsed_msecs); | |||||
kernel_stats[5] = per_second(sum.v_vm_faults - pfaults, elapsed_msecs); | |||||
kernel_stats[6] = per_second(sum.v_swapin + sum.v_vnodein - pagein, elapsed_msecs); | |||||
kernel_stats[7] = per_second(sum.v_swapout + sum.v_vnodeout - pageout, elapsed_msecs); | |||||
kernel_stats[8] = per_second(sum.v_tfree - tfreed, elapsed_msecs); | |||||
ctxsws = sum.v_swtch; | |||||
traps = sum.v_trap; | |||||
intrs = sum.v_intr; | |||||
softs = sum.v_soft; | |||||
forks = (u_int64_t)sum.v_vforks + sum.v_forks + sum.v_rforks; | |||||
pfaults = sum.v_vm_faults; | |||||
pagein = sum.v_swapin + sum.v_vnodein; | |||||
pageout = sum.v_swapout + sum.v_vnodeout; | |||||
tfreed = sum.v_tfree; | |||||
/* convert memory stats to Kbytes */ | |||||
memory_stats[0] = pagetok(sum.v_active_count); | |||||
memory_stats[1] = pagetok(sum.v_inactive_count); | |||||
memory_stats[2] = pagetok(sum.v_wire_count); | |||||
memory_stats[3] = pagetok(sum.v_cache_count); | |||||
memory_stats[4] = bufspace / 1024; | |||||
memory_stats[5] = pagetok(sum.v_free_count); | |||||
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(sum.v_swappgsin - swappgsin); | |||||
swap_stats[5] = pagetok(sum.v_swappgsout - swappgsout); | |||||
} | |||||
swappgsin = sum.v_swappgsin; | |||||
swappgsout = sum.v_swappgsout; | |||||
/* call CPU heavy swap_getdata() only for changes */ | |||||
if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) | |||||
{ | |||||
swap_stats[3] = swap_getdata(&swapavail, &swapfree); | |||||
swap_stats[0] = swapavail; | |||||
swap_stats[1] = swapavail - swapfree; | |||||
swap_stats[2] = swapfree; | |||||
} | |||||
swap_delay = 1; | |||||
swap_stats[6] = -1; | |||||
} | |||||
/* If we're running with ZFS, get ARC stats. */ | |||||
if (arc_enabled) | |||||
{ | |||||
uint64_t arc_stat, arc_stat2; | |||||
get_sysctl(ZFS_ARC_SIZE, &arc_stat, sizeof(arc_stat)); | |||||
arc_stats[0] = arc_stat >> 10; | |||||
get_sysctl(ZFS_MFU_SIZE, &arc_stat, sizeof(arc_stat)); | |||||
arc_stats[1] = arc_stat >> 10; | |||||
get_sysctl(ZFS_MRU_SIZE, &arc_stat, sizeof(arc_stat)); | |||||
arc_stats[2] = arc_stat >> 10; | |||||
get_sysctl(ZFS_ANON_SIZE, &arc_stat, sizeof(arc_stat)); | |||||
arc_stats[3] = arc_stat >> 10; | |||||
get_sysctl(ZFS_HDR_SIZE, &arc_stat, sizeof(arc_stat)); | |||||
get_sysctl(ZFS_L2_HDR_SIZE, &arc_stat2, sizeof(arc_stat2)); | |||||
arc_stats[4] = arc_stat + arc_stat2 >> 10; | |||||
get_sysctl(ZFS_OTHER_SIZE, &arc_stat, sizeof(arc_stat)); | |||||
arc_stats[5] = arc_stat >> 10; | |||||
si->arc = arc_stats; | |||||
} | |||||
/* set arrays and strings */ | |||||
si->cpustates = cpu_states; | |||||
si->kernel = kernel_stats; | |||||
si->memory = memory_stats; | |||||
si->swap = swap_stats; | |||||
/* Get the kern.lastpid sysctl. */ | |||||
if (get_sysctl(K_LASTPID, &lastpid, sizeof(lastpid)) != -1) | |||||
si->last_pid = lastpid; | |||||
else | |||||
si->last_pid = -1; | |||||
lasttime = thistime; | |||||
} | |||||
caddr_t | |||||
get_process_info(struct system_info *si, | |||||
struct process_select *sel, | |||||
int compare_index) | |||||
{ | |||||
int i; | |||||
int total_procs; | |||||
int active_procs; | |||||
struct kinfo_proc **prefp; | |||||
struct kinfo_proc *pp; | |||||
struct kinfo_proc *prev_pp = NULL; | |||||
struct save_proc *savep; | |||||
long proc_io; | |||||
pid_t pid; | |||||
size_t size; | |||||
int nproc; | |||||
/* these are copied out of sel for speed */ | |||||
int show_idle; | |||||
int show_self; | |||||
int show_system; | |||||
int show_uid; | |||||
char *show_command; | |||||
/* get proc table size and give it a boost */ | |||||
nproc = (int)get_sysctlsize(K_PROC) / sizeof(struct kinfo_proc); | |||||
nproc += nproc >> 4; | |||||
size = nproc * sizeof(struct kinfo_proc); | |||||
dprintf("get_process_info: nproc %d, psize %d, size %d\n", nproc, psize, size); | |||||
/* make sure we have enough space allocated */ | |||||
if (nproc > psize) | |||||
{ | |||||
/* reallocate both pbase and pref */ | |||||
pbase = (struct kinfo_proc *)realloc(pbase, size); | |||||
pref = (struct kinfo_proc **)realloc(pref, | |||||
sizeof(struct kinfo_proc *) * nproc); | |||||
psize = nproc; | |||||
} | |||||
/* make sure we got the space we asked for */ | |||||
if (pref == NULL || pbase == NULL) | |||||
{ | |||||
/* abandon all hope */ | |||||
message_error(" Out of memory!"); | |||||
nproc = psize = 0; | |||||
si->p_total = 0; | |||||
si->p_active = 0; | |||||
return NULL; | |||||
} | |||||
/* get all process information (threads, too) */ | |||||
if (size > 0) | |||||
{ | |||||
nproc = get_sysctl(K_PROC, pbase, size); | |||||
if (nproc == -1) | |||||
{ | |||||
nproc = 0; | |||||
} | |||||
else | |||||
{ | |||||
nproc /= sizeof(struct kinfo_proc); | |||||
} | |||||
} | |||||
/* get a pointer to the states summary array */ | |||||
si->procstates = process_states; | |||||
/* set up flags which define what we are going to select */ | |||||
show_idle = sel->idle; | |||||
show_self = 0; | |||||
show_system = sel->system; | |||||
show_uid = sel->uid != -1; | |||||
show_fullcmd = sel->fullcmd; | |||||
show_command = sel->command; | |||||
show_usernames = sel->usernames; | |||||
display_mode = sel->mode; | |||||
#ifdef HAS_SHOWTHREADS | |||||
show_threads = sel->threads; | |||||
#endif | |||||
/* count up process states and get pointers to interesting procs */ | |||||
total_procs = 0; | |||||
active_procs = 0; | |||||
total_io = 0; | |||||
memset((char *)process_states, 0, sizeof(process_states)); | |||||
prefp = pref; | |||||
for (pp = pbase, i = 0; i < nproc; pp++, i++) | |||||
{ | |||||
/* | |||||
* Place pointers to each valid proc structure in pref[]. | |||||
* Process slots that are actually in use have a non-zero | |||||
* status field. Processes with P_SYSTEM set are system | |||||
* processes---these get ignored unless show_sysprocs is set. | |||||
*/ | |||||
pid = PP(pp, pid); | |||||
if (PP(pp, stat) != 0) | |||||
{ | |||||
#ifdef HAS_SHOWTHREADS | |||||
int is_thread; | |||||
lwpid_t tid; | |||||
/* get thread id */ | |||||
tid = PP(pp, tid); | |||||
/* is this just a thread? */ | |||||
is_thread = (prev_pp != NULL && PP(prev_pp, pid) == pid); | |||||
/* count this process and its state */ | |||||
/* only count threads if we are showing them */ | |||||
if (show_threads || !is_thread) | |||||
{ | |||||
total_procs++; | |||||
process_states[(unsigned char) PP(pp, stat)]++; | |||||
} | |||||
/* grab old data from hash */ | |||||
if ((savep = hash_lookup_lwpid(procs, tid)) != NULL) | |||||
{ | |||||
/* verify that this is not a new or different thread */ | |||||
/* (freebsd reuses thread ids fairly quickly) */ | |||||
/* pids must match and time can't have gone backwards */ | |||||
if (pid != savep->sp_pid || PP(pp, runtime) < savep->sp_runtime) | |||||
{ | |||||
/* not the same thread -- reuse the save_proc structure */ | |||||
memset(savep, 0, sizeof(struct save_proc)); | |||||
savep->sp_pid = pid; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
/* havent seen this one before */ | |||||
savep = (struct save_proc *)calloc(1, sizeof(struct save_proc)); | |||||
savep->sp_pid = pid; | |||||
hash_add_lwpid(procs, tid, savep); | |||||
} | |||||
#else /* !HAS_SHOWTHREADS */ | |||||
total_procs++; | |||||
process_states[(unsigned char) PP(pp, stat)]++; | |||||
/* grab old data from hash */ | |||||
if ((savep = hash_lookup_pid(procs, pid)) == NULL) | |||||
{ | |||||
/* havent seen this one before */ | |||||
savep = (struct save_proc *)calloc(1, sizeof(struct save_proc)); | |||||
savep->sp_pid = pid; | |||||
hash_add_pid(procs, pid, savep); | |||||
} | |||||
#endif | |||||
/* save the pointer to the sp struct */ | |||||
SPPTR(pp) = (void *)savep; | |||||
/* calculate %cpu */ | |||||
PPCPU(pp) = ((PP(pp, runtime) - savep->sp_runtime) * 10000) / | |||||
elapsed_time; | |||||
dprintf("%d (%d): runtime %lld, saved_pid %d, saved_runtime %lld, elapsed_time %d, ppcpu %d\n", | |||||
pid, PP(pp, tid), PP(pp, runtime), savep->sp_pid, savep->sp_runtime, | |||||
elapsed_time, PPCPU(pp)); | |||||
/* calculate io differences */ | |||||
proc_io = 0; | |||||
savep->sp_vcsw = (RP(pp, nvcsw) - savep->sp_old_nvcsw); | |||||
savep->sp_ivcsw = (RP(pp, nivcsw) - savep->sp_old_nivcsw); | |||||
proc_io += (savep->sp_inblock = (RP(pp, inblock) - savep->sp_old_inblock)); | |||||
proc_io += (savep->sp_oublock = (RP(pp, oublock) - savep->sp_old_oublock)); | |||||
proc_io += (savep->sp_majflt = (RP(pp, majflt) - savep->sp_old_majflt)); | |||||
total_io += proc_io; | |||||
savep->sp_totalio = proc_io; | |||||
/* save data for next time */ | |||||
savep->sp_runtime = PP(pp, runtime); | |||||
savep->sp_old_nvcsw = RP(pp, nvcsw); | |||||
savep->sp_old_nivcsw = RP(pp, nivcsw); | |||||
savep->sp_old_inblock = RP(pp, inblock); | |||||
savep->sp_old_oublock = RP(pp, oublock); | |||||
savep->sp_old_majflt = RP(pp, majflt); | |||||
/* is this one selected for viewing? */ | |||||
if ((PP(pp, stat) != SZOMB) && | |||||
(show_system || ((PP(pp, flag) & P_SYSTEM) == 0)) && | |||||
(show_idle || (PP(pp, pctcpu) != 0) || | |||||
(PP(pp, stat) == SRUN)) && | |||||
(!show_uid || PRUID(pp) == (uid_t)sel->uid) && | |||||
(show_command == NULL || | |||||
strcasestr(PP(pp, comm), show_command) != NULL) && | |||||
(sel->filter_jail == 0 || sel->jail == PP(pp, jid))) | |||||
{ | |||||
#ifdef HAS_SHOWTHREADS | |||||
/* yes, but make sure it isn't just a thread */ | |||||
if (show_threads || !is_thread) | |||||
{ | |||||
/* we will be showing this thread */ | |||||
*prefp++ = pp; | |||||
active_procs++; | |||||
} | |||||
else | |||||
{ | |||||
/* we will not be showing this thread, but we need to roll | |||||
up its cpu usage in to its process */ | |||||
PP(prev_pp, pctcpu) += PP(pp, pctcpu); | |||||
} | |||||
#else /* !HAS_SHOWTHREADS */ | |||||
/* we will be showing this process */ | |||||
*prefp++ = pp; | |||||
active_procs++; | |||||
#endif | |||||
} | |||||
prev_pp = pp; | |||||
} | |||||
} | |||||
dprintf("total_io: %d\n", total_io); | |||||
if (total_io == 0) total_io = 1; | |||||
/* if requested, sort the "interesting" processes */ | |||||
if (active_procs > 1) | |||||
{ | |||||
qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), | |||||
proc_compares[compare_index]); | |||||
} | |||||
/* remember active and total counts */ | |||||
si->p_total = total_procs; | |||||
si->p_active = pref_len = active_procs; | |||||
/* pass back a handle */ | |||||
handle.next_proc = pref; | |||||
handle.remaining = active_procs; | |||||
return((caddr_t)&handle); | |||||
} | |||||
static char p_header[MAX_COLS]; | |||||
char * | |||||
format_process_header(struct process_select *sel, caddr_t handle, int count) | |||||
{ | |||||
int cols; | |||||
int n; | |||||
int w; | |||||
char *p; | |||||
int *fi; | |||||
struct kinfo_proc **kip; | |||||
struct proc_field *fp; | |||||
/* check for null handle */ | |||||
if (handle == NULL) | |||||
{ | |||||
return(""); | |||||
} | |||||
/* remember how many columns there are on the display */ | |||||
cols = display_columns(); | |||||
/* mode & threads dictate format */ | |||||
fi = display_fields = | |||||
sel->mode == 0 ? | |||||
(sel->threads == 0 ? mode0_display : mode0thr_display) : | |||||
mode1_display; | |||||
/* set username field correctly */ | |||||
if (!sel->usernames) | |||||
{ | |||||
/* display uids */ | |||||
field_subst(fi, FIELD_USERNAME, FIELD_UID); | |||||
} | |||||
else | |||||
{ | |||||
/* display usernames */ | |||||
field_subst(fi, FIELD_UID, FIELD_USERNAME); | |||||
/* we also need to determine the longest username for column width */ | |||||
/* calculate namelength from first "count" processes */ | |||||
kip = ((struct handle *)handle)->next_proc; | |||||
n = ((struct handle *)handle)->remaining; | |||||
if (n > count) | |||||
n = count; | |||||
namelength = 0; | |||||
while (n-- > 0) | |||||
{ | |||||
w = strlen(username(PRUID(*kip))); | |||||
if (w > namelength) namelength = w; | |||||
kip++; | |||||
} | |||||
dprintf("format_process_header: namelength %d\n", namelength); | |||||
/* place it in bounds */ | |||||
if (namelength < 8) | |||||
{ | |||||
namelength = 8; | |||||
} | |||||
/* set the column width */ | |||||
proc_field[FIELD_USERNAME].width = username_length = namelength; | |||||
} | |||||
/* walk thru fields and construct header */ | |||||
/* are we worried about overflow??? */ | |||||
p = p_header; | |||||
while (*fi != -1) | |||||
{ | |||||
fp = &(proc_field[*fi++]); | |||||
if (fp->min_screenwidth <= cols) | |||||
{ | |||||
p += sprintf(p, fp->rjust ? "%*s" : "%-*s", fp->width, fp->name); | |||||
*p++ = ' '; | |||||
} | |||||
} | |||||
*--p = '\0'; | |||||
return p_header; | |||||
} | |||||
static char fmt[MAX_COLS]; /* static area where result is built */ | |||||
char * | |||||
format_next_process(caddr_t handle, char *(*get_userid)(int)) | |||||
{ | |||||
struct kinfo_proc *pp; | |||||
struct handle *hp; | |||||
struct proc_field *fp; | |||||
int *fi; | |||||
int i; | |||||
int cols; | |||||
char *p; | |||||
int len; | |||||
int x; | |||||
/* find and remember the next proc structure */ | |||||
hp = (struct handle *)handle; | |||||
pp = *(hp->next_proc++); | |||||
hp->remaining--; | |||||
/* mode & threads dictate format */ | |||||
fi = display_fields; | |||||
/* screen width is a consideration, too */ | |||||
cols = display_columns(); | |||||
/* build output by field */ | |||||
p = fmt; | |||||
len = MAX_COLS; | |||||
while ((i = *fi++) != -1) | |||||
{ | |||||
fp = &(proc_field[i]); | |||||
if (len > 0 && fp->min_screenwidth <= cols) | |||||
{ | |||||
x = (*(fp->format))(p, len, pp); | |||||
if (x >= len) | |||||
{ | |||||
dprintf("format_next_process: formatter overflow: x %d, len %d, p %08x => %08x, fmt %08x - %08x\n", | |||||
x, len, p, p + len, fmt, fmt + sizeof(fmt)); | |||||
p += len; | |||||
len = 0; | |||||
} | |||||
else | |||||
{ | |||||
p += x; | |||||
*p++ = ' '; | |||||
len -= x + 1; | |||||
} | |||||
} | |||||
} | |||||
*--p = '\0'; | |||||
/* return the result */ | |||||
return(fmt); | |||||
} | |||||
/* comparison routines for qsort */ | |||||
/* | |||||
* 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 unsigned char sorted_state[] = | |||||
{ | |||||
0, /* not used */ | |||||
3, /* sleep */ | |||||
1, /* ABANDONED (WAIT) */ | |||||
6, /* run */ | |||||
5, /* start */ | |||||
2, /* zombie */ | |||||
4 /* stop */ | |||||
}; | |||||
#define ORDERKEY_PCTCPU \ | |||||
if (lresult = (long) PPCPU(p2) - (long) PPCPU(p1), \ | |||||
(result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0) | |||||
#define ORDERKEY_CPTICKS \ | |||||
if ((result = PP(p2, runtime) > PP(p1, runtime) ? 1 : \ | |||||
PP(p2, runtime) < PP(p1, runtime) ? -1 : 0) == 0) | |||||
#define ORDERKEY_STATE \ | |||||
if ((result = sorted_state[(unsigned char) PP(p2, stat)] - \ | |||||
sorted_state[(unsigned char) PP(p1, stat)]) == 0) | |||||
#if OSMAJOR <= 4 | |||||
#define ORDERKEY_PRIO \ | |||||
if ((result = PP(p2, priority) - PP(p1, priority)) == 0) | |||||
#else | |||||
#define ORDERKEY_PRIO \ | |||||
if ((result = PP(p2, pri.pri_level) - PP(p1, pri.pri_level)) == 0) | |||||
#endif | |||||
#define ORDERKEY_RSSIZE \ | |||||
if ((result = VP(p2, rssize) - VP(p1, rssize)) == 0) | |||||
#define ORDERKEY_MEM \ | |||||
if ( (result = PROCSIZE(p2) - PROCSIZE(p1)) == 0 ) | |||||
#define ORDERKEY_IO \ | |||||
if ( (result = SP(p2, totalio) - SP(p1, totalio)) == 0) | |||||
#define ORDERKEY_PID \ | |||||
if ( (result = PP(p1, pid) - PP(p2, pid)) == 0) | |||||
/* compare_cpu - the comparison function for sorting by cpu percentage */ | |||||
int | |||||
proc_compare(struct proc **pp1, struct proc **pp2) | |||||
{ | |||||
struct kinfo_proc *p1; | |||||
struct kinfo_proc *p2; | |||||
int result; | |||||
pctcpu lresult; | |||||
/* remove one level of indirection */ | |||||
p1 = *(struct kinfo_proc **) pp1; | |||||
p2 = *(struct kinfo_proc **) pp2; | |||||
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 proc **pp1, struct proc **pp2) | |||||
{ | |||||
struct kinfo_proc *p1; | |||||
struct kinfo_proc *p2; | |||||
int result; | |||||
pctcpu lresult; | |||||
/* remove one level of indirection */ | |||||
p1 = *(struct kinfo_proc **) pp1; | |||||
p2 = *(struct kinfo_proc **) pp2; | |||||
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 proc **pp1, struct proc **pp2) | |||||
{ | |||||
struct kinfo_proc *p1; | |||||
struct kinfo_proc *p2; | |||||
int result; | |||||
pctcpu lresult; | |||||
/* remove one level of indirection */ | |||||
p1 = *(struct kinfo_proc **) pp1; | |||||
p2 = *(struct kinfo_proc **) pp2; | |||||
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 proc **pp1, struct proc **pp2) | |||||
{ | |||||
struct kinfo_proc *p1; | |||||
struct kinfo_proc *p2; | |||||
int result; | |||||
pctcpu lresult; | |||||
/* remove one level of indirection */ | |||||
p1 = *(struct kinfo_proc **) pp1; | |||||
p2 = *(struct kinfo_proc **) pp2; | |||||
ORDERKEY_CPTICKS | |||||
ORDERKEY_PCTCPU | |||||
ORDERKEY_STATE | |||||
ORDERKEY_PRIO | |||||
ORDERKEY_RSSIZE | |||||
ORDERKEY_MEM | |||||
; | |||||
return(result); | |||||
} | |||||
/* compare_prio - the comparison function for sorting by priority */ | |||||
int | |||||
compare_prio(struct proc **pp1, struct proc **pp2) | |||||
{ | |||||
struct kinfo_proc *p1; | |||||
struct kinfo_proc *p2; | |||||
int result; | |||||
pctcpu lresult; | |||||
/* remove one level of indirection */ | |||||
p1 = *(struct kinfo_proc **) pp1; | |||||
p2 = *(struct kinfo_proc **) pp2; | |||||
ORDERKEY_PRIO | |||||
ORDERKEY_CPTICKS | |||||
ORDERKEY_PCTCPU | |||||
ORDERKEY_STATE | |||||
ORDERKEY_RSSIZE | |||||
ORDERKEY_MEM | |||||
; | |||||
return(result); | |||||
} | |||||
/* compare_io - the comparison function for sorting by io count */ | |||||
int | |||||
compare_io(struct proc **pp1, struct proc **pp2) | |||||
{ | |||||
struct kinfo_proc *p1; | |||||
struct kinfo_proc *p2; | |||||
int result; | |||||
pctcpu lresult; | |||||
/* remove one level of indirection */ | |||||
p1 = *(struct kinfo_proc **) pp1; | |||||
p2 = *(struct kinfo_proc **) pp2; | |||||
ORDERKEY_IO | |||||
ORDERKEY_PCTCPU | |||||
ORDERKEY_CPTICKS | |||||
ORDERKEY_STATE | |||||
ORDERKEY_PRIO | |||||
ORDERKEY_RSSIZE | |||||
ORDERKEY_MEM | |||||
; | |||||
return(result); | |||||
} | |||||
/* compare_pid - the comparison function for sorting by process id */ | |||||
int | |||||
compare_pid(struct proc **pp1, struct proc **pp2) | |||||
{ | |||||
struct kinfo_proc *p1; | |||||
struct kinfo_proc *p2; | |||||
int result; | |||||
/* remove one level of indirection */ | |||||
p1 = *(struct kinfo_proc **) pp1; | |||||
p2 = *(struct kinfo_proc **) pp2; | |||||
ORDERKEY_PID | |||||
; | |||||
return(result); | |||||
} | |||||
/* | |||||
* 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 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. | |||||
*/ | |||||
int | |||||
proc_owner(int pid) | |||||
{ | |||||
int cnt; | |||||
struct kinfo_proc **prefp; | |||||
struct kinfo_proc *pp; | |||||
prefp = pref; | |||||
cnt = pref_len; | |||||
while (--cnt >= 0) | |||||
{ | |||||
pp = *prefp++; | |||||
if (PP(pp, pid) == (pid_t)pid) | |||||
{ | |||||
return((int)PRUID(pp)); | |||||
} | |||||
} | |||||
return(-1); | |||||
} | |||||