📄 sa.c
字号:
#ifndef lintstatic char *sccsid = "@(#)sa.c 4.2 ULTRIX 1/3/91";#endif/************************************************************************ * * * Copyright (c) 1991 by * * Digital Equipment Corporation, Maynard, MA * * All rights reserved. * * * * This software is furnished under a license and may be used and * * copied only in accordance with the terms of such license and * * with the inclusion of the above copyright notice. This * * software or any other copies thereof may not be provided or * * otherwise made available to any other person. No title to and * * ownership of the software is hereby transferred. * * * * This software is derived from software received from the * * University of California, Berkeley, and from Bell * * Laboratories. Use, duplication, or disclosure is subject to * * restrictions under license agreements with University of * * California and with AT&T. * * * * The information in this software is subject to change without * * notice and should not be construed as a commitment by Digital * * Equipment Corporation. * * * * Digital assumes no responsibility for the use or reliability * * of its software on equipment which is not supplied by Digital. * * * ************************************************************************//* * Modification History * * 1/3/91 - dlong * Modified to eliminate unnecessary global declarations, particularily * "bcmp()" which was conflicting with a libc routine. *//* * Extensive modifications to internal data structures * to allow arbitrary number of different commands and users added. * * Also allowed the digit option on the -v flag (interactive * threshold compress) to be a digit string, so one can * set the threshold > 9. * * Also added the -f flag, to force no interactive threshold * compression with the -v flag. * * Robert Henry * UC Berkeley * 31jan81 * * aps00 9/21/83 -- fixed code in doacct() so that the -u * would work properly. * * aps01 9/21/83 -- changed doacct() so that login names are * printed instead of UID's during -u option * *//* * Modified By : Aki Hirai , Digital Equipment Corp. * 30 - May - 1985 * * /aki00/ ------- Fix a floaing point violation problem when -t flag * /aki01/ ------- Fix a rubbish output problem when -u flag * /aki02/ ------- Fix a rubbish command name problem when AFORK * Jon Reeves, November 1988 * /jlr001/ ------ Add portable fix-point conversion code */#include <stdio.h>#include <sys/types.h>#include <sys/acct.h>#include <signal.h>#include <utmp.h>#include <pwd.h>/* interpret command time accounting */#define NC sizeof(acctbuf.ac_comm)#ifndef AHZ#define AHZ 60#endif#ifdef vax#define SCALEF 2#else#define SCALEF 1#endifstatic struct acct acctbuf;static int lflg;static int cflg;static int Dflg;static int dflg;static int iflg;static int jflg;static int Kflg;static int kflg;static int nflg;static int aflg;static int rflg;static int oflg;static int tflg;static int vflg;static int fflg;static int uflg;static int thres = 0;static int sflg;static int bflg;static int mflg;static struct utmp utmp;#define NAMELG (sizeof(utmp.ut_name)+1)struct Olduser{ int Us_cnt; double Us_ctime; double Us_io; double Us_imem;}; struct user { char name[NC]; /* this is <\001><user id><\000> */ struct Olduser oldu; char us_name[NAMELG];};#define us_cnt oldu.Us_cnt#define us_ctime oldu.Us_ctime#define us_io oldu.Us_io#define us_imem oldu.Us_imem/* * We protect ourselves from preposterous user id's by looking * through the passwd file for the highest uid allocated, and * then adding 10 to that. * This prevents the user structure from growing too large. */#define USERSLOP 10static int maxuser = -1; /* highest uid from /etc/passwd, + 10 for slop*/struct process { char name[NC]; int count; double realt; double cput; double syst; double imem; double io;};union Tab{ struct process p; struct user u;};typedef union Tab cell;static int (*cmp)(); /* compares 2 cells; set to appropriate func */static cell *enter();static struct user *finduser();static struct user *wasuser();/* * Table elements are keyed by the name of the file exec'ed. * Because on large systems, many files can be exec'ed, * a static table size may grow to be too large. * * Table elements are allocated in chunks dynamically, linked * together so that they may be retrieved sequentially. * * An index into the table structure is provided by hashing through * a seperate hash table. * The hash table is segmented, and dynamically extendable. * Realize that the hash table and accounting information is kept * in different segments! * * We have a linked list of hash table segments; within each * segment we use a quadratic rehash that touches no more than 1/2 * of the buckets in the hash table when probing. * If the probe does not find the desired symbol, it moves to the * next segment, or allocates a new segment. * * Hash table segments are kept on the linked list with the first * segment always first (that will probably contain the * most frequently executed commands) and * the last added segment immediately after the first segment, * to hopefully gain something by locality of reference. * * We store the per user information in the same structure as * the per exec'ed file information. This allows us to use the * same managers for both, as the number of user id's may be very * large. * User information is keyed by the first character in the name * being a '\001', followed by four bytes of (long extended) * user id number, followed by a null byte. * The actual user names are kept in a seperate field of the * user structure, and is filled in upon demand later. * Iteration through all users by low user id to high user id * is done by just probing the table, which is gross. */#define USERKEY '\001'#define ISPROCESS(tp) (tp->p.name[0] && (tp->p.name[0] != USERKEY))#define ISUSER(tp) (tp->p.name[0] && (tp->p.name[0] == USERKEY))#define TABDALLOP 500struct allocbox{ struct allocbox *nextalloc; cell tabslots[TABDALLOP];};static struct allocbox *allochead; /*head of chunk list*/static struct allocbox *alloctail; /*tail*/static struct allocbox *newbox; /*for creating a new chunk*/static cell *nexttab; /*next table element that is free*/static int tabsleft; /*slots left in current chunk*/static int ntabs;/* * Iterate through all symbols in the symbol table in declaration * order. * struct allocbox *allocwalk; * cell *sp, *ub; * * sp points to the desired item, allocwalk and ub are there * to make the iteration go. */#define DECLITERATE(allocwalk, walkpointer, ubpointer) \ for(allocwalk = allochead; \ allocwalk != 0; \ allocwalk = allocwalk->nextalloc) \ for (walkpointer = &allocwalk->tabslots[0],\ ubpointer = &allocwalk->tabslots[TABDALLOP], \ ubpointer = ubpointer > ( (cell *)alloctail) \ ? nexttab : ubpointer ;\ walkpointer < ubpointer; \ walkpointer++ )#define TABCHUNKS(allocwalk, tabptr, size) \ for (allocwalk = allochead; \ allocwalk != 0; \ allocwalk = allocwalk->nextalloc) \ if ( \ (tabptr = &allocwalk->tabslots[0]), \ (size = \ ( (&allocwalk->tabslots[TABDALLOP]) \ > ((cell *)alloctail) \ ) \ ? (nexttab - tabptr) : TABDALLOP \ ), \ 1 \ )#define PROCESSITERATE(allocwalk, walkpointer, ubpointer) \ DECLITERATE(allocwalk, walkpointer, ubpointer) \ if (ISPROCESS(walkpointer))#define USERITERATE(allocwalk, walkpointer, ubpointer) \ DECLITERATE(allocwalk, walkpointer, ubpointer) \ if (ISUSER(walkpointer))/* * When we have to sort the segmented accounting table, we * create a vector of sorted queues that is merged * to sort the entire accounting table. */struct chunkdesc { cell *chunk_tp; int chunk_n;};/* * Hash table segments and manager */#define NHASH 1103struct hashdallop { int h_nused; struct hashdallop *h_next; cell *h_tab[NHASH];};static struct hashdallop *htab; /* head of the list */static int htabinstall = 1; /* install the symbol */static double treal;static double tcpu;static double tsys;static double tio;static double timem;static cell *junkp = 0;static char *sname;static double ncom;#ifndef vax /* JLR001 */static time_t expand();#endifstatic char *getname();/* * usracct saves records of type Olduser. * There is one record for every possible uid less than * the largest uid seen in the previous usracct or in savacct. * uid's that had no activity correspond to zero filled slots; * thus one can index the file and get the user record out. * It would be better to save only user information for users * that the system knows about to save space, but that is not * upward compatabile with the old system. * * In the old version of sa, uid's greater than 999 were not handled * properly; this system will do that. */#ifdef DEBUG#define USRACCT "./usracct"#define SAVACCT "./savacct"#define ACCT "./acct"#else#define USRACCT "/usr/adm/usracct"#define SAVACCT "/usr/adm/savacct"#define ACCT "/usr/adm/acct"#endif DEBUGstatic int cellcmp();/* * The threshold is built up from digits in the argv ; * eg, -v1s0u1 * will build a value of thres of 101. * * If the threshold is zero after processing argv, it is set to 1 *//* we assume pagesize is at least 1k */static int pgdiv;#define pgtok(x) ((x) / pgdiv)static tcmp(), ncmp(), bcmp(), dcmp(), Dcmp(), kcmp(), Kcmp();extern double sum();main(argc, argv) char **argv;{ FILE *ff; double ft; register struct allocbox *allocwalk; register cell *tp, *ub; int i, j, size, nchunks, smallest; struct chunkdesc *chunkvector; pgdiv = getpagesize() / 1024; if (pgdiv == 0) pgdiv = 1; maxuser = USERSLOP + getmaxuid(); tabinit(); cmp = tcmp; if (argc>1) if (argv[1][0]=='-') { argv++; argc--; for(i=1; argv[0][i]; i++) switch(argv[0][i]) { case 'o': oflg++; break; case 'i': iflg++; break; case 'b': bflg++; cmp = bcmp; break; case 'l': lflg++; break; case 'c': cflg++; break; case 'd': dflg++; cmp = dcmp; break; case 'D': Dflg++; cmp = Dcmp; break; case 'j': jflg++; break; case 'k': kflg++; cmp = kcmp; break; case 'K': Kflg++; cmp = Kcmp; break; case 'n': nflg++; cmp = ncmp; break; case 'a': aflg++; break; case 'r': rflg++; break; case 't': tflg++; break; case 's': sflg++; aflg++; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': thres = thres * 10 + (argv[0][i]-'0'); break; case 'v': vflg++; break; case 'f': fflg++; /* force v option; no tty interaction */ break; case 'u': uflg++; break; case 'm': mflg++; break; } } if (thres == 0) thres = 1; if (iflg==0) init(); if (argc<2) doacct(ACCT); else while (--argc) doacct(*++argv); if (uflg) { return; }/* * cleanup pass * put junk together */ if (vflg) strip(); if(!aflg) PROCESSITERATE(allocwalk, tp, ub){ for(j=0; j<NC; j++) if(tp->p.name[j] == '?') goto yes; if(tp->p.count != 1) continue; yes: if(junkp == 0) junkp = enter("***other"); junkp->p.count += tp->p.count; junkp->p.realt += tp->p.realt; junkp->p.cput += tp->p.cput; junkp->p.syst += tp->p.syst; junkp->p.imem += tp->p.imem; junkp->p.io += tp->p.io; tp->p.name[0] = 0; } if (sflg) { signal(SIGINT, SIG_IGN); if ((ff = fopen(USRACCT, "w")) != NULL) { static struct user ZeroUser = {0}; struct user *up; int uid; /* * Write out just enough user slots, * filling with zero slots for users that * weren't found. * The file can be indexed directly by uid * to get the correct record. */ for (uid = 0; uid < maxuser; uid++){ if ( (up = wasuser(uid)) != 0) fwrite((char *)&(up->oldu), sizeof(struct Olduser),1,ff); else fwrite((char *)&(ZeroUser.oldu), sizeof(struct Olduser),1,ff); } } if ((ff = fopen(SAVACCT, "w")) == NULL) { printf("Can't save\n"); exit(0); } PROCESSITERATE(allocwalk, tp, ub) fwrite((char *)&(tp->p), sizeof(struct process), 1, ff); fclose(ff); creat(sname, 0644); signal(SIGINT, SIG_DFL); }/* * sort and print */ if (mflg) { printmoney(); exit(0); } column(ncom, treal, tcpu, tsys, timem, tio); printf("\n"); /* * the fragmented table is sorted by sorting each fragment * and then merging. */ nchunks = 0; TABCHUNKS(allocwalk, tp, size){ qsort(tp, size, sizeof(cell), cellcmp); nchunks ++; } chunkvector = (struct chunkdesc *)calloc(nchunks, sizeof(struct chunkdesc)); nchunks = 0; TABCHUNKS(allocwalk, tp, size){ chunkvector[nchunks].chunk_tp = tp; chunkvector[nchunks].chunk_n = size; nchunks++; } for(; nchunks; ){ /* * Find the smallest element at the head of the queues. */ smallest = 0; for (i = 1; i < nchunks; i++){ if (cellcmp(chunkvector[i].chunk_tp, chunkvector[smallest].chunk_tp) < 0) smallest = i; } tp = chunkvector[smallest].chunk_tp++; /* * If this queue is drained, drop the chunk count, * and readjust the queues. */ if (--chunkvector[smallest].chunk_n == 0){ nchunks--; for (i = smallest; i < nchunks; i++) chunkvector[i] = chunkvector[i+1]; } if (ISPROCESS(tp)){ ft = tp->p.count; column(ft, tp->p.realt, tp->p.cput, tp->p.syst, tp->p.imem, tp->p.io); printf(" %.14s\n", tp->p.name); } } /* iterate to merge the lists */}static printmoney(){ register i; register char *cp; register struct user *up; getnames(); /* fetches all of the names! */ for (i = 0; i < maxuser; i++) { if ( (up = wasuser(i)) != 0){ if (up->us_cnt) { if (up->us_name[0]) printf("%-8s", up->us_name); else printf("%-8d", i); printf("%7u %9.2fcpu %10.0ftio %12.0fk*sec\n", up->us_cnt, up->us_ctime / 60, up->us_io, up->us_imem / (AHZ * SCALEF)); } } }}static column(n, a, b, c, d, e) double n, a, b, c, d, e;{ printf("%8.0f", n); if(cflg) { if(n == ncom) printf("%9s", ""); else printf("%8.2f%%", 100.*n/ncom); } col(n, a, treal, "re"); if (oflg) col(n, 60*AHZ*(b/(b+c)), tcpu+tsys, "u/s"); else if(lflg) { col(n, b, tcpu, "u"); col(n, c, tsys, "s"); } else col(n, b+c, tcpu+tsys, "cp"); if(tflg)/* aki00 */ if(b+c) printf("%8.1fre/cp", a/(b+c)); else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -