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

proc_linux.cpp

// proc.cpp for Linux  
// *** the class  should be changed to simple functions ***  (fasthyun)
//
// This program is free software. See the file COPYING for details.
// Author: Mattias Engdegård, 1997-1999
//             Oliver


#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <dirent.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sched.h>
#include <libgen.h> // basename()

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

#include <qmessagebox.h>

#include "proc_common.cpp"

#define PROCDIR  "/proc"  // will be removed !



// 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      */
};

#define QPS_SCHED_AFFINITY ok
#ifdef QPS_SCHED_AFFINITY
#ifndef SYS_sched_setaffinity
#define SYS_sched_setaffinity 241
#endif
#ifndef SYS_sched_getaffinity
#define SYS_sched_getaffinity 242
#endif
// Needed for some glibc
int qps_sched_setaffinity(pid_t pid, unsigned int len, unsigned long *mask) {
      return syscall(SYS_sched_setaffinity, pid, len, mask);
};
int qps_sched_getaffinity(pid_t pid, unsigned int len, unsigned long *mask) {
      return syscall(SYS_sched_getaffinity, pid, len, mask);
};
#endif


/*
   When we commute from task group level to task level (or task level
   to task group level) we need to completley destroy the current state of
   proc[]. We exchange global <--> analytical informations. Some fields may
   not be accurate immediately : %WCPU for example.
   Some remarks : suppose we have a task group with TGID = 120, a thread leader 
   with PID=120 and two more threads with PID=125 and PID=127.
   In /proc, we only see /proc/120 and we don't see /proc/125 or /proc/127.
   This does not prevent the possibility to do an open("/proc/125", ...) or an
   open("/proc/127", ...). Also, path like /proc/120/task, /proc/125/task and
   /proc/127/task are ok.
   If the leader thread 120 terminates (it does un pthread_exit()), the directory
   /proc/120 will be maintained supposing 125 or 127 are alives. 
   Nevertheless, many files and directories will be empty in /proc/120. 
   For example, we lose "natual" access to threads 125 and 127 through /proc/120/task.
   At task group level, /proc/120/stat and /proc/120/status keep the global 
   informations ; so CPU time is still incresing. But, as task is empty, we see
   nothing for this task group at thread level.
   */

int Procinfo::page_k_shift;

struct proc_info_ {
      int  proc_id;
      char flag;
      char type;
      int  files;
} proc_info;

struct list_files_ {
      int proc_id;
      int flag;
      char *filename;    //  path + filename 
} list_files;


// Bottleneck  
// DRAFT CODE 
// Description: read /proc/PID/fd/*  check opened file, count opened files
//          this fuction will be called  when every update.
// Return Value : 
int proc_pid_fd(const int pid)  
{
      char  path[256];
      char  buffer[256],fname[256];
      DIR   *d;   
      int       fdnum;
      int       len, path_len;

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

      path_len=strlen(path);
      d = opendir(path);

      if(!d){
            // this happend when the process died already or Zombie process
            // printf("Qps : read fail !! /proc/%d/fd !!! kernel bug ? \n",pid);
            return FALSE;
      }

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

            path[path_len]='/';
            path[path_len+1]=0;
            strcat(path,e->d_name);

            len = readlink(path, fname, sizeof(fname) - 1);             
            if( len > 0) 
            {     
                  fname[len]=0;
                  //printf("DEBUG: %s[%s]\n",path,fname);
                  //if (strcmp(fname,"/dev/null")==0 ) continue;  
            }

            num_opened_files++;
            //strcpy(p, e->d_name);
            //fdnum = atoi(p);
            //read_fd(fdnum, path);
      }
      closedir(d);
      return TRUE;      

}


Procinfo::Procinfo(int pid) : refcnt(1)
{
      first_run=true;
      generation=0;

      detail = 0;
      children = 0;
      fd_files = 0;
      maps = 0;

      sock_inodes = 0;
      socks_current = FALSE;
      usocks_current = FALSE;
      per_cpu_times = 0;

      size=0;
      resident=0;
      trs=0;
      drs=0;
      stack=0;
      share=0;

      environ = 0;
      envblock = 0;
      tgid=0;
      ppid=0;
      selected = FALSE;
      hidekids = FALSE;

      children = new Svec<Procinfo *>(8);
      Procinfo::pid=pid;

      //readproc();
}

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

      //    if(environ)    delete environ;
      if(envblock)
            free(envblock);
      if(sock_inodes)
            delete sock_inodes;


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

      if(children)
      {
            children->clear();
            delete children;
      }

      delete[] per_cpu_times;
}

// miscellaneous static initializations
void Procinfo::init_static()
{
      socks.setAutoDelete(TRUE);
      usocks.setAutoDelete(TRUE);

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

// COMMON :
// 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;
}

// DRAFT CODE !! DRAFT !!!
//  suck function...(by fasthyun@magicn.com) 
//  Description : read /proc/1234/task/*  tasks(thread,LWP)  
//            add to Proc::procs[]  
int Proc::read_pid_tasks(int pid)
{
      char  path[256];
      struct      dirent *e;
      int   thread_pid;
      int   thread_n=0;
      Procinfo *pi;

      sprintf(path, "/proc/%d/task", pid);

      DIR *d = opendir(path);
      if(!d) return -1;  // process dead  already!

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

            thread_pid = atoi(e->d_name); 
            if (pid==thread_pid) continue; // skip

            pi = procs[thread_pid];  

            if(pi==NULL)
            {
                  pi = new Procinfo(thread_pid);
                  procs.replace(pi->pid, pi); // insert to  QDict<int>
                  if(procs.count() > procs.size())
                        procs.resize(procs.count() * 2 - 1);

            }
            if(pi->readproc(pid)>=0)
            {
                  pi->generation = current_gen;
                  if(pid!=thread_pid)
                        pi->cmdline="(thread)";

            }
            thread_n++;
      }
      closedir(d);                        
      return thread_n;
}

