// proc.cpp for Linux  
//
// This program is free software. See the file COPYING for details.
// Author: Mattias Engdeg?rd, 1997-1999
// 		   Oliver

/* 
	LWP (Light Weight Process): just thread, mainly used in Solaris
	Task : thread and process in Linux
	NPTL(Native POSIX Thread Library) 
	TGID thread group leader's pid 
*/  


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

#include <sched.h> 	// sched_rr_get_interval(pid, &ts);
#include <libgen.h> // basename()
#include <unistd.h> // sysconf()  POSIX.1-2001

#include <sys/time.h> 	
//#include <sys/param.h>	//HZ defined, no more used.
//#include "misc.h"			// x_atoi() , userName() ,groupname()

#include "proc.h"
#include "uidstr.h"
#include "ttystr.h"
#include "wchan.h"

#ifdef GTK
#include "detail_gtk.h" 
#else
#include "details.h" //qt
#endif

#include "proc_common.cpp" 		// COMMON code !!!!

#define PROCDIR  "/proc" 		// hmmm

bool flag_SMPsim=false; 		// SMP simulation 

extern int 		flag_thread_ok;
extern bool		flag_schedstat;
extern bool 	flag_show_thread;	
extern bool 	flag_devel;	

int		pagesize;
int 	Proc::update_msec=1024;

// 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

/*
   Thread Problems. 
   pthread_exit()
   */


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

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

// read() the number of bytes read is returned (zero indicates end  of  file)
// return the number of bytes read if ok, -1 if failed
inline int read_file(char *name, char *buf, int max)
{
	int fd = open(name, O_RDONLY);
	if(fd < 0) return -1;
	int r = read(fd, buf, max);
	close(fd);
//	buf[r]=0;
	return r;
}


// Description : read proc files 
// return 0 : if error occurs.
char buffer_proc[1024*4]; // enough..maybe
char * read_proc_file(const char *fname,int pid=-1,int tgid=-1,int *size=NULL)
{
	static int max_size=0;
	char path[256];
	int	 r;
	
	if(pid<0)
		sprintf(path,"/proc/%s",fname);
	else {
		if(tgid>0)
			sprintf(path,"/proc/%d/task/%d/%s",tgid,pid,fname);
		else 
			sprintf(path,"/proc/%d/%s",pid,fname);
	}
	
	if(strcmp(fname,"exe")==0)
	{
		if( (r=readlink(path, buffer_proc, sizeof(buffer_proc) - 1)) >= 0)
		{
			buffer_proc[r]=0; //safer 
			return buffer_proc;
		}
		else
			return 0;
	}

	int fd = open(path, O_RDONLY);
	if(fd < 0) return 0;
	r = read(fd, buffer_proc, sizeof(buffer_proc)-1); //return 0 , -1 , 
	if(r<0) return 0;

	if(max_size<r) max_size=r;
	
	if(size!=0) *size=r;

	buffer_proc[r]=0; //safer 

	return buffer_proc;
	//note: not work  fgets(sbuf, sizeof(64), fp) why???
}

char * read_proc_file2(char *r_path,const char *fname,int *size=NULL)
{
	static int max_size=0;
	char path[256];
	int	 r;
	
	//strcpy(path,r_path);

	sprintf(path,"%s/%s",r_path,fname);
	
	if(strcmp(fname,"exe")==0)
	{
		if( (r=readlink(path, buffer_proc, sizeof(buffer_proc) - 1)) >= 0)
		{
			buffer_proc[r]=0; //safer 
			return buffer_proc;
		}
		else
			return 0;
	}

	int fd = open(path, O_RDONLY);
	if(fd < 0) return 0;
	r = read(fd, buffer_proc, sizeof(buffer_proc)-1); //return 0 , -1 , read_count 
	if(r<0) return 0;

	if(max_size<r) max_size=r;
	
	if(size!=0) *size=r;

	buffer_proc[r]=0; //safer 
	close(fd);

	return buffer_proc;
	//note: not work  fgets(sbuf, sizeof(64), fp) why???
}


// TEST CODE ,  Bottleneck  
// 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;	
}

// new process created 
Procinfo::Procinfo(Proc *system_proc,int process_id, int thread_id) : refcnt(1)
{
	first_run=true;
	clone=false;
	
	proc=system_proc;

	if(thread_id<0) // 
	{
		pid=process_id;
		tgid=process_id; // thread group leader's id
	}
	else {
		pid=thread_id;
		tgid=process_id; // thread group leader's id
	}
	
	ppid=0;   // no parent
	selected = false;
	hidekids = false;
	envblock = 0; //!!

	table_child_seq=-1;
	child_seq_prev=-1;
	
	lastchild=0;
	generation=-1;
	detail = 0;

	/// per_cpu_times = 0; not yet

	size=0;
	resident=0;
	trs=0;
	drs=0;
	stack=0;
	share=0;
	mem=0;
	
	io_read_prev=0; // **
	io_write_prev=0;
	io_read=0; // **
	io_write=0; // **

	//tgid=0;
	pcpu=0;
	pmem=0;

	old_utime=0;	// this must be current utime !
	old_wcpu=0;
	
	command="noname";
	tty=0;
	nice=0;
	starttime=0;
	state='Z';
	cutime=utime=0;

	nthreads=0; /* number of threads */

	hashstr[0]=0;
	hashlen=0;
	
}

