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 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 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 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 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 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 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 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 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 306 hrt2ts(hrtime_t hrt, timestruc_t *tsp) 307 { 308 tsp->tv_sec = hrt / NANOSEC; 309 tsp->tv_nsec = hrt % NANOSEC; 310 } 311