inline void Procinfo::calculate_cpu()
{
      if(first_run) 
      {
            // New process
            // %cpu first time = (cpu time since start) / (time since start)
            int jiffies_since_boot = tv.tv_usec / (1000000 / HZ)
                  + (tv.tv_sec - boot_time) * HZ;
            int dt = jiffies_since_boot - starttime;
            int dcpu = utime;

            if(dcpu<0) {
                  printf("ERROR: dcpu=%d\n",dcpu);
                  exit(0);
            }


            if(dt == 0 )
                  pcpu = 0.0;
            else
                  pcpu = 100.0 * dcpu / dt;

            if(pcpu > 99.99 || pcpu < 0)  
                  pcpu=0;

            wcpu = pcpu;      // just a start

            /*
               if(Procinfo::num_cpus > 1) {
            // first tick: count times from 0
            unsigned long most = 0;
            for(unsigned cpu = 0; cpu < Procinfo::num_cpus; cpu++) {
            unsigned long t = per_cpu_times[cpu];
            if(t > most) {
            most = t;
            which_cpu = cpu;
            }
            }
            } */
      }
      else 
      {
            // printf("%f %d \n",HZ,HZ);
            // calculate pcpu (and wcpu for Linux) from previous procinfo
            int dt = (tv.tv_usec - old_tv.tv_usec) / (1000000 / HZ)
                  + (tv.tv_sec - old_tv.tv_sec) * HZ;

            int dcpu = utime - old_utime;

            // DEBUG 
            if(dcpu<0  or dt<=0) {
                  //    printf("ERROR:[%d] dt=%d dcpu=%d utime=%d pid_prev[%d]\n",pid,dt,dcpu,utime,pid_prev);
                  //    exit(0);
            }


            if(dt == 0 ) // readproc() too fast read again ..
                  pcpu = 0.0;
            else
                  pcpu = 100.0 * dcpu / dt;

            if(pcpu > 99.99) pcpu = 99.99;

            const float a = Procview::avg_factor;
            wcpu = a * old_wcpu + (1 - a) * pcpu;
            if(detail)
                  detail->set_procinfo(this);

            /*
               if(Procinfo::num_cpus > 1) {
            // SMP: see which processor was used the most
            int best_cpu = -1;
            long most = 0;
            for(unsigned cpu = 0; cpu < Procinfo::num_cpus; cpu++) {
            long delta = per_cpu_times[cpu] - old_per_cpu_times[cpu];
            if(delta > most) {
            most = delta;
            best_cpu = cpu;
            }
            // if no cpu time has been spent, use previous value
            which_cpu = (best_cpu >= 0) ? best_cpu : old_which_cpu;
            }
            }*/

      }    

      old_utime=utime;    // **** 
      old_wcpu=wcpu;
      old_tv.tv_usec=tv.tv_usec;
      old_tv.tv_sec=tv.tv_sec;
      old_which_cpu = which_cpu;
      pid_prev=pid;
}


