<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/


#include "condor_common.h"
#include "condor_debug.h"
#include "procapi.h"
#include "procapi_internal.h"

#if HAVE_PROCFS_H
# include <procfs.h>
#endif
#if HAVE_SYS_PROCFS_H
# include <sys/procfs.h>
#endif

// Ugly hack: stat64 prototyps are wacked on HPUX
// These are cut & pasted from the HPUX man pages...                            
#if defined( HPUX )
extern "C" {
    extern int fstat64(int fildes, struct stat64 *buf);
}
#endif


unsigned int pidHashFunc( const pid_t& pid );

HashTable <pid_t, procHashNode *> * ProcAPI::procHash = 
    new HashTable <pid_t, procHashNode *> ( PHBUCKETS, pidHashFunc );

piPTR ProcAPI::allProcInfos = NULL;

#ifndef WIN32
pidlistPTR ProcAPI::pidList = NULL;
int ProcAPI::pagesize		= 0;
#ifdef LINUX
long unsigned ProcAPI::boottime	= 0;
long ProcAPI::boottime_expiration = 0;
#endif // LINUX
#else // WIN32

#include "ntsysinfo.WINDOWS.h"
static CSysinfo ntSysInfo;	// for getting parent pid on NT

	// Windows gives us birthday in 100ns ticks since 01/01/1601
	// (11644473600 seconds before 01/01/1970)
const __int64 EPOCH_SHIFT = 11644473600;

PPERF_DATA_BLOCK ProcAPI::pDataBlock	= NULL;
struct Offset * ProcAPI::offsets	= NULL;

#endif // WIN32

int ProcAPI::MAX_SAMPLES = 5;
int ProcAPI::DEFAULT_PRECISION_RANGE = 4;

#if defined(HZ)
double ProcAPI::TIME_UNITS_PER_SEC = (double) HZ;
#elif defined(_SC_CLK_TCK)
double ProcAPI::TIME_UNITS_PER_SEC = (double) sysconf(_SC_CLK_TCK);
#else // not linux
double ProcAPI::TIME_UNITS_PER_SEC = 1.0;
#endif

procHashNode::procHashNode()
{
	lasttime = 0.0;
	oldtime = 0.0;
	oldusage = 0.0;
	oldminf = 0L;
	oldmajf = 0L;
	majfaultrate = 0L;
	minfaultrate = 0L;
	creation_time = 0L;
	garbage = false;
}

ProcAPI::~ProcAPI() {
        // deallocate stuff like crazy.
#ifndef WIN32
    deallocPidList();
#endif
    deallocAllProcInfos();

    struct procHashNode * phn;
    procHash->startIterations();
    while ( procHash->iterate( phn ) )
        delete phn;
    
    delete procHash;

#ifdef WIN32
    if ( offsets )
        free ( offsets );
    if ( pDataBlock )
        free ( pDataBlock );
#endif
}    

// getProcInfo is the heart of the procapi implementation.

// Each platform gets its own function unless two are so similar that you can
// ifdef between them.

#if defined(Solaris)
// This is the version of getProcInfo for Solaris 2.6 - 2.11

int
ProcAPI::getProcInfo( pid_t pid, piPTR& pi, int &status ) 
{
	// This *could* allocate memory and make pi point to it if pi == NULL.
	// It is up to the caller to get rid of it.
	initpi ( pi );

		// assume sucess
	status = PROCAPI_OK;

	// get the raw system process data
	procInfoRaw procRaw;
	int retVal = ProcAPI::getProcInfoRaw(pid, procRaw, status);
	
		// if a failure occured
	if( retVal != 0 ){
			// return failure
			// status is set by getProcInfoRaw(...)
		return PROCAPI_FAILURE;
	}

		/* clean up and convert raw data */

		// compute the age
	pi->age = procRaw.sample_time - procRaw.creation_time;

		// compute cpu time
	double cpu_time = 
		( procRaw.user_time_1 + 
		  (procRaw.user_time_2 * 1.0e-9) ) +
		( procRaw.sys_time_1 + 
		  (procRaw.sys_time_2 * 1.0e-9) ); 


		// copy the remainder of the fields
	pi->pid		= procRaw.pid;
	pi->ppid	= procRaw.ppid;
	pi->owner = procRaw.owner;
	pi->creation_time = procRaw.creation_time;
	pi->birthday    = procRaw.creation_time;
	pi->imgsize	= procRaw.imgsize;    // already in k!
	pi->rssize	= procRaw.rssize;  // already in k!
	pi->user_time= procRaw.user_time_1;
	pi->sys_time = procRaw.sys_time_1;
	
  /* Now we do that sampling hashtable thing to convert page faults
     into page faults per second */
	do_usage_sampling( pi, cpu_time, procRaw.majfault, procRaw.minfault );

		// success
	return PROCAPI_SUCCESS;
}

