On 11/4/05, Mike Gerdts <[EMAIL PROTECTED]> wrote:
> On 11/4/05, Andrei Dorofeev <[EMAIL PROTECTED]> wrote:
> > Another approach is to have exacct write final accounting records and
> > periodically use getacct(2) system call and take live snapshots for
> > tasks and aggregate them by projects.  I've prototyped a tool called
> > 'taskstat' some
> > time ago that does exactly that and it works pretty good.  Let me know
> > if you're interested and I'll send you the source for it.
>
> That sounds like a very reasonable way to do it.  I hadn't noticed
> getacct(2) until very recently.  It seems as though this approach
> would work even if interval records are written, so long as taskstat
> made use of both final and interval records.  The only part I don't
> like about this solution is that I am still writing (and rotating, and
> deleting) extended accounting records that I really don't want for any
> reason other than to collect summary data.
>
> I think that I could put the code to good use if you were to send it to me.

I've got two versions - one in Perl and another in C.  They are doing
different things, but should be enough for you to get an idea of how
things work.  See attachments.

> This does suggest, however, that there is possibility to do a similar
> trick by walking the process list every second (or other configurable
> period...) and performing per user, (per task?), per project, and per
> zone kstat updates.  A hook would probably need to be added to exit as
> well.

By the way, we're already updating task records on exit.  See
exacct_update_task_mstate() function in exacct.c:
http://cvs.opensolaris.org/source/xref/on/usr/src/uts/common/os/exacct.c
Updating these records every second would be too expensive.  It is much more
efficient to only do it when asked -- and that's exactly what getacct(2) is for.

> It seems as though this would make it so that the type of
> summary data that taskstat was written to collect could easily be
> intergrated into prstat by observing the kstats.  Presumably this
> would mean that prstat -[aTJZ] summary lines would come from kstats
> instead of iterating over the active processes (and missing out on the
> ones that died).

It sounds like a great idea to me.  Would you like to work on that? ;-)
You can download the source code for OpenSolaris, modify and build it
yourself, and I'd be happy to assist you.

- Andrei

Attachment: taskstat.pl
Description: Binary data

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2005 Andrei Dorofeev ([EMAIL PROTECTED])
 * All rights reserved.
 */

/*
 * Compile with
 *
 * 	cc taskstat.c -o taskstat -lexacct -lproject
 */

#include <sys/types.h>
#include <sys/exacct.h>
#include <sys/task.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <exacct.h>
#include <dirent.h>
#include <fcntl.h>
#include <procfs.h>
#include <project.h>

char buf[2048];
int bufsz = 2048;
int interval = 5;
long ncpu = 0;
long ncpu_online = 0;

typedef struct taskinfo {
	taskid_t 	ti_taskid;
	projid_t	ti_projid;
	double 		ti_time;
	double		ti_diff;
	int		ti_done;
	int		ti_set;
	struct taskinfo *ti_next;
	struct taskinfo *ti_prev;
} taskinfo_t;

typedef struct projinfo {
	projid_t	pi_projid;
	double		pi_time;
	double		pi_diff;
	int		pi_set;
	struct projinfo *pi_next;
	struct projinfo *pi_prev;
} projinfo_t;

taskinfo_t *tasks = NULL;
projinfo_t *projects = NULL;

