"Fossies" - the Fresh Open Source Software archive

Member "atsar_linux-1.7/atsadc/atsadc.c" of archive atsar_linux-1.7.tar.gz:


#include "includes.h"

/*
** System Activity Data Collector
**
** The program 'atsadc' offers the possibility to read statistical counters
** from the /proc (sub)directory and write it periodically to stdout or a file.
** The program has been designed to operate as a framework to which
** new counters can be added very easily (table-driven). It cooperates
** with the reporting program 'atsar' in a similar way as the standard UNIX
** 'sadc' cooperates with 'sar'.
** ================================================================
** Author:	Gerlof Langeveld - AT Computing, Nijmegen, Holland 
** E-mail:	gerlof@ATComputing.nl
** Date:	Januar  1995
** LINUX-port:	Februar 1999
**
** $Log: atsadc.c,v $
** Revision 1.6  2004/07/08 12:51:40  gerlof
** Adapted for kernel version 2.6.
**
** Revision 1.5  2000/11/07 09:22:42  gerlof
** *** empty log message ***
**
** Revision 1.4  1999/09/01 07:11:47  gerlof
** Ported to Alpha.
**
** Revision 1.3  1999/08/26 06:58:53  gerlof
** Code-cleanup and new code to determine number of cpu's (this modified
** the header of the statistics-file as well).
**
** Revision 1.2  1999/05/18 08:31:30  gerlof
** Back-port from version 2.2 to 2.0.
**
** Revision 1.1  1999/05/05 11:38:27  gerlof
** Initial revision
**
**
**
** 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, 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.
*/

static const char rcsid[] = "$Id: atsadc.c,v 1.6 2004/07/08 12:51:40 gerlof Exp $";

/*
** definition of the counters to be read from /proc
*/
extern struct fetchdef 	oncedef[];
extern int		oncecnt;	/* number of entries	*/

extern struct fetchdef 	sampdef[];
extern int		sampcnt;	/* number of entries	*/

/*
** Kernel-version info
*/
struct osrel		osrel;

/*
** values of the command-line arguments
*/
char			*outfile;	
int			interval = 0;
int			nsamples = 0;

/*
** Function prototypes
*/
void	writehdr(int, int, int, int, int);
void	prusage(char *);
int	numcpus(void);



