Logo Search packages:      
Sourcecode: qps version File versions  Download package

proc_solaris.cpp

// proc.cpp for Solaris (SunOS)
//
// This program is free software. See the file COPYING for details.
// Author: Mattias Engdegĺrd, 1997-1999
//             José Luis Sánchez, 2005

#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sched.h>

#include <qdir.h>

#include "qps.h"
#include "proc.h"
#include "svec.cpp"
#include "uidstr.h"
#include "ttystr.h"
#include "wchan.h"
#include "details.h"

#include <sys/swap.h>
#include <sys/sysinfo.h>
#include <sys/mkdev.h>
#include <limits.h>


#include "proc_common.cpp"

// socket states, from <linux/net.h> and touched to avoid name collisions
enum {
    SSFREE = 0,               /* not allocated        */
    SSUNCONNECTED,            /* unconnected to any socket  */
    SSCONNECTING,             /* in process of connecting   */
    SSCONNECTED,              /* connected to socket        */
    SSDISCONNECTING           /* in process of disconnecting      */
};

char procdir[128] = "/proc";

int Procinfo::page_k_shift;

int proc_PID_fd(const int pid)
{
    char path[128];
    int numfd;
    
    sprintf(path, "/proc/%d/fd", pid);

    QDir qdir(path);
    numfd = qdir.count();
    num_opened_files += numfd;
    //    printf("PID %d: %d opened files\n", pid, numfd);
    return TRUE;
}


    Procinfo::Procinfo(int proc_pid)
: refcnt(1)
{
    detail = NULL;    
    children = 0;
    fd_files = 0;
    maps = 0;

    envblock = 0;
    if( readproc(proc_pid) < 0 )
        pid = -1;       // invalidate object, will be deleted

    selected = FALSE;
    hidekids = FALSE;
}

    Procinfo::Procinfo(int proc_pid, int lwp)
: refcnt(1)
{
    detail = 0;
    children = 0;
    fd_files = 0;
    maps = 0;

    envblock = 0;
    if( readproc(proc_pid, lwp) < 0 )
        pid = -1;       // invalidate object, will be deleted

    selected = FALSE;
    hidekids = FALSE;
}

Procinfo::~Procinfo()
{
    if( detail )
    {
        detail->process_gone();
        detail = 0;
    }

    if( envblock )
        free(envblock);

    if( maps )
    {
        maps->purge();
        delete maps;
    }

    if( fd_files )
    {
        fd_files->purge();
        delete fd_files;
    }
    delete children;
}

// miscellaneous static initializations
void Procinfo::init_static()
{
    if( !kc )
    {
        kc = kstat_open();
        if( !kc )
        {
            perror("kstat_open");
            exit(1);
        }
    }

    page_k_shift = 0;
    for( int j = getpagesize(); j > 1024; j >>= 1 )
        page_k_shift++;
}

// return number of bytes read if ok, -1 if failed
int Procinfo::read_file(char *name, void *buf, int max)
{
    int fd = open(name, O_RDONLY);
    if( fd < 0 )
        return -1;
    int r = read(fd, buf, max);
    close(fd);
    return r;
}


//  Description : read /proc/1234/task/*  tasks(thread,LWP)  
//            add to Proc::procs[]  
int Proc::read_pid_tasks(int pid)
{
    char    path[256];
    char    *p;
    struct  dirent *e;
    int     thread_id;
    int     thread_n=0;

    //      printf("DEBUG:pid=%d :",pid);
    sprintf(path, "%s/%d/lwp",procdir, pid);

    DIR *d = opendir(path);
    if(!d) return -1;


    while((e = readdir(d)) != 0) {
        if(e->d_name[0] == '.')     continue;   // skip "." , ".."

        thread_id = atoi(e->d_name); // only number !!
        //        printf("tid=%d ",thread_id);

        if(pid!=thread_id)
        {
            Procinfo *pi = new Procinfo(pid, thread_id);

            if(pi->pid == -1)
                delete pi;          // already gone

            else {
                pi->generation = current_gen;
                pi->cmdline="(thread)";
                newproc(pi);
            }

        }

        thread_n++;
    }
    ///printf("\n");
    closedir(d);                    
    return thread_n;
}