/* Fills ProcInfoRaw with the following units:
   imgsize		: KB
   rssize		: KB
   minfault		: total minor faults
   majfault		: total major faults
   user_time_1	: seconds
   user_time_2	: nanos
   sys_time_1	: seconds
   sys_time_2	: nanos
   creation_time: seconds since epoch
   sample_time	: seconds since epoch

*/

int
ProcAPI::getProcInfoRaw( pid_t pid, procInfoRaw& procRaw, int &status ) 
{
	char path[64];
	int fd;
	psinfo_t psinfo;
	prusage_t prusage;

		// assume success
	status = PROCAPI_OK;

		// clear the memory of procRaw
	initProcInfoRaw(procRaw);

		// set the sample time
	procRaw.sample_time = secsSinceEpoch();

		// pids, memory usage, and age can be found in 'psinfo':
	sprintf( path, "/proc/%d/psinfo", pid );
	if( (fd = safe_open_wrapper(path, O_RDONLY)) < 0 ) {

		switch(errno) {
			case ENOENT:
				// pid doesn't exist
				status = PROCAPI_NOPID;
				dprintf( D_FULLDEBUG, "ProcAPI: pid %d does not exist.\n",
						pid );
				break;

			case EACCES:
				status = PROCAPI_PERM;
				dprintf( D_FULLDEBUG, "ProcAPI: No permission to open %s.\n", 
					 	path );
				break;

			default:
				status = PROCAPI_UNSPECIFIED;
				dprintf( D_ALWAYS, "ProcAPI: Error opening %s, errno: %d.\n", 
					 path, errno );
				break;
		}

		return PROCAPI_FAILURE;
	} 

	// grab the information from the file descriptor.
	if( read(fd, &psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t) ) {
		dprintf( D_ALWAYS, 
			"ProcAPI: Unexpected short read while reading %s.\n", path );

		status = PROCAPI_GARBLED;

		return PROCAPI_FAILURE;
	}

	// grab the process owner uid
	procRaw.owner = getFileOwner(fd);

	close( fd );

	// grab the information out of what the kernel told us. 
	procRaw.imgsize	= psinfo.pr_size;
	procRaw.rssize	= psinfo.pr_rssize;
	procRaw.pid		= psinfo.pr_pid;
	procRaw.ppid	= psinfo.pr_ppid;
	procRaw.creation_time = psinfo.pr_start.tv_sec;

  // maj/min page fault info and user/sys time is found in 'usage':
  // I have never seen minor page faults return anything 
  // other than '0' in 2.6.  I have seen a value returned for 
  // major faults, but not that often.  These values are suspicious.
	sprintf( path, "/proc/%d/usage", pid );
	if( (fd = safe_open_wrapper(path, O_RDONLY) ) < 0 ) {

		switch(errno) {
			case ENOENT:
				// pid doesn't exist
				status = PROCAPI_NOPID;
				dprintf( D_FULLDEBUG, "ProcAPI: pid %d does not exist.\n",
						pid );
				break;

			case EACCES:
				status = PROCAPI_PERM;
				dprintf( D_FULLDEBUG, "ProcAPI: No permission to open %s.\n", 
					 	path );
				break;

			default:
				status = PROCAPI_UNSPECIFIED;
				dprintf( D_ALWAYS, "ProcAPI: Error opening %s, errno: %d.\n", 
					 path, errno );
				break;
		}

		return PROCAPI_FAILURE;

	}

	if( read(fd, &prusage, sizeof(prusage_t)) != sizeof(prusage_t) ) {

		dprintf( D_ALWAYS, 
			"ProcAPI: Unexpected short read while reading %s.\n", path );

		status = PROCAPI_GARBLED;

		return PROCAPI_FAILURE;
	}

	close( fd );

	procRaw.minfault = prusage.pr_minf;
	procRaw.majfault = prusage.pr_majf;
	procRaw.user_time_1 = prusage.pr_utime.tv_sec;
	procRaw.user_time_2 = prusage.pr_utime.tv_nsec;
	procRaw.sys_time_1 = prusage.pr_stime.tv_sec;
	procRaw.sys_time_2 = prusage.pr_stime.tv_nsec;

	return PROCAPI_SUCCESS;
}

