ptime.c revision 8131:deb606e4733c
11052Sdg/*
21052Sdg * CDDL HEADER START
31052Sdg *
48870Srgrimes * The contents of this file are subject to the terms of the
51052Sdg * Common Development and Distribution License (the "License").
61052Sdg * You may not use this file except in compliance with the License.
71052Sdg *
81052Sdg * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91052Sdg * or http://www.opensolaris.org/os/licensing.
101052Sdg * See the License for the specific language governing permissions
111052Sdg * and limitations under the License.
121052Sdg *
131052Sdg * When distributing Covered Code, include this CDDL HEADER in each
141052Sdg * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158870Srgrimes * If applicable, add the following below this CDDL HEADER, with the
161052Sdg * fields enclosed by brackets "[]" replaced with your own identifying
171052Sdg * information: Portions Copyright [yyyy] [name of copyright owner]
181052Sdg *
191052Sdg * CDDL HEADER END
201052Sdg */
211052Sdg/*
221052Sdg * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
231052Sdg * Use is subject to license terms.
241052Sdg *
251052Sdg * Portions Copyright 2008 Chad Mynhier
261052Sdg */
278870Srgrimes
2850476Speter#include <stdio.h>
291052Sdg#include <stdlib.h>
301052Sdg#include <unistd.h>
315790Sdg#include <fcntl.h>
3250476Speter#include <string.h>
335790Sdg#include <errno.h>
345790Sdg#include <math.h>
351052Sdg#include <wait.h>
361052Sdg#include <signal.h>
371052Sdg#include <sys/types.h>
381052Sdg#include <sys/time.h>
391052Sdg#include <signal.h>
401052Sdg#include <libproc.h>
411052Sdg
4250817Sluoqistatic	int	look(pid_t);
431052Sdgstatic	void	hr_min_sec(char *, long);
4450817Sluoqistatic	void	prtime(char *, timestruc_t *);
4550817Sluoqistatic	int	perr(const char *);
4650817Sluoqi
478870Srgrimesstatic	void	tsadd(timestruc_t *result, timestruc_t *a, timestruc_t *b);
481052Sdgstatic	void	tssub(timestruc_t *result, timestruc_t *a, timestruc_t *b);
491052Sdgstatic	void	hrt2ts(hrtime_t hrt, timestruc_t *tsp);
50
51static	char	*command;
52static	char	*pidarg;
53static	char	procname[64];
54
55static	int	Fflag;
56static	int	mflag;
57static	int	errflg;
58
59int
60main(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
155static int
156look(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
210static void
211hr_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
223static void
224prtime(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
234static int
235perr(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
245static	void
246tsadd(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
255static	void
256tssub(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
265static void
266hrt2ts(hrtime_t hrt, timestruc_t *tsp)
267{
268	tsp->tv_sec = hrt / NANOSEC;
269	tsp->tv_nsec = hrt % NANOSEC;
270}
271