// Description : read /proc/1234/*
//      be called every UPDATE time. 
//      shoud be simple and clean !
//      return -1 means the process already dead !
int Procinfo::readproc(int tgid_)
{
      char path[128];     // enough
      char buf[512];     
      char sbuf[1024*2];  // should be enough to acommodate /proc/1234/stat
      char cmdbuf[MAX_CMD_LEN];
      int  cmdlen;
      int  len;
      char *p;

      if (flag_thread_ok && flag_show_thread and tgid_ > 0)  // dirty methode... by fasthyun
            sprintf(path, "/proc/%d/task/%d", tgid_,pid); 
      else
            sprintf(path, "/proc/%d", pid);

      //if (proc_pid_fd(pid)== TRUE) ;  // bottleneck !!

      if(first_run)  
      {  
            //COMMAND , TGID, UID , COMMAND_LINE  never changed 
            strcpy(buf, path);  
            strcat(buf, "/status");
            if((len = read_file(buf, sbuf, sizeof(sbuf) - 1)) <= 0) return -1;
            sbuf[len] = '\0';

            if(!(p = strstr(sbuf, "Name:")))
                  return -1;
            sscanf(p, "Name: %s ",cmdbuf);
            command = cmdbuf;

            if(!(p = strstr(sbuf, "Tgid:")))
                  return -1;
            sscanf(p, "Tgid: %d ", &tgid);

            if(!(p = strstr(sbuf, "PPid:")))
                  return -1;
            sscanf(p, "PPid: %d ",&ppid);

            if(!(p = strstr(sbuf, "Uid:")))
                  return -1;
            sscanf(p, "Uid: %d %d %d %d Gid: %d %d %d %d",
                        &uid, &euid, &suid, &fsuid,
                        &gid, &egid, &sgid, &fsgid);

            username=userName(uid,euid);
            groupname=groupName(gid,egid);
            
            //COMMAND_LINE : read /proc/1234/cmdline
            strcpy(buf, path);
            strcat(buf, "/cmdline");
            if((cmdlen = read_file(buf, cmdbuf, MAX_CMD_LEN - 1)) < 0)
                  return -1;

            // kernel 2.6.x bug :  a wrong comandline 
            int bug=0;
            if(cmdbuf[cmdlen-1]!=0) // how to check the bug
            {
                  bug=true;
                  cmdbuf[cmdlen] = 0;
            }

            if(cmdlen == 0) {
                  cmdline = "";
            } else {
                  if(strlen(command)==15)
                        if(!bug)
                              command=basename(cmdbuf); // no memory leak !

                  //change zero,0xA to ' ' 
                  for(int i = 0; i < cmdlen-1; i++)
                        if(cmdbuf[i]==0 or cmdbuf[i]==0x0A) cmdbuf[i] = ' ';

                  // for non-ascii locale language 
                  cmdline = codec->toUnicode(cmdbuf,strlen(cmdbuf));
                  if(bug)
                        cmdline +=" >>! Qps: may be a wrong comandline ";

            }
      }

      // read /proc/1234/stat
      strcpy(buf, path);
      strcat(buf, "/stat");
      int statlen;
      if((statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) <= 0) 
            return -1;
      sbuf[statlen] = '\0';

      // Not all values from /proc/#/stat are interesting; the ones left out
      // have been retained in comments to see where they should go, in case
      // they are needed again.
      //
      // In Linux 2.2.x, timeout has been removed, and the signal information
      // here is obsolete (/proc/#/status has real-time signal info).
      //
      // There are undocumented values after wchan, unused so far:
      // nswap          pages swapped out since process started
      // cnswap         nswap of children
      // exit_signal    (2.2.x) signal sent to parent when process exits
      // (The latter could provide a way to detect cloned processes since
      //  they usually have exit_signal != SIGCHLD, but I prefer waiting
      //  for real TIDs before implementing thread support.)
      //
      int  x_pid;
      long stime, cstime;
      int i_tty;

      // Notes :
      //      1. ppid changed !!

      sscanf(sbuf, "%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu "
                  "%ld %ld %ld %ld %d %d %d %*s %lu %*s %*s %*s %*s %*s %*s %*s %*s "
                  "%*s %*s %*s %*s %lu %*s %*s %*s %u %*s %*s %*s",&x_pid,&cmdbuf[0],
                  &state, &ppid, &pgrp, &session, &i_tty, &tpgid,
                  &flags, &minflt, &cminflt, &majflt, &cmajflt,
                  &utime, &stime, &cutime, &cstime, &priority, &nice,&nthreads,
                  /* itrealvalue */
                  &starttime,
                  /* vsize */
                  /* rss */
                  /* rlim, startcode, endcode, startstack kstkesp kstkeip,
                     signal, blocked, sigignore, sigcatch */
                  &wchan,
                  /* kernel 2.6 ? */
                  /* 0L, 0L, exit_signal */
                  &which_cpu
                  /* rt_priority, policy */
            );


      tty = (dev_t)i_tty;
      utime += stime;         // we make no user/system time distinction 
      cutime += cstime;

      // in Kernel 2.6 obsoleted  
      // read /proc/1234/statm   memory usage
      strcpy(buf, path);
      strcat(buf, "/statm");
      if((statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) <= 0) 
            return -1;
      sbuf[statlen] = '\0';

      sscanf(sbuf, "%lu %lu %lu %lu %lu %lu %lu",
                  &size, &resident, &share, &trs, &lrs, &drs, &dt);
      size  <<= page_k_shift; // total memory
      resident<<= page_k_shift;     // 
      share <<= page_k_shift;
      trs         <<= page_k_shift; // text(code)
      lrs         <<= page_k_shift; // lib : awlays zero in Kernel 2.6
      drs         <<= page_k_shift; // data: wrong in 2.6
      //    dt          <<= page_k_shift; // zero
      pmem = 100.0 * resident / mem_total;

      // read /proc/1234/status check !!
      strcpy(buf, path);  
      strcat(buf, "/status");
      if((statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) <= 0) return -1;
      sbuf[statlen] = '\0';
      int nfld;

      // slpavg : not supported in kernel 2.4; default value of -1
      if((p = strstr(sbuf, "SleepAVG:")))
            sscanf(p, "SleepAVG: %d",&slpavg);
      else 
            slpavg =-1;

      if(p = strstr(sbuf, "VmSize:"))
      {
            sscanf(p, "VmSize: %d",&size);
            //p = strstr(p, "VmRSS:");
            //if(p)
            //sscanf(p, "VmRSS: %d",&resident);
            if(p = strstr(p, "VmData:"))
                  sscanf(p, "VmData: %d",&drs);             
            if(p = strstr(p, "VmStk:"))
                  sscanf(p, "VmStk: %d",&stack);                  
            if(p = strstr(p, "VmExe:"))
                  sscanf(p, "VmExe: %d",&trs);
            if(p = strstr(p, "VmLib:"))
                  sscanf(p, "VmLib: %d",&share);
      }

      // Shared = RSS - ( CODE + DATA + STACK )
      // share=resident - trs -drs -stack;

      // in task_mmu.c 
      // total_vm==size 
      // data = mm->total_vm - mm->shared_vm - mm->stack_vm;
      // swap = p->size - p->resident ;   

      /*
      //which_cpu = 0;
      //per_cpu_times = 0;

      // SMP 
      if(num_cpus > 1) {
      printf("SMP !!!\n");
      per_cpu_times = new unsigned long[num_cpus];
      if (per_cpu_times == NULL){
      printf("Qps: Allocation problem - per_cpu_times\n");
      return -1;
      }
      // less than version 2.6.0  and SMP
      if(flag_24_ok) {
      strcpy(buf, path); // /proc/$PID
      strcat(buf, "/cpu"); // /proc/$PID/cpu
      if( (statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) <= 0) {
      delete per_cpu_times;
      return -1;
      }
      sbuf[statlen] = '\0';
      p = sbuf;
      for(unsigned cpu = 0; cpu < num_cpus; cpu++) {
      p = strchr(p, '\n');
      if (!p) {
      for(cpu = 0; cpu < num_cpus; cpu++)
      per_cpu_times[cpu] = 0;
      break;
      }
      p++;
      unsigned long utime, stime;
      sscanf(p, "%*s %lu %lu", &utime, &stime);
      per_cpu_times[cpu] = utime + stime;
      }
      }
      }
      */

      gettimeofday(&tv, 0);
      policy = -1;            // will get it when needed
      rtprio = -1;            // ditto
      tms = -1;             // ditto

      calculate_cpu();
      num_process++;      // count process 
      first_run=false;
      return pid;
}