void
gettaskinfo(taskid_t taskid, taskinfo_t *ti)
{
	size_t size;
	ea_object_t *op;	
	ea_object_t *eo;
	ea_object_type_t ot;
	int type, catalog, data;
	double time = 0;
	projid_t projid;

	bzero(buf, bufsz);
	size = getacct(P_TASKID, taskid, buf, bufsz);

	if (size == (-1)) {
		if (errno == ENOTACTIVE) {
			fprintf(stderr, "Run this: \n"
			    "  # acctadm -E task\n"
			    "  # acctadm -e extended task\n");
			exit(1);
		} else {
			fprintf(stderr, "getacct() failed : %s\n",
			    strerror(errno));
			exit(1);
		}
	}
	ot = ea_unpack_object(&op, EUP_NOALLOC, buf, size);

	if (ot != EO_GROUP)
		printf("getacct returned not group (%d)\n", ea_error());

	eo = op->eo_group.eg_objs;

	do {
		type = (eo->eo_catalog & EXT_TYPE_MASK) >> 28;
		catalog = (eo->eo_catalog & EXC_CATALOG_MASK) >> 24;
		data = (eo->eo_catalog & EXD_DATA_MASK);

		if (data == EXD_TASK_CPU_USER_SEC)
			time += eo->eo_item.ei_uint64;
		if (data == EXD_TASK_CPU_USER_NSEC)
			time += (double)eo->eo_item.ei_uint64 / 1000000000;
		if (data == EXD_TASK_CPU_SYS_SEC)
			time += eo->eo_item.ei_uint64;
		if (data == EXD_TASK_CPU_SYS_NSEC)
			time += (double)eo->eo_item.ei_uint64 / 1000000000;
		if (data == EXD_TASK_PROJID)
			projid = eo->eo_item.ei_uint32;
		eo = eo->eo_next;
	} while (eo != NULL);

	ea_free_object(op, EUP_NOALLOC);
	ti->ti_time = time;
	ti->ti_projid = projid;
}

void
cleantasks()
{
	taskinfo_t *ti;

	/* those that don't have ti_set == 1 should be removed */
	
	for (ti = tasks; ti != NULL; ti = ti->ti_next) {
		if (ti->ti_set == 0) {

			if (ti == tasks) {
				tasks = ti->ti_next;
				tasks->ti_prev = NULL;
			}
			if (ti->ti_next)
				ti->ti_next->ti_prev = ti->ti_prev;
			if (ti->ti_prev)
				ti->ti_prev->ti_next = ti->ti_next;
			free(ti);
		}
	}
}

void
zerodiffs()
{
	projinfo_t *pi;

	for (pi = projects; pi != NULL; pi = pi->pi_next) {
		pi->pi_diff = 0;
		pi->pi_set = 0;
	}
}

void
cleanprojects()
{
	projinfo_t *pi;

	/* those that don't have pi_set == 1 should be removed */
	
	for (pi = projects; pi != NULL; pi = pi->pi_next) {
		if (pi->pi_set == 0) {

			if (pi == projects) {
				projects = pi->pi_next;
				projects->pi_prev = NULL;
			}
			if (pi->pi_next)
				pi->pi_next->pi_prev = pi->pi_prev;
			if (pi->pi_prev)
				pi->pi_prev->pi_next = pi->pi_next;
			free(pi);
		}
	}
}

void
normalize()
{
	taskinfo_t *ti;
	double total = 0;
	for (ti = tasks; ti != NULL; ti = ti->ti_next) {
		total += ti->ti_diff;
	}
/*	if (total > (double)interval) {
		for (ti = tasks; ti != NULL; ti = ti->ti_next) {
			ti->ti_diff = ti->ti_diff / total;
		}
	}
*/
} 

taskinfo_t *
findtaskinfo(taskid_t taskid)
{
        taskinfo_t *ti;

        for (ti = tasks; ti != NULL; ti = ti->ti_next) {
                if (ti->ti_taskid == taskid) {
			ti->ti_set = 1;
			return (ti);
		}
        }
	return (NULL);
}

projinfo_t *
findprojinfo(projid_t projid)
{
	projinfo_t *pi;

	for (pi = projects; pi != NULL; pi = pi->pi_next) {
		if (pi->pi_projid == projid) {
			pi->pi_set = 1;
			return (pi);
		}
	}
	return (NULL);
}

taskinfo_t *
addtaskinfo(taskid_t taskid)
{
	taskinfo_t *ti = malloc(sizeof (taskinfo_t));

	/* always insert at the beginning */
	ti->ti_taskid = taskid;
	ti->ti_prev = NULL;

	if (tasks == NULL) {
		tasks = ti;
		ti->ti_next = NULL;
	} else {
		ti->ti_next = tasks;
		tasks->ti_prev = ti;
		tasks = ti;
	}
	return (ti);
}