int Procinfo::readproc(int proc_pid)
{
    char path[256];

    pid = proc_pid;

    proc_PID_fd(pid);
    sprintf(path, "%s/%d/psinfo", procdir, proc_pid);
    psinfo_t psi;
    if( read_file(path, (void *)&psi, sizeof(psi)) < (int)sizeof(psi) )
        return -1;

    sprintf(path, "%s/%d/usage", procdir, proc_pid);
    prusage_t pru;
    if( read_file(path, (void *)&pru, sizeof(pru)) < (int)sizeof(pru) )
        return -1;

    uid = psi.pr_uid;
    euid = psi.pr_euid;
    gid = psi.pr_gid;
    egid = psi.pr_egid;

    make_printable(psi.pr_psargs);
    cmdline = psi.pr_psargs;

    state = psi.pr_lwp.pr_sname;
    command = (state == 'Z') ? "<zombie>" : psi.pr_fname;
    ppid = psi.pr_ppid;
    pgrp = psi.pr_pgid;
    session = psi.pr_sid;
    tty = psi.pr_ttydev;      // type?
    flags = psi.pr_flag;
    const int ns_ticks = 1000000000 / HZ;
    utime = psi.pr_time.tv_sec * HZ + psi.pr_time.tv_nsec / ns_ticks;
    cutime = psi.pr_ctime.tv_sec * HZ + psi.pr_ctime.tv_nsec / ns_ticks;
    priority = psi.pr_lwp.pr_pri;
    nice = psi.pr_lwp.pr_nice;
    if( Qps::normalize_nice )
        nice -= NZERO;
    starttime = (psi.pr_start.tv_sec - boot_time) * HZ
        + psi.pr_start.tv_nsec / ns_ticks;
    wchan = psi.pr_lwp.pr_wchan;
    minflt = pru.pr_minf;
    majflt = pru.pr_majf;
    size = psi.pr_size;
    resident = psi.pr_rssize;
    nthreads = psi.pr_nlwp;
    addr_bits = psi.pr_dmodel == PR_MODEL_ILP32 ? 32 : 64;
    which_cpu = psi.pr_lwp.pr_onpro;
    env_ofs = psi.pr_envp;

    // pr_pctcpu and pr_pctmem are scaled so that 1.0 is stored as 0x8000.
    // We rescale pcpu so a CPU-bound process is shown as 100%. (This means
    // that wcpu may exceed 100% with several LWPs.)
    wcpu = psi.pr_pctcpu * (1 / 327.68) * num_cpus;
    pmem = psi.pr_pctmem * (1 / 327.68);

    gettimeofday(&tv, 0);
    rtprio = -1;        // ditto
    policy_name[0] = psi.pr_lwp.pr_clname[0];
    policy_name[1] = psi.pr_lwp.pr_clname[1];
    num_process++;
    return pid;
}

int Procinfo::readproc(int proc_pid, int thread)
{
    char ppath[256];
    char upath[256];

    pid = proc_pid;

    proc_PID_fd(pid);
    if (!flag_thread_ok || !flag_show_thread) 
    {
        return readproc(proc_pid);
    }
    // get the process stuff
    readproc(proc_pid);
    pid = thread;
    sprintf(ppath, "%s/%d/lwp/%d/lwpsinfo", procdir, proc_pid, thread);
    sprintf(upath, "%s/%d/lwp/%d/lwpusage", procdir, proc_pid, thread);

    lwpsinfo_t psi;
    if( read_file(ppath, (void *)&psi, sizeof(psi)) < (int)sizeof(psi) ) {
        return -1;
    }

    prusage_t pru;
    if( read_file(upath, (void *)&pru, sizeof(pru)) < (int)sizeof(pru) ) {
        return -1;
    }

    state = psi.pr_sname;
    flags = psi.pr_flag;
    const int ns_ticks = 1000000000 / HZ;
    utime = psi.pr_time.tv_sec * HZ + psi.pr_time.tv_nsec / ns_ticks;
    priority = psi.pr_pri;
    nice = psi.pr_nice;
    if( Qps::normalize_nice )
        nice -= NZERO;
    starttime = (psi.pr_start.tv_sec - boot_time) * HZ
        + psi.pr_start.tv_nsec / ns_ticks;
    wchan = psi.pr_wchan;

    minflt = pru.pr_minf;
    majflt = pru.pr_majf;
    which_cpu = psi.pr_onpro;

    // pr_pctcpu and pr_pctmem are scaled so that 1.0 is stored as 0x8000.
    // We rescale pcpu so a CPU-bound process is shown as 100%. (This means
    // that wcpu may exceed 100% with several LWPs.)
    if (psi.pr_pctcpu) {
        wcpu = (psi.pr_pctcpu  / 327.68 ) * num_cpus;
    } else {
        wcpu = 0.0;
    }
    gettimeofday(&tv, 0);
    rtprio = -1;        // ditto
    policy_name[0] = psi.pr_clname[0];
    policy_name[1] = psi.pr_clname[1];
    num_process++;
    return pid;
}

float Procinfo::loadavg[] = {0.0, 0.0, 0.0};
int Procinfo::mem_total = 0;
int Procinfo::mem_free = 0;
int Procinfo::swap_total = 0;
int Procinfo::swap_free = 0;
unsigned *Procinfo::cpu_times_vec = 0;
unsigned *Procinfo::old_cpu_times_vec = 0;
long Procinfo::boot_time = 0;
unsigned int Procinfo::num_cpus = 0;
unsigned int Procinfo::old_num_cpus = 0;
long    Procinfo::num_process = 0;
long    Procinfo::num_network_process = 0;
kstat_ctl_t *Procinfo::kc = 0;