Procinfo::~Procinfo()
{	
	if(!clone)
	{
		void watchdog_check_if_finish(QString cmd,Procinfo *p);
		watchdog_check_if_finish(command,this);

		if(detail) {
		//	printf("~Procinfo() : pid=%d\n",pid);
			detail->process_gone();
			detail = 0;
		}

	//    if(environ)    delete environ;
		if(envblock)
			free(envblock);/// double free , SEGFAULT 
	}

//	fd_files.squeeze();
//	maps.squeeze();
/*
	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 Proc::init_static()
{

	//socks.setAutoDelete(TRUE);
	///usocks.setAutoDelete(TRUE);
	
	pagesize=sysconf(_SC_PAGESIZE); // same getpagesize()  in <unistd.h>
//	printf("pagesize=%d, %d\n",getpagesize(), sysconf(_SC_PAGESIZE)); //4027
}



//  tricky function...(by fasthyun@magicn.com) 
//  Description : 
//  	let's deal thread as normal process!
//		read /proc/PID/task/* and 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=0;

	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.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++;
	}
	closedir(d);				
	return thread_n;
}

// update wcpu,%cpu field 
void Procinfo::calculate_cpu()  //
{
}

// using cache for Speed up
int  Procinfo::hashcmp(char *sbuf)  
{
	int statlen; 
	
	statlen=strlen(sbuf);	
	if(statlen>sizeof(hashstr))
	{
		// some user reported 265byte.
		printf("Qps BUG:  hashstr shortage statlen(%d) > hashstr(%d), report this message to fasthyun@magicn.com \n",statlen,sizeof(hashstr));
		abort();
	}
	else
		if(statlen==hashlen)
		{
			if(memcmp(hashstr,sbuf,statlen)==0)
			{
				pcpu=0;
				// 1. I am a sleeping process  
				//printf("[%d] sleep process \n",pid);
				return 1;
			}
		}
	memcpy(hashstr,sbuf,statlen); // to back
	hashlen=statlen;
	return 0;
}

int mini_sscanf(const char *s,const char *fmt, ...);

// Description :	read /proc/PID/*   or	read /proc/PID/task/*
//      be called every refresh() time. 
//      return -1 means the process already dead !
int Procinfo::readproc() 
{
	char cmdbuf[MAX_CMD_LEN];
	char path[64];
	int  len;
	char *sbuf;  // should be enough to acommodate /proc/PID/stat
	char *buf;

	int x_pid; // just pid
	int	i_tty; //
	long stime, cstime;
	

	// Note :  /proc/PID/* is not same /proc/task/PID/*
	if(isThread())// flag_thread_ok
	{
		sprintf(path,"/proc/%d/task/%d",tgid,pid);
	}
	else 
		sprintf(path,"/proc/%d",pid);


	if(first_run)  
	{
		//	Note: COMMAND(?) , TGID, UID , COMMAND_LINE  never change ! 
		old_wcpu = wcpu = pcpu = 0.0; 
		
		// read /proc/PID/status
		if((buf= read_proc_file2(path,"status")) ==0 ) return -1;
		
		// Note: Process_name from   
		// 		1.status (15 chars-name)
		// 		2.stat (15 chars-name, pass)
		// 		3.cmdline : full name  (sometimes have null, Thread can't use cmdline)
		// 		4.exe : full name (frequently this does not exist, thread can't use exe)
		// 		5.comm : 15 chars ?
		//
		// Note: 
		//  1. thread's name_max is 15 chars
	
		if(mini_sscanf(buf,"Name: %S\n",cmdbuf)==0 ) return -1;
		else
		{	
			command = cmdbuf;
			if(command.contains("kthread")) hidekids=true; // kthread, kthreadd , ///Procinfo::qps_pid=pid;
		}

		if(mini_sscanf(buf, "Tgid: %d ", &tgid)==0) return -1;
		if(mini_sscanf(buf, "Uid: %d %d %d %d", &uid, &euid, &suid, &fsuid) !=4) return -1; 
		if(mini_sscanf(buf, "Gid: %d %d %d %d", &gid, &egid, &sgid, &fsgid) !=4) return -1;
		
		username=userName(uid,euid);
		groupname=groupName(gid,egid);
		
		int bug=0;
		char cmdline_cmd[4096]; // some cmdline very large!  ex)chrome
		//read /proc/pid/cmdline
		int size;
		cmdline_cmd[0]=0;

		// anyone can read [cmdline]
		if((buf= read_proc_file2(path,"cmdline", &size)) ==0 ) return -1;
		else {
			//printf("DEBUG: size=%d \n",size);
			int  cmdlen=strlen(buf);
		
			if(cmdlen == 0) {
				// 1. kthread 
				// printf("Qps:debug no_cmdline pid=%d\n",pid );
				cmdline = "";
			} 	
			// for non-ascii locale language 
			// cmdline = codec->toUnicode(cmdbuf,strlen(cmdbuf));
			else
			{
				
				//change 0x00,0xA to ' ' 
				for(int i = 0; i < size - 1; i++)  //OVERFLOW
					if(buf[i]==0 or buf[i]==0x0A) 
						buf[i] = ' ';
				cmdline = buf; 
				strcpy(cmdline_cmd,buf);
			}
		}

		// VERY COMPLEX CODE 
		// because Command's MAX_length is only 15, so sometimes cmd_name truncated,
		// we should guess ...
		//
		// The solution is...
		// 1.check [exe] file ( only owner can read it)
		// 2.check [cmdline] ( anyone can read it )
		// 3.check [comm] 
		//
		if(command.size()==15) 
		{
		// only root & owner can read [exe] link
		/*
			if((buf= read_proc_file2(path,"exe")) !=0 )
			{	
				// printf("Qps:debug %s\n",buf );
				if(strlen(basename(buf))>15 and 
					strncmp(qPrintable(command),basename(buf),15)==0 )
					command=basename(buf); // no memory leak !
				else  ;// just use command 
				//printf("Qps:debug %s\n",buf );
			} */

			if(true) // guess the full name of the command
			{
				// Use /proc/PID/cmdline, comm, status 
				// ex) 
				//     /usr/lib/chromium/chromium --option1 --option2 
				//     python /usr/lib/system-service-d
				//	   pam: gdm-password
				//	   hald-addon-input: Listing On /dev~
				//
				char *p;	
				p=strstr(cmdline_cmd,": ");  // cut the options !
				if ( p!=0 ) *p=0; 
				p=strchr(cmdline_cmd,' ');  // cut the options !
				if ( p!=0 )	*p=0;

				// printf("Qps:debug %s\n",cmdline_cmd );
				char *pstart=strstr(basename(cmdline_cmd), cmdbuf);
				if(pstart!=0)
				{
					command=pstart;	//copy
					///printf("Qps:debug2 %s\n",basename(cmdline_cmd));
				}
			
			}
		}

		if(isThread())	cmdline=command + " (thread)";

		if(flag_devel and bug)
		{	
			//command.append("^");
			cmdline +=" ^ Qps: may be a wrong commandline ";
		}

		void watchdog_check_if_start(QString cmd,Procinfo *ps);
		watchdog_check_if_start(command,this); // segfault possible.

		first_run=false;
	}

	if(flag_schedstat==true)	
	{	
		// if no change then return.    twice faster !
		// MAX_256 bytes check...?
		// 2.6.9 upper only and some system no has
		if((sbuf= read_proc_file2(path,"schedstat")) ==0 ) return -1;  
		
		if(hashcmp(sbuf)) 
			return 1; // no change
	}
	// read /proc/PID/stat 
	if((sbuf= read_proc_file2(path,"stat")) ==0 ) return -1; 
		
	if(flag_schedstat==false)// if no change then return.    twice faster !
	{	
		if(hashcmp(sbuf)) return 1;
	}
	
	///if (proc_pid_fd(pid)== TRUE) ;  // bottleneck !!
	
	/*
		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.
	
		Notes :
	  		1. man -S 5 proc
	  		2. man -S 2 times
	  		3. ppid can be changed when parent dead !
			4. initial utime maybe 0, so %CPU field NotAnumber !!
		utime: user time
		stime: kernel mode tick	
		cutime : The  number  of jiffies that this process's waited-for children have been scheduled in user mode. 
		
		#jiffies == tick 
	*/
	unsigned int guest_utime,cguest_utime;
