/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Portions Copyright 2008 Chad Mynhier
*/
/*
* Copyright 2016 Joyent, Inc.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <wait.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>
#include <libproc.h>
static int look(pid_t);
static void hr_min_sec(char *, long);
static void prtime(char *, timestruc_t *);
static int perr(const char *);
static void tsadd(timestruc_t *result, timestruc_t *a, timestruc_t *b);
static void tssub(timestruc_t *result, timestruc_t *a, timestruc_t *b);
static void hrt2ts(hrtime_t hrt, timestruc_t *tsp);
static char *command;
static char *pidarg;
static char procname[64];
static int Fflag;
static int mflag;
static int errflg;
static int pflag;
static int pfirst;
static int
ptime_pid(const char *pidstr)
{
struct ps_prochandle *Pr;
pid_t pid;
int gret;
if ((Pr = proc_arg_grab(pidstr, PR_ARG_PIDS,
Fflag | PGRAB_RDONLY, &gret)) == NULL) {
(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
command, pidstr, Pgrab_error(gret));
return (1);
}
pid = Pstatus(Pr)->pr_pid;
(void) sprintf(procname, "%d", (int)pid); /* for perr() */
(void) look(pid);
Prelease(Pr, 0);
return (0);
}
int
main(int argc, char **argv)
{
int opt, exit;
pid_t pid;
struct siginfo info;
int status;
int gret;
struct ps_prochandle *Pr;
if ((command = strrchr(argv[0], '/')) != NULL)
command++;
else
command = argv[0];
while ((opt = getopt(argc, argv, "Fhmp:")) != EOF) {
switch (opt) {
case 'F': /* force grabbing (no O_EXCL) */
Fflag = PGRAB_FORCE;
break;
case 'm': /* microstate accounting */
mflag = 1;
break;
case 'p':
pflag = 1;
pidarg = optarg;
break;
default:
errflg = 1;
break;
}
}
argc -= optind;
argv += optind;
if (((pidarg != NULL) ^ (argc < 1)) || errflg) {
(void) fprintf(stderr,
"usage:\t%s [-mh] [-p pidlist | command [ args ... ]]\n",
command);
(void) fprintf(stderr,
" (time a command using microstate accounting)\n");
return (1);
}
if (pflag) {
char *pp;
exit = 0;
(void) signal(SIGINT, SIG_IGN);
(void) signal(SIGQUIT, SIG_IGN);
pp = strtok(pidarg, ", ");
if (pp == NULL) {
(void) fprintf(stderr, "%s: invalid argument for -p\n",
command);
return (1);
}
exit = ptime_pid(pp);
while ((pp = strtok(NULL, ", ")) != NULL) {
exit |= ptime_pid(pp);
}
return (exit);
}
if ((Pr = Pcreate(argv[0], &argv[0], &gret, NULL, 0)) == NULL) {
(void) fprintf(stderr, "%s: failed to exec %s: %s\n",
command, argv[0], Pcreate_error(gret));
return (1);
}
if (Psetrun(Pr, 0, 0) == -1) {
(void) fprintf(stderr, "%s: failed to set running %s: "
"%s\n", command, argv[0], strerror(errno));
return (1);
}
pid = Pstatus(Pr)->pr_pid;
(void) sprintf(procname, "%d", (int)pid); /* for perr() */
(void) signal(SIGINT, SIG_IGN);
(void) signal(SIGQUIT, SIG_IGN);
(void) waitid(P_PID, pid, &info, WEXITED | WNOWAIT);
(void) look(pid);
(void) waitpid(pid, &status, 0);
if (WIFEXITED(status))
return (WEXITSTATUS(status));
if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status);
char name[SIG2STR_MAX];
(void) fprintf(stderr, "%s: command terminated "
"abnormally by %s\n", command,
proc_signame(sig, name, sizeof (name)));
}
return (status | WCOREFLG); /* see time(1) */
}
static int
look(pid_t pid)
{
char pathname[100];
int rval = 0;
int fd;
psinfo_t psinfo;
prusage_t prusage;
timestruc_t real, user, sys;
hrtime_t hrtime;
prusage_t *pup = &prusage;
pfirst++;
if (proc_get_psinfo(pid, &psinfo) < 0)
return (perr("read psinfo"));
(void) sprintf(pathname, "/proc/%d/usage", (int)pid);
if ((fd = open(pathname, O_RDONLY)) < 0)
return (perr("open usage"));
if (read(fd, &prusage, sizeof (prusage)) != sizeof (prusage))
rval = perr("read usage");
else {
if (pidarg) {
hrtime = gethrtime();
hrt2ts(hrtime, &real);
} else {
real = pup->pr_term;
}
tssub(&real, &real, &pup->pr_create);
user = pup->pr_utime;
sys = pup->pr_stime;
if (!mflag)
tsadd(&sys, &sys, &pup->pr_ttime);
if (!pflag || pfirst > 1)
(void) fprintf(stderr, "\n");
if (pflag)
(void) fprintf(stderr, "%d:\t%.70s\n",
(int)psinfo.pr_pid, psinfo.pr_psargs);
prtime("real", &real);
prtime("user", &user);
prtime("sys", &sys);
if (mflag) {
prtime("trap", &pup->pr_ttime);
prtime("tflt", &pup->pr_tftime);
prtime("dflt", &pup->pr_dftime);
prtime("kflt", &pup->pr_kftime);
prtime("lock", &pup->pr_ltime);
prtime("slp", &pup->pr_slptime);
prtime("lat", &pup->pr_wtime);
prtime("stop", &pup->pr_stoptime);
}
}
(void) close(fd);
return (rval);
}
static void
hr_min_sec(char *buf, long sec)
{
if (sec >= 3600)
(void) sprintf(buf, "%ld:%.2ld:%.2ld",
sec / 3600, (sec % 3600) / 60, sec % 60);
else if (sec >= 60)
(void) sprintf(buf, "%ld:%.2ld",
sec / 60, sec % 60);
else
(void) sprintf(buf, "%ld", sec);
}
static void
prtime(char *name, timestruc_t *ts)
{
char buf[32];
hr_min_sec(buf, ts->tv_sec);
(void) fprintf(stderr, "%-4s %8s.%.9u\n",
name, buf, (uint_t)ts->tv_nsec);
}
static int
perr(const char *s)
{
if (s)
(void) fprintf(stderr, "%s: ", procname);
else
s = procname;
perror(s);
return (1);
}
static void
tsadd(timestruc_t *result, timestruc_t *a, timestruc_t *b)
{
result->tv_sec = a->tv_sec + b->tv_sec;
if ((result->tv_nsec = a->tv_nsec + b->tv_nsec) >= 1000000000) {
result->tv_nsec -= 1000000000;
result->tv_sec += 1;
}
}
static void
tssub(timestruc_t *result, timestruc_t *a, timestruc_t *b)
{
result->tv_sec = a->tv_sec - b->tv_sec;
if ((result->tv_nsec = a->tv_nsec - b->tv_nsec) < 0) {
result->tv_nsec += 1000000000;
result->tv_sec -= 1;
}
}
static void
hrt2ts(hrtime_t hrt, timestruc_t *tsp)
{
tsp->tv_sec = hrt / NANOSEC;
tsp->tv_nsec = hrt % NANOSEC;
}