float       Procinfo::loadavg[] = {0.0, 0.0, 0.0};  // CPU load avg 1min,5min,15minute
int   Procinfo::mem_total = 0;
int   Procinfo::mem_free = 0;
int   Procinfo::mem_buffers = 0;
int   Procinfo::mem_cached = 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;
bool Procinfo::socks_current = FALSE;
QIntDict<Sockinfo> Procinfo::socks(17);
QIntDict<UnixSocket> Procinfo::usocks(17);
bool  Procinfo::usocks_current = FALSE;
unsigned int Procinfo::num_cpus = 0;
unsigned int Procinfo::old_num_cpus = 0;
long  Procinfo::num_process = 0;
long  Procinfo::num_network_process = 0;
//DEL? int Procinfo::mem_shared = 0; // only linux kernel 2.4.x

// just grab the load averages
void Procinfo::read_loadavg()
{
      char path[80];
      char buf[512];
      int  n;
      strcpy(path, "/proc/loadavg");
      if((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) {
            fprintf(stderr,   "qps: Cannot open /proc/loadavg  (make sure /proc is mounted)\n");
            exit(1);
      }
      buf[n] = '\0';
      sscanf(buf, "%f %f %f", &loadavg[0], &loadavg[1], &loadavg[2]);
}

// Description:  read common information  for all processes
void Procinfo::read_common()
{
      char path[80];
      char buf[4096 + 1];

      char *p;
      int n;


      /* Version 2.4.x ? */
      strcpy(path,"/proc/vmstat");
      if (!stat(path, (struct stat*)buf) )
            flag_24_ok=FALSE;
      else
            flag_24_ok=TRUE;

      /* NTPL tasking ? */
      strcpy(path,"/proc/1/task");
      if (!stat(path, (struct stat*)buf) )
            flag_thread_ok = TRUE;
      else
            flag_thread_ok = FALSE;

      // read memory info
      strcpy(path, PROCDIR);
      strcat(path, "/meminfo");
      if((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) return;
      buf[n] = '\0';

      // Skip the old /meminfo cruft, making this work in post-2.1.42 kernels
      // as well.  (values are now in kB)
      if( (p = strstr(buf, "MemTotal:")) != NULL )
            sscanf(p, "MemTotal: %d kB\n", &mem_total);
      if( (p = strstr(buf, "MemFree:")) != NULL )
            sscanf(p, "MemFree: %d kB\n", &mem_free);
      if( (p = strstr(buf, "Buffers:")) != NULL )
            sscanf(p, "Buffers: %d kB\n", &mem_buffers);
      if( (p = strstr(buf, "Cached:")) != NULL )
            sscanf(p, "Cached: %d kB\n", &mem_cached);

      p = strstr(buf, "SwapTotal:");
      sscanf(p, "SwapTotal: %d kB\nSwapFree: %d kB\n", &swap_total, &swap_free);

      // read system status
      strcpy(path, PROCDIR);
      strcat(path, "/stat"); // /proc/stat
      if((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) return;
      buf[n] = '\0';

      if(!num_cpus) {
            // count cpus
            //for only static CPU system
            char *p;
            p = strstr(buf, "cpu");
            while(p < buf + sizeof(buf) - 4 && strncmp(p, "cpu", 3) == 0) {
                  num_cpus++;
                  if(strncmp(p, "cpu0", 4) == 0)
                        num_cpus--;
                  p = strchr(p, '\n');
                  if(p)
                        p++;
            }
            cpu_times_vec = new unsigned[CPUTIMES * num_cpus];
            old_cpu_times_vec = new unsigned[CPUTIMES * num_cpus];

            //init
            for(unsigned cpu = 0; cpu < num_cpus; cpu++)
                  for(int i = 0; i < CPUTIMES; i++)
                        CPU_TIMES(cpu, i)=0;
      }

      // DRAFT
      if(num_cpus != old_num_cpus) {
            // first time & changed cpu_num

            old_num_cpus=num_cpus;
      }


      for(unsigned cpu = 0; cpu < num_cpus; cpu++)
            for(int i = 0; i < CPUTIMES; i++)
            {
                  old_cpu_times(cpu, i) = CPU_TIMES(cpu, i);
            }

      // check kernel 2.4 cpu0 exist ?
      if(num_cpus == 1) {
            unsigned iowait, irq, sftirq, nflds;
            nflds = sscanf(buf, "cpu %u %u %u %u %u %u %u",
                        &cpu_times(0, CPUTIME_USER), &cpu_times(0, CPUTIME_NICE),
                        &cpu_times(0, CPUTIME_SYSTEM), &cpu_times(0, CPUTIME_IDLE),
                        &iowait, &irq, &sftirq);
            if( nflds > 4 ) {
                  CPU_TIMES(0, CPUTIME_SYSTEM)+=(irq+sftirq);
                  CPU_TIMES(0, CPUTIME_IDLE)+=iowait;
            }
      } else {
            // SMP
            /*
               in /proc/stat 
               cpu0 3350 0 535 160879 1929 105 326 0
               cpu#,user,nice,system,idle,iowait,irq,sftirq
               */
            for(unsigned cpu = 0; cpu < num_cpus; cpu++) {
                  char cpu_buf[10];
                  sprintf(cpu_buf, "cpu%d", cpu);
                  if((p = strstr(buf, cpu_buf)) != 0) {
                        unsigned iowait, irq, sftirq, nflds;
                        nflds = sscanf(p, "%*s %u %u %u %u %u %u %u",
                                    &CPU_TIMES(cpu, CPUTIME_USER), &CPU_TIMES(cpu, CPUTIME_NICE),
                                    &CPU_TIMES(cpu, CPUTIME_SYSTEM), &CPU_TIMES(cpu, CPUTIME_IDLE),
                                    &iowait, &irq, &sftirq);

                        if( nflds > 4 ) {
                              // kernel 2.6.x 
                              CPU_TIMES(cpu, CPUTIME_SYSTEM)+=(irq+sftirq);
                              CPU_TIMES(cpu, CPUTIME_IDLE)+=iowait;
                        }
                        // 2.4.27-SMP bug 
                        //if(old_cpu_times(cpu, i) >= CPU_TIMES(cpu, i) );    
                        //if(old_cpu_times(cpu, CPUTIME_IDLE) >= CPU_TIMES(cpu, CPUTIME_IDLE) )
                        //    CPU_TIMES(cpu, CPUTIME_IDLE)=old_cpu_times(cpu, CPUTIME_IDLE) ;

                  } else {
                        fprintf(stderr, "Error reading info for cpu %d\n", cpu);
                        abort();
                  }
            }
      }

      // 2.0.x kernels (at least up to 2.0.33) have an SMP bug that reports
      // cpu_time(CPUTIME_IDLE) incorrectly, since it doesn't take the number of
      // cpus into account. This is fixed in 2.1.x kernels, and since 2.0.x
      // is rather old (and unsuited for SMP anyway) we don't work around it.
      p = strstr(buf, "btime") + 6;
      sscanf(p, "%lu", &boot_time);
}


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;
}
double Procinfo::get_tms()
{
      struct timespec ts;
      if(sched_rr_get_interval(pid, &ts)==-1)
            tms = -1; // should not be possible
      else {
            tms = ts.tv_nsec;
            tms /= 1000000000;
            tms +=ts.tv_sec;
      }
      return tms;
}

unsigned long Procinfo::get_affcpu()
{
#ifdef QPS_SCHED_AFFINITY
      if(qps_sched_getaffinity(pid, sizeof(unsigned long), &affcpu)==-1)
            affcpu=(unsigned long)0; 
#else
      if(sched_getaffinity(pid, sizeof(unsigned long), (cpu_set_t*)&affcpu)==-1)
            affcpu=(unsigned long)0; 
#endif
      return affcpu;
}

// Description : read  /proc/1234/fd/* (SYMBOLIC LINK NAME)  
//
/* We need to implement support for IPV6 and sctp ? */
void Procinfo::read_fd(int fdnum, char *path)
{
      int len;
      char buf[80];
      struct stat sb;

      // The fd mode is contained in the link permission bits
      if(lstat(path, &sb) < 0)
            return;
      int mode = 0;
      if(sb.st_mode & 0400) mode |= OPEN_READ;
      if(sb.st_mode & 0200) mode |= OPEN_WRITE;

      if( (len = readlink(path, buf, sizeof(buf) - 1)) > 0) {
            buf[len] = '\0';
            unsigned long dev, ino;
            if((buf[0] == '[' // Linux 2.0 style /proc/fd
                              && sscanf(buf, "[%lx]:%lu", &dev, &ino) == 2
                              && dev == 0)
                        || sscanf(buf, "socket:[%lu]", &ino) > 0) { // Linux 2.1
                  Sockinfo *si = Procinfo::socks[ino];
                  char buf[80];
                  if(si) {
                        // a TCP or UDP socket
                        sock_inodes->add(new SockInode(fdnum, ino));
                        sprintf(buf, "%sp socket %lu",
                                    si->proto == Sockinfo::TCP ? "tc" : "ud", ino);
                        fd_files->add(new Fileinfo(fdnum, buf, mode));
                        return;
                  } else {
                        // maybe a unix domain socket?
                        read_usockets();
                        UnixSocket *us = Procinfo::usocks[ino];
                        if(us) {
                              char *tp = "?", *st = "?";
                              switch(us->type) {
                                    case SOCK_STREAM: tp = "stream"; break;
                                    case SOCK_DGRAM: tp = "dgram"; break;
                              }
                              switch(us->state) {
                                    case SSFREE: st = "free"; break;
                                    case SSUNCONNECTED: st = "unconn"; break;
                                    case SSCONNECTING: st = "connecting"; break;
                                    case SSCONNECTED: st = "connected"; break;
                                    case SSDISCONNECTING: st = "disconn"; break;
                              }
                              sprintf(buf, "unix domain socket %lu (%s, %s) ",
                                          ino, tp, st);
                              QString s = buf;
                              s.append(us->name);
                              fd_files->add(new Fileinfo(fdnum, s, mode));
                              return;
                        }
                  }
            }
            // assume fds will be read in increasing order
            fd_files->add(new Fileinfo(fdnum, buf, mode));
      }
}


// Description : read /PID/fd opened files 
// return TRUE if /proc/PID/fd could be read, FALSE otherwise
//    store fileinfo, and also socket inodes separately
//    called by Detail()
bool Procinfo::read_fds()
{
      char path[80], *p;
      if (flag_thread_ok && flag_show_thread)
            sprintf(path,"/proc/%d/task/%d/fd",pid,pid);
      else
            sprintf(path, "/proc/%d/fd", pid);

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

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

      if(!sock_inodes)
            sock_inodes = new Svec<SockInode*>(4);
      else 
            sock_inodes->purge();

      p = path + strlen(path) + 1;  // foolish solution...(by fasthyun@magicn.com) 
      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;
}


bool Procinfo::read_socket_list(Sockinfo::proto_t proto, char *pseudofile)
{
      char path[80];
      strcpy(path, PROCDIR);
      strcat(path, "/net/");
      strcat(path, pseudofile);
      FILE *f = fopen(path, "r");
      if(!f) return FALSE;

      char buf[256];
      fgets(buf, sizeof(buf), f);   // skip header
      while(fgets(buf, sizeof(buf), f) != 0) {
            Sockinfo *si = new Sockinfo;
            si->proto = proto;
            unsigned local_port, rem_port, st, tr;
            sscanf(buf + 6, "%x:%x %x:%x %x %x:%x %x:%x %x %d %d %d",
                        &si->local_addr, &local_port, &si->rem_addr, &rem_port,
                        &st, &si->tx_queue, &si->rx_queue,
                        &tr, &si->tm_when, &si->rexmits,
                        &si->uid, &si->timeout, &si->inode);
            // fix fields that aren't sizeof(int)
            si->local_port = local_port;
            si->rem_port = rem_port;
            si->st = st;
            si->tr = tr;

            socks.insert(si->inode, si);
            if(socks.count() > socks.size() * 3)
                  socks.resize(socks.count());
      }
      fclose(f);
      return TRUE;
}


bool Procinfo::read_usocket_list()
{
      char path[80];
      strcpy(path, PROCDIR);
      strcat(path, "/net/unix");
      FILE *f = fopen(path, "r");
      if(!f) return FALSE;

      char buf[256];
      fgets(buf, sizeof(buf), f);   // skip header
      while(fgets(buf, sizeof(buf), f)) {
            if(buf[0])
                  buf[strlen(buf) - 1] = '\0'; // chomp newline
            UnixSocket *us = new UnixSocket;
            unsigned q;
            unsigned type, state;
            int n;
            sscanf(buf, "%x: %x %x %x %x %x %ld %n",
                        &q, &q, &q, &us->flags, &type, &state, &us->inode, &n);
            us->name = buf + n;
            us->type = type;
            us->state = state;
            usocks.insert(us->inode, us);
            if(usocks.count() > usocks.size() * 3)
                  usocks.resize(usocks.count());
      }
      fclose(f);
      return TRUE;
}

void Procinfo::read_sockets()
{
      if(socks_current)
            return;
      socks.clear();
      if(!read_socket_list(Sockinfo::TCP, "tcp") || !read_socket_list(Sockinfo::UDP, "udp"))
            return;

      socks_current = TRUE;
}

void Procinfo::read_usockets()
{
      if(usocks_current)
            return;

      usocks.clear();
      if(!read_usocket_list())
            return;

      usocks_current = TRUE;
}

void Procinfo::invalidate_sockets()
{
      socks_current = usocks_current = FALSE;
}

// return TRUE if /proc/XX/maps could be read, FALSE otherwise
bool Procinfo::read_maps()
{
      // idea: here we could readlink /proc/XX/exe to identify the executable
      // when running 2.0.x
      char name[80];

      if (flag_thread_ok && flag_show_thread)
            sprintf(name, "/proc/%d/task/%d/maps", pid,pid);
      else
            sprintf(name, "/proc/%d/maps", pid);

      FILE *f = fopen(name, "r");
      if(!f) return false;

      char line[1024];        // lines can be this long, or longer
      if(!maps)
            maps = new Svec<Mapsinfo *>;
      else
            maps->purge();

      while(fgets(line, sizeof(line), f)) {
            Mapsinfo *mi = new Mapsinfo;
            int n;
            sscanf(line, "%lx-%lx %4c %lx %x:%x %lu%n",
                        &mi->from, &mi->to, mi->perm, &mi->offset,
                        &mi->major, &mi->minor, &mi->inode, &n);
            if(line[n] != '\n') {
                  int len = strlen(line);
                  if(line[len - 1] == '\n')
                        line[len - 1] = '\0';
                  while(line[n] == ' ' && line[n]) n++;
                  mi->filename = line + n;
            } else if((mi->major | mi->minor | mi->inode) == 0)
                  mi->filename = "(anonymous)";
            maps->add(mi);
      }
      fclose(f);
      return TRUE;
}

// DRAFT CODE:
// return TRUE if /proc/1234/environ could be read, FALSE otherwise
bool Procinfo::read_environ()
{
      int     bsize=0;
      int         size;
      char    path[256];

      if(flag_thread_ok && flag_show_thread)
            sprintf(path, "/proc/%d/task/%d/environ", pid,pid);
      else
            sprintf(path, "/proc/%d/environ", pid);

      bsize=fsize(path);
      if(bsize<=0)
            return false;

      if(envblock)     free(envblock); // refresh()

      envblock = (char *)malloc(bsize+1);
      size=read_file(path,envblock,bsize+1);
      if(size<=0)
            return false;

      // kernel 2.6.x has a bug 
      if(envblock[size-2]==0) // how to check the bug.
      {
            char *p="Kernel Bug?=Qps: wrong environments !  please,check /proc/pid/environ !!";
            size=strlen(p)+1;
            if(bsize>size)
                  strcpy(envblock,p);
      }

      int i=0,n=0,v=0;
      char ch;
      environ.purge();

      for(i=0; i<size ; i++)
      {
            ch=envblock[i];
            if(ch=='=')
            {
                  envblock[i]=0;
                  v=i+1;
            }
            if(ch==0)
            {
                  environ.add(new NameValue(&envblock[n],&envblock[v]));
                  //printf("%s %s\n",&envblock[n],&envblock[v]); 
                  n=i+1;
            }
      }
      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];

            if (flag_thread_ok && flag_show_thread)
                  sprintf(path, "/proc/%d/task/%d/%s", p->pid,p->pid,dir);
            else
                  sprintf(path, "/proc/%d/%s", p->pid, dir);

            int n = readlink(path, buf, sizeof(buf) - 1);
            if(n < 0) {
                  // Either a kernel process, or access denied.
                  // A hyphen is visually least disturbing here.
                  p->*cache = "-";
                  return p->*cache;
            } else if(buf[0] != '[') {
                  // linux >= 2.1.x: path name directly in link
                  buf[n] = '\0';
                  p->*cache = buf;
                  return p->*cache;
            }

            // 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("   ");
      int ni = p->nice;

      s[0] = p->state;
      s[1] = (p->resident == 0 && p->state != 'Z') ? 'W' : ' ';
      s[2] = (ni > 0) ? 'N' : ((ni < 0) ? '<' : ' ');
      return s;
}

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

QString Cat_policy::string(Procinfo *p)
{
      QString s;
      switch(p->get_policy()) {
            case SCHED_FIFO:
                  s = "FI"; break;  // first in, first out
            case SCHED_RR:
                  s = "RR"; break;  // round-robin
            case SCHED_OTHER:
                  s = "TS"; break;  // time-sharing
            default:
                  s = "??"; break;
      }
      return s;
}

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

      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_tms::Cat_tms(const char *heading,const char *explain)
: Category(heading,explain)
{}

QString Cat_tms::string(Procinfo *p)
{
      QString s;
      p->tms = p->get_tms();
      s.sprintf("%.3f",p->tms);
      return s;
}
int Cat_tms::compare(Procinfo *a, Procinfo *b)
{
      return (int)((b->get_tms() - a->get_tms())*1000) ;
}

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

QString Cat_affcpu::string(Procinfo *p)
{
      QString s;
      p->affcpu = p->get_affcpu();
      s.sprintf("%lx",p->affcpu);
      return s;
}
/*
   int Cat_affcpu::compare(Procinfo *a, Procinfo *b)
   {
   return (int)(b->affcpu - a->affcpu);
   }
   */

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

QString Cat_time::string(Procinfo *p)
{
      QString s;
      char buff[64];
      int ticks = p->utime;
      int hundreds;     
      if(Qps::cumulative)
            ticks += p->cutime;

      int t = ticks / HZ;           // seconds

      if(t < 10) {
            hundreds = ticks / (HZ / 100) % 100;
            sprintf(buff,"%1d.%02ds", t, hundreds);
      } else if(t < 100 * 60) {
            sprintf(buff,"%2d:%02d", t / 60, t % 60);
      } else if(t < 100 * 3600) {
            int h = t / 3600;
            t %= 3600;
            sprintf(buff,"%2d:%02dh", h, t / 60);
      } else {
            int d = t / 86400;
            t %= 86400;
            sprintf(buff,"%dd%dh", d, t / 3600);
      }
      s=buff;
      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)
{
      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);
}

