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 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 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 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 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 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 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 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 266 hrt2ts(hrtime_t hrt, timestruc_t *tsp) 267 { 268 tsp->tv_sec = hrt / NANOSEC; 269 tsp->tv_nsec = hrt % NANOSEC; 270 } 271