#if 1
		char *p,*p1;
		// in odd cases the name can contain spaces and '(' or ')' and numbers, so this makes 
		// parsing more difficult. We scan for the outermost '(' ')' to find the name.
		p = strchr(sbuf, '(');
		p1 = strrchr(sbuf, ')');
		if (p == 0 || p1 == 0) return -1;
		p1++;
		// we can safely use sscanf() on the rest of the string
		sscanf(p1, " %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 %u %u",
#else
		// some errors will occur !
		mini_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 %u %u",
			&x_pid, &cmdbuf[0], 
#endif			
			&state, &ppid, &pgrp, &session, &i_tty, &tpgid,
			&flags, &minflt, &cminflt, &majflt, &cmajflt,
			&utime, &stime , &cutime, &cstime, &priority, &nice,
			&nthreads /* number of threads v2.6 */,
			/* itrealvalue */
			&starttime,	/* start time(static) */ // jiffes
			/* vsize */
			/* rss */
			/* rlim, startcode, endcode, startstack kstkesp kstkeip,
			   signal, blocked, sigignore, sigcatch */
			&wchan,
			/* 0L, 0L, exit_signal */
			&which_cpu
			/* rt_priority, policy, delayacct_blkio_ticks  */  
			,&guest_utime,&cguest_utime);
	
	starttime= proc->boot_time /* secs */ + (starttime/proc->clk_tick); 

	tty = (dev_t)i_tty; // hmmm	
	//if(tty!=0)	printf("pid=%d tty =%d\n",pid,tty);

	//	if(guest_utime>0 or cguest_utime>0)	
	//	printf("cmd [%s] guest_utime=%d cguest_utime =%d\n",qPrintable(command),guest_utime,cguest_utime);

	utime += stime;		// we make no user/system time distinction 
	cutime += cstime;	


	if(old_utime>0) // check..
	{
		int dcpu; 
		dcpu = utime - old_utime; // user_time from proc 
		if(dcpu<0 ) {
			// why.. this occurs ?
			// Qps exception:[3230,firefox] dcpu=-22 utime=39268 old_utime=39290 why occur?
			if(flag_devel) 
				printf("Qps :[%d,%s] dcpu=%d utime=%d old_utime=%d why occurs?\n"
						,pid,qPrintable(command),dcpu,utime,old_utime);
			return 1;		
		} 

		//gettimeofday(&tv, 0); 	//sys/time
		if(proc->dt_total>0)	// move to Proc ??
		{
			pcpu = 100.0 * dcpu / proc->dt_total;
			if(Procview::flag_pcpu_single==true) pcpu*=proc->num_cpus; // 
		}
		//else  too fast read again 
		//printf("Qps exception: dt_total=%d  report to fasthyun@magicn.com \n",Proc::dt_total);

		if(flag_devel and pcpu>100) //DEBUG CODE
		{
			printf("Qps pcpu error: %0.0f%% [%d,%s] dt_total=%d dcpu=%d utime=%d old_utime=%d \n"
					,pcpu,pid,qPrintable(command), proc->dt_total,dcpu,utime,old_utime);
			pcpu=99.99;
		}

		const float a = Procview::avg_factor;
		wcpu = a * old_wcpu + (1 - a) * pcpu;
	}
	old_tv=tv;
	old_wcpu=wcpu;
	old_utime=utime;    // **** 

	// read /proc/%PID/statm  - memory usage
	if(1) 
	{
		if((buf= read_proc_file2(path,"statm")) ==0 ) return -1; // kernel 2.2 ?
		sscanf(buf, "%lu %lu %lu %lu %lu %lu %lu",
				&size, &resident, &share, &trs, &lrs, &drs, &dt);
		size	*=pagesize/1024; // total memory in kByte 
		resident*=pagesize/1024;
		share	*=pagesize/1024; // share 
	//	trs		;	// text(code)	
	//	lrs		;	// zero : lib, awlays zero in Kernel 2.6
	//	drs		;	// data: wrong in kernel 2.6
	//	dt		;	// zero : in Kernel 2.6
		mem= resident-share;
		//pmem = 100.0 * resident / proc->mem_total;
		pmem = 100.0 * mem / proc->mem_total;
	}

	// read /proc/PID/status check !!
	if((buf= read_proc_file2(path,"status")) ==0 ) return -1; 
	else 
	{
		// slpavg : not supported in kernel 2.4; default value of -1
		if(mini_sscanf(buf, "SleepAVG:%d",&slpavg)==0) slpavg =-1;

		if(strstr(buf, "VmSize:"))
		{
			//mini_sscanf(p, "VmSize: %d",&size);	// XXX
			//mini_sscanf(p, "VmRSS: %d",&resident);
			//mini_sscanf(sbuf, "VmLib: %d",&share);
			mini_sscanf(buf, "VmData: %d",&drs);	//data	in kByte
			mini_sscanf(buf, "VmStk: %d",&stack);	//stack	in kByte
			mini_sscanf(buf, "VmExe: %d",&trs);		//text 
		}
		
	}

	/* 
	  generally 
	  shared = RSS - ( CODE + DATA + STACK )
	  share= resident - trs -drs -stack;
	  
	 // Defines from task_mmu.c of kernel source 
	 total_vm==size 
	 data = mm->total_vm - mm->shared_vm - mm->stack_vm;
	 swap = p->size - p->resident ; 	
	*/
	
	// read /proc/PID/file_io 
	// NOTE:  2.6.11 dont have IO file
	// COMPLEX_CODE
	if((buf= read_proc_file2(path,"io")) !=0 ) 
	{
		// rchar = ... not file maybe sockread
		//
		mini_sscanf(buf, "read_bytes:%d",&io_read);
		mini_sscanf(buf, "write_bytes:%d",&io_write);
		
		//if(io_read_prev!=0)
		{
			if(io_read_prev==0) 	io_read_prev=io_read; 
			if(io_write_prev==0)	io_write_prev=io_write;

			// NOTE: Kbps right????
			io_read_KBps =(io_read-io_read_prev)/proc->update_msec; // not accurate....
			io_write_KBps =(io_write-io_write_prev)/proc->update_msec;

			proc->io_byte+=io_read_KBps; //test
			proc->io_byte+=io_write_KBps; 
		}

		io_read_prev=io_read;
		io_write_prev=io_write;
		
		//io_read>>=10; //  divide by 1024
		//io_write>>=10; //  divide by 1024
	}
	//per_cpu_times = 0; // not yet
	
	if((buf= read_proc_file2(path,"wchan")) !=0)
	{
		wchan_str=buf;
	}

	policy = -1;		// will get it when needed
	rtprio = -1;		// ditto
	tms = -1;		    // ditto

 	// useless ?  if(detail)	detail->set_procinfo(this);  // BAD !!!
	return 2; // return ok.
}


