1 /* $NetBSD: ps.c,v 1.1 1999/02/21 21:48:07 jwise Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 5 * The NetBSD Foundation, Inc. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 /* 37 * XXX Notes XXX 38 * showps -- print data needed at each refresh 39 * fetchps -- get data used by mode (done at each refresh) 40 * labelps -- print labels (ie info not needing refreshing) 41 * initps -- prepare once-only data structures for mode 42 * openps -- prepare per-run information for mode, return window 43 * closeps -- close mode to prepare to switch modes 44 * cmdps -- optional, handle commands 45 */ 46 47 #include <sys/param.h> 48 #include <sys/dkstat.h> 49 #include <sys/dir.h> 50 #include <sys/time.h> 51 #include <sys/proc.h> 52 #include <sys/sysctl.h> 53 #include <sys/user.h> 54 #include <curses.h> 55 #include <math.h> 56 #include <nlist.h> 57 #include <pwd.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <tzfile.h> 61 #include <unistd.h> 62 63 #include "extern.h" 64 #include "systat.h" 65 #include "ps.h" 66 67 int compare_pctcpu_noidle __P((const void *, const void *)); 68 char *state2str __P((struct kinfo_proc *)); 69 char *tty2str __P((struct kinfo_proc *)); 70 int rss2int __P((struct kinfo_proc *)); 71 int vsz2int __P((struct kinfo_proc *)); 72 char *comm2str __P((struct kinfo_proc *)); 73 float pmem2float __P((struct kinfo_proc *)); 74 char *start2str __P((struct kinfo_proc *)); 75 char *time2str __P((struct kinfo_proc *)); 76 77 static time_t now; 78 79 void 80 labelps () 81 { 82 mvwaddstr(wnd, 0, 0, "USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND"); 83 } 84 85 void 86 showps () 87 { 88 int i, k, y, vsz, rss; 89 const char *user, *comm, *state, *tty, *start, *time; 90 pid_t pid; 91 float pctcpu, pctmem; 92 struct eproc *ep; 93 94 now = 0; /* force start2str to reget current time */ 95 96 qsort(pt, nproc + 1, sizeof (struct p_times), compare_pctcpu_noidle); 97 98 y = 1; 99 i = nproc + 1; 100 if (i > getmaxy(wnd)-2) 101 i = getmaxy(wnd)-1; 102 for (k = 0; i > 0 ; i--, y++, k++) { 103 if (pt[k].pt_kp == NULL) /* We're all the way down to the imaginary idle proc */ 104 return; 105 106 ep = &pt[k].pt_kp->kp_eproc; 107 user = user_from_uid(ep->e_ucred.cr_uid, 0); 108 pid = pt[k].pt_kp->kp_proc.p_pid; 109 pctcpu = pt[k].pt_pctcpu; 110 pctmem = pmem2float(pt[k].pt_kp); 111 vsz = vsz2int(pt[k].pt_kp); 112 rss = rss2int(pt[k].pt_kp); 113 tty = tty2str(pt[k].pt_kp); 114 state = state2str(pt[k].pt_kp); 115 start = start2str(pt[k].pt_kp); 116 time = time2str(pt[k].pt_kp); 117 comm = comm2str(pt[k].pt_kp); 118 /*comm = pt[k].pt_kp->kp_proc.p_comm; */ 119 120 wmove(wnd, y, 0); 121 wclrtoeol(wnd); 122 mvwprintw(wnd, y, 0, "%-8.8s%5d %4.1f %4.1f %6d %5d %-3s %-4s %7s %10.10s %s", 123 user, pid, pctcpu, pctmem, vsz, rss, tty, state, start, time, comm); 124 } 125 } 126 127 int 128 compare_pctcpu_noidle (a, b) 129 const void *a, *b; 130 { 131 if (((struct p_times *) a)->pt_kp == NULL) 132 return 1; 133 134 if (((struct p_times *) b)->pt_kp == NULL) 135 return -1; 136 137 return (((struct p_times *) a)->pt_pctcpu > 138 ((struct p_times *) b)->pt_pctcpu)? -1: 1; 139 } 140 141 /* from here down adapted from .../src/usr.bin/ps/print.c . Any mistakes are my own, however. */ 142 char * 143 state2str(kp) 144 struct kinfo_proc *kp; 145 { 146 struct proc *p; 147 struct eproc *e; 148 int flag; 149 char *cp; 150 char buf[5]; 151 static char statestr[4]; 152 153 p = &(kp->kp_proc); 154 e = &(kp->kp_eproc); 155 156 flag = p->p_flag; 157 cp = buf; 158 159 switch (p->p_stat) { 160 case SSTOP: 161 *cp = 'T'; 162 break; 163 164 case SSLEEP: 165 if (flag & P_SINTR) /* interuptable (long) */ 166 *cp = p->p_slptime >= MAXSLP ? 'I' : 'S'; 167 else 168 *cp = 'D'; 169 break; 170 171 case SRUN: 172 case SIDL: 173 *cp = 'R'; 174 break; 175 176 case SZOMB: 177 *cp = 'Z'; 178 break; 179 180 default: 181 *cp = '?'; 182 } 183 cp++; 184 if (flag & P_INMEM) { 185 } else 186 *cp++ = 'W'; 187 if (p->p_nice < NZERO) 188 *cp++ = '<'; 189 else if (p->p_nice > NZERO) 190 *cp++ = 'N'; 191 if (flag & P_TRACED) 192 *cp++ = 'X'; 193 if (flag & P_WEXIT && p->p_stat != SZOMB) 194 *cp++ = 'E'; 195 if (flag & P_PPWAIT) 196 *cp++ = 'V'; 197 if ((flag & P_SYSTEM) || p->p_holdcnt) 198 *cp++ = 'L'; 199 if (e->e_flag & EPROC_SLEADER) 200 *cp++ = 's'; 201 if ((flag & P_CONTROLT) && e->e_pgid == e->e_tpgid) 202 *cp++ = '+'; 203 *cp = '\0'; 204 sprintf(statestr, "%-s", buf); 205 206 return statestr; 207 } 208 209 char * 210 tty2str(kp) 211 struct kinfo_proc *kp; 212 { 213 static char ttystr[4]; 214 char *ttyname; 215 struct eproc *e; 216 217 e = &(kp->kp_eproc); 218 219 if (e->e_tdev == NODEV || (ttyname = devname(e->e_tdev, S_IFCHR)) == NULL) 220 strcpy(ttystr, "??"); 221 else { 222 if (strncmp(ttyname, "tty", 3) == 0) 223 ttyname += 3; 224 sprintf(ttystr, "%s%c", ttyname, e->e_flag & EPROC_CTTY ? ' ' : '-'); 225 } 226 227 return ttystr; 228 } 229 230 #define pgtok(a) (((a)*getpagesize())/1024) 231 232 int 233 vsz2int(kp) 234 struct kinfo_proc *kp; 235 { 236 struct eproc *e; 237 int i; 238 239 e = &(kp->kp_eproc); 240 i = pgtok(e->e_vm.vm_dsize + e->e_vm.vm_ssize + e->e_vm.vm_tsize); 241 242 return ((i < 0) ? 0 : i); 243 } 244 245 int 246 rss2int(kp) 247 struct kinfo_proc *kp; 248 { 249 struct eproc *e; 250 int i; 251 252 e = &(kp->kp_eproc); 253 i = pgtok(e->e_vm.vm_rssize); 254 255 /* XXX don't have info about shared */ 256 return ((i < 0) ? 0 : i); 257 } 258 259 char * 260 comm2str(kp) 261 struct kinfo_proc *kp; 262 { 263 char **argv, **pt; 264 static char commstr[41]; 265 struct proc *p; 266 267 p = &(kp->kp_proc); 268 commstr[0]='\0'; 269 270 argv = kvm_getargv(kd, kp, 40); 271 if ((pt = argv) != NULL) { 272 while (*pt) { 273 strcat(commstr, *pt); 274 pt++; 275 strcat(commstr, " "); 276 } 277 } else { 278 commstr[0] = '('; 279 commstr[1] = '\0'; 280 strncat(commstr, p->p_comm, sizeof(commstr) - 1); 281 strcat(commstr, ")"); 282 } 283 284 return commstr; 285 } 286 287 float 288 pmem2float(kp) 289 struct kinfo_proc *kp; 290 { 291 struct proc *p; 292 struct eproc *e; 293 double fracmem; 294 int szptudot; 295 296 p = &(kp->kp_proc); 297 e = &(kp->kp_eproc); 298 299 if ((p->p_flag & P_INMEM) == 0) 300 return (0.0); 301 /* XXX want pmap ptpages, segtab, etc. (per architecture) */ 302 szptudot = USPACE/getpagesize(); 303 /* XXX don't have info about shared */ 304 fracmem = ((float)e->e_vm.vm_rssize + szptudot)/CLSIZE/mempages; 305 return (fracmem >= 0) ? 100.0 * fracmem : 0; 306 } 307 308 char * 309 start2str(kp) 310 struct kinfo_proc *kp; 311 { 312 struct proc *p; 313 struct pstats pstats; 314 struct timeval u_start; 315 struct tm *tp; 316 time_t startt; 317 static char startstr[10]; 318 319 p = &(kp->kp_proc); 320 321 kvm_read(kd, (u_long)&(p->p_addr->u_stats), (char *)&pstats, sizeof(pstats)); 322 u_start = pstats.p_start; 323 324 startt = u_start.tv_sec; 325 tp = localtime(&startt); 326 if (now == 0) 327 time(&now); 328 if (now - u_start.tv_sec < 24 * SECSPERHOUR) { 329 /* I *hate* SCCS... */ 330 static char fmt[] = __CONCAT("%l:%", "M%p"); 331 strftime(startstr, sizeof(startstr) - 1, fmt, tp); 332 } else if (now - u_start.tv_sec < 7 * SECSPERDAY) { 333 /* I *hate* SCCS... */ 334 static char fmt[] = __CONCAT("%a%", "I%p"); 335 strftime(startstr, sizeof(startstr) - 1, fmt, tp); 336 } else 337 strftime(startstr, sizeof(startstr) - 1, "%e%b%y", tp); 338 339 return startstr; 340 } 341 342 char * 343 time2str(kp) 344 struct kinfo_proc *kp; 345 { 346 long secs; 347 long psecs; /* "parts" of a second. first micro, then centi */ 348 static char timestr[10]; 349 struct proc *p; 350 351 p = &(kp->kp_proc); 352 353 if (p->p_stat == SZOMB) { 354 secs = 0; 355 psecs = 0; 356 } else { 357 /* 358 * This counts time spent handling interrupts. We could 359 * fix this, but it is not 100% trivial (and interrupt 360 * time fractions only work on the sparc anyway). XXX 361 */ 362 secs = p->p_rtime.tv_sec; 363 psecs = p->p_rtime.tv_usec; 364 /* if (sumrusage) { 365 secs += k->ki_u.u_cru.ru_utime.tv_sec + 366 k->ki_u.u_cru.ru_stime.tv_sec; 367 psecs += k->ki_u.u_cru.ru_utime.tv_usec + 368 k->ki_u.u_cru.ru_stime.tv_usec; 369 } */ 370 /* 371 * round and scale to 100's 372 */ 373 psecs = (psecs + 5000) / 10000; 374 secs += psecs / 100; 375 psecs = psecs % 100; 376 } 377 snprintf(timestr, sizeof(timestr), "%3ld:%02ld.%02ld", secs/60, secs%60, psecs); 378 379 return timestr; 380 } 381