1 /*- 2 * Copyright (c) 2007, 2011 Robert N. M. Watson 3 * Copyright (c) 2015 Allan Jude <allanjude@freebsd.org> 4 * Copyright (c) 2017 Dell EMC 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <sys/param.h> 32 #include <sys/sysctl.h> 33 #include <sys/user.h> 34 35 #include <err.h> 36 #include <libprocstat.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <sysexits.h> 41 #include <unistd.h> 42 43 #include "procstat.h" 44 45 static int aflag, bflag, cflag, eflag, fflag, iflag, jflag, kflag; 46 static int lflag, Lflag, rflag, sflag, tflag, vflag, xflag, Sflag; 47 int hflag, nflag, Cflag, Hflag; 48 49 static void 50 usage(void) 51 { 52 53 xo_error( 54 "usage: procstat [--libxo] [-Hhn] [-M core] " 55 "[-N system] [-w interval]\n" 56 " [-S | -b | -c | -e | -i | -j | -k | -kk | " 57 "-l | -r | -s | \n" 58 " -t | -v | -x]\n" 59 " [-a | pid ... | core ...]\n" 60 " procstat [--libxo] -Cf [-hn] [-M core] " 61 "[-N system] [-a | pid ... | core ...]\n" 62 " [-S | -b | -c | -e | -i | -j | -k | -kk | " 63 "-l | -r | -s | \n" 64 " procstat [--libxo] -L [-hn] [-M core] " 65 "[-N system] [-w interval]\n" 66 " [-S | -b | -c | -e | -i | -j | -k | -kk | " 67 "-l | -r | -s | \n" 68 " -t | -v | -x]\n" 69 " [core ...]\n"); 70 xo_finish(); 71 exit(EX_USAGE); 72 } 73 74 static void 75 procstat(struct procstat *prstat, struct kinfo_proc *kipp) 76 { 77 char *pidstr = NULL; 78 79 asprintf(&pidstr, "%d", kipp->ki_pid); 80 if (pidstr == NULL) 81 xo_errc(1, ENOMEM, "Failed to allocate memory in procstat()"); 82 xo_open_container(pidstr); 83 84 if (bflag) 85 procstat_bin(prstat, kipp); 86 else if (cflag) 87 procstat_args(prstat, kipp); 88 else if (eflag) 89 procstat_env(prstat, kipp); 90 else if (fflag) 91 procstat_files(prstat, kipp); 92 else if (iflag) 93 procstat_sigs(prstat, kipp); 94 else if (jflag) 95 procstat_threads_sigs(prstat, kipp); 96 else if (kflag) 97 procstat_kstack(prstat, kipp, kflag); 98 else if (lflag) 99 procstat_rlimit(prstat, kipp); 100 else if (Lflag) 101 procstat_ptlwpinfo(prstat); 102 else if (rflag) 103 procstat_rusage(prstat, kipp); 104 else if (sflag) 105 procstat_cred(prstat, kipp); 106 else if (tflag) 107 procstat_threads(prstat, kipp); 108 else if (vflag) 109 procstat_vm(prstat, kipp); 110 else if (xflag) 111 procstat_auxv(prstat, kipp); 112 else if (Sflag) 113 procstat_cs(prstat, kipp); 114 else 115 procstat_basic(kipp); 116 117 xo_close_container(pidstr); 118 free(pidstr); 119 } 120 121 /* 122 * Sort processes first by pid and then tid. 123 */ 124 static int 125 kinfo_proc_compare(const void *a, const void *b) 126 { 127 int i; 128 129 i = ((const struct kinfo_proc *)a)->ki_pid - 130 ((const struct kinfo_proc *)b)->ki_pid; 131 if (i != 0) 132 return (i); 133 i = ((const struct kinfo_proc *)a)->ki_tid - 134 ((const struct kinfo_proc *)b)->ki_tid; 135 return (i); 136 } 137 138 void 139 kinfo_proc_sort(struct kinfo_proc *kipp, int count) 140 { 141 142 qsort(kipp, count, sizeof(*kipp), kinfo_proc_compare); 143 } 144 145 const char * 146 kinfo_proc_thread_name(const struct kinfo_proc *kipp) 147 { 148 static char name[MAXCOMLEN+1]; 149 150 strlcpy(name, kipp->ki_tdname, sizeof(name)); 151 strlcat(name, kipp->ki_moretdname, sizeof(name)); 152 if (name[0] == '\0' || strcmp(kipp->ki_comm, name) == 0) { 153 name[0] = '-'; 154 name[1] = '\0'; 155 } 156 157 return (name); 158 } 159 160 int 161 main(int argc, char *argv[]) 162 { 163 int ch, interval, tmp; 164 int i; 165 struct kinfo_proc *p; 166 struct procstat *prstat, *cprstat; 167 long l; 168 pid_t pid; 169 char *dummy; 170 char *nlistf, *memf; 171 const char *xocontainer; 172 int cnt; 173 174 interval = 0; 175 memf = nlistf = NULL; 176 argc = xo_parse_args(argc, argv); 177 xocontainer = "basic"; 178 179 while ((ch = getopt(argc, argv, "abCcefHhijkLlM:N:nrSstvw:x")) != -1) { 180 switch (ch) { 181 case 'C': 182 Cflag++; 183 break; 184 185 case 'H': 186 Hflag++; 187 break; 188 189 case 'M': 190 memf = optarg; 191 break; 192 case 'N': 193 nlistf = optarg; 194 break; 195 case 'S': 196 Sflag++; 197 xocontainer = "cs"; 198 break; 199 case 'a': 200 aflag++; 201 break; 202 203 case 'b': 204 bflag++; 205 xocontainer = "binary"; 206 break; 207 208 case 'c': 209 cflag++; 210 xocontainer = "arguments"; 211 break; 212 213 case 'e': 214 eflag++; 215 xocontainer = "environment"; 216 break; 217 218 case 'f': 219 fflag++; 220 xocontainer = "files"; 221 break; 222 223 case 'i': 224 iflag++; 225 xocontainer = "signals"; 226 break; 227 228 case 'j': 229 jflag++; 230 xocontainer = "thread_signals"; 231 break; 232 233 case 'k': 234 kflag++; 235 xocontainer = "kstack"; 236 break; 237 238 case 'l': 239 lflag++; 240 xocontainer = "rlimit"; 241 break; 242 243 case 'L': 244 Lflag++; 245 xocontainer = "ptlwpinfo"; 246 break; 247 248 case 'n': 249 nflag++; 250 break; 251 252 case 'h': 253 hflag++; 254 break; 255 256 case 'r': 257 rflag++; 258 xocontainer = "rusage"; 259 break; 260 261 case 's': 262 sflag++; 263 xocontainer = "credentials"; 264 break; 265 266 case 't': 267 tflag++; 268 xocontainer = "threads"; 269 break; 270 271 case 'v': 272 vflag++; 273 xocontainer = "vm"; 274 break; 275 276 case 'w': 277 l = strtol(optarg, &dummy, 10); 278 if (*dummy != '\0') 279 usage(); 280 if (l < 1 || l > INT_MAX) 281 usage(); 282 interval = l; 283 break; 284 285 case 'x': 286 xflag++; 287 xocontainer = "auxv"; 288 break; 289 290 case '?': 291 default: 292 usage(); 293 } 294 295 } 296 argc -= optind; 297 argv += optind; 298 299 /* We require that either 0 or 1 mode flags be set. */ 300 tmp = bflag + cflag + eflag + fflag + iflag + jflag + (kflag ? 1 : 0) + 301 lflag + rflag + sflag + tflag + vflag + xflag + Sflag; 302 if (!(tmp == 0 || tmp == 1)) 303 usage(); 304 305 /* We allow -k to be specified up to twice, but not more. */ 306 if (kflag > 2) 307 usage(); 308 309 /* Must specify either the -a flag or a list of pids. */ 310 if (!(aflag == 1 && argc == 0) && !(aflag == 0 && argc > 0)) 311 usage(); 312 313 /* Only allow -C with -f. */ 314 if (Cflag && !fflag) 315 usage(); 316 317 if (memf != NULL) 318 prstat = procstat_open_kvm(nlistf, memf); 319 else 320 prstat = procstat_open_sysctl(); 321 if (prstat == NULL) 322 xo_errx(1, "procstat_open()"); 323 do { 324 xo_set_version(PROCSTAT_XO_VERSION); 325 xo_open_container("procstat"); 326 xo_open_container(xocontainer); 327 328 if (aflag) { 329 p = procstat_getprocs(prstat, KERN_PROC_PROC, 0, &cnt); 330 if (p == NULL) 331 xo_errx(1, "procstat_getprocs()"); 332 kinfo_proc_sort(p, cnt); 333 for (i = 0; i < cnt; i++) { 334 procstat(prstat, &p[i]); 335 336 /* Suppress header after first process. */ 337 hflag = 1; 338 xo_flush(); 339 } 340 procstat_freeprocs(prstat, p); 341 } 342 for (i = 0; i < argc; i++) { 343 l = strtol(argv[i], &dummy, 10); 344 if (*dummy == '\0') { 345 if (l < 0) 346 usage(); 347 pid = l; 348 349 p = procstat_getprocs(prstat, KERN_PROC_PID, 350 pid, &cnt); 351 if (p == NULL) 352 xo_errx(1, "procstat_getprocs()"); 353 if (cnt != 0) 354 procstat(prstat, p); 355 procstat_freeprocs(prstat, p); 356 } else { 357 cprstat = procstat_open_core(argv[i]); 358 if (cprstat == NULL) { 359 warnx("procstat_open()"); 360 continue; 361 } 362 p = procstat_getprocs(cprstat, KERN_PROC_PID, 363 -1, &cnt); 364 if (p == NULL) 365 xo_errx(1, "procstat_getprocs()"); 366 if (cnt != 0) 367 procstat(cprstat, p); 368 procstat_freeprocs(cprstat, p); 369 procstat_close(cprstat); 370 } 371 /* Suppress header after first process. */ 372 hflag = 1; 373 } 374 375 xo_close_container(xocontainer); 376 xo_close_container("procstat"); 377 xo_finish(); 378 if (interval) 379 sleep(interval); 380 } while (interval); 381 382 procstat_close(prstat); 383 384 exit(0); 385 } 386