#elif defined(LINUX)

int
ProcAPI::getProcInfo( pid_t pid, piPTR& pi, int &status ) 
{


	// This *could* allocate memory and make pi point to it if pi == NULL.
	// It is up to the caller to get rid of it.
	initpi( pi );

		// get the raw system process data
	procInfoRaw procRaw;
	int retVal = ProcAPI::getProcInfoRaw(pid, procRaw, status);
	
		// if a failure occurred
	if( retVal != 0 ){
			// return failure
			// status is set by getProcInfoRaw(...)
		return PROCAPI_FAILURE;
	}

		/* clean up and convert the raw data */

		// if the page size has not yet been found, get it.
	if( pagesize == 0 ) {
		pagesize = getpagesize() / 1024;
	}

		/*
		  Zero out thread memory, because Linux (as of kernel 2.4)
		  shows one process per thread, with the mem stats for each
		  thread equal to the memory usage of the entire process.
		  This causes ImageSize to be far bigger than reality when
		  there are many threads, so if the job gets evicted, it might
		  never be able to match again.

		  There is no perfect method for knowing if a given process
		  entry is actually a thread.  One way is to compare the
		  memory usage to the parent process, and if they are
		  identical, it is probably a thread.  However, there is a
		  small race condition if one of the entries is updated
		  between reads; this could cause threads not to be weeded out
		  every now and then, which can cause the ImageSize problem
		  mentioned above.

		  So instead, we use the PF_FORKNOEXEC (64) process flag.
		  This is always turned on in threads, because they are
		  produced by fork (actually clone), and they continue on from
		  there in the same code, i.e.  there is no call to exec.  In
		  some rare cases, a process that is not a thread will have
		  this flag set, because it has not called exec, and it was
		  created by a call to fork (or equivalently clone with
		  options that cause memory not to be shared).  However, not
		  only is this rare, it is not such a lie to zero out the
		  memory usage, because Linux does copy-on-write handling of
		  the memory.  In other words, memory is only duplicated when
		  the forked process writes to it, so we are once again in
		  danger of over-counting memory usage.  When in doubt, zero
		  it out!

		  One exception to this rule is made for processes inherited
		  by init (ppid=1).  These are clearly not threads but are
		  background processes (such as condor_master) that fork and
		  exit from the parent branch.
		*/
	pi->imgsize = procRaw.imgsize / 1024;  //bytes to k
	pi->rssize = procRaw.rssize * pagesize;  // pages to k
	if ((procRaw.proc_flags & 64) && procRaw.ppid != 1) { //64 == PF_FORKNOEXEC
		//zero out memory usage
		pi->imgsize = 0;
		pi->rssize = 0;
	}

		// convert system time and user time into seconds from jiffies
		// and calculate cpu time
	long	hertz;
# if defined(HZ)
	hertz = HZ;
# elif defined(_SC_CLK_TCK)
	hertz = sysconf(_SC_CLK_TCK);
# else
#   error "Unable to determine system clock"
# endif
	pi->user_time   = procRaw.user_time_1 / hertz;
	pi->sys_time    = procRaw.sys_time_1  / hertz;
	double cpu_time = (procRaw.user_time_1 + procRaw.sys_time_1) / (double)hertz;

		// use the raw value (which is in jiffies since boot time)
		// as the birthday, which is useful for comparing whether
		// two processes with the same  PID are in fact the same
		// process
	pi->birthday = procRaw.creation_time;

		// convert the creation time from jiffies since boot
		// to epoch time

		// 1. ensure we have a "good" boottime
	if( checkBootTime(procRaw.sample_time) == PROCAPI_FAILURE ){
		status = PROCAPI_UNSPECIFIED;
		dprintf( D_ALWAYS, "ProcAPI: Problem getting boottime\n");
		return PROCAPI_FAILURE;
	}
	
		// 2. Convert from jiffies to seconds
	long bday_secs = procRaw.creation_time / hertz;
	
		// 3. Convert from seconds since boot to seconds since epoch
	pi->creation_time = bday_secs + boottime;

		// calculate age
	pi->age = procRaw.sample_time - pi->creation_time;

		// There seems to be something weird about jiffie_start_time
		// in that it can grow faster than the process itself.  This
		// can result in a negative age (on both SMP and non-SMP 2.0.X
		// kernels, at least).  So, instead of allowing a negative
		// age, which will lead to a negative cpu usage and
		// CondorLoadAvg, we just set it to 0, since that's basically
		// what's up.  -<NAME> and <NAME> 3/24/99.
	if( pi->age < 0 ) {
		pi->age = 0;
	}
	
	
		// copy the remainder of the fields
	pi->owner = procRaw.owner;
	pi->pid = procRaw.pid;
	pi->ppid = procRaw.ppid;

		/* here we've got to do some sampling ourself to get the
		   cpuusage and make the page faults a rate...
		*/
	do_usage_sampling ( pi, cpu_time, procRaw.majfault, procRaw.minfault );
		// Note: sanity checking done in above call.

		/* grab out the environment, if possible. I've noticed that
		   under linux it appears that once the /proc/<pid>/environ
		   file is made, it never changes. Luckily, we're only looking
		   for specific stuff the parent only puts into the child's
		   environment.

		   We don't care if it fails, its optional
		*/
	fillProcInfoEnv(pi);

		// success
	return PROCAPI_SUCCESS;
}