// just grab the load averages
// called by 
void Proc::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]);
}

int Proc::countCpu()
{
	static bool first_run=true;
	char path[80];
	char buf[1024*8]; // for SMP 

	int num_cpus=0,n;
	// read system status  /proc/stat
	strcpy(path, "/proc/stat"); 
	//if((buf= read_proc_file("stat:)) ==0 ) return -1;
	if((n = read_file(path, buf, sizeof(buf) - 1)) <= 0)
	{ 
		printf("Qps Error: /proc/stat can't be read ! check it and report to fasthyun@magicn.com\n");
		abort();//	return 0;
	}
	buf[n] = '\0';


	// count (current) cpu of 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)	Proc::num_cpus--;
		p = strchr(p, '\n');
		if(p)
			p++;
	}

	if(flag_devel and flag_SMPsim )
	{	
		//num_cpus=64;
		int vals[]={2,4,8,16,32};
		int r=rand()%5;
		num_cpus=vals[r];
	}
	return num_cpus;
}

// LINUX
// Description:  read common information  for all processes 
// return value 
// 		-1 : too fast refresh ! 
int Proc::read_system() // 
{
	static bool first_run=true;
	char path[80];
	char buf[1024*8]; // for SMP 

	char *p;
	int n;

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

		/* NPTL(Native POSIX Thread Library) */  
		strcpy(path,"/proc/1/task");
		if (!stat(path, (struct stat*)buf) )
			flag_thread_ok = true;
		else
			flag_thread_ok = false;
		
		/* check schedstat  */  
		strcpy(path,"/proc/1/schedstat");  // some system doesn't have 
		if (!stat(path, (struct stat*)buf) )
			flag_schedstat = true;
		else
			flag_schedstat = false;

		strcpy(path, "/proc/stat"); 
		if((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) return 0;
		buf[n] = '\0';
		p = strstr(buf, "btime");  
		if(p==NULL)
		{
			// used  
			printf("Qps: A bug occurs ! [boot_time] \n"); 
			//boot_time= current time
		}else 
		{
			p+=6;
			//sscanf(p, "%d", &Proc::boot_time); //???? why segfault???
			sscanf(p, "%d", &boot_time);
		}
	
		// Max SMP 1024 cpus,  MOVETO: COMMON
		int max_cpus=512;
		cpu_times_vec = new unsigned[CPUTIMES * max_cpus]; //??? +2
		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;
			}

		//first_run=false; // not yet , at the bottom of this function
	}

	// read system status  /proc/stat
	strcpy(path, "/proc/stat"); 
	//if((buf= read_proc_file("stat:)) ==0 ) return -1;
	if((n = read_file(path, buf, sizeof(buf) - 1)) <= 0)
	{ 
		printf("Qps Error: /proc/stat can't be read ! check it and report to fasthyun@magicn.com\n");
		abort();//	return 0;
	}
	buf[n] = '\0';


	if(true) {
		// count (current) cpu of system 
		char *p;
		p = strstr(buf, "cpu");
		num_cpus=0;
		while(p < buf + sizeof(buf) - 4 && strncmp(p, "cpu", 3) == 0) 
		{
			num_cpus++;
			if(strncmp(p, "cpu0", 4) == 0)	Proc::num_cpus--;
			p = strchr(p, '\n');
			if(p)
				p++;
		}

		if(flag_SMPsim )
		{	
			int vals[]={2,4,8,16,32};
			int r=rand()%5;
			num_cpus=vals[r];
			num_cpus=8;
		}

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

	}




	// backup old values :  important*******
	for(int cpu = 0; cpu < Proc::num_cpus+1; cpu++)
	{
		for(int i = 0; i < CPUTIMES; i++)
			old_cpu_times(cpu, i) = cpu_times(cpu, i);
	}

	/*
		/proc/stat 
		cpu#	user	nice	system	idle		iowait(2.6)	irq(2.6)	sft(2.6)	steal(2.6.11) 	guest(2.6.24)
		cpu0	3350 	9		535		160879		1929		105			326			5				1200
			
		Q1: kernel 2.4 cpu0 exist ?
	*/

	// Total_cpu  
	int total_cpu=Proc::num_cpus;
	unsigned user,nice,system,idle,iowait, irq, sftirq, steal,guest, nflds;
	nflds = sscanf(buf, "cpu %u %u %u %u %u %u %u %u %u", &user, &nice, &system, &idle, &iowait, &irq, &sftirq, &steal,&guest);
	if( nflds > 4 ) {	
		// kernel 2.6.x 
		system+=(irq+sftirq);
		idle+=iowait;
	}	
	if( nflds == 9 ){
		system+=steal; 
		system+=guest;
	}
	cpu_times(Proc::num_cpus, CPUTIME_USER)=user;
	cpu_times(Proc::num_cpus, CPUTIME_NICE)=nice;
	cpu_times(Proc::num_cpus, CPUTIME_SYSTEM)=system;
	cpu_times(Proc::num_cpus, CPUTIME_IDLE)=idle;
	

	// DRAFT!
	// num_cpus == total_cpu
	//
	// dt_total= user + system + nice + idle
	// dt_used= user + system;
	Proc::dt_used=user - old_cpu_times(Proc::num_cpus, CPUTIME_USER); // infobar uses this value 
	Proc::dt_used+=system - old_cpu_times(Proc::num_cpus, CPUTIME_SYSTEM);
	Proc::dt_total= dt_used +
					nice - old_cpu_times(Proc::num_cpus, CPUTIME_NICE)+
					idle - old_cpu_times(Proc::num_cpus, CPUTIME_IDLE);

	load_cpu=(float)Proc::dt_used/Proc::dt_total;  // COMMON

	if(first_run)
	{
		//	printf("\n==================== tooo fast =================================\n");
		//	printf("DEBUG:dt_total=%d dt_used=%d\n",Proc::dt_total,Proc::dt_used);
		//	return -1; // too early refresh again  !!
	}
	if(Proc::dt_total==0)
	{	
		//?????
		printf("Error: dt_total=0 , dt_used=%d(%d)  report to fasthyun@magicn.com\n",Proc::dt_used,old_cpu_times(Proc::num_cpus,CPUTIME_IDLE));
		dt_total=500; //more tolerable?
		//abort(); // stdlib.h
	}