// LINUX ?
      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()
{
      char *command, *command_line;
      // 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_TGID, new Cat_int("TGID","Task group ID ( parent of threads )",6, &Procinfo::tgid));
      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", "Terminal"));
      allcats.set(F_TPGID,new Cat_int("TPGID", "Process group ID of tty owner", 6, &Procinfo::tpgid));

      allcats.set(F_USER, new Cat_string("USER", "Owner (*=suid root, +=suid a user)",&Procinfo::username));
      allcats.set(F_GROUP,new Cat_string("GROUP", "Group name (*=sgid other)",&Procinfo::groupname));

      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_SUID, new Cat_int("SUID", "Saved user ID (Posix)", 6,&Procinfo::suid));
      allcats.set(F_FSUID,new Cat_int("FSUID", "File system user ID", 6,&Procinfo::fsuid));
      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_SGID, new Cat_int("SGID", "Saved group ID (Posix)", 6,&Procinfo::sgid));
      allcats.set(F_FSGID,new Cat_int("FSGID", "File system group ID", 6,&Procinfo::fsgid));
      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_NLWP, new Cat_int("NLWP", "Number of tasks(threads) in task group",5, &Procinfo::nthreads));

      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_TMS,  new Cat_tms("TMS", "Time slice in milliseconds"));
      allcats.set(F_SLPAVG, new Cat_int("%SAVG", "Percentage average sleep time (-1 -> N/A)",4,&Procinfo::slpavg));
      allcats.set(F_AFFCPU, new Cat_affcpu("CPUSET", "Affinity CPU mask (0 -> API not supported)"));  // ??? 
      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", 8, &Procinfo::size));
      allcats.set(F_RSS,  new Cat_memory("RSS", "Resident set size", 8, &Procinfo::resident));
      allcats.set(F_TRS,  new Cat_memory("TRS", "Text(code) resident set size", 8, &Procinfo::trs));
      allcats.set(F_DRS,  new Cat_memory("DRS", "Data resident set size(malloc+global variable)", 8, &Procinfo::drs));
      allcats.set(F_STACK,new Cat_memory("STACK", "Stack size", 8, &Procinfo::stack));
      allcats.set(F_SHARE,new Cat_memory("SHARE", "Shared memory with other libs", 8, &Procinfo::share));
      allcats.set(F_SWAP, new Cat_swap  ("SWAP", "Kbytes on swap device"));

      allcats.set(F_DT,   new Cat_uintl("DT", "Number of dirty (non-written) pages", 7, &Procinfo::dt));
      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));

      command="COMMAND";      // label in  /proc/1234/stat  
      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));

      command_line="COMMAND_LINE";  //reference to /proc/1234/cmdline
      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; // !
}

