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