int
main(int argc, char *argv[])
{
	register int 		i;
	char			newfile;	/* boolean: appended file */
	int			ofd;		/* output file-descriptor */
	int			cycle;		/* count for nr of samples */

	struct filehdr		filehdr;
	struct samphdr		samphdr;
	struct tms		timbuf;

	int		 	namosize, namssize;
	int		 	varosize, varssize;
	register unsigned long	 tmpoff;
	register char		*tmpcnt;

	struct utsname		utsname;

	struct timeval 		tval;
	long			intervalnano;
	long			secsnext;

	/*
	** check if enough parameters have been specified
	*/
	if ( argc > 1 )
	{
		if ( isdigit(*argv[1]) )	/* file or timeout as first */
		{
			interval = atoi(argv[1]);

			if (interval <= 0)
				prusage(argv[0]);

			if ( argc < 3 || !isdigit(*argv[2]) )
				prusage(argv[0]);

			nsamples  = atoi(argv[2]);

			if ( argc > 3 )
				outfile = argv[3];
		}
		else
		{
			outfile = argv[1];
		}
	}

	/*
	** open the counter output file	(default: stdout)
	*/
	if ( !outfile )
	{
		ofd = dup(1);
		newfile = 1;
	}
	else
	{
		if ( access(outfile, W_OK) == -1 )
		{
			/*
			** check if call during init-switch to state 2
			**                ----> filename, but no interval
			*/
			if (!interval)
				exit(0);	/* ignore call */

			/*
			** file does not exist yet; create new
			*/
			if ( (ofd = open(outfile, O_WRONLY|O_CREAT, 0664)) ==-1)
			{
				perror("create output file");
				exit(1);
			}

			newfile = 1;
		}
		else
		{
			/*
			** file exists: check if this is a statistics file;
			** if it is, seek to the end for appending
			*/
			if ( (ofd = open(outfile, O_RDWR)) == -1)
			{
				perror("open output file");
				exit(1);
			}

			if ( read(ofd, &filehdr, sizeof(filehdr)) == -1)
			{
				perror("verify output file");
				exit(1);
			}

			if ( filehdr.magic != ATMAGIC )
			{
				fprintf(stderr,
					"existing file %s has wrong format\n",
					outfile);
				exit(6);
			}

			(void) lseek(ofd, 0, SEEK_END);

			newfile = 0;

			/*
			** write a dummy sample header to indicate
			** a system-reboot if started without t and n.
			*/
			if (!interval)
			{
				samphdr.curtim	= time(0);
				samphdr.curlbolt= times(&timbuf);
				samphdr.cntsize	= 0;              /* dummy */

				if ( write(ofd, &samphdr, sizeof(samphdr))==-1)
				{
					perror("write restart");
					exit(1);
				}

				(void) close(ofd);
				exit(0);
			}
		}
	}

	/*
	** determine the kernel-version of this system
	*/
        uname(&utsname);

        sscanf(utsname.release, "%d.%d.%d",
                &(osrel.rel), &(osrel.vers), &(osrel.sub));

        if ( osrel.rel < 2 )
        {
                fprintf(stderr, "\nLINUX-version %d.%d not supported!\n",
                                                 osrel.rel, osrel.vers);
                exit(5);
        }

	/*
	** check total space in file needed for names of kernel
	** variables and for the counters themselves
	**
	** all variable sizes are rounded up to 8-bytes units to avoid
	** alignment problems with subsequent variables
	*/
	for (i=0, namosize=0, varosize=0; i < oncecnt; i++)
	{
		if (oncedef[i].initcnt)
			(oncedef[i].initcnt)(&oncedef[i], &osrel);

		if (oncedef[i].cdef.ksize & 7)
			oncedef[i].cdef.ksize = 
					((oncedef[i].cdef.ksize >> 3) + 1) << 3;

		varosize += oncedef[i].cdef.ksize * oncedef[i].cdef.kinst;
		namosize += strlen(oncedef[i].cdef.kname) + 1;
	}

	if (namosize & 0x0f)	/* not multiple of 16 bytes ?*/
		namosize = ((namosize>>4) +1) << 4;  

	for (i=0, namssize=0, varssize=0; i < sampcnt; i++)
	{
		if (sampdef[i].initcnt)
			(sampdef[i].initcnt)(&sampdef[i], &osrel);

		if (sampdef[i].cdef.ksize & 7)
			sampdef[i].cdef.ksize = 
					((sampdef[i].cdef.ksize >> 3) + 1) << 3;

		varssize += sampdef[i].cdef.ksize * sampdef[i].cdef.kinst;
		namssize += strlen(sampdef[i].cdef.kname) + 1;
	}

	if (namssize & 0x0f)	/* not multiple of 16 bytes ?*/
		namssize = ((namssize>>4) +1) << 4;  

	/*
	** write the headers only for a new file or pipe;
	** if appending to an existing file, check the consistency and
	** take care that the counter-areas keep the same size
	*/
	if (newfile)
	{
		writehdr(ofd, namosize, namssize, varosize, varssize);
	}
	else
	{
		if (	filehdr.oncecnt 	!= oncecnt 		 ||
			filehdr.oncenames	!= namosize		 ||
			filehdr.oncevars	!= varosize		 ||
			filehdr.sampcnt 	!= sampcnt		 ||
			filehdr.sampnames	!= namssize		 ||
			filehdr.sampvars	!= varssize		 ||
			strcmp(filehdr.utsinfo.release, utsname.release)   )
		{
			fprintf(stderr, 
				"This existing file cannot be appended\n");
			exit(7);
		}
	}

	/*
	** allocate a buffer which is big enough to hold all
	** counters of one sample
	*/
	if ( (tmpcnt = (char *) malloc(varssize)) == NULL)
	{
		perror("cannot malloc");
		exit(5);
	}

	/*
	** main loop:
	** 	read all defined statistic structures and 
	**	transfer to file/pipe
	*/
	gettimeofday(&tval, (struct timezone *) 0);
	secsnext     = tval.tv_sec;
	intervalnano = interval * 1000000;

	for (cycle=0; cycle < nsamples; cycle++)
	{
		/*
		** wait for interval
		** except for the very first cycle
		*/
		if (cycle)
		{
			/* sleep(interval);  --> may cause time-deviation */

			gettimeofday(&tval, (struct timezone *) 0);

			if(tval.tv_sec >= secsnext)
				usleep(intervalnano - tval.tv_usec);
			else
				usleep(intervalnano - tval.tv_usec + 1000000);

			secsnext += interval;
		}

		/*
		** gather all counters by calling functions
		** defined in sampdef-table
		*/
		samphdr.curtim	= time(0);
		samphdr.curlbolt= times(&timbuf);
		samphdr.cntsize	= varssize;

		for (i=0, tmpoff=0; i < sampcnt; i++)
		{
			(sampdef[i].getcnt)(&sampdef[i], &osrel, tmpcnt+tmpoff);

			tmpoff += sampdef[i].cdef.ksize * sampdef[i].cdef.kinst;
		}

		/*
		** write the sample-header and -data
		*/
		if ( write(ofd, &samphdr, sizeof(samphdr)) < 0)
		{
			perror("write of sample header");
			exit(1);
		}

		if ( write(ofd, tmpcnt, varssize) < 0)
		{
			perror("write of sample counters");
			exit(1);
		}
	}

	(void) close(ofd);
	return 0;
}