static float getscaled(kstat_t *ks, const char *name)
{
    // load avgs are scaled by 256
    kstat_named_t *kn = (kstat_named_t *)kstat_data_lookup(ks, (char *)name);
    return kn ? kn->value.ui32 * (1 / 256.0) : 0.0;
}

void Procinfo::read_loadavg()
{
    kstat_chain_update(kc);

    kstat_t *ks = kstat_lookup(kc, (char *)"unix", 0, (char *)"system_misc");
    if( !ks || kstat_read(kc, ks, 0) == -1 )
    {
        perror("kstat_lookup/read");
        exit(1);
    }

    loadavg[0] = getscaled(ks, "avenrun_1min");
    loadavg[1] = getscaled(ks, "avenrun_5min");
    loadavg[2] = getscaled(ks, "avenrun_15min");

    // we might as well get the boot time too since it's in the same kstat
    // (not that it is going to change)
    kstat_named_t *kn;
    kn = (kstat_named_t *)kstat_data_lookup(ks, (char *)"boot_time");
    if( kn )
        boot_time = kn->value.ui32;
}

void Procinfo::read_common()
{
    // memory info: this is easy - just use sysconf

    mem_total = sysconf(_SC_PHYS_PAGES) << page_k_shift;
    mem_free = sysconf(_SC_AVPHYS_PAGES) << page_k_shift;

    // get swap info: somewhat trickier - we have to count all swap spaces

    int nswaps = swapctl(SC_GETNSWP, 0);
    swaptbl_t *st = (swaptbl_t *)malloc(sizeof(int)
            + nswaps * sizeof(swapent_t));
    st->swt_n = nswaps;
    // We are not interested in the paths, just the values, so we allocate
    // one scratch buffer for all paths to keep swapctl happy.
    char path_buf[PATH_MAX + 1];

    flag_thread_ok = TRUE;
    for(int i = 0; i < nswaps; i++)
        st->swt_ent[i].ste_path = path_buf;
    swapctl(SC_LIST, st);

    // count the swap spaces
    swap_total = swap_free = 0;
    for(int i = 0; i < nswaps; i++)
    {
        swap_total += st->swt_ent[i].ste_pages;
        swap_free += st->swt_ent[i].ste_free;
    }
    swap_total <<= page_k_shift;
    swap_free <<= page_k_shift;
    free(st);

    if(cpu_times_vec)
    {
        if(old_cpu_times_vec)
            free(old_cpu_times_vec);
        old_cpu_times_vec = cpu_times_vec;
        cpu_times_vec = (unsigned *)malloc(sizeof(unsigned) *
                num_cpus * CPUTIMES);
    }
    old_num_cpus = num_cpus;

    // cpu states: are stored as kstats named "cpu_statN", where N is the
    // cpu number. Unfortunately, the cpu numbers are not guessable so we
    // sweep the kstat chain for all of them, assuming (foolishly?)
    // that they are in order.

    kstat_chain_update(kc);
    int cpu = 0;
    for( kstat_t *ks = kc->kc_chain; ks; ks = ks->ks_next )
    {
        if( strncmp(ks->ks_name, "cpu_stat", 8) == 0 )
        {
            if( kstat_read(kc, ks, NULL) == -1 )
            {
                perror("kstat_read");
                exit(1);
            }
            cpu_stat_t *cs = (cpu_stat_t *)ks->ks_data;
            if( cpu + 1 >= (int)num_cpus )
            {
                num_cpus = cpu + 1;
                cpu_times_vec = (unsigned *)realloc(cpu_times_vec,
                        num_cpus * CPUTIMES
                        * sizeof(unsigned));
            }
            cpu_times(cpu, CPUTIME_USER) = cs->cpu_sysinfo.cpu[CPU_USER];
            cpu_times(cpu, CPUTIME_SYSTEM) = cs->cpu_sysinfo.cpu[CPU_KERNEL];
            cpu_times(cpu, CPUTIME_WAIT) = cs->cpu_sysinfo.cpu[CPU_WAIT];
            cpu_times(cpu, CPUTIME_IDLE) = cs->cpu_sysinfo.cpu[CPU_IDLE];
            cpu++;
        }
    }
}

int Procinfo::get_policy()
{
    if( policy == -1 )
        policy = sched_getscheduler(pid);
    return policy;
}

int Procinfo::get_rtprio()
{
    if(rtprio == -1)
    {
        struct sched_param p;
        if( sched_getparam(pid, &p) == 0 )
            rtprio = p.sched_priority;
    }
    return rtprio;
}

void Procinfo::read_fd(int fdnum, char *path)
{
    struct stat sb;

    if( lstat(path, &sb) < 0 )
    {
        // The file has been closed, or we could really not stat it despite
        // having it open (could be a fd passed from another process).
        fd_files->add(new Fileinfo(fdnum, "(no info available)"));
        return;
    }
    // We could in principle find out more about the fd, such as its mode
    // (RDONLY, RDWR etc) and flags, but it's messy. pfiles uses an agent lwp
    // for this, but I don't know how to do it.
    QString s;
    const char *n;
    switch( sb.st_mode & S_IFMT )
    {
        case S_IFCHR:
            // if it is a tty, we might know its real name
            if( sb.st_rdev != (dev_t)-1 )
            {
                QString t = Ttystr::name(sb.st_rdev);
                if( t[0] != '?' )
                {
                    s = "/dev/";
                    s.append(t);
                    break;
                }
            }
            s.sprintf("char device %u:%u",
                    (unsigned)major(sb.st_rdev), (unsigned)minor(sb.st_rdev));
            break;

        case S_IFBLK:
            s.sprintf("block device %u:%u",
                    (unsigned)major(sb.st_rdev), (unsigned)minor(sb.st_rdev));
            break;

        case S_IFLNK:
            // Directories appear as symlinks in /proc/#/fd; we chdir() to it
            // and see where we end up. Not efficient though.
            // Besides, we change cwd a lot in unpredictable ways. This makes
            // core dumps hard to find, if they are generated at all.
            s = "directory ";
            if( chdir(path) >= 0 )
            {
                char buf[512];
                if( getcwd(buf, sizeof(buf)) >= 0 )
                {
                    s.append(buf);
                    break;
                }
            }
            s.append("(unknown)");
            break;

        default:
            switch( sb.st_mode & S_IFMT )
            {
                case S_IFIFO:       // fifo or anonymous pipe
                    n = "pipe";
                    break;
                case S_IFDIR:       // this shouldn't happen
                    n = "directory";
                    break;
                case S_IFREG:
                    n = "file";
                    break;
                case S_IFSOCK:
                    n = "unix domain socket";
                    break;
                case S_IFDOOR:
                    n = "door";
                    break;
                default:
                    n = "unknown";
                    break;
            }
            s.sprintf("%s, dev %u:%u inode %u", n, (unsigned)major(sb.st_dev),
                    (unsigned)minor(sb.st_dev), (unsigned)sb.st_ino);
            break;
    }
    fd_files->add( new Fileinfo(fdnum, s) );
}

// return TRUE if /proc/PID/fd could be read, FALSE otherwise
// store fileinfo, and also socket inodes separately
bool Procinfo::read_fds()
{
    char path[80], *p;

    sprintf(path, "%s/%d/fd", procdir, pid);

    DIR *d = opendir(path);
    if( !d )
        return FALSE;

    if( !fd_files )
        fd_files = new Svec<Fileinfo*>(8);
    fd_files->clear();

    p = path + strlen(path) + 1;
    p[-1] = '/';

    struct dirent *e;
    while( (e = readdir(d)) != 0 )
    {
        if( e->d_name[0] == '.' )
            continue;         // skip . and ..
        strcpy(p, e->d_name);
        int fdnum = atoi(p);
        read_fd(fdnum, path);
    }
    closedir(d);
    return TRUE;
}

// return TRUE if the process environment could be read, FALSE otherwise
bool Procinfo::read_environ()
{
    int fd;
    char file[128];

    sprintf(file, "/proc/%d/as", pid);
    if( (fd = open(file, O_RDONLY)) < 0 )
        return FALSE;

    // Just read the first 8K from the environment. Adaptive code here is
    // possible, but not really worth the effort.
    int bs = 8192;
    if( envblock )
        free(envblock);
    envblock = (char *)malloc(bs);
    if( pread(fd, envblock, bs, env_ofs) < 0 )
    {
        free(envblock);
        envblock = 0;
        return FALSE;
    }
    close(fd);
    envblock[bs - 1] = '\0';

    environ.purge();

    for( int i = 0; i * (int)sizeof(char *) < bs && ((char **)envblock)[i];
            i++ )
    {
        int b = ((char **)envblock)[i] - (char *)env_ofs;
        if( b < 0 || b >= bs )
            continue;           // outside retrieved memory block
        char *val = strchr(envblock + b, '=');
        if( val )
            *val++ = '\0';
        else
            val = (char *)""; // degenerate: treat as name with empty value
        make_printable(envblock + b);
        make_printable(val);
        environ.add(new NameValue(envblock + b, val) );
    }
    return TRUE;
}

// Try using /proc/bin/pmap to add file names to the memory map
void Procinfo::read_pmap_maps()
{
    char buf[256];

    sprintf(buf, "/usr/proc/bin/pmap %d 2>/dev/null", pid);

    FILE *pmap = popen(buf, "r");
    if( !pmap )
        return;

    // skip first line
    if( !fgets(buf, sizeof buf, pmap) )
    {
        pclose(pmap);
        return;
    }

    int map_num = 0;
    while( fgets(buf, sizeof buf, pmap) )
    {
        // Each output line from pmap looks like
        // <address> <size>K <mode> <file>
        // We use <address> and <size> only to match with previously read
        // map info, and only use <file> here.

        unsigned long addr;
        unsigned len;
        int next;
        char *p;

        if( sscanf(buf, "%lx %dK%n", &addr, &len, &next) != 2 )
            continue;

        // Correlate this with info already gathered. Assume they are in the
        // same order (ascending by address).
        Mapsinfo *mi;
        int i = map_num;
        while( i < maps->size() && (mi = (*maps)[i])->from != addr )
            i++;
        // Mismatches can happen since changes can have taken place since
        // we read the maps. If so, skip this mapping and try the next.
        if( mi->to != addr + ((unsigned long)len << 10) || i == maps->size() )
            continue;
        map_num = i + 1;

        while( buf[next] == ' ' )
            next++;
        while( buf[next] && buf[next] != ' ' )
            next++;
        while( buf[next] == ' ' )
            next++;
        // At this point  we are looking at a file name, or at a suitable
        // designator like [ heap ], [ anon ] or [ stack ]. Use it right away
        // (after peeling off the newline)
        int l = strlen(buf + next);
        if( buf[next + l - 1] == '\n' )
            buf[next + l - 1] = '\0';
        mi->filename = buf + next;
    }

    pclose(pmap);
    return;
}

// return TRUE if /proc/XX/map could be read, FALSE otherwise
bool Procinfo::read_maps()
{
    char name[128];
    sprintf(name, "%s/%d/map", procdir, pid);

    FILE *f = fopen(name, "r");
    if( !f )
        return FALSE;
    if( !maps )
        maps = new Svec<Mapsinfo *>;
    else
        maps->clear();

    prmap_t pm;
    while( fread(&pm, sizeof(pm), 1, f) == 1 )
    {
        Mapsinfo *mi = new Mapsinfo;
        mi->from = pm.pr_vaddr;
        mi->to = pm.pr_vaddr + pm.pr_size;
        mi->offset = pm.pr_offset;
        mi->perm[0] = pm.pr_mflags & MA_READ   ? 'r' : '-';
        mi->perm[1] = pm.pr_mflags & MA_WRITE  ? 'w' : '-';
        mi->perm[2] = pm.pr_mflags & MA_EXEC   ? 'x' : '-';
        mi->perm[3] = pm.pr_mflags & MA_SHARED ? 's' : 'p';

        if( pm.pr_mapname[0] )
        {
            // To find device/inode, stat the file in /proc/#/object:
            char obj[128];
            sprintf(obj, "%s/%d/object/%s", procdir, pid, pm.pr_mapname);
            struct stat sb;
            if( lstat(obj, &sb) < 0 )
            {
                delete mi;
                continue;
            }
            mi->major = major(sb.st_dev);
            mi->minor = minor(sb.st_dev);
            mi->inode = sb.st_ino;
            if( strcmp(pm.pr_mapname, "a.out") == 0 )
                mi->filename = "(executable)";
        }
        else
        {
            mi->major = mi->minor = mi->inode = 0;
            mi->filename = "(anonymous)";
        }

        maps->add(mi);
    }
    fclose(f);

    // If desired and possible, use /usr/proc/bin/pmap to get the
    // names of the mapped files
    static int myeuid = geteuid();
    if( Qps::use_pmap && (myeuid == 0 || myeuid == euid) )
        read_pmap_maps();

    return TRUE;
}


Cat_dir::Cat_dir(const char *heading, const char *explain, const char *dirname,
        QString Procinfo::*member)
: Cat_string(heading, explain),
    dir(dirname),
    cache(member)
{}

QString Cat_dir::string(Procinfo *p)
{
    if( (p->*cache).isNull() )
    {
        char path[128], buf[512];
        sprintf(path, "/proc/%d/%s", p->pid, dir);

        // Either a Linux 2.0 link in [device]:inode form, or a Solaris link.
        // To resolve it, we just chdir() to it and see where we end up.
        // Perhaps we should change back later?
        if( chdir(path) < 0 )
        {
            p->*cache = "-";    // Most likely access denied
        }
        else
        {
            // getcwd() is fairly expensive, but this is cached anyway
            if( !getcwd(buf, sizeof(buf)) )
            {
                p->*cache = "(deleted)";
            }
            else 
                p->*cache = buf;
        }
    }
    return p->*cache;
}

    Cat_state::Cat_state(const char *heading, const char *explain)
: Category(heading, explain)
{}

QString Cat_state::string(Procinfo *p)
{
    QString s("   ");
    s[0] = p->state;
    if( p->state == 'Z' )
        return s;
    s[1] = (p->resident == 0 && p->state != 'Z') ? 'W' : ' ';
    int ni = p->nice;
    if( !Qps::normalize_nice )
        ni -= NZERO;
    s[2] = (ni > 0) ? 'N' : ((ni < 0) ? '<' : ' ');
    return s;
}

    Cat_policy::Cat_policy(const char *heading, const char *explain)