projinfo_t *
addprojinfo(projid_t projid)
{
	projinfo_t *pi = malloc(sizeof(projinfo_t));

	pi->pi_projid = projid;
	pi->pi_set = 1;

	if (projects == NULL) {
		projects = pi;
		pi->pi_next = NULL;
	} else {
		pi->pi_next = projects;
		projects = pi;
	}
	return (pi);
}

DIR *procdir;

void
printtasks()
{
	dirent_t *direntp;
	char *pidstr;
	psinfo_t info;
	char psfile[80];
	int fd;
	taskid_t taskid;
	taskinfo_t *ti;
	projinfo_t *pi;
	double time;
	double pct;

	zerodiffs();

	for (rewinddir(procdir); (direntp = readdir(procdir)); ) {
		pidstr = direntp->d_name;
		if (pidstr[0] == '.')
			continue;
		(void) snprintf(psfile, 80, "/proc/%s/psinfo", pidstr);
		if ((fd = open(psfile, O_RDONLY)) == -1)
			continue;
		if (pread(fd, &info, sizeof (psinfo_t), 0) != sizeof (psinfo_t))
			continue;
		taskid = info.pr_taskid;
		close(fd);
		ti = findtaskinfo(taskid);
		if (ti == NULL) {
			/* a new task just entered */
			ti = addtaskinfo(taskid);
			gettaskinfo(taskid, ti);
			ti->ti_diff = ti->ti_time;
			ti->ti_set = 1;
			ti->ti_done = 1;

			pi = findprojinfo(ti->ti_projid);
			if (pi == NULL)
				pi = addprojinfo(ti->ti_projid);
			pi->pi_time += ti->ti_time;
			pi->pi_diff +=	ti->ti_diff;

			continue;
		} else {
			if (ti->ti_done == 1) {
				continue;
			} else {
				time = ti->ti_time;
				gettaskinfo(taskid, ti);
				ti->ti_diff = ti->ti_time - time;
				ti->ti_done = 1;
				ti->ti_set = 1;

				pi = findprojinfo(ti->ti_projid);
				if (pi == NULL)
					pi = addprojinfo(ti->ti_projid);
				pi->pi_time += ti->ti_time;
				pi->pi_diff +=  ti->ti_diff;

				continue;
			}
		}
	}

	cleantasks();
	cleanprojects();
	normalize();


	for (ti = tasks; ti != NULL; ti = ti->ti_next) {
		ti->ti_done = 0;
		ti->ti_set = 0;
	}

/*
	printf("TASKID     CPU (sec)        CPU (%%)\n");
	for (ti = tasks; ti != NULL; ti = ti->ti_next) {
	pct = (((double)100 * ti->ti_diff) / interval) / ncpu_online;
		printf("%-10d %02.9f      %2.2f%%\n",
		    ti->ti_taskid, ti->ti_diff, pct);
		ti->ti_done = 0;
		ti->ti_set = 0;
	}
*/
	printf("PROJID     CPU (sec)        CPU (%%)\n");
	for (pi = projects; pi != NULL; pi = pi->pi_next) {
	pct = (((double)100 * pi->pi_diff) / interval) / ncpu_online;
		printf("%-10d %02.9f      %2.2f%%\n",
		    pi->pi_projid, pi->pi_diff, pct);
	}


}

int main(int argc, char *argv[])
{
	double time;
	int i;

	if (getuid() != 0) {
		fprintf(stderr, "Got root?\n");
		exit(1);
	}

	if (argc > 1)
		interval = strtol(argv[1], NULL, 10);

	ncpu = sysconf(_SC_CPUID_MAX);
	for (i = 0; i < ncpu; i++)
		if (p_online(i, P_STATUS) == P_ONLINE)
			ncpu_online++;

	if ((procdir = opendir("/proc")) == NULL) {
		fprintf(stderr, "cannot open /proc\n");
		exit(1);
	}
	
	while (1) {
		printtasks();
		sleep(interval);
	}
}
_______________________________________________
perf-discuss mailing list
perf-discuss@opensolaris.org

Reply via email to