/*
** write all static header-info to file/pipe, except the sample data
** 	ofd	- file descriptor of open output file
** 	namosz	- size of total space for 'once' names
** 	namssz	- size of total space for 'samp' names
** 	varosz	- size of total space for 'once' counters
** 	varssz	- size of total space for 'samp' counters
*/
void
writehdr(int ofd, int namosz, int namssz, int varosz, int varssz)
{
	register int		i;
	struct filehdr		filehdr;

	char			*tmpname;
	struct countdef		*tmpdef;
	register unsigned long	 tmpoff;
	register char		*tmpcnt;

	int			oncetot;	/* total countdef size	*/
	int			samptot;	/* total countdef size	*/


	oncetot	= oncecnt * sizeof(struct countdef);
	samptot	= sampcnt * sizeof(struct countdef);

	filehdr.magic	 	= ATMAGIC;
	filehdr.hdrsize	 	= sizeof(struct filehdr);
	filehdr.oncecnt 	= oncecnt;
	filehdr.oncenames	= namosz;
	filehdr.oncevars	= varosz;
	filehdr.sampcnt 	= sampcnt;
	filehdr.sampnames	= namssz;
	filehdr.sampvars	= varssz;
	filehdr.hertz		= HZ;
	filehdr.numcpu		= numcpus();

        uname(&filehdr.utsinfo);

	if ( write(ofd, &filehdr, sizeof(filehdr)) < 0)
	{
		perror("write of header data");
		exit(1);
	}

	/*
	** build name table for counters which are only read once
	*/
	if ( (tmpdef = (struct countdef *) malloc(oncetot)) == NULL)
	{
		perror("cannot malloc");
		exit(5);
	}

	if ( (tmpname = (char *) malloc(namosz)) == NULL)
	{
		perror("cannot malloc");
		exit(5);
	}

	for (i=0, tmpoff=0; i < oncecnt; i++)
	{
		register int len;

		len = strlen(oncedef[i].cdef.kname)+1;

		memcpy(tmpname+tmpoff, oncedef[i].cdef.kname, len);

		(tmpdef+i)->kname	= (char *) tmpoff;
		(tmpdef+i)->ksize	= oncedef[i].cdef.ksize;
		(tmpdef+i)->kinst	= oncedef[i].cdef.kinst;

		tmpoff	+= len;
	}

	if ( write(ofd, tmpdef, oncetot) < 0)
	{
		perror("write of counter definition data");
		exit(1);
	}

	if ( write(ofd, tmpname, namosz) < 0)
	{
		perror("write of counter names");
		exit(1);
	}

	(void) free(tmpname);
	(void) free(tmpdef);
	
	/*
	** build name table for counters which are read each sample
	*/
	if ( (tmpdef = (struct countdef *) malloc(samptot)) == NULL)
	{
		perror("cannot malloc");
		exit(5);
	}

	if ( (tmpname = (char *) malloc(namssz)) == NULL)
	{
		perror("cannot malloc");
		exit(5);
	}

	for (i=0, tmpoff=0; i < sampcnt; i++)
	{
		register int len;

		len = strlen(sampdef[i].cdef.kname)+1;

		memcpy(tmpname+tmpoff, sampdef[i].cdef.kname, len);

		(tmpdef+i)->kname	= (char *) tmpoff;
		(tmpdef+i)->ksize	= sampdef[i].cdef.ksize;
		(tmpdef+i)->kinst	= sampdef[i].cdef.kinst;

		tmpoff	+= len;
	}

	if ( write(ofd, tmpdef, samptot) < 0)
	{
		perror("write of counter definition data");
		exit(1);
	}

	if ( write(ofd, tmpname, namssz) < 0)
	{
		perror("write of counter names");
		exit(1);
	}

	(void) free(tmpname);
	(void) free(tmpdef);

	/*
	** read only once the non-changing kernel counters 
	** and transfer to file/pipe
	*/
	if ( (tmpcnt = (char *) malloc(varosz)) == NULL)
	{
		perror("cannot malloc");
		exit(5);
	}

	for (i=0, tmpoff=0; i < oncecnt; i++)
	{
		(oncedef[i].getcnt)(&oncedef[i], &osrel, tmpcnt+tmpoff);

		tmpoff += oncedef[i].cdef.ksize * oncedef[i].cdef.kinst; 
	}

	if ( write(ofd, tmpcnt, varosz) < 0)
	{
		perror("write of counters");
		exit(1);
	}

	(void) free(tmpcnt);
}

/*
** print the usage string and abort
*/
void
prusage(char *pname)
{
	fprintf(stderr, "usage: %s [t n] [ofile]\n", pname);
	exit(1);
}

/*
** Determine number of cpu's active on this system (return value).
*/
int
numcpus(void)
{
	FILE 	*fp;
	int	cnt = 0;
	char	linebuf[1024], fw[80];

	/*
	** obtain info about number of processors installed by counting
	** all lines in the file /proc/stat which start with the
	** string "cpu[0-9]" (per-cpu lines); if these lines are not
	** present, just one cpu is installed
	*/
	if ( (fp = fopen("/proc/stat", "r")) == NULL)
		return 1;			/* assume one cpu */

	while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
	{
		sscanf(linebuf, "%s ", fw);

		if (fw[0] == 'c' && fw[1] == 'p' && fw[2] == 'u' && fw[3] !=0)
			cnt++;
	}

	fclose(fp);

	return (cnt > 0 ? cnt : 1);
}