//	void watchdog_syscpu(int ); 
//	watchdog_syscpu((user-old_cpu_times(num_cpus,CPUTIME_USER))*100/dt_total); // test
	
	//if(flag_devel and flag_SMPsim )
	if(flag_SMPsim )
	{
		// for Developer only !!! 
		// printf("user%d nuce%d system%d idle%d\n",user,nice,system,idle);
		for(int cpu = 0; cpu < num_cpus ; cpu++) {
			//stdlib.h, int rand();
			if(dt_used!=0) cpu_times(cpu, CPUTIME_USER)=old_cpu_times(cpu, CPUTIME_USER ) + rand()%dt_used;
			else 	cpu_times(cpu, CPUTIME_USER)=0;
			cpu_times(cpu, CPUTIME_NICE)=nice;
			cpu_times(cpu, CPUTIME_SYSTEM)=system;
			cpu_times(cpu, CPUTIME_IDLE)=idle;	

		}
	}
	else
	{
	// Single-CPU and SMP(Multi-CPU) 
		for(int cpu = 0; cpu < num_cpus ; cpu++) {
			char cpu_buf[10];
			sprintf(cpu_buf, "cpu%d", cpu);
			if((p = strstr(buf, cpu_buf)) != 0) {
				nflds = sscanf(p, "%*s %u %u %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,&steal,&guest);
				//cpu_times(cpu, CPUTIME_USER),cpu_times(cpu, CPUTIME_NICE),
				if( nflds > 4 ) {
					// kernel 2.6.x 
					cpu_times(cpu, CPUTIME_SYSTEM)+=(irq+sftirq);
					cpu_times(cpu, CPUTIME_IDLE)+=iowait;
				}
					if( nflds == 9 ){
						cpu_times(cpu, CPUTIME_SYSTEM)+=(steal+guest);
				}

				// 2.4.27-SMP bug ?

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

	// read memory info
	strcpy(path, PROCDIR);
	strcat(path, "/meminfo");
	if((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) return 0;
	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:"))
		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);

	first_run=false;
	return 0;	
}


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) //POSIX
		tms = -1; // should not be possible
	else {
		tms = ts.tv_nsec; // nano seconds
		tms /= 1000000;  // mili seconds
		tms +=ts.tv_sec*1000;
	}
	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/PID/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[128];
	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;
		
		// check socket_fd
		if((buf[0] == '[' and sscanf(buf, "[%lx]:%lu", &dev, &ino) == 2	and dev == 0) // Linux 2.0 style 
				|| sscanf(buf, "socket:[%lu]", &ino) > 0) // Linux 2.1 upper
		{
			Sockinfo *si = NULL;
			si=proc->socks.value(ino,NULL); // sock
			char buf[80];
			if(si) {
				printf("sock ino=%d\n",ino);
				si->pid=pid;
				// a TCP or UDP socket
				sock_inodes.append(new SockInode(fdnum, ino));
				sprintf(buf, "%s socket %lu",si->proto == Sockinfo::TCP ? "TCP" : "UDP", ino);
				fd_files.append(new Fileinfo(fdnum, buf, mode));
				return;
			} else {
				// maybe a unix domain socket?
				// read_usockets(); 
				UnixSocket *us = NULL;

				us=proc->usocks.value(ino,NULL);
				if(us) {
					const 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.append(new Fileinfo(fdnum, s, mode));
					return;
				} 
			}
		}
		// normal filess
		// assume fds will be read in increasing order
		fd_files.append(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[128], *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(!sock_inodes) 	sock_inodes = new Svec<SockInode*>(4);
	else 	sock_inodes->purge(); */

	strcat(path,"/");
	
	fd_files.clear(); //
	struct dirent *e;
	while((e = readdir(d)) != 0) {
		char str[128];
		if(e->d_name[0] == '.')
			continue;		// skip . and ..
		int fdnum = atoi(e->d_name);
		strcpy(str,path);
		strcat(str,e->d_name);
	//	printf("str=%s\n",str);
		read_fd(fdnum, str);
	}
//	printf("end str=\n");
	closedir(d);
	return TRUE;
}

// tcp, udp
bool Proc::read_socket_list(Sockinfo::proto_t proto,const char *filename)
{
	char path[80];
	sprintf(path, "/proc/net/%s", filename);
	FILE *f = fopen(path, "r");
	if(!f) return false;

	char buf[128*3];
	// Header 
	// sl  local_addr rem_addr  st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
  
	Sockinfo si;
		
	printf("read_socket_list()\n");
	fgets(buf, sizeof(buf), f);	// skip header
	while(fgets(buf, sizeof(buf), f) != 0) {
	//	Sockinfo *si = new Sockinfo;
		si.pid=-1;
		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;
		
		Sockinfo *psi;
		psi=socks.value(si.inode,NULL);
		if(psi==NULL)
		{	
			printf("inode =%d \n",si.inode);
			psi=new Sockinfo;
			*psi=si;
			socks.insert(si.inode, psi);
		}
		else *psi= si;

		//linear_socks.add(si);
	}
	fclose(f);
	return true;	
}


bool Proc::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;
		UnixSocket us;

		unsigned q;
		unsigned type, state;
		int n;
		sscanf(buf, "%x: %x %x %x %x %x %ld %n",
			// Num	refcount protocol flags type state indoe path
				&q, &q, &q /*protocol*/ , &us.flags, &type, &state, &us.inode, &n);
		us.name = buf + n;
		us.type = type;
		us.state = state;
	
		UnixSocket *pus;
		pus=usocks.value(us.inode,NULL);
		if(pus==NULL)
		{	
			printf("inode =%d \n",us.inode);
			
			pus=new UnixSocket;
			*pus=us;
			usocks.insert(us.inode, pus);
		}
		else *pus= us;

	}
	fclose(f);
	return TRUE;
}

