home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / unix / volume26 / cputt < prev    next >
Encoding:
Text File  |  1992-04-18  |  26.5 KB  |  836 lines

  1. Newsgroups: comp.sources.unix
  2. From: vesper@kong.gsfc.nasa.gov (Greg Vesper Code 520.9)
  3. Subject: v26i009: cputt - monitor top ten (by cpu time) processes on SunOS
  4. Sender: unix-sources-moderator@pa.dec.com
  5. Approved: vixie@pa.dec.com
  6.  
  7. Submitted-By: vesper@kong.gsfc.nasa.gov (Greg Vesper Code 520.9)
  8. Posting-Number: Volume 26, Issue 9
  9. Archive-Name: cputt
  10.  
  11. This utility is called 'cputt'.  It provides dynamic updates of the top
  12. ten cpu using processes on your system.  It has been trimmed down to
  13. run as efficiently as possible and should compile and run on any system
  14. running SUNOS 4.1.1.  This program is similar in functionality to 'top',
  15. also available in the public domain, but cputt is more efficient and
  16. is tailored for 4.1.1.  Please give it a try and let me know what you
  17. think.
  18.               Greg Vesper Code 520.9
  19.            Goddard Space Flight Center
  20.             vesper@kong.gsfc.nasa.gov
  21.                Greenbelt, Maryland
  22.  
  23. [ The output of this tool looks like this:
  24.  
  25.     sunburst% ./cputt 3 3
  26.  
  27.       PID USER        CPU    MEM  COMMANDS - 1 42
  28.     10152 vixie      3.97   5.79  ./cputt 3 3
  29.  
  30.       PID USER        CPU    MEM  COMMANDS - 2 42
  31.     10152 vixie     20.00   6.14  ./cputt 3 3
  32.      3038 root       0.53   2.48  in.rwhod
  33.  
  34.       PID USER        CPU    MEM  COMMANDS - 2 42
  35.     10152 vixie     19.73   6.26  ./cputt 3 3
  36.     10008 root       0.26   2.83  in.rlogind
  37.  
  38.   I edited the Makefile for cosmetic things.  This builds and runs
  39.   on a Sun SPARC machine.  --vix ]
  40.  
  41. #! /bin/sh
  42. # This is a shell archive.  Remove anything before this line, then unpack
  43. # it by saving it into a file and typing "sh file".  To overwrite existing
  44. # files, type "sh file -c".  You can also feed this as standard input via
  45. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  46. # will see the following message at the end:
  47. #        "End of shell archive."
  48. # Contents:  README Makefile cputt.h getcmd.c globals.c hash.c init.c
  49. #   main.c routines.c
  50. # Wrapped by vixie@cognition.pa.dec.com on Sun Apr 19 07:06:29 1992
  51. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  52. if test -f 'README' -a "${1}" != "-c" ; then 
  53.   echo shar: Will not clobber existing file \"'README'\"
  54. else
  55. echo shar: Extracting \"'README'\" \(3576 characters\)
  56. sed "s/^X//" >'README' <<'END_OF_FILE'
  57. X          Cputt - CPU Top Ten monitor for SUNOS 4.1.1
  58. X          ===========================================
  59. X
  60. DISCLAIMER:
  61. X
  62. X     This software has no warranty whatsoever, explicit or implicit.
  63. X     The author and his employer shall not be held responsible for any
  64. X     damage, direct or indirect, resulting from the use of this
  65. X     software.  That being said, feel free to use it, its a useful
  66. X     little utility.
  67. X
  68. PURPOSE:
  69. X
  70. X     Cputt is a cpu monitoring utility which gives you a continuous
  71. X     update of the top ten cpu-using processes on your system.
  72. X
  73. IMPLEMENTATION:
  74. X
  75. X     Cputt takes two snapshots of the kernel process table, sleeping
  76. X     for a user supplied number of seconds in between the two
  77. X     snapshots.  It calculates cpu usage for each process over that
  78. X     interval and prints out the results.  Execution continues for a
  79. X     user supplied number of iterations.
  80. X
  81. USAGE:
  82. X
  83. X     Cputt should be invoked with two arguments as follows:
  84. X
  85. X          cputt <length of interval> <number of iterations>
  86. X
  87. X     Cputt prints a sorted list of cpu intensive processes as follows:
  88. X
  89. X          PID  USER  CPU  MEM  COMMANDS  <active processes> <ptable size>
  90. X
  91. INSTALLATION:
  92. X
  93. X     Cputt should compile on any system running SUNOS 4.1.1.  Check
  94. X     the #define values in cputt.h before compiling.  The only one
  95. X     you need to change is MAXUSERS which should be two times the
  96. X     number of /etc/passwd entries on your machine.  Just type
  97. X     'make' to compile, 'make install' to install.  You must be root
  98. X     to install Cputt.  The default destination is /usr/etc.
  99. X
  100. SUGGESTION:
  101. X
  102. X     Cputt becomes more and more processor intensive as the length of the
  103. X     interval between process table lookups decreases.  Thus a first
  104. X     arguement of less than 5 is probably not real useful.  I usually
  105. X     invoke it with one of the following:
  106. X
  107. X     cputt 5  100  - for short term monitoring of cpu usage
  108. X     cputt 10 1000 - for long term monitoring of cpu usage
  109. X
  110. ACKNOWLEDGEMENTS:
  111. X
  112. X     The idea for Cputt came from Larry Schuler, a friend and co-worker.
  113. X     The implementation for Cputt was largely dependent on J. Robert Ward's
  114. X     'sps' utility.  I used Robert's source code to learn how to make the 
  115. X     appropriate system calls for interrogating the kernel process table.
  116. X     The rest of the alogorithm and implementation are my own.
  117. X
  118. IMPROVEMENTS:
  119. X
  120. X     I've used all the shortcuts I know to make Cputt as trim as possible.
  121. X     Floating point arithmetic is kept to a bare minimum.  Internal data
  122. X     structures are allocated dynamically based on the changing status of
  123. X     the process table.  Command arguements for each process are kept in
  124. X     core instead of being re-evaluated at each iteration.  The sort table
  125. X     contains only active processes and no extraneous information.  The
  126. X     kernel reads account for the bulk of Cputt's processing overhead and
  127. X     I'm unaware of anyway to alleviate this cost.  If anyone has any well
  128. X     considered suggestions about how I can improve the implemenation and
  129. X     performance of Cputt, I would like to hear them.
  130. X
  131. COPYRIGHT:
  132. X
  133. X     Feel free to distribute Cputt to any interested parties.  Please
  134. X     provide a copy of this README with any distribution.
  135. X
  136. X:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  137. X              Greg Vesper Code 520.9
  138. X           Goddard Space Flight Center
  139. X            vesper@kong.gsfc.nasa.gov
  140. X               Greenbelt, Maryland
  141. X
  142. X"As He died to make men holy, let us live to make men free.
  143. X While God is marching on.."
  144. X:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  145. END_OF_FILE
  146. if test 3576 -ne `wc -c <'README'`; then
  147.     echo shar: \"'README'\" unpacked with wrong size!
  148. fi
  149. # end of 'README'
  150. fi
  151. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  152.   echo shar: Will not clobber existing file \"'Makefile'\"
  153. else
  154. echo shar: Extracting \"'Makefile'\" \(581 characters\)
  155. sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  156. DESTROOT =
  157. DESTPATH = $(DESTROOT)/usr/local
  158. X
  159. PROG    = cputt
  160. OBJS    = globals.o init.o getcmd.o hash.o routines.o main.o
  161. CC      = cc
  162. CDEBUG    = -O
  163. CFLAGS  = $(CDEBUG)
  164. LIBS    = -lkvm
  165. DIRINSTALL = $(DESTPATH)/bin
  166. X
  167. all:      $(PROG)
  168. X
  169. X.c.o:
  170. X        $(CC) $(CFLAGS) -c -R $<
  171. X
  172. globals.o:
  173. X        $(CC) $(CFLAGS) -c $<
  174. X
  175. X$(OBJS):  cputt.h
  176. X
  177. X$(PROG):  $(OBJS)
  178. X        $(CC) -o $@ $(OBJS) $(LIBS)
  179. X
  180. install:  $(PROG)
  181. X        strip $(PROG)
  182. X        cp $(PROG) $(DIRINSTALL)/$(PROG)
  183. X        /etc/chown root $(DIRINSTALL)/$(PROG)
  184. X        chgrp kmem $(DIRINSTALL)/$(PROG)
  185. X        chmod 2755 $(DIRINSTALL)/$(PROG)
  186. X
  187. clean:
  188. X        rm -f $(OBJS) $(PROG)
  189. END_OF_FILE
  190. if test 581 -ne `wc -c <'Makefile'`; then
  191.     echo shar: \"'Makefile'\" unpacked with wrong size!
  192. fi
  193. # end of 'Makefile'
  194. fi
  195. if test -f 'cputt.h' -a "${1}" != "-c" ; then 
  196.   echo shar: Will not clobber existing file \"'cputt.h'\"
  197. else
  198. echo shar: Extracting \"'cputt.h'\" \(2312 characters\)
  199. sed "s/^X//" >'cputt.h' <<'END_OF_FILE'
  200. X# include     <sys/param.h>
  201. X# include     <sys/user.h>
  202. X# include     <sys/proc.h>
  203. X
  204. X# define MAXUSERS       300 /* set this to (length of /etc/passwd * 2) */
  205. X# define MAXACTIVE       50 /* max cpu-positive processes per interval */
  206. X# define MAXOUT          10 /* output is limitted to this many processes */
  207. X# define COMMAND_SIZE    51 /* set this to (width of your window - 29) */
  208. X# define UNAMELEN         8 /* max length for username */
  209. X
  210. X/*
  211. X   cputt attempts to truncate unused slots at the end of the process
  212. X   table.  This usually reduces its runtime size considerably.  If the
  213. X   process table grows or shrinks by a significant amount, cputt will
  214. X   restart itself and resize its internal data structures accordingly.
  215. X*/
  216. X
  217. X# define GROWTH_BUF      40 /* growth buffer, use 0 to disable truncation */
  218. X# define MAX_GROW        20 /* restart after proc table grows this much */
  219. X# define MAX_SHRINK      10 /* restart after proc table shrinks this much */
  220. X
  221. X/* info concerning a symbol table entry */
  222. X
  223. struct symbol
  224. X{
  225. X     char          *s_kname;      /* kernel symbol name */
  226. X     char           s_indirect;   /* value requires indirection */
  227. X     caddr_t       *s_info;       /* corresponding info address */
  228. X     char          *s_wait;       /* reason for wait, if any */
  229. X};
  230. X
  231. X/* single hash table entry for mapping uid's to usernames */
  232. X
  233. struct hashtab
  234. X{
  235. X     short          h_uid ;               /* uid of user entry */
  236. X     char           h_uname[ UNAMELEN ] ; /* corresponding name */
  237. X};
  238. X
  239. X/* critical system info */
  240. X
  241. struct info
  242. X{
  243. X     struct proc   *i_proc0;               /* address of process table */
  244. X     int            i_nproc;               /* length of process table */
  245. X     int            i_sproc;               /* size of process table */
  246. X     int            i_ecmx;                /* max physical memory address*/
  247. X     struct hashtab i_hnames[MAXUSERS];    /* hash table for usernames */
  248. X};
  249. X
  250. X/* for reading process upages from kernel memory */
  251. X
  252. union userstate
  253. X{
  254. X     struct user     u_us ;
  255. X     char            u_pg[ UPAGES ][ NBPG ] ;
  256. X};
  257. X
  258. X/* summary info for processes with positive cpu utilization */
  259. X
  260. struct procdata
  261. X{
  262. X     struct proc    *proc;         /* proc structure */
  263. X     long            pctcpu;       /* cpu usage */
  264. X     int             pr_cmd;       /* command string index */
  265. X};
  266. END_OF_FILE
  267. if test 2312 -ne `wc -c <'cputt.h'`; then
  268.     echo shar: \"'cputt.h'\" unpacked with wrong size!
  269. fi
  270. # end of 'cputt.h'
  271. fi
  272. if test -f 'getcmd.c' -a "${1}" != "-c" ; then 
  273.   echo shar: Will not clobber existing file \"'getcmd.c'\"
  274. else
  275. echo shar: Extracting \"'getcmd.c'\" \(1102 characters\)
  276. sed "s/^X//" >'getcmd.c' <<'END_OF_FILE'
  277. X# include       "cputt.h"
  278. X# include       <kvm.h>
  279. X
  280. X/* GETCMD - gets the command string for process p and copies it to argbuf */
  281. X
  282. getcmd ( p, argbuf )
  283. X
  284. struct   proc      *p;
  285. register char      *argbuf;
  286. X
  287. X{
  288. X     register char           *cp,**ap ;
  289. X     char                    **argv ;
  290. X     register int            sp=-1;
  291. X     extern kvm_t            *Flkvm ;
  292. X     extern union userstate  User ;
  293. X
  294. X     if (!p->p_pid)
  295. X        { strcpy(&argbuf[0],"Swapper"); return; }
  296. X     if (p->p_pid == 2)
  297. X        { strcpy(&argbuf[0],"Pager"); return; }
  298. X
  299. X     if (kvm_getcmd(Flkvm,p,&User.u_us,&argv,(char ***)NULL)<0||argv==NULL)
  300. X     {
  301. X         argbuf[0] = '(';
  302. X         strncpy(&argbuf[1],User.u_us.u_comm,sizeof(User.u_us.u_comm));
  303. X         argbuf[sizeof(User.u_us.u_comm)+1] = '\0';
  304. X         strcat(argbuf, ")" ) ;
  305. X     }
  306. X     else
  307. X     {
  308. X         ap = argv;
  309. X         while (*ap && sp < COMMAND_SIZE-1)
  310. X         {
  311. X             for (cp = *ap++; *cp && sp < COMMAND_SIZE-2;)
  312. X                  argbuf[++sp] = *cp++;
  313. X             argbuf[++sp] = ' ';
  314. X         }
  315. X         argbuf[sp] = '\0';
  316. X         free(argv);
  317. X     }
  318. X}
  319. END_OF_FILE
  320. if test 1102 -ne `wc -c <'getcmd.c'`; then
  321.     echo shar: \"'getcmd.c'\" unpacked with wrong size!
  322. fi
  323. # end of 'getcmd.c'
  324. fi
  325. if test -f 'globals.c' -a "${1}" != "-c" ; then 
  326.   echo shar: Will not clobber existing file \"'globals.c'\"
  327. else
  328. echo shar: Extracting \"'globals.c'\" \(624 characters\)
  329. sed "s/^X//" >'globals.c' <<'END_OF_FILE'
  330. X# include       "cputt.h"
  331. X# include       "kvm.h"
  332. X
  333. struct info                     Info ; /* System Addresses and Info */
  334. X
  335. kvm_t                          *Flkvm; /* Descriptor for Kernel Memory */
  336. X
  337. union  userstate                User;  /* Structure to Hold Upages */
  338. X
  339. struct symbol   Symbollist[] =         /* Symbols to Lookup */
  340. X{
  341. X     { "_proc",      1,  (caddr_t*)&Info.i_proc0,    (char*)0        },
  342. X     { "_nproc",     1,  (caddr_t*)&Info.i_nproc,    (char*)0        },
  343. X     { "_maxmem",    1,  (caddr_t*)&Info.i_ecmx,     (char*)0        },
  344. X     { (char*)0,     0,  (caddr_t*)0,                (char*)0        }
  345. X};
  346. END_OF_FILE
  347. if test 624 -ne `wc -c <'globals.c'`; then
  348.     echo shar: \"'globals.c'\" unpacked with wrong size!
  349. fi
  350. # end of 'globals.c'
  351. fi
  352. if test -f 'hash.c' -a "${1}" != "-c" ; then 
  353.   echo shar: Will not clobber existing file \"'hash.c'\"
  354. else
  355. echo shar: Extracting \"'hash.c'\" \(1047 characters\)
  356. sed "s/^X//" >'hash.c' <<'END_OF_FILE'
  357. X# include       "cputt.h"
  358. X# include       <pwd.h>
  359. X
  360. X# define        HASHFN1( a ) (((unsigned)(a)*91 + 17) % MAXUSERS)
  361. X
  362. X/*
  363. X   HASHUID - Returns a pointer to a slot in the hash table that corresponds
  364. X   to the hash table entry for `uid'. It returns a null pointer if there is
  365. X   no such slot.
  366. X*/
  367. X
  368. struct hashtab  *hashuid ( uid )
  369. X
  370. int    uid ;
  371. X
  372. X{
  373. X     struct hashtab *hp ;
  374. X     extern struct info      Info ;
  375. X
  376. X     hp = &Info.i_hnames[ HASHFN1( uid ) ];
  377. X     if ( hp->h_uid == uid )
  378. X          return ( hp ) ;
  379. X     return ( (struct hashtab*)0 ) ;
  380. X}
  381. X
  382. X/*
  383. X   INITUSERS - builds the uid hash table.
  384. X*/
  385. X
  386. initusers ()
  387. X{
  388. X     struct passwd  *pw ;
  389. X     struct hashtab *hp ;
  390. X     struct passwd  *getpwent() ;
  391. X
  392. X     while ( pw = getpwent() )
  393. X     {
  394. X          /* Try to find a free slot in the hash table and fill it. */
  395. X
  396. X          hp = &Info.i_hnames[ HASHFN1(pw->pw_uid) ];
  397. X          if ( !hp->h_uname[0] )
  398. X          {
  399. X               hp->h_uid = pw->pw_uid ;
  400. X               strncpy( hp->h_uname, pw->pw_name, UNAMELEN );
  401. X          }
  402. X     }
  403. X     endpwent() ;
  404. X}
  405. END_OF_FILE
  406. if test 1047 -ne `wc -c <'hash.c'`; then
  407.     echo shar: \"'hash.c'\" unpacked with wrong size!
  408. fi
  409. # end of 'hash.c'
  410. fi
  411. if test -f 'init.c' -a "${1}" != "-c" ; then 
  412.   echo shar: Will not clobber existing file \"'init.c'\"
  413. else
  414. echo shar: Extracting \"'init.c'\" \(2183 characters\)
  415. sed "s/^X//" >'init.c' <<'END_OF_FILE'
  416. X# include       "cputt.h"
  417. X# include       <sys/file.h>
  418. X# include       <kvm.h>
  419. X# include       <nlist.h>
  420. X# include       <stdio.h>
  421. X
  422. X/*
  423. X   INIT - Reads the kernel's symbol table for critical system
  424. X   information and stores this information in the Symbollist array.
  425. X*/
  426. X
  427. init ()
  428. X{
  429. X     register struct nlist   *np ;
  430. X     register struct symbol  *s ;
  431. X     register struct nlist   *np0 ;
  432. X     extern kvm_t            *Flkvm ;
  433. X     extern struct symbol     Symbollist[] ;
  434. X     extern struct info       Info ;
  435. X     char                    *getcore() ;
  436. X
  437. X     /* find length of the list of desired symbols */
  438. X
  439. X     for ( s = Symbollist ; s->s_kname ; s++ )
  440. X          ;
  441. X     /* allocate core for nlist array */
  442. X
  443. X     np0 = (struct nlist*)getcore( (s-Symbollist+1)*sizeof( struct nlist ));
  444. X
  445. X     /* initialize nlist array */
  446. X
  447. X     for ( s = Symbollist, np = np0 ; s->s_kname ; s++, np++ )
  448. X     {
  449. X          np->n_name = s->s_kname ;
  450. X          np[1].n_name = (char*)0 ;
  451. X          np->n_value = 0 ;
  452. X     }
  453. X
  454. X     /* read symbols from symbol table into nlist array */
  455. X
  456. X     if ( kvm_nlist( Flkvm, np0 ) == -1 )
  457. X     {
  458. X          fprintf( stderr, "sps - Can't read symbol file \n");
  459. X          sysperror() ;
  460. X     }
  461. X
  462. X     /* derive system info from nlist and store in Symbollist */
  463. X
  464. X     for ( s = Symbollist, np = np0 ; s->s_kname ; s++, np++ )
  465. X     {                                       
  466. X          if ( !np->n_value )             
  467. X          {
  468. X              fprintf( stderr, "sps - Can't find symbol %s in %s", np->n_name);
  469. X              *s->s_info = (caddr_t)0 ;
  470. X              continue ;
  471. X          }
  472. X
  473. X          /* If no indirection is required, just copy the obtained value
  474. X             into the `Info' structure. */
  475. X
  476. X          if ( !s->s_indirect )           
  477. X          {                               
  478. X               *s->s_info = (caddr_t)np->n_value ;
  479. X               continue ;              
  480. X          }                               
  481. X
  482. X          /* Otherwise one level of indirection is required. Using the
  483. X             obtained address, look again in the kernel for the value */
  484. X
  485. X          (void)getkmem((long)np->n_value, (char*)s->s_info, sizeof(caddr_t));
  486. X     }
  487. X     free( (char*)np0 ) ;
  488. X}
  489. END_OF_FILE
  490. if test 2183 -ne `wc -c <'init.c'`; then
  491.     echo shar: \"'init.c'\" unpacked with wrong size!
  492. fi
  493. # end of 'init.c'
  494. fi
  495. if test -f 'main.c' -a "${1}" != "-c" ; then 
  496.   echo shar: Will not clobber existing file \"'main.c'\"
  497. else
  498. echo shar: Extracting \"'main.c'\" \(7210 characters\)
  499. sed "s/^X//" >'main.c' <<'END_OF_FILE'
  500. X#include        "cputt.h"
  501. X#include        <kvm.h>
  502. X#include        <fcntl.h>
  503. X#include        <sys/stat.h>
  504. X#include        <sys/types.h>
  505. X#include        <sys/timeb.h>
  506. X#include        <sys/resource.h>
  507. X
  508. X/*
  509. X   MAIN - Computes CPU utilization for all processes from two snapshots
  510. X   of the kernel process table taken over a user supplied interval
  511. X   (arg1).  Sorts and prints the top ten CPU using processes for that
  512. X   interval.  Execution continues for a user supplied number of
  513. X   iterations (arg2).
  514. X
  515. X   The kernel process table snapshots are stored in two arrays,
  516. X   'oldproc' and 'newproc'.  Resource utilization information is stored
  517. X   in two arrays, 'oldusage' and 'newusage'.  The command strings for
  518. X   each process are stored in a 2D array, 'cmds'.  CPU utilization for
  519. X   each interval is stored in the 'pdata' array along with pointers to
  520. X   the appropriate 'proc' and 'cmds' entries.
  521. X*/
  522. X
  523. main (argc, argv)
  524. X
  525. int argc; char *argv[];
  526. X
  527. X{
  528. X     extern struct info        Info;
  529. X     extern kvm_t             *Flkvm;
  530. X     extern union userstate    User;
  531. X
  532. X     struct proc              *oldproc,*newproc,*tproc;
  533. X     struct rusage            *oldusage,*newusage,*tusage;
  534. X     struct procdata          *pdata;
  535. X     struct hashtab           *hp;
  536. X
  537. X     register long             time1,utime1,time2,utime2;
  538. X     register long             s_delta, p_delta;
  539. X     register int              i,j,k;
  540. X
  541. X     struct timeb              ot,nt;
  542. X     struct hashtab           *hashuid();
  543. X     int                       trim=0,reps,cmp();
  544. X     double                    percentmem();
  545. X     char                     **cmds;
  546. X
  547. X     if (argc != 3)
  548. X        {
  549. X         printf("usage: cputt <interval> <iterations>\n");
  550. X         exit();
  551. X        }
  552. X
  553. X     setpriority(PRIO_PROCESS,0,-20);
  554. X
  555. X     /* open kernel memory  */
  556. X
  557. X     Flkvm = kvm_open(NULL, NULL, NULL, O_RDONLY, "cputt");
  558. X
  559. X     /* get critical system parameters, build uid hash table */
  560. X
  561. X     init();
  562. X     initusers();
  563. X
  564. X     /* truncate unused portion of the process table */
  565. X
  566. X     Info.i_sproc = Info.i_nproc * sizeof(struct proc);
  567. X     if (GROWTH_BUF)
  568. X     {
  569. X         oldproc = (struct proc *) getcore(Info.i_nproc*sizeof(struct proc));
  570. X         readstatus(oldproc);
  571. X         for (i=0; i<Info.i_nproc; i++)
  572. X              if (oldproc[i].p_stat && getupage(oldproc[i]))
  573. X                  j=i;
  574. X         free(oldproc);
  575. X         if (j+GROWTH_BUF < Info.i_nproc)
  576. X         {
  577. X             Info.i_nproc = j+GROWTH_BUF;
  578. X             Info.i_sproc = Info.i_nproc * sizeof(struct proc);
  579. X             trim = 1;
  580. X         }
  581. X     }
  582. X
  583. X     /* allocate core for internal data structures */
  584. X
  585. X     cmds = (char **) getcore(Info.i_nproc*sizeof(char *));
  586. X     for (i=0;i<Info.i_nproc;i++)
  587. X          cmds[i] = (char *) getcore(COMMAND_SIZE);
  588. X
  589. X     pdata     = (struct procdata *)
  590. X                  getcore(MAXACTIVE*sizeof(struct procdata));
  591. X     oldusage  = (struct rusage *)
  592. X                  getcore(Info.i_nproc*sizeof(struct rusage));
  593. X     newusage  = (struct rusage *)
  594. X                  getcore(Info.i_nproc*sizeof(struct rusage));
  595. X     oldproc   = (struct proc *) getcore(Info.i_nproc*sizeof(struct proc));
  596. X     newproc   = (struct proc *) getcore(Info.i_nproc*sizeof(struct proc));
  597. X
  598. X     /* get initial time, process table, upage, and command strings */
  599. X
  600. X     ftime(&ot);
  601. X     readstatus(oldproc);
  602. X     for (i=0;i<Info.i_nproc;i++) 
  603. X          if (oldproc[i].p_stat && getupage(oldproc[i]))
  604. X             {
  605. X              oldusage[i] = User.u_us.u_ru;
  606. X              getcmd(oldproc[i],cmds[i]);
  607. X             }
  608. X
  609. X     /* compare process tables every argv[1] seconds for argv[2] iterations */
  610. X
  611. X     for (reps = 0; reps < atoi(argv[2]); reps++)
  612. X     {    
  613. X          sleep(atoi(argv[1]));        /* sleep for agrv[1] seconds */
  614. X          ftime(&nt);                  /* get new time          */
  615. X          readstatus(newproc);         /* get new process table */
  616. X
  617. X          /* find matching pids, compute cpu usage, store results in pdata */
  618. X
  619. X          for (i=k=0; i<Info.i_nproc; i++)
  620. X               if (newproc[i].p_stat && getupage(newproc[i]))
  621. X               {
  622. X                   j=i; /* record latest proc table location */
  623. X                   newusage[i] = User.u_us.u_ru;
  624. X                   if (oldproc[i].p_pid==newproc[i].p_pid)
  625. X                   {
  626. X                       time1 =   oldusage[i].ru_utime.tv_sec +
  627. X                                 oldusage[i].ru_stime.tv_sec;
  628. X                       utime1 =  oldusage[i].ru_utime.tv_usec +
  629. X                                 oldusage[i].ru_stime.tv_usec;
  630. X                       time2 =   newusage[i].ru_utime.tv_sec +
  631. X                                 newusage[i].ru_stime.tv_sec;
  632. X                       utime2 =  newusage[i].ru_utime.tv_usec +
  633. X                                 newusage[i].ru_stime.tv_usec;
  634. X                       p_delta = (time2*1000000+utime2) -
  635. X                                 (time1*1000000+utime1);
  636. X                       if (p_delta)
  637. X                       {
  638. X                           s_delta = (nt.time*1000+nt.millitm) -
  639. X                                     (ot.time*1000+ot.millitm);
  640. X                           pdata[k].pctcpu = (p_delta*10)/s_delta;
  641. X                           pdata[k].proc   = &oldproc[i];
  642. X                           pdata[k].pr_cmd = i;
  643. X                       /*
  644. X
  645. X                       If you've settled on a reliable MAXACTIVE value,
  646. NOTE =>                you'll get a slight performance gain by replacing
  647. X                       the following if-statement with this line:
  648. X
  649. X                           k++;
  650. X
  651. X                       */
  652. X                           if (k < MAXACTIVE - 1)
  653. X                               k++;
  654. X                           else
  655. X                               printf("\ncputt: MAXACTIVE limit exceeded.\n");
  656. X                       }
  657. X                   }
  658. X                   else
  659. X                   {
  660. X                       getcmd(newproc[i],cmds[i]);
  661. X                   }
  662. X               } 
  663. X
  664. X          /* check process table variance at each iteration */
  665. X
  666. X          if (trim &&
  667. X             (i-j < GROWTH_BUF - MAX_GROW || i-j > GROWTH_BUF + MAX_SHRINK))
  668. X          {
  669. X              printf("\nProcess table variance greater than %d, restarting..\n",
  670. X                      MAX_GROW + MAX_SHRINK);
  671. X              execvp(argv[0],argv);
  672. X          }
  673. X
  674. X          /* sort and print results */
  675. X
  676. X          qsort(pdata,k,sizeof(struct procdata),cmp); 
  677. X          printf("\n  PID USER        CPU    MEM  COMMANDS - %d %d\n",k,j);
  678. X          for (i=--k; k>-1 && k>i-MAXOUT; --k)
  679. X          {
  680. X               printf("%5d ",pdata[k].proc->p_pid);
  681. X               if (hp = hashuid(pdata[k].proc->p_suid))
  682. X                   printf("%-8.8s ", hp->h_uname);
  683. X               else
  684. X                   printf("user%-4.4d ", pdata[k].proc->p_uid);
  685. X               printf("%6.2f ",(float)pdata[k].pctcpu/100);
  686. X               printf("%6.2f ",percentmem(pdata[k].proc));
  687. X               printf(" %s\n",cmds[pdata[k].pr_cmd]);
  688. X          }
  689. X       
  690. X          /* swap old and new pointers */
  691. X
  692. X          tusage   = oldusage;   tproc   = oldproc;
  693. X          oldusage = newusage;   oldproc = newproc;
  694. X          newusage = tusage;     newproc = tproc;
  695. X          ot = nt;
  696. X
  697. X     }    /* for all iterations.. */
  698. X
  699. X}    /* main */
  700. END_OF_FILE
  701. if test 7210 -ne `wc -c <'main.c'`; then
  702.     echo shar: \"'main.c'\" unpacked with wrong size!
  703. fi
  704. # end of 'main.c'
  705. fi
  706. if test -f 'routines.c' -a "${1}" != "-c" ; then 
  707.   echo shar: Will not clobber existing file \"'routines.c'\"
  708. else
  709. echo shar: Extracting \"'routines.c'\" \(2758 characters\)
  710. sed "s/^X//" >'routines.c' <<'END_OF_FILE'
  711. X# include       "cputt.h"
  712. X# include       <varargs.h>
  713. X# include       <kvm.h>
  714. X# include       <stdio.h>
  715. X
  716. X/*
  717. X   GETUPAGE - Reads the upage for the specified process as well as sufficient
  718. X   page tables entries for reading the command arguments. The pte's are read
  719. X   into the argument `p'. The upage is read into the external variable
  720. X   `User'. Returns 1 if the upage was successfully read.
  721. X*/
  722. X
  723. getupage ( p )
  724. X
  725. register struct proc        *p ;
  726. X
  727. X{
  728. X     struct user            *upage ;
  729. X     extern union userstate  User ;
  730. X     extern kvm_t           *Flkvm ;
  731. X
  732. X     if (upage = kvm_getu( Flkvm, p ))
  733. X     {
  734. X          bcopy( (char *)upage, User.u_pg[0], sizeof( struct user ) ) ;
  735. X          return ( 1 ) ;
  736. X     }
  737. X/*
  738. X     fprintf( stderr, "cputt - Can't read upage of process %d s= %o f= %o\n",
  739. X              p->p_pid, p->p_stat, p->p_flag ) ;
  740. X*/
  741. X     return ( 0 ) ;
  742. X}
  743. X
  744. X/* READSTATUS - Reads process table from kernel memory */
  745. X
  746. readstatus ( ptable )
  747. X
  748. struct proc *ptable;
  749. X
  750. X{
  751. X     extern struct info      Info ;
  752. X
  753. X     if (getkmem((long)Info.i_proc0,(char*)ptable,Info.i_sproc) != Info.i_sproc)
  754. X     {
  755. X          fprintf(stderr,"cputt - Can't read system process table\n" ) ;
  756. X          exit();
  757. X     }
  758. X}
  759. X
  760. X/* PERCENTMEM - Returns the percentage of real memory used by this process */
  761. X
  762. double  percentmem ( p )
  763. X
  764. struct proc         *p ;
  765. X
  766. X{
  767. X     double                  fracmem ;
  768. X     extern struct info      Info ;
  769. X
  770. X     if ( !(p->p_flag & SLOAD) )
  771. X          return ( 0.0 ) ;
  772. X     fracmem = ( (double)p->p_rssize + UPAGES ) ;
  773. X     return ( 100.0 * fracmem / (double)Info.i_ecmx ) ;
  774. X}
  775. X
  776. X/* GETKMEM - read kernel memory */
  777. X
  778. getkmem ( addr, buf, bufsize )
  779. X
  780. long                            addr ;
  781. char                            *buf ;
  782. int                             bufsize ;
  783. X{
  784. X     extern kvm_t            *Flkvm ;
  785. X
  786. X     return( kvm_read( Flkvm, (long)addr, buf, bufsize ) ) ;
  787. X}
  788. X
  789. X/* SYSPERROR - Reports a system defined error msg and then exits gracefully */
  790. X
  791. sysperror ()
  792. X{
  793. X     extern int              errno ;
  794. X     extern int              sys_nerr ;
  795. X     extern char             *sys_errlist[] ;
  796. X
  797. X     if ( 0 < errno && errno < sys_nerr )
  798. X          fprintf( stderr, " : %s", sys_errlist[errno] ) ;
  799. X     (void)fputc( '\n', stderr ) ;
  800. X     exit( 1 ) ;
  801. X}
  802. X
  803. X/* GETCORE - Allocate and return a pointer to the asked for amount of core */
  804. X
  805. char    *getcore ( size )
  806. X
  807. register int                    size ;
  808. X
  809. X{
  810. X     register char           *chp ;
  811. X     char                    *malloc() ;
  812. X
  813. X     if ( chp = malloc( (unsigned)size ) )
  814. X          return ( chp ) ;
  815. X     fprintf( stderr, "cputt - Out of core" ) ;
  816. X     sysperror() ;
  817. X}
  818. X
  819. X/* CMP - used by qsort() to perform comparisons */
  820. X
  821. int cmp(p1,p2)
  822. X
  823. register struct procdata *p1, *p2;
  824. X
  825. X{
  826. X     return (p1->pctcpu - p2->pctcpu);
  827. X}   
  828. END_OF_FILE
  829. if test 2758 -ne `wc -c <'routines.c'`; then
  830.     echo shar: \"'routines.c'\" unpacked with wrong size!
  831. fi
  832. # end of 'routines.c'
  833. fi
  834. echo shar: End of shell archive.
  835. exit 0
  836.