// DRAFT
// Description: update the process list 
//          read /proc/*
//          called by Procview::refresh(), every UPDATE time.
void Proc::refresh()
{

      current_gen=!current_gen; // 0,1,0,1,0 ....

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


      DIR *d = opendir("/proc");
      struct dirent *e;

      while((e = readdir(d)) != 0) {
            if(e->d_name[0] >= '0' && e->d_name[0] <= '9') { // good idea !
                  int pid;
                  int newbie=0;
                  Procinfo *pi;

                  pid=atoi(e->d_name);
                  pi = procs[pid];  

                  if (pi==NULL)   // new process
                  {
                        pi = new Procinfo(pid);     
                        procs.replace(pi->pid, pi);         // insert to  QDict<int>

                        if(procs.count() > procs.size()) // size up 
                              procs.resize(procs.count() * 2 - 1);

                        newbie=1;
                  }

                  if(pi->readproc()>=0)
                  {
                        pi->generation = current_gen;

                        /*
                           if(newbie){ 
                           Procinfo *parent;
                           parent =procs[pi->ppid];
                           if(parent)
                           {
                           parent->children->add(pi);        
                           }
                           else
                           {
                        //root_procs.add(p);
                        printf("Qps : parent null  pid[%d]\n",pi->pid);
                        }        
                        } */

                        if(flag_thread_ok && flag_show_thread) 
                        {
                              read_pid_tasks(pid);   //for threads 
                        }

                  }
                  else 
                        ;// already /proc/PID  gone !!
            }        
      }

      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);  //QIntDict<Procinfo> procs;    
                  delete p;
            } else
                  ++it;
      }
}

