You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1345 lines
36 KiB
1345 lines
36 KiB
/*
|
|
* proc.cpp for Solaris (SunOS)
|
|
*
|
|
* Copyright 1997-1999 Mattias Engdegård
|
|
* Copyright 2005 José Luis Sánchez
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/param.h> //HZ
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <dirent.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <sched.h>
|
|
|
|
#include "qps.h"
|
|
#include "proc.h"
|
|
#include "uidstr.h"
|
|
#include "ttystr.h"
|
|
#include "wchan.h"
|
|
#include "details.h"
|
|
|
|
// Solaris...
|
|
#include <sys/swap.h>
|
|
#include <sys/sysinfo.h>
|
|
#include <sys/mkdev.h>
|
|
#include <limits.h>
|
|
|
|
//#include <sys/procfs.h>
|
|
#define _STRUCTURED_PROC 1
|
|
#include <sys/procfs.h> // psinfo_t ,
|
|
/*
|
|
*
|
|
If you want an "#ifdef solaris", the portable way is
|
|
|
|
#if defined (__SVR4) && defined (__sun)
|
|
|
|
This should work on gcc, sun cc, and lots o other compilers, on both sparc and
|
|
intel.
|
|
If for some reason, you want to know that Sun forte CC (c++) compiler is being
|
|
used, something that seems to work is
|
|
|
|
#if defined(__SUNPRO_CC)
|
|
|
|
Whereas for forte cc (regular C), you can use
|
|
|
|
#if defined(__SUNPRO_C)
|
|
|
|
*/
|
|
#include "proc_common.cpp"
|
|
|
|
char procdir[128] = "/proc";
|
|
|
|
int page_k_shift; //
|
|
|
|
extern int flag_thread_ok;
|
|
extern bool flag_show_thread;
|
|
extern bool flag_devel;
|
|
|
|
kstat_ctl_t *Proc::kc = 0;
|
|
|
|
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(Proc *system_proc, int process_id, int thread_id) : refcnt(1)
|
|
{
|
|
proc = system_proc; //
|
|
first_run = true;
|
|
clone = false;
|
|
|
|
if (thread_id < 0)
|
|
{
|
|
pid = process_id;
|
|
tgid = process_id;
|
|
}
|
|
else
|
|
{
|
|
pid = thread_id;
|
|
tgid = process_id;
|
|
}
|
|
|
|
detail = NULL;
|
|
/// children = 0;
|
|
|
|
envblock = 0;
|
|
// if( readproc(proc_pid) < 0 ) pid = -1; //
|
|
// invalidate object,
|
|
// will be deleted
|
|
|
|
ppid = 0; // no parent important!!
|
|
selected = false;
|
|
hidekids = false;
|
|
envblock = 0; //!!
|
|
|
|
table_child_seq = -1;
|
|
child_seq_prev = -1;
|
|
|
|
lastchild = 0;
|
|
generation = -1;
|
|
detail = 0;
|
|
|
|
size = 0;
|
|
resident = 0;
|
|
// trs=0;
|
|
// drs=0;
|
|
// stack=0;
|
|
// share=0;
|
|
mem = 0;
|
|
|
|
io_read_prev = 0;
|
|
io_write_prev = 0;
|
|
|
|
// tgid=0;
|
|
pcpu = 0;
|
|
pmem = 0;
|
|
|
|
command = "noname";
|
|
tty = 0;
|
|
nice = 0;
|
|
starttime = 0;
|
|
state = 'Z';
|
|
cutime = utime = 0;
|
|
|
|
old_utime = 0;
|
|
}
|
|
|
|
// COMMON?
|
|
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 Proc::init_static()
|
|
{
|
|
if (!kc)
|
|
{
|
|
kc = kstat_open();
|
|
if (!kc)
|
|
{
|
|
perror("kstat_open");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
int pagesize = sysconf(_SC_PAGESIZE); // same getpagesize() in <unistd.h>
|
|
printf("pagesize=%d, %d\n ", getpagesize(), sysconf(_SC_PAGESIZE)); // 4027
|
|
|
|
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 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;
|
|
}
|
|
|
|
// SOLARIS
|
|
// 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_pid;
|
|
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 "." , ".."
|
|
|
|
Procinfo *pi;
|
|
thread_pid = atoi(e->d_name); // only number !!
|
|
|
|
if (pid == thread_pid)
|
|
continue; // skip
|
|
|
|
/*
|
|
Procinfo *pi = new Procinfo(this,pid, thread_id);
|
|
|
|
if(pi->pid == -1)
|
|
delete pi; // already gone
|
|
|
|
else {
|
|
pi->generation = current_gen;
|
|
pi->cmdline="(thread)";
|
|
// newproc(pi);
|
|
}
|
|
*/
|
|
pi = procs.value(thread_pid, NULL);
|
|
|
|
if (pi == NULL)
|
|
{
|
|
pi = new Procinfo(this, pid, thread_pid);
|
|
procs.insert(thread_pid, pi);
|
|
}
|
|
if (pi->readproc() >= 0)
|
|
{
|
|
pi->generation = current_gen;
|
|
// if(pid!=thread_pid)
|
|
// pi->cmdline="(thread)";
|
|
}
|
|
|
|
thread_n++;
|
|
}
|
|
/// printf("\n");
|
|
closedir(d);
|
|
return thread_n;
|
|
}
|
|
|
|
// DEL ( because of 2byte language)
|
|
static inline bool isprintable(unsigned char c)
|
|
{
|
|
// assume, somewhat na.vely, that all latin-1 characters are printable
|
|
return (c >= 0x20 && c < 0x7f) || c >= 0xa0;
|
|
}
|
|
|
|
// DEL replace unprintables by spaces
|
|
static void make_printable(char *s)
|
|
{
|
|
while (*s)
|
|
{
|
|
if (!isprintable(*s))
|
|
*s = ' ';
|
|
++s;
|
|
}
|
|
}
|
|
|
|
// what this fuck..??? move to readproc
|
|
/* 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;
|
|
// }
|
|
{
|
|
// New process
|
|
p->wcpu = p->pcpu; // just a start
|
|
}
|
|
} */
|
|
|
|
// read proc/PID Solaris 10, not tested in Solaris 9
|
|
int Procinfo::readproc()
|
|
{
|
|
char path[256];
|
|
|
|
// Procinfo:pid;
|
|
proc_PID_fd(pid); // check!!
|
|
sprintf(path, "%s/%d/psinfo", procdir, pid);
|
|
|
|
psinfo_t psi;
|
|
if (read_file(path, (void *)&psi, sizeof(psi)) < (int)sizeof(psi))
|
|
return -1;
|
|
|
|
sprintf(path, "%s/%d/usage", procdir, pid);
|
|
prusage_t pru;
|
|
if (read_file(path, (void *)&pru, sizeof(pru)) < (int)sizeof(pru))
|
|
return -1;
|
|
|
|
if (first_run)
|
|
{
|
|
|
|
first_run = false;
|
|
}
|
|
minflt = pru.pr_minf;
|
|
majflt = pru.pr_majf;
|
|
|
|
uid = psi.pr_uid; // ok
|
|
euid = psi.pr_euid; // ok
|
|
gid = psi.pr_gid; // ok
|
|
egid = psi.pr_egid; // ok
|
|
|
|
// make_printable(psi.pr_psargs);
|
|
cmdline = psi.pr_psargs; // ok
|
|
command = psi.pr_fname;
|
|
|
|
ppid = psi.pr_ppid; // ok
|
|
pgrp = psi.pr_pgid; // ok
|
|
session = psi.pr_sid; // ok
|
|
tty = psi.pr_ttydev; // ok type?
|
|
|
|
nthreads = psi.pr_nlwp; // num of threads
|
|
|
|
//------- no checked
|
|
const int ns_ticks = 1000000000 / HZ; // HZ=100, clk_tick
|
|
// printf("ns_ticks=%d\n",ns_ticks);// 10000000
|
|
// starttime = (psi.pr_start.tv_sec) * HZ +
|
|
// psi.pr_start.tv_nsec /
|
|
// ns_ticks;
|
|
starttime = psi.pr_start.tv_sec; // discard tv_nsec
|
|
env_ofs = psi.pr_envp;
|
|
addr_bits = psi.pr_dmodel == PR_MODEL_ILP32 ? 32 : 64;
|
|
|
|
gettimeofday(&tv, 0); // current_time, sys/time , tv.tv_sec, tv.tv_usec
|
|
|
|
// god dam!!!! if that dont reel in the ladies then nothing will!!!!
|
|
|
|
// No
|
|
state = psi.pr_lwp.pr_sname; // no
|
|
command = (state == 'Z') ? "<zombie>" : psi.pr_fname;
|
|
flags = psi.pr_flag; // no
|
|
|
|
// No
|
|
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;
|
|
|
|
// printf("[%d] utime=%d\n",pid,utime);
|
|
|
|
int dcpu;
|
|
if (old_utime == 0 and utime == 0)
|
|
{
|
|
pcpu = 0;
|
|
}
|
|
else
|
|
{
|
|
dcpu = utime - old_utime;
|
|
|
|
// 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;
|
|
|
|
if (dt == 0)
|
|
pcpu = 0;
|
|
else
|
|
pcpu = 100.0 * dcpu / dt;
|
|
|
|
// if( pcpu > 99.99 ) pcpu = 99.99;
|
|
|
|
old_utime = utime;
|
|
old_tv = tv;
|
|
}
|
|
|
|
priority = psi.pr_lwp.pr_pri;
|
|
nice = psi.pr_lwp.pr_nice;
|
|
if (Qps::normalize_nice) //???
|
|
nice -= NZERO;
|
|
|
|
wchan = psi.pr_lwp.pr_wchan;
|
|
|
|
// MEM
|
|
size = psi.pr_size;
|
|
resident = psi.pr_rssize;
|
|
which_cpu = psi.pr_lwp.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.)
|
|
wcpu = psi.pr_pctcpu * (1 / 327.68) * proc->num_cpus;
|
|
pmem = psi.pr_pctmem * (1 / 327.68);
|
|
|
|
rtprio = -1; // ditto
|
|
policy_name[0] = psi.pr_lwp.pr_clname[0];
|
|
policy_name[1] = psi.pr_lwp.pr_clname[1];
|
|
|
|
if (dcpu == 0)
|
|
return 1;
|
|
return 2; // this process consumed jiffi of cpu
|
|
}
|
|
|
|
// thread Solaris 10
|
|
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();
|
|
}
|
|
// get the process stuff
|
|
// readproc();
|
|
|
|
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 - proc->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) * proc->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;
|
|
// thread Solaris10
|
|
}
|
|
|
|
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 Proc::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;
|
|
}
|
|
|
|
// read /proc
|
|
// called by Proc::fresh();
|
|
int Proc::read_system()
|
|
{
|
|
static bool first_run = true;
|
|
if (first_run)
|
|
{
|
|
flag_thread_ok = true;
|
|
|
|
// 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;
|
|
|
|
// Max SMP 1024 cpus . COMMON
|
|
int max_cpus = 1024;
|
|
// cpu_times_vec = (unsigned *)malloc(sizeof(unsigned) *
|
|
// num_cpus *
|
|
// CPUTIMES);
|
|
cpu_times_vec = new unsigned[CPUTIMES * max_cpus];
|
|
old_cpu_times_vec = new unsigned[CPUTIMES * max_cpus];
|
|
|
|
// init
|
|
for (int cpu = 0; cpu < max_cpus; cpu++)
|
|
for (int i = 0; i < CPUTIMES; i++)
|
|
{
|
|
cpu_times(cpu, i) = 0;
|
|
old_cpu_times(cpu, i) = 0;
|
|
}
|
|
|
|
/*
|
|
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;
|
|
*/
|
|
first_run = 0;
|
|
}
|
|
|
|
// 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];
|
|
|
|
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);
|
|
|
|
// 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)
|
|
{
|
|
// printf("kstat name :%s\n",ks->ks_name);
|
|
|
|
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;
|
|
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++;
|
|
}
|
|
}
|
|
Proc::num_cpus = cpu;
|
|
|
|
// exit(1);
|
|
dt_total = 0;
|
|
dt_used = 0;
|
|
|
|
// dt_used= user + system;
|
|
// dt_total= user + system + nice + idle
|
|
// dt_used+=cpu_times(cpu, CPUTIME_USER) + cpu_times(cpu,
|
|
// CPUTIME_SYSTEM);
|
|
// dt_total=dt_used
|
|
|
|
cpu_times(num_cpus, CPUTIME_USER) = 0;
|
|
cpu_times(num_cpus, CPUTIME_SYSTEM) = 0;
|
|
cpu_times(num_cpus, CPUTIME_WAIT) = 0;
|
|
cpu_times(num_cpus, CPUTIME_IDLE) = 0;
|
|
for (int cpu = 0; cpu < num_cpus; cpu++)
|
|
{
|
|
cpu_times(num_cpus, CPUTIME_USER) += cpu_times(cpu, CPUTIME_USER);
|
|
cpu_times(num_cpus, CPUTIME_SYSTEM) += cpu_times(cpu, CPUTIME_SYSTEM);
|
|
cpu_times(num_cpus, CPUTIME_WAIT) += cpu_times(cpu, CPUTIME_WAIT);
|
|
cpu_times(num_cpus, CPUTIME_IDLE) += cpu_times(cpu, CPUTIME_IDLE);
|
|
}
|
|
|
|
cpu = num_cpus;
|
|
dt_used +=
|
|
cpu_times(cpu, CPUTIME_USER) + cpu_times(cpu, CPUTIME_SYSTEM); // Kernel
|
|
dt_total += cpu_times(cpu, CPUTIME_USER) + cpu_times(cpu, CPUTIME_SYSTEM) +
|
|
cpu_times(cpu, CPUTIME_WAIT) + cpu_times(cpu, CPUTIME_IDLE);
|
|
|
|
load_cpu = (float)Proc::dt_used / Proc::dt_total;
|
|
|
|
// Hotplugging Detection : save total_cpu
|
|
if (Proc::num_cpus != Proc::old_num_cpus)
|
|
{
|
|
// for(int i = 0; i < CPUTIMES; i++)
|
|
// cpu_times(num_cpus, i) =
|
|
// cpu_times(Proc::old_num_cpus, i);
|
|
// Proc::old_num_cpus=Proc::num_cpus;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// called by bool Procinfo::read_fds()
|
|
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.append(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.append(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;
|
|
|
|
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];
|
|
|
|
return 0;
|
|
|
|
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.append(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;
|
|
|
|
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.append(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 (Procview::flag_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 (Procview::flag_cumulative)
|
|
{
|
|
at += a->cutime;
|
|
bt += b->cutime;
|
|
}
|
|
return bt - at;
|
|
}
|
|
|
|
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()
|
|
{
|
|
|
|
categories.insert(F_PID,
|
|
new Cat_int("PID", "Process ID", 6, &Procinfo::pid));
|
|
categories.insert(
|
|
F_PPID, new Cat_int("PPID", "Parent process ID", 6, &Procinfo::ppid));
|
|
categories.insert(
|
|
F_PGID, new Cat_int("PGID", "Process group ID", 6, &Procinfo::pgrp));
|
|
categories.insert(F_SID,
|
|
new Cat_int("SID", "Session ID", 6, &Procinfo::session));
|
|
categories.insert(F_TTY, new Cat_tty("TTY", "Controlling tty"));
|
|
categories.insert(
|
|
F_USER, new Cat_user("USER", "Owner (*=suid root, +=suid other user)"));
|
|
categories.insert(F_GROUP,
|
|
new Cat_group("GROUP", "Group name (*=sgid other)"));
|
|
categories.insert(F_UID,
|
|
new Cat_int("UID", "Real user ID", 6, &Procinfo::uid));
|
|
categories.insert(
|
|
F_EUID, new Cat_int("EUID", "Effective user ID", 6, &Procinfo::euid));
|
|
categories.insert(F_GID,
|
|
new Cat_int("GID", "Real group ID", 6, &Procinfo::gid));
|
|
categories.insert(
|
|
F_EGID, new Cat_int("EGID", "Effective group ID", 6, &Procinfo::egid));
|
|
categories.insert(
|
|
F_PRI, new Cat_int("PRI", "Dynamic priority", 4, &Procinfo::priority));
|
|
categories.insert(F_NICE,
|
|
new Cat_int("NICE",
|
|
"Scheduling favour (higher -> less cpu time)",
|
|
4, &Procinfo::nice));
|
|
categories.insert(F_PLCY, new Cat_policy("PLCY", "Scheduling policy"));
|
|
categories.insert(
|
|
F_RPRI,
|
|
new Cat_rtprio("RPRI", "Realtime priority (0-99, more is better)"));
|
|
categories.insert(F_NLWP,
|
|
new Cat_int("NLWP", "Number of threads in process", 5,
|
|
&Procinfo::nthreads));
|
|
categories.insert(F_ARCH, new Cat_int("ARCH", "Architecture (address bits)",
|
|
2, &Procinfo::addr_bits));
|
|
categories.insert(F_MAJFLT,
|
|
new Cat_uintl("MAJFLT",
|
|
"Number of major faults (disk access)", 8,
|
|
&Procinfo::majflt));
|
|
categories.insert(F_MINFLT,
|
|
new Cat_uintl("MINFLT",
|
|
"Number of minor faults (no disk access)",
|
|
8, &Procinfo::minflt));
|
|
|
|
// Memory
|
|
categories.insert(F_SIZE,
|
|
new Cat_memory("SIZE",
|
|
"Virtual image size of process in Kbytes",
|
|
8, &Procinfo::size));
|
|
categories.insert(F_SWAP, new Cat_swap("SWAP", "Kbytes on swap device"));
|
|
categories.insert(
|
|
F_RSS,
|
|
new Cat_memory("RSS", "Resident set size; Kbytes of program in memory",
|
|
8, &Procinfo::resident));
|
|
|
|
categories.insert(F_STAT, new Cat_state("STAT", "State of the process"));
|
|
categories.insert(F_FLAGS, new Cat_hex("FLAGS", "Process flags (hex)", 9,
|
|
&Procinfo::flags));
|
|
categories.insert(
|
|
F_WCHAN,
|
|
new Cat_wchan("WCHAN", "Kernel function where process is sleeping"));
|
|
|
|
categories.insert(
|
|
F_WCPU,
|
|
new Cat_percent("%WCPU", "Weighted percentage of CPU (30 s average)", 6,
|
|
&Procinfo::wcpu));
|
|
categories.insert(
|
|
F_CPU,
|
|
new Cat_percent(" %CPU", "Percentage of CPU used since last update", 6,
|
|
&Procinfo::pcpu));
|
|
categories.insert(
|
|
F_MEM,
|
|
new Cat_percent(" %MEM", "Percentage of memory used (RSS/total mem)", 6,
|
|
&Procinfo::pmem));
|
|
categories.insert(F_START, new Cat_start("START", "Time process started"));
|
|
categories.insert(F_TIME,
|
|
new Cat_time("TIME", "Total CPU time used since start"));
|
|
categories.insert(F_CPUNUM,
|
|
new Cat_int("CPU", "CPU the process is executing on", 3,
|
|
&Procinfo::which_cpu));
|
|
categories.insert(F_CMD, new Cat_string("COMMAND",
|
|
"Command that started the process",
|
|
&Procinfo::command));
|
|
categories.insert(F_CWD, new Cat_dir("CWD", "Current working directory",
|
|
"cwd", &Procinfo::cwd));
|
|
categories.insert(F_ROOT, new Cat_dir("ROOT", "Root directory of process",
|
|
"root", &Procinfo::root));
|
|
categories.insert(F_CMDLINE,
|
|
new Cat_cmdline("COMMAND_LINE",
|
|
"Command line that started the process"));
|
|
|
|
commonPostInit();
|
|
|
|
Proc::init_static();
|
|
}
|
|
|
|
// update the process list
|
|
// called by void Proc::refresh()
|
|
void Proc::read_proc_all()
|
|
{
|
|
|
|
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!
|
|
{
|
|
|
|
/* if(flag_thread_ok && flag_show_thread)
|
|
read_pid_tasks(pid);
|
|
|
|
*/
|
|
|
|
Procinfo *pi;
|
|
int pid;
|
|
|
|
pid = atoi(e->d_name);
|
|
|
|
pi = procs.value(pid, NULL); // if not found pid then, return Null
|
|
|
|
if (pi == NULL) // new process
|
|
{
|
|
pi = new Procinfo(this, pid);
|
|
procs.insert(pid, pi);
|
|
}
|
|
int ret = pi->readproc();
|
|
if (ret > 0)
|
|
{
|
|
pi->generation = current_gen; // this process is alive
|
|
|
|
// if(flag_show_thread and flag_thread_ok )
|
|
// read_pid_tasks(pid); //for threads
|
|
|
|
// add to History /// COMMONZ
|
|
if (ret == 2)
|
|
{
|
|
Procinfo *p = new Procinfo(*pi); // copy
|
|
p->clone = true;
|
|
hprocs->insert(pid, p);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// already gone. /proc/PID dead!
|
|
// later remove this process ! not yet
|
|
}
|
|
}
|
|
}
|
|
closedir(d);
|
|
}
|
|
|
|
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::basic_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};
|
|
|
|
void Procview::set_fields()
|
|
{
|
|
switch (viewfields)
|
|
{
|
|
case USER:
|
|
set_fields_list(basic_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;
|
|
}
|
|
} */
|
|
}
|
|
|
|
void check_system_requirement() {}
|
|
|
|
bool Procinfo::isThread()
|
|
{
|
|
return pid != tgid; // not a thread !
|
|
}
|
|
|
|
Proc::~Proc() {}
|