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
main(int argc,char ** argv)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
look(pid_t pid)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
hr_min_sec(char * buf,long sec)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
prtime(char * name,timestruc_t * ts)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
perr(const char * s)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
tsadd(timestruc_t * result,timestruc_t * a,timestruc_t * b)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
tssub(timestruc_t * result,timestruc_t * a,timestruc_t * b)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
hrt2ts(hrtime_t hrt,timestruc_t * tsp)266 hrt2ts(hrtime_t hrt, timestruc_t *tsp)
267 {
268 tsp->tv_sec = hrt / NANOSEC;
269 tsp->tv_nsec = hrt % NANOSEC;
270 }
271