: Category(heading, explain)
{}

QString Cat_policy::string(Procinfo *p)
{
    QString s;
    s = "  ";
    s[0] = p->policy_name[0];
    s[1] = p->policy_name[1];
    return s;
}

int Cat_policy::compare(Procinfo *a, Procinfo *b)
{
    int r = b->policy_name[0] - a->policy_name[0];
    return r ? r : b->policy_name[1] - a->policy_name[1];
}

    Cat_rtprio::Cat_rtprio(const char *heading, const char *explain)
: Category(heading, explain)
{}

QString Cat_rtprio::string(Procinfo *p)
{
    QString s;
    s.setNum(p->get_rtprio());
    return s;
}

int Cat_rtprio::compare(Procinfo *a, Procinfo *b)
{
    return b->get_rtprio() - a->get_rtprio();
}

    Cat_time::Cat_time(const char *heading, const char *explain)
: Category(heading, explain)
{}

QString Cat_time::string(Procinfo *p)
{
    QString s;
    int ticks = p->utime;
    if( Qps::cumulative )
        ticks += p->cutime;
    int t = ticks / HZ;       // seconds
    if( t < 10 )
    {
        int hundreds = ticks / (HZ / 100) % 100;
        s.sprintf("%1d.%02ds", t, hundreds);
    }
    else
        if(t < 100 * 60)
        {
            s.sprintf("%2d:%02d", t / 60, t % 60);
        }
        else
            if( t < 100 * 3600 )
            {
                int h = t / 3600;
                t %= 3600;
                s.sprintf("%2d:%02dh", h, t / 60);
            }
            else
            {
                int d = t / 86400;
                t %= 86400;
                s.sprintf("%dd%dh", d, t / 3600);
            }
    return s;
}

int Cat_time::compare(Procinfo *a, Procinfo *b)
{
    int at = a->utime, bt = b->utime;
    if( Qps::cumulative )
    {
        at += a->cutime;
        bt += b->cutime;
    }
    return bt - at;
}

    Cat_start::Cat_start(const char *heading, const char *explain)
: Category(heading, explain)
{}

QString Cat_start::string(Procinfo *p)
{
    if( p->state == 'Z' )
        return "-";           // Solaris zombies have no valid start time

    time_t start = p->boot_time + p->starttime / (unsigned)HZ;
    QString s;
    char *ct = ctime(&start);
    if( p->tv.tv_sec - start < 86400 )
    {
        ct[16] = '\0';
        s = ct + 11;
    }
    else
    {
        ct[10] = '\0';
        s = ct + 4;
    }
    return s;
}

int Cat_start::compare(Procinfo *a, Procinfo *b)
{
    unsigned long bs = b->starttime, as = a->starttime;
    return bs >= as ? (bs == as ? 0 : 1) : -1;
}

Cat_percent::Cat_percent(const char *heading, const char *explain, int w,
        float Procinfo::*member)
: Category(heading, explain), float_member(member), field_width(w)
{}

QString Cat_percent::string(Procinfo *p)
{
    QString s;
    s.sprintf("%01.2f", (double)(p->*float_member));
    return s;
}

int Cat_percent::compare(Procinfo *a, Procinfo *b)
{
    float at = a->*float_member, bt = b->*float_member;
    return at < bt ? 1 : (at > bt ? -1 : 0);
}

    Cat_tty::Cat_tty(const char *heading, const char *explain)
: Cat_string(heading, explain)
{}

QString Cat_tty::string(Procinfo *p)
{
    return Ttystr::name(p->tty);
}

