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