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 }