Proc::Proc()
{
    // Note: When adding/removing/changing the fields, the save file
    // version must be increased!
    allcats.set(F_PID,  new Cat_int("PID", "Process ID", 6, &Procinfo::pid));
    allcats.set(F_PPID, new Cat_int("PPID", "Parent process ID", 6, &Procinfo::ppid));
    allcats.set(F_PGID, new Cat_int("PGID", "Process group ID", 6, &Procinfo::pgrp));
    allcats.set(F_SID,  new Cat_int("SID", "Session ID", 6, &Procinfo::session));
    allcats.set(F_TTY,  new Cat_tty("TTY", "Controlling tty"));
    allcats.set(F_USER, new Cat_user("USER", "Owner (*=suid root, +=suid other user)"));
    allcats.set(F_GROUP, new Cat_group("GROUP", "Group name (*=sgid other)"));
    allcats.set(F_UID,  new Cat_int("UID", "Real user ID", 6, &Procinfo::uid));
    allcats.set(F_EUID, new Cat_int("EUID", "Effective user ID", 6,&Procinfo::euid));
    allcats.set(F_GID,  new Cat_int("GID", "Real group ID", 6, &Procinfo::gid));
    allcats.set(F_EGID, new Cat_int("EGID", "Effective group ID", 6,&Procinfo::egid));
    allcats.set(F_PRI,  new Cat_int("PRI", "Dynamic priority", 4, &Procinfo::priority));
    allcats.set(F_NICE, new Cat_int("NICE", "Scheduling favour (higher -> less cpu time)", 4, &Procinfo::nice));
    allcats.set(F_PLCY, new Cat_policy("PLCY", "Scheduling policy"));
    allcats.set(F_RPRI, new Cat_rtprio("RPRI", "Realtime priority (0-99, more is better)"));
    allcats.set(F_NLWP, new Cat_int("NLWP", "Number of threads in process", 5, &Procinfo::nthreads));
    allcats.set(F_ARCH, new Cat_int("ARCH", "Architecture (address bits)", 2, &Procinfo::addr_bits));
    allcats.set(F_MAJFLT, new Cat_uintl("MAJFLT", "Number of major faults (disk access)", 8, &Procinfo::majflt));
    allcats.set(F_MINFLT, new Cat_uintl("MINFLT", "Number of minor faults (no disk access)", 8, &Procinfo::minflt));
    //Memory
    allcats.set(F_SIZE, new Cat_memory("SIZE",   "Virtual image size of process in Kbytes", 8, &Procinfo::size));
    allcats.set(F_SWAP, new Cat_swap("SWAP",    "Kbytes on swap device"));  
    allcats.set(F_RSS,  new Cat_memory("RSS",     "Resident set size; Kbytes of program in memory", 8, &Procinfo::resident));


    allcats.set(F_STAT, new Cat_state("STAT",   "State of the process"));
    allcats.set(F_FLAGS, new Cat_hex("FLAGS",   "Process flags (hex)", 9, &Procinfo::flags));
    allcats.set(F_WCHAN, new Cat_wchan("WCHAN",  "Kernel function where process is sleeping"));

    allcats.set(F_WCPU, new Cat_percent("%WCPU","Weighted percentage of CPU (30 s average)",6, &Procinfo::wcpu));
    allcats.set(F_CPU,  new Cat_percent(" %CPU","Percentage of CPU used since last update", 6, &Procinfo::pcpu));
    allcats.set(F_MEM,  new Cat_percent(" %MEM", "Percentage of memory used (RSS/total mem)", 6, &Procinfo::pmem));
    allcats.set(F_START, new Cat_start("START", "Time process started"));
    allcats.set(F_TIME, new Cat_time("TIME",    "Total CPU time used since start"));
    allcats.set(F_CPUNUM, new Cat_int("CPU", "CPU the process is executing on", 3, &Procinfo::which_cpu));
    allcats.set(F_COMM, new Cat_string("COMMAND","Command that started the process",  &Procinfo::command));
    allcats.set(F_CWD,  new Cat_dir("CWD", "Current working directory", "cwd", &Procinfo::cwd));
    allcats.set(F_ROOT, new Cat_dir("ROOT", "Root directory of process", "root", &Procinfo::root));
    allcats.set(F_CMDLINE, new Cat_cmdline("COMMAND_LINE","Command line that started the process"));

    for( int i = 0; i < allcats.size(); i++ )
        allcats[i]->index = i;

    Procinfo::init_static();

    current_gen=0; // !
}

void Proc::newproc(Procinfo *p)
{
    Procinfo *oldp = procs[p->pid];

    if(oldp && flag_thread_ok && (previous_flag_show_thread != flag_show_thread) ){
        oldp->deref();
        oldp =NULL;
    }

    if( oldp )
    {
        // calculate pcpu (and wcpu for Linux) from previous procinfo
        int dt = (p->tv.tv_usec - oldp->tv.tv_usec) / (1000000 / HZ)
            + (p->tv.tv_sec - oldp->tv.tv_sec) * HZ;
        int dcpu = p->utime - oldp->utime;
        p->pcpu = 100.0 * dcpu / dt;
        if( p->pcpu > 99.99 )
            p->pcpu = 99.99;

        // propagate some fields to new incarnation
        p->selected = oldp->selected;
        p->detail = oldp->detail;
        p->hidekids = oldp->hidekids;
        oldp->detail = 0;
        if( p->detail )
            p->detail->set_procinfo(p);

        oldp->deref();
    }
    else
    {
        // New process
        p->wcpu = p->pcpu;    // just a start
    }
    procs.replace(p->pid, p);
    if( procs.count() > procs.size() )
        procs.resize(procs.count() * 2 - 1);
}

