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 * Copyright 2016 Joyent, Inc.
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <math.h>
38 #include <wait.h>
39 #include <signal.h>
40 #include <sys/types.h>
41 #include <sys/time.h>
42 #include <signal.h>
43 #include <libproc.h>
44
45 static int look(pid_t);
46 static void hr_min_sec(char *, long);
47 static void prtime(char *, timestruc_t *);
48 static int perr(const char *);
49
50 static void tsadd(timestruc_t *result, timestruc_t *a, timestruc_t *b);
51 static void tssub(timestruc_t *result, timestruc_t *a, timestruc_t *b);
52 static void hrt2ts(hrtime_t hrt, timestruc_t *tsp);
53
54 static char *command;
55 static char *pidarg;
56 static char procname[64];
57
58 static int Fflag;
59 static int mflag;
60 static int errflg;
61 static int pflag;
62 static int pfirst;
63
64 static int
ptime_pid(const char * pidstr)65 ptime_pid(const char *pidstr)
66 {
67 struct ps_prochandle *Pr;
68 pid_t pid;
69 int gret;
70
71 if ((Pr = proc_arg_grab(pidstr, PR_ARG_PIDS,
72 Fflag | PGRAB_RDONLY, &gret)) == NULL) {
73 (void) fprintf(stderr, "%s: cannot examine %s: %s\n",
74 command, pidstr, Pgrab_error(gret));
75 return (1);
76 }
77
78 pid = Pstatus(Pr)->pr_pid;
79 (void) sprintf(procname, "%d", (int)pid); /* for perr() */
80 (void) look(pid);
81 Prelease(Pr, 0);
82 return (0);
83 }
84
85 int
main(int argc,char ** argv)86 main(int argc, char **argv)
87 {
88 int opt, exit;
89 pid_t pid;
90 struct siginfo info;
91 int status;
92 int gret;
93 struct ps_prochandle *Pr;
94
95 if ((command = strrchr(argv[0], '/')) != NULL)
96 command++;
97 else
98 command = argv[0];
99
100 while ((opt = getopt(argc, argv, "Fhmp:")) != EOF) {
101 switch (opt) {
102 case 'F': /* force grabbing (no O_EXCL) */
103 Fflag = PGRAB_FORCE;
104 break;
105 case 'm': /* microstate accounting */
106 mflag = 1;
107 break;
108 case 'p':
109 pflag = 1;
110 pidarg = optarg;
111 break;
112 default:
113 errflg = 1;
114 break;
115 }
116 }
117
118 argc -= optind;
119 argv += optind;
120
121 if (((pidarg != NULL) ^ (argc < 1)) || errflg) {
122 (void) fprintf(stderr,
123 "usage:\t%s [-mh] [-p pidlist | command [ args ... ]]\n",
124 command);
125 (void) fprintf(stderr,
126 " (time a command using microstate accounting)\n");
127 return (1);
128 }
129
130 if (pflag) {
131 char *pp;
132
133 exit = 0;
134 (void) signal(SIGINT, SIG_IGN);
135 (void) signal(SIGQUIT, SIG_IGN);
136
137 pp = strtok(pidarg, ", ");
138 if (pp == NULL) {
139 (void) fprintf(stderr, "%s: invalid argument for -p\n",
140 command);
141 return (1);
142 }
143 exit = ptime_pid(pp);
144 while ((pp = strtok(NULL, ", ")) != NULL) {
145 exit |= ptime_pid(pp);
146 }
147 return (exit);
148 }
149
150
151 if ((Pr = Pcreate(argv[0], &argv[0], &gret, NULL, 0)) == NULL) {
152 (void) fprintf(stderr, "%s: failed to exec %s: %s\n",
153 command, argv[0], Pcreate_error(gret));
154 return (1);
155 }
156 if (Psetrun(Pr, 0, 0) == -1) {
157 (void) fprintf(stderr, "%s: failed to set running %s: "
158 "%s\n", command, argv[0], strerror(errno));
159 return (1);
160 }
161
162 pid = Pstatus(Pr)->pr_pid;
163
164 (void) sprintf(procname, "%d", (int)pid); /* for perr() */
165 (void) signal(SIGINT, SIG_IGN);
166 (void) signal(SIGQUIT, SIG_IGN);
167
168 (void) waitid(P_PID, pid, &info, WEXITED | WNOWAIT);
169
170 (void) look(pid);
171
172 (void) waitpid(pid, &status, 0);
173
174 if (WIFEXITED(status))
175 return (WEXITSTATUS(status));
176
177 if (WIFSIGNALED(status)) {
178 int sig = WTERMSIG(status);
179 char name[SIG2STR_MAX];
180
181 (void) fprintf(stderr, "%s: command terminated "
182 "abnormally by %s\n", command,
183 proc_signame(sig, name, sizeof (name)));
184 }
185
186 return (status | WCOREFLG); /* see time(1) */
187 }
188
189 static int
look(pid_t pid)190 look(pid_t pid)
191 {
192 char pathname[100];
193 int rval = 0;
194 int fd;
195 psinfo_t psinfo;
196 prusage_t prusage;
197 timestruc_t real, user, sys;
198 hrtime_t hrtime;
199 prusage_t *pup = &prusage;
200
201 pfirst++;
202
203 if (proc_get_psinfo(pid, &psinfo) < 0)
204 return (perr("read psinfo"));
205
206 (void) sprintf(pathname, "/proc/%d/usage", (int)pid);
207 if ((fd = open(pathname, O_RDONLY)) < 0)
208 return (perr("open usage"));
209
210 if (read(fd, &prusage, sizeof (prusage)) != sizeof (prusage))
211 rval = perr("read usage");
212 else {
213 if (pidarg) {
214 hrtime = gethrtime();
215 hrt2ts(hrtime, &real);
216 } else {
217 real = pup->pr_term;
218 }
219 tssub(&real, &real, &pup->pr_create);
220 user = pup->pr_utime;
221 sys = pup->pr_stime;
222 if (!mflag)
223 tsadd(&sys, &sys, &pup->pr_ttime);
224
225 if (!pflag || pfirst > 1)
226 (void) fprintf(stderr, "\n");
227 if (pflag)
228 (void) fprintf(stderr, "%d:\t%.70s\n",
229 (int)psinfo.pr_pid, psinfo.pr_psargs);
230 prtime("real", &real);
231 prtime("user", &user);
232 prtime("sys", &sys);
233
234 if (mflag) {
235 prtime("trap", &pup->pr_ttime);
236 prtime("tflt", &pup->pr_tftime);
237 prtime("dflt", &pup->pr_dftime);
238 prtime("kflt", &pup->pr_kftime);
239 prtime("lock", &pup->pr_ltime);
240 prtime("slp", &pup->pr_slptime);
241 prtime("lat", &pup->pr_wtime);
242 prtime("stop", &pup->pr_stoptime);
243 }
244 }
245
246 (void) close(fd);
247 return (rval);
248 }
249
250 static void
hr_min_sec(char * buf,long sec)251 hr_min_sec(char *buf, long sec)
252 {
253 if (sec >= 3600)
254 (void) sprintf(buf, "%ld:%.2ld:%.2ld",
255 sec / 3600, (sec % 3600) / 60, sec % 60);
256 else if (sec >= 60)
257 (void) sprintf(buf, "%ld:%.2ld",
258 sec / 60, sec % 60);
259 else
260 (void) sprintf(buf, "%ld", sec);
261 }
262
263 static void
prtime(char * name,timestruc_t * ts)264 prtime(char *name, timestruc_t *ts)
265 {
266 char buf[32];
267
268 hr_min_sec(buf, ts->tv_sec);
269
270 (void) fprintf(stderr, "%-4s %8s.%.9u\n",
271 name, buf, (uint_t)ts->tv_nsec);
272 }
273
274 static int
perr(const char * s)275 perr(const char *s)
276 {
277 if (s)
278 (void) fprintf(stderr, "%s: ", procname);
279 else
280 s = procname;
281 perror(s);
282 return (1);
283 }
284
285 static void
tsadd(timestruc_t * result,timestruc_t * a,timestruc_t * b)286 tsadd(timestruc_t *result, timestruc_t *a, timestruc_t *b)
287 {
288 result->tv_sec = a->tv_sec + b->tv_sec;
289 if ((result->tv_nsec = a->tv_nsec + b->tv_nsec) >= 1000000000) {
290 result->tv_nsec -= 1000000000;
291 result->tv_sec += 1;
292 }
293 }
294
295 static void
tssub(timestruc_t * result,timestruc_t * a,timestruc_t * b)296 tssub(timestruc_t *result, timestruc_t *a, timestruc_t *b)
297 {
298 result->tv_sec = a->tv_sec - b->tv_sec;
299 if ((result->tv_nsec = a->tv_nsec - b->tv_nsec) < 0) {
300 result->tv_nsec += 1000000000;
301 result->tv_sec -= 1;
302 }
303 }
304
305 static void
hrt2ts(hrtime_t hrt,timestruc_t * tsp)306 hrt2ts(hrtime_t hrt, timestruc_t *tsp)
307 {
308 tsp->tv_sec = hrt / NANOSEC;
309 tsp->tv_nsec = hrt % NANOSEC;
310 }
311