void Proc::read_sockets()
{
	//socks.clear();
	
	//memory leak !!
	if(!read_socket_list(Sockinfo::TCP, "tcp") || !read_socket_list(Sockinfo::UDP, "udp"))
		return;
	// remove no more socket ino
	read_usocket_list();

	socks_current = true;
}

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

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

	usocks_current = TRUE;
}

void Proc::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 longer , SEGFAULT

	while(fgets(line, sizeof(line), f)) {
		Mapsinfo *mi = new Mapsinfo;
		int n;
		sscanf(line, "%lx-%lx %4c %lx %x:%x %lu%n",
		//sscanf(line, "%lx-%lx %4c %lx %x:%x %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.append(mi);
	}
	fclose(f);
	if(maps.size()==0) return false;

	return true;
}

// DRAFT CODE:
// return TRUE if /proc/777/environ could be read, false otherwise
int fsize(char *fname);
bool Procinfo::read_environ()
{
	int     file_size=0;
	int		size;
	char    path[256];

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

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


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

	// kernel 2.6.x has a bug 
	if(envblock[size-2]==0) // how to check the bug.
	{
		char buf[128];
		sprintf(buf,"Kernel Bug= wrong environments !  please, check /proc/%d/environ !",pid);
		size=strlen(buf)+1;
		if(file_size>size)
			strcpy(envblock,buf);
	}

	int i=0,n=0,v=0;
	char ch;

	for(i=0; i<size ; i++)
	{
		ch=envblock[i];
		if(ch=='=')
		{
			envblock[i]=0;
			v=i+1;
		}
		if(ch==0)
		{
			NameValue nv(&envblock[n],&envblock[v]);
			environ.append(nv);
			//printf("%s %s\n",&envblock[n],&envblock[v]); 
			n=i+1;
		}
	}
	if(envblock)  
	{
		free(envblock); // refresh() // INVALID VALGRIND
		envblock=0;
	}
	return true;
}