/* Fills in procInfoRaw with the following units:
   imgsize		: bytes
   rssize		: pages
   minfault		: total minor faults
   majfault		: total major faults
   user_time_1	: jiffies (1/100 of a second)
   user_time_2	: not set
   sys_time_1	: jiffies (1/100 of a second)
   sys_time_2	: not set
   creation_time: jiffies since system boot
   sample_time	: seconds since epoch
   proc_flags	: special process flags
*/
int
ProcAPI::getProcInfoRaw( pid_t pid, procInfoRaw& procRaw, int &status ) 
{

// This is the Linux version of getProcInfoRaw.  Everything is easier and
// actually seems to work in Linux...nice, but annoyingly different.

	char path[64];
	FILE *fp;
	
	int number_of_attempts;
	long i;
	unsigned long u;
	char c;
	char s[256];
	int num_attempts = 5;

		// assume success
	status = PROCAPI_OK;

		// clear the memory of procRaw
	initProcInfoRaw(procRaw);

		// set the sample time
	procRaw.sample_time = secsSinceEpoch();

	// read the entry a certain number of times since it appears that linux
	// often simply does something stupid while reading.
	sprintf( path, "/proc/%d/stat", pid );
	number_of_attempts = 0;
	while (number_of_attempts < num_attempts) {

		number_of_attempts++;

		// in case I must restart, assume that everything is ok again...
		status = PROCAPI_OK;
		initProcInfoRaw(procRaw);

		if( (fp = safe_fopen_wrapper(path, "r")) == NULL ) {
			if( errno == ENOENT ) {
				// /proc/pid doesn't exist
				status = PROCAPI_NOPID;
				dprintf( D_FULLDEBUG, 
					"ProcAPI::getProcInfo() pid %d does not exist.\n", pid );
			} else if ( errno == EACCES ) {
				status = PROCAPI_PERM;
				dprintf( D_FULLDEBUG, 
					"ProcAPI::getProcInfo() No permission to open %s.\n", 
					 path );
			} else { <commit_msg>Make MonitorSelfAge work on Linux, re-set sample_time after wiping procRaw, #2089<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/


#include "condor_common.h"
#include "condor_debug.h"
#include "procapi.h"
#include "procapi_internal.h"

#if HAVE_PROCFS_H
# include <procfs.h>
#endif
#if HAVE_SYS_PROCFS_H
# include <sys/procfs.h>
#endif

// Ugly hack: stat64 prototyps are wacked on HPUX
// These are cut & pasted from the HPUX man pages...                            
#if defined( HPUX )
extern "C" {
    extern int fstat64(int fildes, struct stat64 *buf);
}
#endif


unsigned int pidHashFunc( const pid_t& pid );

HashTable <pid_t, procHashNode *> * ProcAPI::procHash = 
    new HashTable <pid_t, procHashNode *> ( PHBUCKETS, pidHashFunc );

piPTR ProcAPI::allProcInfos = NULL;

#ifndef WIN32
pidlistPTR ProcAPI::pidList = NULL;
int ProcAPI::pagesize		= 0;
#ifdef LINUX
long unsigned ProcAPI::boottime	= 0;
long ProcAPI::boottime_expiration = 0;
#endif // LINUX
#else // WIN32

#include "ntsysinfo.WINDOWS.h"
static CSysinfo ntSysInfo;	// for getting parent pid on NT

	// Windows gives us birthday in 100ns ticks since 01/01/1601
	// (11644473600 seconds before 01/01/1970)
const __int64 EPOCH_SHIFT = 11644473600;

PPERF_DATA_BLOCK ProcAPI::pDataBlock	= NULL;
struct Offset * ProcAPI::offsets	= NULL;

#endif // WIN32

int ProcAPI::MAX_SAMPLES = 5;
int ProcAPI::DEFAULT_PRECISION_RANGE = 4;

#if defined(HZ)
double ProcAPI::TIME_UNITS_PER_SEC = (double) HZ;
#elif defined(_SC_CLK_TCK)
double ProcAPI::TIME_UNITS_PER_SEC = (double) sysconf(_SC_CLK_TCK);
#else // not linux
double ProcAPI::TIME_UNITS_PER_SEC = 1.0;
#endif

procHashNode::procHashNode()
{
	lasttime = 0.0;
	oldtime = 0.0;
	oldusage = 0.0;
	oldminf = 0L;
	oldmajf = 0L;
	majfaultrate = 0L;
	minfaultrate = 0L;
	creation_time = 0L;
	garbage = false;
}

ProcAPI::~ProcAPI() {
        // deallocate stuff like crazy.
#ifndef WIN32
    deallocPidList();
#endif
    deallocAllProcInfos();

    struct procHashNode * phn;
    procHash->startIterations();
    while ( procHash->iterate( phn ) )
        delete phn;
    
    delete procHash;

#ifdef WIN32
    if ( offsets )
        free ( offsets );
    if ( pDataBlock )
        free ( pDataBlock );
#endif
}    

// getProcInfo is the heart of the procapi implementation.

// Each platform gets its own function unless two are so similar that you can
// ifdef between them.

#if defined(Solaris)
// This is the version of getProcInfo for Solaris 2.6 - 2.11

int
ProcAPI::getProcInfo( pid_t pid, piPTR& pi, int &status ) 
{
	// This *could* allocate memory and make pi point to it if pi == NULL.
	// It is up to the caller to get rid of it.
	initpi ( pi );

		// assume sucess
	status = PROCAPI_OK;

	// get the raw system process data
	procInfoRaw procRaw;
	int retVal = ProcAPI::getProcInfoRaw(pid, procRaw, status);
	
		// if a failure occured
	if( retVal != 0 ){
			// return failure
			// status is set by getProcInfoRaw(...)
		return PROCAPI_FAILURE;
	}

		/* clean up and convert raw data */

		// compute the age
	pi->age = procRaw.sample_time - procRaw.creation_time;

		// compute cpu time
	double cpu_time = 
		( procRaw.user_time_1 + 
		  (procRaw.user_time_2 * 1.0e-9) ) +
		( procRaw.sys_time_1 + 
		  (procRaw.sys_time_2 * 1.0e-9) ); 


		// copy the remainder of the fields
	pi->pid		= procRaw.pid;
	pi->ppid	= procRaw.ppid;
	pi->owner = procRaw.owner;
	pi->creation_time = procRaw.creation_time;
	pi->birthday    = procRaw.creation_time;
	pi->imgsize	= procRaw.imgsize;    // already in k!
	pi->rssize	= procRaw.rssize;  // already in k!
	pi->user_time= procRaw.user_time_1;
	pi->sys_time = procRaw.sys_time_1;
	
  /* Now we do that sampling hashtable thing to convert page faults
     into page faults per second */
	do_usage_sampling( pi, cpu_time, procRaw.majfault, procRaw.minfault );

		// success
	return PROCAPI_SUCCESS;
}

/* Fills ProcInfoRaw with the following units:
   imgsize		: KB
   rssize		: KB
   minfault		: total minor faults
   majfault		: total major faults
   user_time_1	: seconds
   user_time_2	: nanos
   sys_time_1	: seconds
   sys_time_2	: nanos
   creation_time: seconds since epoch
   sample_time	: seconds since epoch

*/

int
ProcAPI::getProcInfoRaw( pid_t pid, procInfoRaw& procRaw, int &status ) 
{
	char path[64];
	int fd;
	psinfo_t psinfo;
	prusage_t prusage;

		// assume success
	status = PROCAPI_OK;

		// clear the memory of procRaw
	initProcInfoRaw(procRaw);

		// set the sample time
	procRaw.sample_time = secsSinceEpoch();

		// pids, memory usage, and age can be found in 'psinfo':
	sprintf( path, "/proc/%d/psinfo", pid );
	if( (fd = safe_open_wrapper(path, O_RDONLY)) < 0 ) {

		switch(errno) {
			case ENOENT:
				// pid doesn't exist
				status = PROCAPI_NOPID;
				dprintf( D_FULLDEBUG, "ProcAPI: pid %d does not exist.\n",
						pid );
				break;

			case EACCES:
				status = PROCAPI_PERM;
				dprintf( D_FULLDEBUG, "ProcAPI: No permission to open %s.\n", 
					 	path );
				break;

			default:
				status = PROCAPI_UNSPECIFIED;
				dprintf( D_ALWAYS, "ProcAPI: Error opening %s, errno: %d.\n", 
					 path, errno );
				break;
		}

		return PROCAPI_FAILURE;
	} 

	// grab the information from the file descriptor.
	if( read(fd, &psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t) ) {
		dprintf( D_ALWAYS, 
			"ProcAPI: Unexpected short read while reading %s.\n", path );

		status = PROCAPI_GARBLED;

		return PROCAPI_FAILURE;
	}

	// grab the process owner uid
	procRaw.owner = getFileOwner(fd);

	close( fd );

	// grab the information out of what the kernel told us. 
	procRaw.imgsize	= psinfo.pr_size;
	procRaw.rssize	= psinfo.pr_rssize;
	procRaw.pid		= psinfo.pr_pid;
	procRaw.ppid	= psinfo.pr_ppid;
	procRaw.creation_time = psinfo.pr_start.tv_sec;

  // maj/min page fault info and user/sys time is found in 'usage':
  // I have never seen minor page faults return anything 
  // other than '0' in 2.6.  I have seen a value returned for 
  // major faults, but not that often.  These values are suspicious.
	sprintf( path, "/proc/%d/usage", pid );
	if( (fd = safe_open_wrapper(path, O_RDONLY) ) < 0 ) {

		switch(errno) {
			case ENOENT:
				// pid doesn't exist
				status = PROCAPI_NOPID;
				dprintf( D_FULLDEBUG, "ProcAPI: pid %d does not exist.\n",
						pid );
				break;

			case EACCES:
				status = PROCAPI_PERM;
				dprintf( D_FULLDEBUG, "ProcAPI: No permission to open %s.\n", 
					 	path );
				break;

			default:
				status = PROCAPI_UNSPECIFIED;
				dprintf( D_ALWAYS, "ProcAPI: Error opening %s, errno: %d.\n", 
					 path, errno );
				break;
		}

		return PROCAPI_FAILURE;

	}

	if( read(fd, &prusage, sizeof(prusage_t)) != sizeof(prusage_t) ) {

		dprintf( D_ALWAYS, 
			"ProcAPI: Unexpected short read while reading %s.\n", path );

		status = PROCAPI_GARBLED;

		return PROCAPI_FAILURE;
	}

	close( fd );

	procRaw.minfault = prusage.pr_minf;
	procRaw.majfault = prusage.pr_majf;
	procRaw.user_time_1 = prusage.pr_utime.tv_sec;
	procRaw.user_time_2 = prusage.pr_utime.tv_nsec;
	procRaw.sys_time_1 = prusage.pr_stime.tv_sec;
	procRaw.sys_time_2 = prusage.pr_stime.tv_nsec;

	return PROCAPI_SUCCESS;
}

#elif defined(LINUX)

int
ProcAPI::getProcInfo( pid_t pid, piPTR& pi, int &status ) 
{


	// This *could* allocate memory and make pi point to it if pi == NULL.
	// It is up to the caller to get rid of it.
	initpi( pi );

		// get the raw system process data
	procInfoRaw procRaw;
	int retVal = ProcAPI::getProcInfoRaw(pid, procRaw, status);
	
		// if a failure occurred
	if( retVal != 0 ){
			// return failure
			// status is set by getProcInfoRaw(...)
		return PROCAPI_FAILURE;
	}

		/* clean up and convert the raw data */

		// if the page size has not yet been found, get it.
	if( pagesize == 0 ) {
		pagesize = getpagesize() / 1024;
	}

		/*
		  Zero out thread memory, because Linux (as of kernel 2.4)
		  shows one process per thread, with the mem stats for each
		  thread equal to the memory usage of the entire process.
		  This causes ImageSize to be far bigger than reality when
		  there are many threads, so if the job gets evicted, it might
		  never be able to match again.

		  There is no perfect method for knowing if a given process
		  entry is actually a thread.  One way is to compare the
		  memory usage to the parent process, and if they are
		  identical, it is probably a thread.  However, there is a
		  small race condition if one of the entries is updated
		  between reads; this could cause threads not to be weeded out
		  every now and then, which can cause the ImageSize problem
		  mentioned above.

		  So instead, we use the PF_FORKNOEXEC (64) process flag.
		  This is always turned on in threads, because they are
		  produced by fork (actually clone), and they continue on from
		  there in the same code, i.e.  there is no call to exec.  In
		  some rare cases, a process that is not a thread will have
		  this flag set, because it has not called exec, and it was
		  created by a call to fork (or equivalently clone with
		  options that cause memory not to be shared).  However, not
		  only is this rare, it is not such a lie to zero out the
		  memory usage, because Linux does copy-on-write handling of
		  the memory.  In other words, memory is only duplicated when
		  the forked process writes to it, so we are once again in
		  danger of over-counting memory usage.  When in doubt, zero
		  it out!

		  One exception to this rule is made for processes inherited
		  by init (ppid=1).  These are clearly not threads but are
		  background processes (such as condor_master) that fork and
		  exit from the parent branch.
		*/
	pi->imgsize = procRaw.imgsize / 1024;  //bytes to k
	pi->rssize = procRaw.rssize * pagesize;  // pages to k
	if ((procRaw.proc_flags & 64) && procRaw.ppid != 1) { //64 == PF_FORKNOEXEC
		//zero out memory usage
		pi->imgsize = 0;
		pi->rssize = 0;
	}

		// convert system time and user time into seconds from jiffies
		// and calculate cpu time
	long	hertz;
# if defined(HZ)
	hertz = HZ;
# elif defined(_SC_CLK_TCK)
	hertz = sysconf(_SC_CLK_TCK);
# else
#   error "Unable to determine system clock"
# endif
	pi->user_time   = procRaw.user_time_1 / hertz;
	pi->sys_time    = procRaw.sys_time_1  / hertz;
	double cpu_time = (procRaw.user_time_1 + procRaw.sys_time_1) / (double)hertz;

		// use the raw value (which is in jiffies since boot time)
		// as the birthday, which is useful for comparing whether
		// two processes with the same  PID are in fact the same
		// process
	pi->birthday = procRaw.creation_time;

		// convert the creation time from jiffies since boot
		// to epoch time

		// 1. ensure we have a "good" boottime
	if( checkBootTime(procRaw.sample_time) == PROCAPI_FAILURE ){
		status = PROCAPI_UNSPECIFIED;
		dprintf( D_ALWAYS, "ProcAPI: Problem getting boottime\n");
		return PROCAPI_FAILURE;
	}
	
		// 2. Convert from jiffies to seconds
	long bday_secs = procRaw.creation_time / hertz;
	
		// 3. Convert from seconds since boot to seconds since epoch
	pi->creation_time = bday_secs + boottime;

		// calculate age
	pi->age = procRaw.sample_time - pi->creation_time;

		// There seems to be something weird about jiffie_start_time
		// in that it can grow faster than the process itself.  This
		// can result in a negative age (on both SMP and non-SMP 2.0.X
		// kernels, at least).  So, instead of allowing a negative
		// age, which will lead to a negative cpu usage and
		// CondorLoadAvg, we just set it to 0, since that's basically
		// what's up.  -<NAME> and <NAME> 3/24/99.
	if( pi->age < 0 ) {
		pi->age = 0;
	}
	
	
		// copy the remainder of the fields
	pi->owner = procRaw.owner;
	pi->pid = procRaw.pid;
	pi->ppid = procRaw.ppid;

		/* here we've got to do some sampling ourself to get the
		   cpuusage and make the page faults a rate...
		*/
	do_usage_sampling ( pi, cpu_time, procRaw.majfault, procRaw.minfault );
		// Note: sanity checking done in above call.

		/* grab out the environment, if possible. I've noticed that
		   under linux it appears that once the /proc/<pid>/environ
		   file is made, it never changes. Luckily, we're only looking
		   for specific stuff the parent only puts into the child's
		   environment.

		   We don't care if it fails, its optional
		*/
	fillProcInfoEnv(pi);

		// success
	return PROCAPI_SUCCESS;
}

/* Fills in procInfoRaw with the following units:
   imgsize		: bytes
   rssize		: pages
   minfault		: total minor faults
   majfault		: total major faults
   user_time_1	: jiffies (1/100 of a second)
   user_time_2	: not set
   sys_time_1	: jiffies (1/100 of a second)
   sys_time_2	: not set
   creation_time: jiffies since system boot
   sample_time	: seconds since epoch
   proc_flags	: special process flags
*/
int
ProcAPI::getProcInfoRaw( pid_t pid, procInfoRaw& procRaw, int &status ) 
{

// This is the Linux version of getProcInfoRaw.  Everything is easier and
// actually seems to work in Linux...nice, but annoyingly different.

	char path[64];
	FILE *fp;
	
	int number_of_attempts;
	long i;
	unsigned long u;
	char c;
	char s[256];
	int num_attempts = 5;

		// assume success
	status = PROCAPI_OK;

	// read the entry a certain number of times since it appears that linux
	// often simply does something stupid while reading.
	sprintf( path, "/proc/%d/stat", pid );
	number_of_attempts = 0;
	while (number_of_attempts < num_attempts) {

		number_of_attempts++;

		// in case I must restart, assume that everything is ok again...
		status = PROCAPI_OK;
		// clear the memory of procRaw
		initProcInfoRaw(procRaw);
		// set the sample time
		procRaw.sample_time = secsSinceEpoch();

		if( (fp = safe_fopen_wrapper(path, "r")) == NULL ) {
			if( errno == ENOENT ) {
				// /proc/pid doesn't exist
				status = PROCAPI_NOPID;
				dprintf( D_FULLDEBUG, 
					"ProcAPI::getProcInfo() pid %d does not exist.\n", pid );
			} else if ( errno == EACCES ) {
				status = PROCAPI_PERM;
				dprintf( D_FULLDEBUG, 
					"ProcAPI::getProcInfo() No permission to open %s.\n", 
					 path );
			} else { 
{'fraction_non_alphanumeric': 0.10377038043478261, 'fraction_numerical': 0.012364130434782608, 'mean_word_length': 3.17188607056823, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 71, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6863354', 'n_tokens_mistral': 11040, 'n_tokens_neox': 9822, 'n_words': 3435}