Home | History | Annotate | Download | only in ptime
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  *
     25  * Portions Copyright 2008 Chad Mynhier
     26  */
     27 
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <unistd.h>
     31 #include <fcntl.h>
     32 #include <string.h>
     33 #include <errno.h>
     34 #include <math.h>
     35 #include <wait.h>
     36 #include <signal.h>
     37 #include <sys/types.h>
     38 #include <sys/time.h>
     39 #include <signal.h>
     40 #include <libproc.h>
     41 
     42 static	int	look(pid_t);
     43 static	void	hr_min_sec(char *, long);
     44 static	void	prtime(char *, timestruc_t *);
     45 static	int	perr(const char *);
     46 
     47 static	void	tsadd(timestruc_t *result, timestruc_t *a, timestruc_t *b);
     48 static	void	tssub(timestruc_t *result, timestruc_t *a, timestruc_t *b);
     49 static	void	hrt2ts(hrtime_t hrt, timestruc_t *tsp);
     50 
     51 static	char	*command;
     52 static	char	*pidarg;
     53 static	char	procname[64];
     54 
     55 static	int	Fflag;
     56 static	int	mflag;
     57 static	int	errflg;
     58 
     59 int
     60 main(int argc, char **argv)
     61 {
     62 	int opt;
     63 	pid_t pid;
     64 	struct siginfo info;
     65 	int status;
     66 	int gret;
     67 	struct ps_prochandle *Pr;
     68 
     69 	if ((command = strrchr(argv[0], '/')) != NULL)
     70 		command++;
     71 	else
     72 		command = argv[0];
     73 
     74 	while ((opt = getopt(argc, argv, "Fhmp:")) != EOF) {
     75 		switch (opt) {
     76 		case 'F':		/* force grabbing (no O_EXCL) */
     77 			Fflag = PGRAB_FORCE;
     78 			break;
     79 		case 'm':		/* microstate accounting */
     80 			mflag = 1;
     81 			break;
     82 		case 'p':
     83 			pidarg = optarg;
     84 			break;
     85 		default:
     86 			errflg = 1;
     87 			break;
     88 		}
     89 	}
     90 
     91 	argc -= optind;
     92 	argv += optind;
     93 
     94 	if (((pidarg != NULL) ^ (argc < 1)) || errflg) {
     95 		(void) fprintf(stderr,
     96 		    "usage:\t%s [-mh] [-p pid | command [ args ... ]]\n",
     97 		    command);
     98 		(void) fprintf(stderr,
     99 		    "  (time a command using microstate accounting)\n");
    100 		return (1);
    101 	}
    102 
    103 	if (pidarg != NULL) {
    104 		if ((Pr = proc_arg_grab(pidarg, PR_ARG_PIDS,
    105 		    Fflag | PGRAB_RDONLY, &gret)) == NULL) {
    106 			(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
    107 			    command, pidarg, Pgrab_error(gret));
    108 			return (1);
    109 		}
    110 	} else {
    111 		if ((Pr = Pcreate(argv[0], &argv[0], &gret, NULL, 0)) == NULL) {
    112 			(void) fprintf(stderr, "%s: failed to exec %s: %s\n",
    113 			    command, argv[0], Pcreate_error(gret));
    114 			return (1);
    115 		}
    116 		if (Psetrun(Pr, 0, 0) == -1) {
    117 			(void) fprintf(stderr, "%s: failed to set running %s: "
    118 			    "%s\n", command, argv[0], strerror(errno));
    119 			return (1);
    120 		}
    121 	}
    122 
    123 	pid = Pstatus(Pr)->pr_pid;
    124 	(void) sprintf(procname, "%d", (int)pid);	/* for perr() */
    125 	(void) signal(SIGINT, SIG_IGN);
    126 	(void) signal(SIGQUIT, SIG_IGN);
    127 
    128 	if (pidarg == NULL)
    129 		(void) waitid(P_PID, pid, &info, WEXITED | WNOWAIT);
    130 
    131 	(void) look(pid);
    132 
    133 	if (pidarg != NULL) {
    134 		Prelease(Pr, 0);
    135 		return (0);
    136 	} else {
    137 		(void) waitpid(pid, &status, 0);
    138 
    139 		if (WIFEXITED(status))
    140 			return (WEXITSTATUS(status));
    141 
    142 		if (WIFSIGNALED(status)) {
    143 			int sig = WTERMSIG(status);
    144 			char name[SIG2STR_MAX];
    145 
    146 			(void) fprintf(stderr, "%s: command terminated "
    147 			    "abnormally by %s\n", command,
    148 			    proc_signame(sig, name, sizeof (name)));
    149 		}
    150 
    151 		return (status | WCOREFLG); /* see time(1) */
    152 	}
    153 }
    154 
    155 static int
    156 look(pid_t pid)
    157 {
    158 	char pathname[100];
    159 	int rval = 0;
    160 	int fd;
    161 	psinfo_t psinfo;
    162 	prusage_t prusage;
    163 	timestruc_t real, user, sys;
    164 	hrtime_t hrtime;
    165 	prusage_t *pup = &prusage;
    166 
    167 	if (proc_get_psinfo(pid, &psinfo) < 0)
    168 		return (perr("read psinfo"));
    169 
    170 	(void) sprintf(pathname, "/proc/%d/usage", (int)pid);
    171 	if ((fd = open(pathname, O_RDONLY)) < 0)
    172 		return (perr("open usage"));
    173 
    174 	if (read(fd, &prusage, sizeof (prusage)) != sizeof (prusage))
    175 		rval = perr("read usage");
    176 	else {
    177 		if (pidarg) {
    178 			hrtime = gethrtime();
    179 			hrt2ts(hrtime, &real);
    180 		} else {
    181 			real = pup->pr_term;
    182 		}
    183 		tssub(&real, &real, &pup->pr_create);
    184 		user = pup->pr_utime;
    185 		sys = pup->pr_stime;
    186 		if (!mflag)
    187 			tsadd(&sys, &sys, &pup->pr_ttime);
    188 
    189 		(void) fprintf(stderr, "\n");
    190 		prtime("real", &real);
    191 		prtime("user", &user);
    192 		prtime("sys", &sys);
    193 
    194 		if (mflag) {
    195 			prtime("trap", &pup->pr_ttime);
    196 			prtime("tflt", &pup->pr_tftime);
    197 			prtime("dflt", &pup->pr_dftime);
    198 			prtime("kflt", &pup->pr_kftime);
    199 			prtime("lock", &pup->pr_ltime);
    200 			prtime("slp", &pup->pr_slptime);
    201 			prtime("lat", &pup->pr_wtime);
    202 			prtime("stop", &pup->pr_stoptime);
    203 		}
    204 	}
    205 
    206 	(void) close(fd);
    207 	return (rval);
    208 }
    209 
    210 static void
    211 hr_min_sec(char *buf, long sec)
    212 {
    213 	if (sec >= 3600)
    214 		(void) sprintf(buf, "%ld:%.2ld:%.2ld",
    215 		    sec / 3600, (sec % 3600) / 60, sec % 60);
    216 	else if (sec >= 60)
    217 		(void) sprintf(buf, "%ld:%.2ld",
    218 		    sec / 60, sec % 60);
    219 	else
    220 		(void) sprintf(buf, "%ld", sec);
    221 }
    222 
    223 static void
    224 prtime(char *name, timestruc_t *ts)
    225 {
    226 	char buf[32];
    227 
    228 	hr_min_sec(buf, ts->tv_sec);
    229 
    230 	(void) fprintf(stderr, "%-4s %8s.%.9u\n",
    231 	    name, buf, (uint_t)ts->tv_nsec);
    232 }
    233 
    234 static int
    235 perr(const char *s)
    236 {
    237 	if (s)
    238 		(void) fprintf(stderr, "%s: ", procname);
    239 	else
    240 		s = procname;
    241 	perror(s);
    242 	return (1);
    243 }
    244 
    245 static	void
    246 tsadd(timestruc_t *result, timestruc_t *a, timestruc_t *b)
    247 {
    248 	result->tv_sec = a->tv_sec + b->tv_sec;
    249 	if ((result->tv_nsec = a->tv_nsec + b->tv_nsec) >= 1000000000) {
    250 		result->tv_nsec -= 1000000000;
    251 		result->tv_sec += 1;
    252 	}
    253 }
    254 
    255 static	void
    256 tssub(timestruc_t *result, timestruc_t *a, timestruc_t *b)
    257 {
    258 	result->tv_sec = a->tv_sec - b->tv_sec;
    259 	if ((result->tv_nsec = a->tv_nsec - b->tv_nsec) < 0) {
    260 		result->tv_nsec += 1000000000;
    261 		result->tv_sec -= 1;
    262 	}
    263 }
    264 
    265 static void
    266 hrt2ts(hrtime_t hrt, timestruc_t *tsp)
    267 {
    268 	tsp->tv_sec = hrt / NANOSEC;
    269 	tsp->tv_nsec = hrt % NANOSEC;
    270 }
    271