// CWD,ROOT only so...
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;
}

// 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();
}

// maybe tms COMMON 
	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);
	//s.sprintf("%d",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 or COMMON?
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 ms;

	int proctick=p->proc->clk_tick;

	if(Procview::flag_cumulative)
		ticks += p->cutime;

	int t = ticks / p->proc->clk_tick;		// seconds
	//COMPLEX CODE
	if(t < 10) { // ex. 9.23s  
		ms = ticks / (p->proc->clk_tick / 100) % 100; // Need FIX 
		sprintf(buff,"%1d.%02ds", t, ms);
	} else if(t < 60 ) { // ex. 48s 
		sprintf(buff,"%5ds", t);
	} else if(t < 60 * 10) { // ex. 8.9m, 9.0m 
		sprintf(buff,"%2d.%1dm", t / 60, (t % 60) / 6 );
	} else if(t < 60 * 60) { // 58m 
		sprintf(buff,"%5dm", t / 60);
	} else if(t < 24 * 3600) { // 
		int h = t / 3600; // 1hour = 3600 = 60m*60s
		t %= 3600;
		sprintf(buff,"%2d:%02d", h, t / 60);
	} else if(t < 10 * 24 * 3600 ) { // 
		int d = t / 86400; // 1 day = 24* 3600s 
		t %= 86400;
		sprintf(buff,"%2d.%1dd", d, (t*10 / (3600*24)));
	} else {
		int d = t / 86400; // 1 day = 24* 3600s 
		sprintf(buff,"%5dd", d);
	}
	s=buff;
	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;
}