// update the process list
void Proc::refresh()
{
    current_gen =! current_gen; // 1,2,3,4....

    num_opened_files = 0;
    Procinfo::num_process = 0; //init
    Procinfo::num_network_process=0; //init
    Procinfo::read_common();

    int pid;

    DIR *d = opendir(procdir);
    struct dirent *e;
    while( (e = readdir(d)) != 0 )
    {
        if( e->d_name[0] >= '0' && e->d_name[0] <= '9') // good idea!
        {

            pid = atoi(e->d_name);
            Procinfo *pi = new Procinfo(pid);

            if(flag_thread_ok && flag_show_thread)
                read_pid_tasks(pid);

            if( pi->pid == -1 )
                delete pi;          // already gone
            else
            {
                pi->generation = current_gen;
                newproc(pi);
            }
        }
    }
    closedir(d);

    // remove Procinfos of nonexisting processes
    for( QIntDictIterator<Procinfo> it(procs); it.current(); )
    {
        Procinfo *p = it.current();
        if( p->generation != current_gen )
        {
            procs.remove(p->pid);
            p->deref();
        }
        else
            ++it;
    }
}

Category *Proc::cat_by_name(const char *s)
{
    if( s )
    {
        for( int i = 0; i < allcats.size(); i++ )
            if( strcmp(allcats[i]->name, s) == 0 )
                return allcats[i];
    }
    return 0;
}

int  Proc::field_id_by_name(const char *s)
{
    if( s )
    {
        for( int i = 0; i < allcats.size(); i++ )
            if( strcmp(allcats[i]->name, s) == 0 )
                return i;
    }
    return -1;
}

int Procview::custom_fields[] = {F_PID, F_TTY, F_USER, F_NICE,
    F_NLWP,
    F_SIZE, F_RSS,
    F_STAT, F_CPU, F_START, F_TIME,
    F_CMDLINE, F_END};


int Procview::user_fields[] = {F_PID, F_TTY, F_USER, F_NICE,
    F_NLWP,
    F_SIZE, F_RSS,
    F_STAT, F_CPU, F_START, F_TIME,
    F_CMDLINE, F_END};

int Procview::jobs_fields[] = {F_PID, F_PPID, F_PGID, F_SID, 
    F_TTY,
    F_STAT, F_UID, F_TIME, F_CMDLINE, F_END};

int Procview::mem_fields[] = {F_PID, 
    F_TTY, F_MAJFLT, F_MINFLT,
    F_SIZE, F_SWAP, F_RSS,
    F_CMDLINE, 
    F_END};

// SOLARIS
void Procview::build_tree()
{
    if( root_procs.size() > 0 )
    {
        Procinfo *p;
        for( QIntDictIterator<Procinfo> it(proc->procs); (p = it.current());
                ++it)
            if( p->children )
                p->children->clear();
        root_procs.clear();
    }

    Procinfo *p;
    for( QIntDictIterator<Procinfo> it(proc->procs); (p = it.current()); ++it)
    {
        if( accept_proc(p) )
        {
            Procinfo *parent = 0;

            if( p->ppid && (parent = proc->procs[p->ppid])
                    && accept_proc(parent))
            {
                if( !parent->children )
                    parent->children = new Svec<Procinfo *>(4);
                parent->children->add(p);
            }
            else
                root_procs.add(p);
        }
        else
            p->selected = FALSE;
    }
}

//Solaris: re-sort the process info
void Procview::rebuild()
{
    for( int i = 0; i < procs.size(); i++ )
        procs[i]->deref(); // delete 
    procs.clear();
    if( treeview )
    {
        build_tree();
        parent_rows.clear();
        linearize_tree(&root_procs, 0, -1);
    }
    else
    {
        for( QIntDictIterator<Procinfo> it(proc->procs); it.current(); ++it )
        {
            Procinfo *p = it.current();
            if( accept_proc(p) )
                procs.add(p->ref());
            else
                p->selected = FALSE;
        }
        static_sortcat = sortcat;
        procs.sort(reversed ? compare_backwards : compare);
    }
}



void Procview::set_fields()
{
    switch(viewfields)
    {
        case USER:
            set_fields_list(user_fields);
            break;
        case JOBS:
            set_fields_list(jobs_fields);
            break;
        case MEM:
            set_fields_list(mem_fields);
            break;
        case CUSTOM:
            set_fields_list(custom_fields);
            break;
    }
}


//SOLARIS:
//deduce whether the currently selected fields correspond to a field list
void Procview::deduce_fields()
{
    return;

    if( viewfields != CUSTOM )
        return;

    Procview::fieldstates tags[3] = {USER, JOBS, MEM};
    int *lists[3] = {user_fields, jobs_fields, mem_fields};
    for( int i = 0; i < 3; i++ )
    {
        int *l = lists[i];
        int j;
        for( j = 0; l[j] != F_END; j++ )
            if( findCol(l[j]) < 0 )
                break;
        if( l[j] == F_END && j == cats.size() )
        {
            viewfields = tags[i];
            return;
        }
    }
}


// vim: ts=4 sw=4 et

Generated by  Doxygen 1.6.0   Back to index