6416 ptime -p could support multiple processes
[unleashed.git] / usr / src / cmd / ptools / ptime / ptime.c
blob2da2f8d281c38e6be78c0325e558dd5c5cfac0b5
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * Portions Copyright 2008 Chad Mynhier
28 * Copyright 2016 Joyent, Inc.
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>
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 *);
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);
54 static char *command;
55 static char *pidarg;
56 static char procname[64];
58 static int Fflag;
59 static int mflag;
60 static int errflg;
61 static int pflag;
62 static int pfirst;
64 static int
65 ptime_pid(const char *pidstr)
67 struct ps_prochandle *Pr;
68 pid_t pid;
69 int gret;
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);
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);
85 int
86 main(int argc, char **argv)
88 int opt, exit;
89 pid_t pid;
90 struct siginfo info;
91 int status;
92 int gret;
93 struct ps_prochandle *Pr;
95 if ((command = strrchr(argv[0], '/')) != NULL)
96 command++;
97 else
98 command = argv[0];
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;
118 argc -= optind;
119 argv += optind;
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);
130 if (pflag) {
131 char *pp;
133 exit = 0;
134 (void) signal(SIGINT, SIG_IGN);
135 (void) signal(SIGQUIT, SIG_IGN);
137 pp = strtok(pidarg, ", ");
138 if (pp == NULL) {
139 (void) fprintf(stderr, "%s: invalid argument for -p\n",
140 command);
141 return (1);
143 exit = ptime_pid(pp);
144 while ((pp = strtok(NULL, ", ")) != NULL) {
145 exit |= ptime_pid(pp);
147 return (exit);
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);
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);
162 pid = Pstatus(Pr)->pr_pid;
164 (void) sprintf(procname, "%d", (int)pid); /* for perr() */
165 (void) signal(SIGINT, SIG_IGN);
166 (void) signal(SIGQUIT, SIG_IGN);
168 (void) waitid(P_PID, pid, &info, WEXITED | WNOWAIT);
170 (void) look(pid);
172 (void) waitpid(pid, &status, 0);
174 if (WIFEXITED(status))
175 return (WEXITSTATUS(status));
177 if (WIFSIGNALED(status)) {
178 int sig = WTERMSIG(status);
179 char name[SIG2STR_MAX];
181 (void) fprintf(stderr, "%s: command terminated "
182 "abnormally by %s\n", command,
183 proc_signame(sig, name, sizeof (name)));
186 return (status | WCOREFLG); /* see time(1) */
189 static int
190 look(pid_t pid)
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;
201 pfirst++;
203 if (proc_get_psinfo(pid, &psinfo) < 0)
204 return (perr("read psinfo"));
206 (void) sprintf(pathname, "/proc/%d/usage", (int)pid);
207 if ((fd = open(pathname, O_RDONLY)) < 0)
208 return (perr("open usage"));
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;
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);
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);
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);
246 (void) close(fd);
247 return (rval);
250 static void
251 hr_min_sec(char *buf, long sec)
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);
263 static void
264 prtime(char *name, timestruc_t *ts)
266 char buf[32];
268 hr_min_sec(buf, ts->tv_sec);
270 (void) fprintf(stderr, "%-4s %8s.%.9u\n",
271 name, buf, (uint_t)ts->tv_nsec);
274 static int
275 perr(const char *s)
277 if (s)
278 (void) fprintf(stderr, "%s: ", procname);
279 else
280 s = procname;
281 perror(s);
282 return (1);
285 static void
286 tsadd(timestruc_t *result, timestruc_t *a, timestruc_t *b)
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;
295 static void
296 tssub(timestruc_t *result, timestruc_t *a, timestruc_t *b)
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;
305 static void
306 hrt2ts(hrtime_t hrt, timestruc_t *tsp)
308 tsp->tv_sec = hrt / NANOSEC;
309 tsp->tv_nsec = hrt % NANOSEC;