Category *Proc::cat_by_name(const char *s)
{
      if(s) {
            for(int i = 0; i < allcats.size(); i++)
            {
                  char *p;
                  p=(char *)allcats[i]->name;
                  if(*p==' ') p++;
                  if(strcmp(p, 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;
}

// LINUX
int Procview::custom_fields[] = {F_PID, F_TTY, F_USER, F_NICE,
      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_STAT,F_RSS,
      F_MEM, F_CPU, F_START, F_TIME,
      F_CMDLINE, F_END};

int Procview::user_fields_smp[] = {F_PID, F_TTY, F_USER,F_CPUNUM, F_NICE,
      F_STAT,F_RSS,
      F_MEM, F_CPU, F_START, F_TIME,
      F_CMDLINE, F_END};


int Procview::jobs_fields[] = {F_PID, F_TGID, F_PPID, F_PGID, F_SID, 
      F_TTY,F_TPGID, 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_RSS,
      F_TRS, F_DRS,F_STACK,F_SHARE,
      //    F_DT,
      F_CMDLINE,F_END};

int Procview::sched_fields[] = {F_PID,F_TGID,F_NLWP,F_STAT,F_FLAGS,F_PLCY,
      F_PRI,F_NICE,F_TMS,F_SLPAVG,F_RPRI,F_AFFCPU,F_CPU,F_START,F_TIME,
      F_CMDLINE,F_END};


/*
   build_tree_24 is ok for 2.4 UP and SMP
   i don't know why  the new build_tree is ok for 2.4 UP, 2.6 UP and SMP
   but is not ok for 2.4 SMP
   */
void Procview::build_tree_24()
{
      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;
      }
}

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

      for(QIntDictIterator<Procinfo> it(proc->procs); (p = it.current()); ++it) 
            if(accept_proc(p)) 
                  p->accepted=true;
            else
            {   
                  p->accepted=false;
                  p->selected=false;
            }

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

                  if(p->tgid==p->pid) 
                        // this means ,the process has no thread!!
                        parent = proc->procs[p->ppid];
                  else
                  {
                        //if(p->tgid!=p->ppid) printf("pid[%d]: tgid =%d , ppid %d \n",p->pid,p->tgid,p->ppid);
                        parent = proc->procs[p->tgid];
                  }

                  //if(parent==0) printf("thread_leader=0 (%d) %s\n",p->tgid,p->command.ascii());
                  if(p->ppid && parent && parent->accepted)   
                  {
                        parent->children->add(p);
                  } 
                  else                                
                  {
                        // 1.init(1) process  
                        // 2.some process which his parent not accepted 
                        // 3.(null) thread has TGID=0,PPID=0
                        root_procs.add(p);
                  }

            } 

      }
}