// 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()
{
	// Note: 
	categories.insert(F_PID,  new Cat_int("PID", "Process ID", 6, &Procinfo::pid));
	categories.insert(F_TGID, new Cat_int("TGID","Task group ID ( parent of threads )",6, &Procinfo::tgid));
	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", "Terminal"));
	categories.insert(F_TPGID,new Cat_int("TPGID", "Process group ID of tty owner", 6, &Procinfo::tpgid));

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

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

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

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

	//command_line="COMMAND_LINE";	//reference to /proc/1234/cmdline
	categories.insert(F_CMDLINE, new Cat_cmdline("COMMAND_LINE", "Command line that started the process"));
	
	commonPostInit();

	socks_current = false;
	usocks_current = false;

	Proc::init_static();
}


Proc::~Proc()
{
	// killall procinfos
	
}

// COMMON for LINUX,SOLARIS
// Polling /proc/PID/*
void Proc::read_proc_all()
{
	DIR *d = opendir("/proc");
	struct dirent *e;


	while((e = readdir(d)) != 0) {
		if(e->d_name[0] >= '0' and e->d_name[0] <= '9') { // good idea !
			int pid;
			Procinfo *pi=NULL;
			
			//inline int x_atoi(const char *sstr);
			//pid=x_atoi(e->d_name);	//if(pid<100) continue;
			pid=atoi(e->d_name);	
			
			pi=procs.value(pid,NULL);	
			
			if (pi==NULL)   // new process
			{
				pi = new Procinfo(this,pid);     
				procs.insert(pid,pi);
				/*	
					Procinfo *parent;
				   	parent =procs[pi->ppid];
				   	if(parent)
				   		parent->children->add(pi);        
					printf("Qps : parent null  pid[%d]\n",pi->pid);	} 
				*/
			}
			int ret=pi->readproc();
			if(ret>0)
			{
				pi->generation = current_gen; // this process is alive
				//printf(" [%s] %d %d\n",pi->command.toAscii().data(),pi->generation,current_gen);
				
				if(flag_show_thread and flag_thread_ok ) 
					read_pid_tasks(pid);   //for threads 
			
				// add to History expect thread 
				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);
}

// COMMON , redesign
Proclist Proc::getHistory(int pos)
{
	Proclist l;
	if(pos<=0)
	{
		return l;
	}
	int size=history.size();
	if(size>pos)
		l=history[size-pos]->procs;
	return l;	
}

void Proc::setHistory(int tick)
{
	return;
	if(tick<=0)
	{
		mprocs=0;
		return;
	}
	int size=history.size();
	if(size>tick)
		mprocs=&history[size-tick]->procs;
	else mprocs=0;
}

bool Procinfo::isThread()
{
	return pid!=tgid; // how to check
}

// LINUX  size=64
int Procview::custom_fields[] = {F_PID, F_TTY, F_USER, F_NICE,
	F_SIZE, F_MEM,
	F_STAT, F_CPU, F_START, F_TIME,
	F_CMDLINE, F_END};

// COMMON: basic field
int Procview::basic_fields[] = {F_PID, F_TTY, F_USER,F_CPUNUM,
	F_STAT,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_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};



void Procview::set_fields()
{
	switch(viewfields) {
		case USER: // BASIC FIELD
			set_fields_list(basic_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;
		default:
			printf("Error ? set_fields_list \n");
	}
	fieldArrange();
}


// 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] = {basic_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;
		}
	}
}



//move to Proc.cpp
#include <sys/utsname.h>    // uname() 
int get_kernel_version()
{
	int version = 0;
	char *p;
	struct utsname uname_info;
	if(uname (&uname_info)==0)	// man -S 2 uname	
	{
	    //printf("sysname =%s \n",uname_info.sysname);
	    if(strcasecmp(uname_info.sysname,"linux")==0)
	    {
			uname_info.release[0];
	    }
		p=uname_info.release;
		char str[32];
	    int major, minor, patch;
	    int result;

        result = sscanf(p, "%d.%d.%d", &major, &minor, &patch);
        if(result < 3) {
			fprintf(stderr, "Qps: can't determine version, read %s \n",	p);
			fprintf(stderr, "please report this bug.\n");
			exit(1);
        }
        version = major * 10000 + minor * 100  + patch;
		//ex) 2.6.17 == 20617 , 2.4.8  == 20408
        //printf("DEBUG: version = %d\n", version);
    } else {
		fprintf(stderr, "Qps: uname() failed. (%d) \n",version);
		fprintf(stderr, "please report this bug.\n");
		exit(1);
    }
	return version;
}

void check_system_requirement()
{
	int	kernel_version=0;			
	kernel_version=get_kernel_version();
	if(kernel_version< 20600)  // less 2.6 
	{
	    printf("Qps: kernel 2.4.x not supported !!!\n\n"); // because of 2.4.x SMP bugs 
	    exit(0);
	}
}


