source: trunk/third/top/machine/m_next32.c @ 9084

Revision 9084, 28.2 KB checked in by ghudson, 28 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r9083, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * top - a top users display for Unix
3 * NEXTSTEP v.0.3  2/14/1996 tpugh
4 *
5 * SYNOPSIS:  any m68k or intel NEXTSTEP v3.x system
6 *
7 * DESCRIPTION:
8 *      This is the machine-dependent module for NEXTSTEP v3.x
9 *      Reported to work for NEXTSTEP v3.0, v3.2, and v3.3 Mach OS:
10 *              NEXTSTEP v3.0 on Motorola machines.
11 *              NEXTSTEP v3.2 on Intel and Motorola machines.
12 *              NEXTSTEP v3.3 on Intel and Motorola machines.
13 *      Problem with command column for (Choose next40 for fix):
14 *              NEXTSTEP v3.2 on HP machines.
15 *              NEXTSTEP v3.3 on HP and Sparc machines.
16 *      Has not been tested for NEXTSTEP v2.x machines, although it should work.
17 *      Has not been tested for NEXTSTEP v3.1 machines, although it should work.
18 *      Install "top" with the permissions 4755.
19 *              hostname# chmod 4755 top
20 *              hostname# ls -lg top
21 *              -rwsr-xr-x  1 root     kmem      121408 Sep  1 10:14 top*
22 *      With the kmem group sticky bit set, we can read kernal memory without problems,
23 *      but to communicate with the Mach kernal for task and thread info, it requires
24 *      root privileges. Therefore, "top" must be setuid 4755 with the owner as root.
25 *
26 * LIBS:
27 *
28 * Need the compiler flag, "-DSHOW_UTT", to see the user task and thread task
29 * data structures to report process info.
30 *
31 * CFLAGS: -DSHOW_UTT
32 *
33 *
34 * AUTHORS:             Tim Pugh <tpugh@oce.orst.edu>
35 */
36
37#include <sys/types.h>
38#include <sys/signal.h>
39#include <sys/param.h>
40
41#include <stdio.h>
42#include <nlist.h>
43#include <math.h>
44#include <sys/dir.h>
45#include <sys/user.h>
46#include <sys/proc.h>
47#include <sys/dk.h>
48#include <sys/vm.h>
49#include <sys/file.h>
50#include <sys/time.h>
51#import <mach/mach.h>
52#include <sys/vmmeter.h>
53#import <mach/vm_statistics.h>
54
55#import "machine/m_next_task.h"
56
57/*  Problems on NS/HPPA machines.  Also, not currently used by source code.
58 *#define DOSWAP
59 */
60
61#include "top.h"
62#include "machine.h"
63#include "utils.h"
64
65extern int errno, sys_nerr;
66extern char *sys_errlist[];
67#define strerror(e) (((e) >= 0 && (e) < sys_nerr) ? sys_errlist[(e)] : "Unknown error")
68
69#define VMUNIX  "/mach"
70#define KMEM    "/dev/kmem"
71#define MEM     "/dev/mem"
72#ifdef DOSWAP
73#define SWAP    "/dev/drum"
74#endif
75
76/* NeXT BSD process structure does not contain locations to hold info such as
77 * cpu usage, memory usage, resident core memory, or cpu time data.  So I've made
78 * a new process structure which composites the NeXT structure and the missing
79 * system info.
80 */
81struct proc_unix {
82        struct proc *p_self;            /* Each p_self points to a element in pbase. */
83        int p_pctcpu;                           /* Scaled percent cpu usage. */
84        int p_vsize;                            /* Total VM memory usage. */
85        int p_rsize;                            /* Resident core memory usage. */
86        int p_cptime;                           /* scaled CPU Time */
87        int run_state;                          /* Task run state. */
88        int flags;                                      /* Task state flags. */
89        int nthreads;                           /* Number of threads per Task. */
90        int cur_priority;                       /* Current main thread priority */
91};
92
93/* Contains the list of processes. */
94struct handle
95{
96    struct proc_unix *list;             /* points to list of valid proc pointer */
97    int count;                                  /* number of pointers */
98        int current;                            /* Index of the current process formatting */
99};
100
101/* declarations for load_avg */
102#include "loadavg.h"
103#define LSCALE  1000    /* scaling for "fixed point" arithmetic - <sys/kernel.h> */
104
105/* define what weighted cpu is. */
106/*
107 *#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
108 *                       ((pct) / (1.0 - exp((pp)->p_time * logcpu))))
109 */
110
111/*  The following three variables are not defined in NeXT's process structure.
112 *      So they are zeroed until other ways of obtaining the info are found.
113 */
114/* what we consider to be process size: */
115/* #define PROCSIZE(pp) ((pp)->p_tsize + (pp)->p_dsize + (pp)->p_ssize) */
116#define PROCSIZE(pp)    (0)
117
118/* #define P_RSSIZE(pp) ((pp)->p_rssize) */
119#define P_RSSIZE(pp)    (0)
120
121/* #define P_CPTICKS(pp)        ((pp)->p_cpticks) */
122#define P_CPTICKS(pp)   (0)
123
124
125extern int thread_stats(int p_pid, struct thread_basic_info *info, int *count);
126extern int mach_load_avg(void);
127extern kern_return_t task_stats(int p_pid, struct task_basic_info *info);
128
129/* definitions for indices in the nlist array */
130#define X_AVENRUN       0
131#define X_CCPU          1
132#define X_NPROC         2
133#define X_PROC          3
134#define X_TOTAL         4
135#define X_CP_TIME       5
136#define X_MPID          6
137#define X_HZ            7
138
139static struct nlist nlst[] = {
140    { "_avenrun" },             /* 0 */
141    { "_cpu_clk" },             /* 1 */
142    { "_max_proc" },    /* 2 */
143    { "_allproc" },             /* 3 */
144    { "_total" },               /* 4 */
145    { "_cp_time" },             /* 5 */
146    { "_mpid" },                /* 6 */
147    { "_hz" },                  /* 7 */
148    { 0 }
149};
150
151/*
152 *  These definitions control the format of the per-process area
153 */
154
155static char header[] =
156  "  PID X        STATE PRI NICE  THR VSIZE RSIZE   %MEM   %CPU   TIME COMMAND";
157/* static char header[] =
158 * "  PID X        STATE PRI NICE  THR VSIZE RSIZE   %MEM  %WCPU   %CPU   TIME COMMAND";
159 */
160
161/* 0123456   -- field to fill in starts at header+6 */
162#define UNAME_START 6
163
164#define Proc_format \
165        "%5d %-8.8s %-5s %3d %4d %4d %5s %5s %6.2f %6.2f %6s %.14s"
166/* #define Proc_format \
167 *      "%5d %-8.8s %-5s %3d %4d %4d %5s %5s %6.2f %6.2f %6.2f %6s %.14s"
168 */
169
170
171/* process state names for the "STATE" column of the display */
172/* the extra nulls in the string "run" are for adding a slash and
173   the processor number when needed */
174char *state_abbrev[] =
175{
176    "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop"
177};
178char *mach_state[] =
179{
180    "", "R", "T", "S", "U", "H"
181};
182char *flags_state[] =
183{
184    "", "W", "I"
185};
186
187/* these are for detailing the process states */
188int process_states[7];
189/* char *procstatenames[] = {
190 *    "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ",
191 *    " zombie, ", " stopped, ",
192 *    NULL
193 *};
194 */
195char *procstatenames[] = {
196    "", " running, ", " stopped, ", " sleeping, ", " uninterruptable, ",
197    " halted, ", " zombie ", NULL
198};
199
200
201static int kmem, mem;
202#ifdef DOSWAP
203static int swap;
204#endif
205
206/* values that we stash away in _init and use in later routines */
207
208/* static double logcpu; */
209
210/* these are retrieved from the kernel in _init */
211
212static unsigned long proc;
213static          int  nproc;
214static          long hz;
215static load_avg  ccpu;
216static          int  ncpu = 0;
217
218/* these are offsets obtained via nlist and used in the get_ functions */
219
220static unsigned long avenrun_offset;
221static unsigned long mpid_offset;
222static unsigned long total_offset;
223static unsigned long cp_time_offset;
224
225/* these are for calculating cpu state percentages */
226
227static long cp_time[CPUSTATES];
228static long cp_old[CPUSTATES];
229static long cp_diff[CPUSTATES];
230
231/* these are for detailing the cpu states */
232
233int cpu_states[4];
234char *cpustatenames[] = {
235    "user", "nice", "system", "idle", NULL
236};
237
238/* these are for detailing the memory statistics */
239int memory_stats[7];
240/* char *memorynames[] = {
241 *   "Real: ", "K/", "K act/tot  ", "Virtual: ", "K/",
242 *    "K act/tot  ", "Free: ", "K", NULL
243 * };
244 */
245char *memorynames[] = {
246    "K Tot, ", "K Act, ", "K Inact, ", "K Wired, ", "K Free, ", "K in, ", "K out ", NULL
247};
248
249/* these are for keeping track of the proc array */
250static int bytes;
251static int pref_count;
252static struct proc *pbase;
253static struct proc_unix *pref;
254
255/* these are for getting the memory statistics */
256
257static int pageshift;           /* log base 2 of the pagesize */
258
259/* define pagetok in terms of pageshift */
260#define pagetok(size) ((size) << pageshift)
261
262/* useful externals */
263extern int errno;
264extern char *sys_errlist[];
265
266long lseek();
267long time();
268
269machine_init(struct statics *statics)
270{
271    register int i = 0;
272    register int pagesize;
273       
274    if ((kmem = open(KMEM, O_RDONLY)) == -1) {
275        perror(KMEM);
276        return(-1);
277    }
278    if ((mem = open(MEM, O_RDONLY)) == -1) {
279        perror(MEM);
280        return(-1);
281    }
282
283#ifdef DOSWAP
284    if ((swap = open(SWAP, O_RDONLY)) == -1) {
285        perror(SWAP);
286        return(-1);
287    }
288#endif
289
290    /* get the list of symbols we want to access in the kernel */
291    (void) nlist(VMUNIX, nlst);
292    if (nlst[0].n_type == 0)
293    {
294                fprintf(stderr, "top: nlist failed\n");
295                return(-1);
296    }
297
298    /* make sure they were all found */
299    if (i > 0 && check_nlist(nlst) > 0)
300    {
301                return(-1);
302    }
303
304    /* get the symbol values out of kmem */
305    (void) getkval(nlst[X_PROC].n_value, (int *)(&proc), sizeof(proc),
306                                nlst[X_PROC].n_un.n_name);
307    (void) getkval(nlst[X_NPROC].n_value, &nproc, sizeof(nproc),
308                                nlst[X_NPROC].n_un.n_name);
309    (void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz),
310                                nlst[X_HZ].n_un.n_name);
311/*    (void) getkval(nlst[X_CCPU].n_value, (int *)(&ccpu), sizeof(ccpu),
312 *                              nlst[X_CCPU].n_un.n_name);
313 */
314
315    /* stash away certain offsets for later use */
316    mpid_offset = nlst[X_MPID].n_value;
317    avenrun_offset = nlst[X_AVENRUN].n_value;
318    total_offset = nlst[X_TOTAL].n_value;
319    cp_time_offset = nlst[X_CP_TIME].n_value;
320       
321
322    /* this is used in calculating WCPU -- calculate it ahead of time */
323/*      ccpu = mach_load_avg();
324 *   logcpu = log((double)(ccpu)/LOAD_SCALE);
325 */
326
327    /* allocate space for proc structure array and array of pointers */
328    bytes = nproc * sizeof(struct proc);
329    pbase = (struct proc *)malloc(bytes);
330    pref  = (struct proc_unix *)malloc((nproc+1) * sizeof(struct proc_unix *));
331
332    /* Just in case ... */
333    if (pbase == (struct proc *)NULL || pref == (struct proc_unix *)NULL)
334    {
335        fprintf(stderr, "top: can't allocate sufficient memory\n");
336        return(-1);
337    }
338
339    /* get the page size with "getpagesize" and calculate pageshift from it */
340    pagesize = getpagesize();
341    pageshift = ceil(log(pagesize)/log(2.0));
342
343    /* we only need the amount of log(2)1024 for our conversion */
344    pageshift -= LOG1024;
345
346    /* fill in the statics information */
347    statics->procstate_names = procstatenames;
348    statics->cpustate_names = cpustatenames;
349    statics->memory_names = memorynames;
350
351    /* all done! */
352    return(0);
353}
354
355char *format_header(register char *uname_field)
356{
357    register char *ptr;
358
359    ptr = header + UNAME_START;
360    while (*uname_field != '\0')
361    {
362        *ptr++ = *uname_field++;
363    }
364
365    return(header);
366}
367
368static int swappgsin = -1;
369static int swappgsout = -1;
370static vm_statistics_data_t vm_stats;
371static host_basic_info_data_t  host_stats;
372
373get_system_info(struct system_info *si)
374{
375    long avenrun[3];
376    long total;
377
378    /* get the cp_time array */
379    (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
380                   "_cp_time");
381
382    /* get load average array */
383    (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
384                   "_avenrun");
385
386    /* get mpid -- process id of last process */
387    (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid),
388                   "_mpid");
389
390    /* convert load averages to doubles */
391    {
392        register int i;
393        for(i=0; i<3; i++)
394                si->load_avg[i] = ((double)avenrun[i])/LSCALE;
395    }
396
397    /* convert cp_time counts to percentages */
398    total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
399
400    /* sum memory statistics */
401    {
402                /* get total -- systemwide main memory usage structure */
403                /* Does not work on NeXT system.  Use vm_statistics() for paging info. */
404                /* struct vmtotal total;
405                 * (void) getkval(total_offset, (int *)(&total), sizeof(total),
406                 *             "_total");
407                 */
408                /* convert memory stats to Kbytes */
409                /* memory_stats[0] = -1;
410                 * memory_stats[1] = pagetok(total.t_arm);
411                 * memory_stats[2] = pagetok(total.t_rm);
412                 * memory_stats[3] = -1;
413                 * memory_stats[4] = pagetok(total.t_avm);
414                 * memory_stats[5] = pagetok(total.t_vm);
415                 * memory_stats[6] = -1;
416                 * memory_stats[7] = pagetok(total.t_free);
417                 */
418                kern_return_t status;
419                unsigned int count=HOST_BASIC_INFO_COUNT;
420                status = vm_statistics(task_self(), &vm_stats);
421#ifdef DEBUG
422                if(status != KERN_SUCCESS)
423                mach_error("An error calling vm_statistics()!", status);
424#endif
425                status = host_info(host_self(), HOST_BASIC_INFO, (host_info_t)&host_stats, &count);
426#ifdef DEBUG
427                if(status != KERN_SUCCESS)
428                mach_error("An error calling host_info()!", status);
429#endif
430                /* convert memory stats to Kbytes */
431                memory_stats[0] = pagetok(host_stats.memory_size / vm_stats.pagesize);
432                memory_stats[1] = pagetok(vm_stats.active_count);
433                memory_stats[2] = pagetok(vm_stats.inactive_count);
434                memory_stats[3] = pagetok(vm_stats.wire_count);
435                memory_stats[4] = pagetok(vm_stats.free_count);
436        if (swappgsin < 0)
437                {
438                        memory_stats[5] = 1;
439                        memory_stats[6] = 1;
440                } else {
441                        memory_stats[5] = pagetok(((vm_stats.pageins - swappgsin)));
442                        memory_stats[6] = pagetok(((vm_stats.pageouts - swappgsout)));
443                }
444                swappgsin = vm_stats.pageins;
445                swappgsout = vm_stats.pageouts;
446    }
447
448    /* set arrays and strings */
449    si->cpustates = cpu_states;
450    si->memory = memory_stats;
451}
452
453static struct handle handle;
454
455caddr_t get_process_info(struct system_info *si,
456                                                 struct process_select *sel,
457                                                 int (*compare)())
458{
459    int i, j;
460    int total_procs;
461    int active_procs;
462    struct proc *pp;
463        struct task_basic_info taskInfo;
464        struct thread_basic_info threadInfo;
465        kern_return_t thread_status;
466        kern_return_t task_status;
467        int threadCount;
468
469    /* these are copied out of sel for speed */
470    int show_idle;
471    int show_system;
472    int show_uid;
473    int show_command;
474
475    /* get a pointer to the states summary array */
476    si->procstates = process_states;
477
478    /* set up flags which define what we are going to select */
479    show_idle = sel->idle;
480    show_system = sel->system;
481    show_uid = sel->uid != -1;
482    show_command = sel->command != NULL;
483
484    (void) getkval(nlst[X_PROC].n_value, (int *)(&proc), sizeof(proc),
485                                nlst[X_PROC].n_un.n_name);
486
487    /* count up process states and get pointers to interesting procs */
488    total_procs = 0;
489    active_procs = 0;
490    memset((char *)process_states, 0, sizeof(process_states));
491        i = 0;
492        j = 0;
493        do {
494                if(i == 0) {
495                        /* read first proc structure */
496                (void) getkval(proc, (int *)&pbase[i], sizeof(struct proc), "first proc");
497                } else {
498                (void) getkval(pp->p_nxt, (int *)&pbase[i], sizeof(struct proc), "nxt proc");
499                }
500                pp = &pbase[i];
501
502                thread_status = thread_stats(pp->p_pid, &threadInfo, &threadCount);
503                task_status = task_stats(pp->p_pid, &taskInfo);
504        /*
505         *  Process slots that are actually in use have a non-zero
506         *  status field.  Processes with SSYS set are system
507         *  processes---these get ignored unless show_sysprocs is set.
508         */
509                if (pp->p_stat != 0 &&
510                (show_system || ((pp->p_flag & SSYS) == 0)))
511                {
512                total_procs++;
513/* Using thread info for process states. */
514/*              process_states[pp->p_stat]++; */
515                        if(thread_status==KERN_SUCCESS)
516                        process_states[threadInfo.run_state]++;
517                if ((pp->p_stat != SZOMB) &&
518                                (show_idle || (pp->p_stat == SRUN)) &&
519                                (!show_uid || pp->p_uid == (uid_t)sel->uid))
520                {
521                                pref[j].p_self = pp;
522                                if(thread_status==KERN_SUCCESS)
523                                {
524                                        pref[j].run_state = threadInfo.run_state;
525                                        pref[j].flags = threadInfo.flags;
526                                        pref[j].p_pctcpu = threadInfo.cpu_usage;
527                                        pref[j].p_cptime = threadInfo.user_time.seconds +
528                                                                           threadInfo.system_time.seconds;
529                                        pref[j].cur_priority = threadInfo.cur_priority;
530                                        pref[j].nthreads = threadCount;
531                                } else {
532                                        pref[j].run_state = 0;
533                                        pref[j].flags = 0;
534                                        pref[j].p_pctcpu = 0;
535                                        pref[j].p_cptime = 0;
536                                }
537                                /* Get processes memory usage and cputime */
538                                if(task_status==KERN_SUCCESS)
539                                {
540                                        pref[j].p_rsize = taskInfo.resident_size/1024;
541                                        pref[j].p_vsize = taskInfo.virtual_size/1024;
542                                } else {
543                                        pref[j].p_rsize = 0;
544                                        pref[j].p_vsize = 0;
545                                }
546                                active_procs++;
547                                j++;
548                }
549                }
550                i++;
551        } while(pp->p_nxt != 0);
552        pref[j].p_self = NULL;  /*  End list of processes with NULL */
553
554    /* if requested, sort the "interesting" processes */
555     if (compare != NULL)
556    {
557                qsort((char *)pref, active_procs, sizeof(struct proc_unix), compare);
558    }
559
560    /* remember active and total counts */
561    si->p_total = total_procs;
562    si->p_active = pref_count = active_procs;
563
564    /* pass back a handle */
565    handle.list = pref;
566    handle.count = active_procs;
567    handle.current = 0;
568    return((caddr_t)&handle);
569}
570
571char fmt[MAX_COLS];             /* static area where result is built */
572
573char *format_next_process(caddr_t handle, char *(*get_userid)())
574{
575    register struct proc *pp;
576    register long cputime;
577    register double pct, wcpu, pctmem;
578    int where;
579    struct user u;
580    struct handle *hp;
581        register int p_pctcpu;
582        register int rm_size;
583        register int vm_size;
584        register int run_state;
585        register int flags;
586        register int nthreads;
587        register int cur_priority;
588        char state_str[10];
589
590    /* find and remember the next proc structure */
591    hp = (struct handle *)handle;
592        pp = hp->list[hp->current].p_self;
593    p_pctcpu = hp->list[hp->current].p_pctcpu;
594    cputime = hp->list[hp->current].p_cptime;
595    rm_size = hp->list[hp->current].p_rsize;
596    vm_size = hp->list[hp->current].p_vsize;
597        run_state = hp->list[hp->current].run_state;
598        flags = hp->list[hp->current].flags;
599        nthreads = hp->list[hp->current].nthreads;
600        cur_priority = hp->list[hp->current].cur_priority;
601        hp->current++;
602    hp->count--;
603
604    /* get the process's user struct and set cputime */
605    where = getu(pp, &u);
606    if (where == -1)
607    {
608                (void) strcpy(u.u_comm, "<swapped>");
609                cputime = 0;
610    }
611    else
612    {
613                /* set u_comm for system processes */
614                if (u.u_comm[0] == '\0')
615                {
616                if (pp->p_pid == 0)
617                {
618                                (void) strcpy(u.u_comm, "Swapper");
619                }
620                else if (pp->p_pid == 2)
621                {
622                                (void) strcpy(u.u_comm, "Pager");
623                }
624                        }
625                if (where == 1) {
626                /*
627                * Print swapped processes as <pname>
628                */
629                char buf[sizeof(u.u_comm)];
630                (void) strncpy(buf, u.u_comm, sizeof(u.u_comm));
631                u.u_comm[0] = '<';
632                (void) strncpy(&u.u_comm[1], buf, sizeof(u.u_comm) - 2);
633                u.u_comm[sizeof(u.u_comm) - 2] = '\0';
634                (void) strncat(u.u_comm, ">", sizeof(u.u_comm) - 1);
635                u.u_comm[sizeof(u.u_comm) - 1] = '\0';
636                }
637/*      User structure does not work.  Use Thread Info to get cputime for process. */
638/*              cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec; */
639    }
640
641
642    /* calculate the base for cpu percentages */
643    pct = (double)(p_pctcpu)/TH_USAGE_SCALE;
644/*      wcpu = weighted_cpu(pct, pp);
645 */
646        pctmem = (double)(rm_size*1024.) / (double)(host_stats.memory_size);
647       
648        /* Get process state description */
649        if(run_state)
650        {
651                strcpy(state_str, mach_state[run_state]);
652                strcat(state_str, flags_state[flags]);
653        } else {
654                strcpy(state_str, state_abbrev[pp->p_stat]);
655        }
656       
657    /* format this entry */
658    sprintf(fmt,
659            Proc_format,
660            pp->p_pid,
661            (*get_userid)(pp->p_uid),
662            state_str,
663                cur_priority,
664/*          pp->p_pri - PZERO, */
665            pp->p_nice - NZERO,
666                nthreads,
667            format_k(vm_size),
668            format_k(rm_size),
669            100.0 * pctmem,
670/*          100.0 * wcpu, */
671            100.0 * pct,
672            format_time(cputime),
673            printable(u.u_comm));
674
675    /* return the result */
676    return(fmt);
677}
678
679/*
680 *  getu(p, u) - get the user structure for the process whose proc structure
681 *      is pointed to by p.  The user structure is put in the buffer pointed
682 *      to by u.  Return 0 if successful, -1 on failure (such as the process
683 *      being swapped out).
684 */
685
686getu(register struct proc *p, struct user *u)
687{
688    register int nbytes, n;
689        struct task task;
690        struct utask utask;
691        struct uthread thread;
692
693    /*
694     *  Check if the process is currently loaded or swapped out.  The way we
695     *  get the u area is totally different for the two cases.  For this
696     *  application, we just don't bother if the process is swapped out.
697     */
698        /* NEXTSTEP proc.h
699         * One structure allocated per active
700         * process. It contains all data needed
701         * about the process while the
702         * process may be swapped out.
703         * Other per process data (user.h)
704         * is swapped with the process.
705         */
706
707    if ((p->p_flag & SLOAD) == 0) {
708/* User info is always in core.
709 * #ifdef DOSWAP
710 *              if (lseek(swap, (long)dtob(p->p_swaddr), 0) == -1) {
711 *              perror("lseek(swap)");
712 *              return(-1);
713 *              }
714 *              if (read(swap, (char *) u, sizeof(struct user)) != sizeof(struct user))  {
715 *              perror("read(swap)");
716 *              return(-1);
717 *              }
718 *              return (1);
719 * #else
720 */
721                return(-1);
722/*#endif
723 */
724    }
725
726    /*
727     *  Process is currently in memory, we hope!
728     */
729        if(!getkval(p->task, (int *)&task, sizeof(struct task), "task")) {
730#ifdef DEBUG
731                perror("getkval(p->task)");
732#endif
733                /* we can't seem to get to it, so pretend it's swapped out */
734                return(-1);
735        }
736
737        if(!getkval(task.u_address, (int *)&utask, sizeof(struct utask), "task.u_address")) {
738#ifdef DEBUG
739                perror("getkval(task->utask)");
740#endif
741                /* we can't seem to get to it, so pretend it's swapped out */
742                return(-1);
743        }
744
745        /* Copy utask and uthread info into struct user *u */
746        /*  This is incomplete.  Only copied info needed. */
747        u->u_procp = utask.uu_procp;
748        u->u_ar0 = utask.uu_ar0;
749        u->u_ru = utask.uu_ru;
750        strcpy(u->u_comm, utask.uu_comm);
751        nbytes = strlen(u->u_comm);
752        for(n=nbytes; n<MAXCOMLEN; n++)
753                u->u_comm[n] = ' ';
754        u->u_comm[MAXCOMLEN] = '\0';
755        return(0);
756}
757
758/*
759 * check_nlist(nlst) - checks the nlist to see if any symbols were not
760 *              found.  For every symbol that was not found, a one-line
761 *              message is printed to stderr.  The routine returns the
762 *              number of symbols NOT found.
763 */
764
765int check_nlist(register struct nlist *nlst)
766{
767    register int i;
768
769    /* check to see if we got ALL the symbols we requested */
770    /* this will write one line to stderr for every symbol not found */
771
772    i = 0;
773    while (nlst->n_un.n_name != NULL)
774    {
775        if (nlst->n_type == 0 && nlst->n_value == 0)
776        {
777            /* this one wasn't found */
778            fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_un.n_name);
779            i = 1;
780        }
781        nlst++;
782    }
783
784    return(i);
785}
786
787
788/*
789 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
790 *      "offset" is the byte offset into the kernel for the desired value,
791 *      "ptr" points to a buffer into which the value is retrieved,
792 *      "size" is the size of the buffer (and the object to retrieve),
793 *      "refstr" is a reference string used when printing error meessages,
794 *          if "refstr" starts with a '!', then a failure on read will not
795 *          be fatal (this may seem like a silly way to do things, but I
796 *          really didn't want the overhead of another argument).
797 *     
798 */
799
800getkval(unsigned long offset, int *ptr, int size, char *refstr)
801{
802    if (lseek(kmem, (long)offset, L_SET) == -1) {
803        if (*refstr == '!')
804            refstr++;
805        (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
806                       refstr, strerror(errno));
807        quit(23);
808    }
809    if (read(kmem, (char *) ptr, size) == -1) {
810        if (*refstr == '!')
811            return(0);
812        else {
813            (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM,
814                           refstr, strerror(errno));
815            quit(23);
816        }
817    }
818    return(1);
819}
820   
821/* comparison routine for qsort */
822
823/*
824 *  proc_compare - comparison function for "qsort"
825 *      Compares the resource consumption of two processes using five
826 *      distinct keys.  The keys (in descending order of importance) are:
827 *      percent cpu, cpu ticks, state, resident set size, total virtual
828 *      memory usage.  The process states are ordered as follows (from least
829 *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
830 *      array declaration below maps a process state index into a number
831 *      that reflects this ordering.
832 */
833
834static unsigned char sorted_state[] =
835{
836    0,  /* not used             */
837    3,  /* sleep                */
838    1,  /* ABANDONED (WAIT)     */
839    6,  /* run                  */
840    5,  /* start                */
841    2,  /* zombie               */
842    4   /* stop                 */
843};
844 
845proc_compare(struct proc_unix *pp1, struct proc_unix *pp2)
846{
847    register struct proc *p1 = pp1->p_self;
848    register struct proc *p2 = pp2->p_self;
849    register int result;
850    register pctcpu lresult;
851
852    /* compare percent cpu (pctcpu) */
853    if ((lresult = pp2->p_pctcpu - pp1->p_pctcpu) == 0)
854    {
855        /* use cpticks to break the tie */
856        if ((result = P_CPTICKS(p2) - P_CPTICKS(p1)) == 0)
857        {
858            /* use process state to break the tie */
859            if ((result = sorted_state[p2->p_stat] - sorted_state[p1->p_stat])  == 0)
860            {
861                /* use priority to break the tie */
862                if ((result = p2->p_pri - p1->p_pri) == 0)
863                {
864                    /* use resident set size (rssize) to break the tie */
865                    if ((result = pp2->p_rsize - pp1->p_rsize) == 0)
866                    {
867                        /* use total memory to break the tie */
868                        result = pp2->p_vsize - pp1->p_vsize;
869                    }
870                }
871            }
872        }
873    }
874    else
875    {
876        result = lresult < 0 ? -1 : 1;
877    }
878
879    return(result);
880}
881
882/*
883 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
884 *              the process does not exist.
885 *              It is EXTREMLY IMPORTANT that this function work correctly.
886 *              If top runs setuid root (as in SVR4), then this function
887 *              is the only thing that stands in the way of a serious
888 *              security problem.  It validates requests for the "kill"
889 *              and "renice" commands.
890 */
891
892int proc_owner(int pid)
893{
894    register int cnt;
895    register struct proc *pp;
896
897    cnt = pref_count;
898    while (--cnt >= 0)
899    {
900                pp = pref[cnt].p_self;
901                if( pp->p_pid == pid )  /* Modified (pid_t)pid to pid, compiler error. */
902                {
903                        return((int)pp->p_uid);
904                }
905    }
906    return(-1);
907}
908
909int thread_stats(int pid, struct thread_basic_info *info, int *thread_count)
910{
911        int                                       i;
912        kern_return_t             status;
913        kern_return_t                     status_dealloc;
914        task_t                                    p_task;
915        thread_array_t                    thread_list, list;
916        struct thread_basic_info  threadInfo;
917        unsigned int              info_count = THREAD_BASIC_INFO_COUNT;
918
919        /* Get the task pointer for the process. */
920        status = task_by_unix_pid( task_self(), pid, &p_task);
921        if (status!=KERN_SUCCESS)
922        {
923#ifdef DEBUG
924                printf("pid = %i\n", pid);
925        mach_error("Error calling task_by_unix_pid()", status);
926#endif
927                return status;
928        }
929       
930        /* Get the list of threads for the task. */
931        status = task_threads(p_task, &thread_list, thread_count);
932        if (status!=KERN_SUCCESS)
933        {
934#ifdef DEBUG
935        mach_error("Error calling task_threads()", status);
936#endif
937                return status;
938        }
939
940        /* Get the pctcpu value for each thread and sum the values */
941        info->user_time.seconds = 0;
942        info->user_time.microseconds = 0;
943        info->system_time.seconds = 0;
944        info->system_time.microseconds = 0;
945        info->cpu_usage = 0;
946        info->sleep_time = 0;
947
948        for(i=0; i<*thread_count; i++)
949        {
950                status = thread_info(thread_list[i], THREAD_BASIC_INFO,
951                                                (thread_info_t)&threadInfo, &info_count);
952                if (status!=KERN_SUCCESS)
953                {
954#ifdef DEBUG
955                mach_error("Error calling thread_info()", status);
956#endif
957                        break;
958                } else {
959                        if(i==0)
960                        {
961                                info->base_priority = threadInfo.base_priority;
962                                info->cur_priority = threadInfo.cur_priority;
963                                info->run_state = threadInfo.run_state;
964                                info->flags = threadInfo.flags;
965                                info->suspend_count = threadInfo.suspend_count;
966                                info->sleep_time += threadInfo.sleep_time;
967                        }
968                        info->user_time.seconds += threadInfo.user_time.seconds;
969                        info->user_time.microseconds += threadInfo.user_time.microseconds;
970                        info->system_time.seconds += threadInfo.system_time.seconds;
971                        info->system_time.microseconds += threadInfo.system_time.microseconds;
972                        info->cpu_usage += threadInfo.cpu_usage;
973                }
974        }
975
976        /* Deallocate the list of threads. */
977    status_dealloc = vm_deallocate(task_self(), (vm_address_t)thread_list,
978                                                   sizeof(thread_list)*(*thread_count));
979    if (status_dealloc != KERN_SUCCESS)
980        {
981#ifdef DEBUG
982        mach_error("Trouble freeing thread_list", status_dealloc);
983#endif
984                status = status_dealloc;
985        }
986        return status;
987}
988
989int mach_load_avg(void)
990{
991        kern_return_t                    status;
992        host_t                           host;
993        unsigned int                     info_count;
994        struct processor_set_basic_info  info;
995        processor_set_t                  default_set;
996
997        status=processor_set_default(host_self(), &default_set);
998        if (status!=KERN_SUCCESS){
999        mach_error("Error calling processor_set_default", status);
1000        exit(1);
1001        }
1002
1003        info_count=PROCESSOR_SET_BASIC_INFO_COUNT;
1004        status=processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO,
1005                                                        &host, (processor_set_info_t)&info, &info_count);
1006#ifdef DEBUG
1007        if (status != KERN_SUCCESS)
1008        mach_error("Error calling processor_set_info", status);
1009#endif
1010        return info.load_average;
1011}
1012
1013kern_return_t task_stats(int pid, struct task_basic_info *info)
1014{
1015        kern_return_t             status;
1016        task_t                                    p_task;
1017        unsigned int              info_count=TASK_BASIC_INFO_COUNT;
1018
1019        /* Get the task pointer for the process. */
1020        status = task_by_unix_pid( task_self(), pid, &p_task);
1021        if (status!=KERN_SUCCESS) {
1022#ifdef DEBUG
1023                printf("pid = %i\n", pid);
1024        mach_error("Error calling task_by_unix_pid()", status);
1025#endif
1026                return(status);
1027        }
1028
1029        status=task_info(p_task, TASK_BASIC_INFO, (task_info_t)info, &info_count);
1030        if (status!=KERN_SUCCESS) {
1031#ifdef DEBUG
1032        mach_error("Error calling task_info()", status);
1033#endif
1034                return(status);
1035        }               
1036        return(KERN_SUCCESS);
1037}
Note: See TracBrowser for help on using the repository browser.