//NO 3 BOTTLENECK 
//Linux: re-sort the process info
void Procview::rebuild()
{
      procs.clear();  //Svec<Procinfo *> procs; in Proview
      tags_kernel.clear();

      if(treeview) {
            if (flag_24_ok)
                  build_tree_24();
            else
                  build_tree();
            parent_rows.clear();
            linearize_tree(&root_procs, 0, -1);
      //    procs.add(tag_kernel)
      } else {
            for(QIntDictIterator<Procinfo> it(proc->procs); it.current(); ++it) {
                  Procinfo *p = it.current();
                  if(accept_proc(p))
                        procs.add(p);
                  else
                        p->selected = FALSE;
            }
            static_sortcat = sortcat;
            procs.sort(reversed ? compare_backwards : compare);
      }
}

void Procview::set_fields()
{
      switch(viewfields) {
            case USER: // BASIC FIELD
                  if(Procinfo::num_cpus>1)
                        set_fields_list(user_fields_smp);
                  else
                        set_fields_list(user_fields);
                  break;
            case JOBS:
                  set_fields_list(jobs_fields);
                  break;
            case MEM:
                  set_fields_list(mem_fields);
                  break;
            case SCHED:
                  set_fields_list(sched_fields);
                  break;
            case CUSTOM:
                  set_fields_list(custom_fields);
                  break;
      }
}


// LINUX:
// deduce whether the currently selected fields correspond to a field list
void Procview::deduce_fields()
{
      return; // under development (by fasthyun@magicn.com) 2006/05/24

      if(viewfields != CUSTOM)
            return;

      Procview::fieldstates tags[4] = {USER, JOBS, MEM,SCHED};
      int *lists[4] = {user_fields, jobs_fields, mem_fields,sched_fields};
      for(int i = 0; i < 4; 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;
            }
      }
}



Generated by  Doxygen 1.6.0   Back to index