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 }