]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/procstat/procstat.c
Initial import from vendor-sys branch of openzfs
[FreeBSD/FreeBSD.git] / usr.bin / procstat / procstat.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2007, 2011 Robert N. M. Watson
5  * Copyright (c) 2015 Allan Jude <allanjude@freebsd.org>
6  * Copyright (c) 2017 Dell EMC
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32
33 #include <sys/param.h>
34 #include <sys/sysctl.h>
35 #include <sys/user.h>
36
37 #include <err.h>
38 #include <libprocstat.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sysexits.h>
43 #include <unistd.h>
44
45 #include "procstat.h"
46
47 enum {
48         PS_CMP_NORMAL = 0x00,
49         PS_CMP_PLURAL = 0x01, 
50         PS_CMP_SUBSTR = 0x02
51 };
52
53 struct procstat_cmd {
54         const char *command;
55         const char *xocontainer;
56         const char *usage;
57         void (*cmd)(struct procstat *, struct kinfo_proc *);
58         void (*opt)(int, char * const *);
59         int cmp;
60 };
61
62 int procstat_opts = 0;
63
64 static void cmdopt_none(int argc, char * const argv[]);
65 static void cmdopt_verbose(int argc, char * const argv[]);
66 static void cmdopt_signals(int argc, char * const argv[]);
67 static void cmdopt_rusage(int argc, char * const argv[]);
68 static void cmdopt_files(int argc, char * const argv[]);
69 static void cmdopt_cpuset(int argc, char * const argv[]);
70
71 static const struct procstat_cmd cmd_table[] = {
72         { "argument", "arguments", NULL, &procstat_args, &cmdopt_none,
73             PS_CMP_PLURAL | PS_CMP_SUBSTR },
74         { "auxv", "auxv", NULL, &procstat_auxv, &cmdopt_none, PS_CMP_NORMAL },
75         { "basic", "basic", NULL, &procstat_basic, &cmdopt_none,
76             PS_CMP_NORMAL },
77         { "binary", "binary", NULL, &procstat_bin, &cmdopt_none,
78             PS_CMP_SUBSTR },
79         { "cpuset", "cs", NULL, &procstat_cs, &cmdopt_cpuset, PS_CMP_NORMAL },
80         { "cs", "cs", NULL, &procstat_cs, &cmdopt_cpuset, PS_CMP_NORMAL },
81         { "credential", "credentials", NULL, &procstat_cred, &cmdopt_none,
82             PS_CMP_PLURAL | PS_CMP_SUBSTR },
83         { "environment", "environment", NULL, &procstat_env, &cmdopt_none,
84             PS_CMP_SUBSTR },
85         { "fd", "files", "[-C]", &procstat_files, &cmdopt_files,
86             PS_CMP_PLURAL },
87         { "file", "files", "[-C]", &procstat_files, &cmdopt_files,
88             PS_CMP_PLURAL },
89         { "kstack", "kstack", "[-v]", &procstat_kstack, &cmdopt_verbose,
90             PS_CMP_NORMAL },
91         { "ptlwpinfo", "ptlwpinfo", NULL, &procstat_ptlwpinfo, &cmdopt_none,
92             PS_CMP_NORMAL },
93         { "rlimit", "rlimit", NULL, &procstat_rlimit, &cmdopt_none,
94             PS_CMP_NORMAL },
95         { "rusage", "rusage", "[-Ht]", &procstat_rusage, &cmdopt_rusage,
96             PS_CMP_NORMAL },
97         { "sigfastblock", "sigfastblock", NULL, &procstat_sigfastblock,
98             &cmdopt_none, PS_CMP_NORMAL },
99         { "signal", "signals", "[-n]", &procstat_sigs, &cmdopt_signals,
100             PS_CMP_PLURAL | PS_CMP_SUBSTR },
101         { "thread", "threads", NULL, &procstat_threads, &cmdopt_none,
102             PS_CMP_PLURAL },
103         { "tsignal", "thread_signals", "[-n]", &procstat_threads_sigs,
104             &cmdopt_signals, PS_CMP_PLURAL | PS_CMP_SUBSTR },
105         { "vm", "vm", NULL, &procstat_vm, &cmdopt_none, PS_CMP_NORMAL }
106 };
107
108 static void
109 usage(void)
110 {
111         size_t i, l;
112         int multi;
113
114         xo_error("usage: procstat [--libxo] [-h] [-M core] [-N system]"
115             " [-w interval] command\n"
116             "                [pid ... | core ...]\n"
117             "       procstat [--libxo] -a [-h] [-M core] [-N system] "
118             " [-w interval] command\n"
119             "       procstat [--libxo] [-h] [-M core] [-N system]"
120             " [-w interval]\n"
121             "                [-S | -b | -c | -e | -f [-C] | -i [-n] | "
122             "-j [-n] | -k [-k] |\n"
123             "                 -l | -r [-H] | -s | -t | -v | -x] "
124             "[pid ... | core ...]\n"
125             "       procstat [--libxo] -a [-h] [-M core] [-N system]"
126             " [-w interval]\n"
127             "                [-S | -b | -c | -e | -f [-C] | -i [-n] | "
128             "-j [-n] | -k [-k] |\n"
129             "                 -l | -r [-H] | -s | -t | -v | -x]\n"
130             "       procstat [--libxo] -L [-h] [-M core] [-N system] core ...\n"
131             "Available commands:\n");
132         for (i = 0, l = nitems(cmd_table); i < l; i++) {
133                 multi = i + 1 < l && cmd_table[i].cmd == cmd_table[i + 1].cmd;
134                 xo_error("       %s%s%s", multi ? "[" : "",
135                     cmd_table[i].command, (cmd_table[i].cmp & PS_CMP_PLURAL) ?
136                     "(s)" : "");
137                 for (; i + 1 < l && cmd_table[i].cmd == cmd_table[i + 1].cmd;
138                     i++)
139                         xo_error(" | %s%s", cmd_table[i + 1].command,
140                             (cmd_table[i].cmp & PS_CMP_PLURAL) ? "(s)" : "");
141                 if (multi)
142                         xo_error("]");
143                 if (cmd_table[i].usage != NULL)
144                         xo_error(" %s", cmd_table[i].usage);
145                 xo_error("\n");
146         }
147         xo_finish();
148         exit(EX_USAGE);
149 }
150
151 static void
152 procstat(const struct procstat_cmd *cmd, struct procstat *prstat,
153     struct kinfo_proc *kipp)
154 {
155         char *pidstr = NULL;
156
157         asprintf(&pidstr, "%d", kipp->ki_pid);
158         if (pidstr == NULL)
159                 xo_errc(1, ENOMEM, "Failed to allocate memory in procstat()");
160         xo_open_container(pidstr);
161         cmd->cmd(prstat, kipp);
162         xo_close_container(pidstr);
163         free(pidstr);
164 }
165
166 /*
167  * Sort processes first by pid and then tid.
168  */
169 static int
170 kinfo_proc_compare(const void *a, const void *b)
171 {
172         int i;
173
174         i = ((const struct kinfo_proc *)a)->ki_pid -
175             ((const struct kinfo_proc *)b)->ki_pid;
176         if (i != 0)
177                 return (i);
178         i = ((const struct kinfo_proc *)a)->ki_tid -
179             ((const struct kinfo_proc *)b)->ki_tid;
180         return (i);
181 }
182
183 void
184 kinfo_proc_sort(struct kinfo_proc *kipp, int count)
185 {
186
187         qsort(kipp, count, sizeof(*kipp), kinfo_proc_compare);
188 }
189
190 const char *
191 kinfo_proc_thread_name(const struct kinfo_proc *kipp)
192 {
193         static char name[MAXCOMLEN+1];
194
195         strlcpy(name, kipp->ki_tdname, sizeof(name));
196         strlcat(name, kipp->ki_moretdname, sizeof(name));
197         if (name[0] == '\0' || strcmp(kipp->ki_comm, name) == 0) {
198                 name[0] = '-';
199                 name[1] = '\0';
200         }
201
202         return (name);
203 }
204
205 static const struct procstat_cmd *
206 getcmd(const char *str)
207 {
208         const struct procstat_cmd *cmd;
209         size_t i, l;
210         int cmp, s;
211
212         if (str == NULL)
213                 return (NULL);
214         cmd = NULL;
215         if ((l = strlen(str)) == 0)
216                 return (getcmd("basic"));
217         s = l > 1 && strcasecmp(str + l - 1, "s") == 0;
218         for (i = 0; i < nitems(cmd_table); i++) {
219                 /*
220                  * After the first match substring matches are disabled,
221                  * allowing subsequent full matches to take precedence.
222                  */
223                 if (cmd == NULL && (cmd_table[i].cmp & PS_CMP_SUBSTR))
224                         cmp = strncasecmp(str, cmd_table[i].command, l -
225                             ((cmd_table[i].cmp & PS_CMP_PLURAL) && s ? 1 : 0));
226                 else if ((cmd_table[i].cmp & PS_CMP_PLURAL) && s &&
227                     l == strlen(cmd_table[i].command) + 1)
228                         cmp = strncasecmp(str, cmd_table[i].command, l - 1);
229                 else
230                         cmp = strcasecmp(str, cmd_table[i].command);
231                 if (cmp == 0)
232                         cmd = &cmd_table[i];
233         }
234         return (cmd);
235 }
236
237 int
238 main(int argc, char *argv[])
239 {
240         int ch, interval;
241         int i;
242         struct kinfo_proc *p;
243         const struct procstat_cmd *cmd;
244         struct procstat *prstat, *cprstat;
245         long l;
246         pid_t pid;
247         char *dummy;
248         char *nlistf, *memf;
249         int aflag;
250         int cnt;
251
252         interval = 0;
253         cmd = NULL;
254         memf = nlistf = NULL;
255         aflag = 0;
256         argc = xo_parse_args(argc, argv);
257
258         while ((ch = getopt(argc, argv, "abCcefHhijkLlM:N:nrSstvw:x")) != -1) {
259                 switch (ch) {
260                 case 'a':
261                         aflag++;
262                         break;
263                 case 'b':
264                         if (cmd != NULL)
265                                 usage();
266                         cmd = getcmd("binary");
267                         break;
268                 case 'C':
269                         procstat_opts |= PS_OPT_CAPABILITIES;
270                         break;
271                 case 'c':
272                         if (cmd != NULL)
273                                 usage();
274                         cmd = getcmd("arguments");
275                         break;
276                 case 'e':
277                         if (cmd != NULL)
278                                 usage();
279                         cmd = getcmd("environment");
280                         break;
281                 case 'f':
282                         if (cmd != NULL)
283                                 usage();
284                         cmd = getcmd("files");
285                         break;
286                 case 'H':
287                         procstat_opts |= PS_OPT_PERTHREAD;
288                         break;
289                 case 'h':
290                         procstat_opts |= PS_OPT_NOHEADER;
291                         break;
292                 case 'i':
293                         if (cmd != NULL)
294                                 usage();
295                         cmd = getcmd("signals");
296                         break;
297                 case 'j':
298                         if (cmd != NULL)
299                                 usage();
300                         cmd = getcmd("tsignals");
301                         break;
302                 case 'k':
303                         if (cmd != NULL && cmd->cmd == procstat_kstack) {
304                                 if ((procstat_opts & PS_OPT_VERBOSE) != 0)
305                                         usage();
306                                 procstat_opts |= PS_OPT_VERBOSE;
307                         } else {
308                                 if (cmd != NULL)
309                                         usage();
310                                 cmd = getcmd("kstack");
311                         }
312                         break;
313                 case 'L':
314                         if (cmd != NULL)
315                                 usage();
316                         cmd = getcmd("ptlwpinfo");
317                         break;
318                 case 'l':
319                         if (cmd != NULL)
320                                 usage();
321                         cmd = getcmd("rlimit");
322                         break;
323                 case 'M':
324                         memf = optarg;
325                         break;
326                 case 'N':
327                         nlistf = optarg;
328                         break;
329                 case 'n':
330                         procstat_opts |= PS_OPT_SIGNUM;
331                         break;
332                 case 'r':
333                         if (cmd != NULL)
334                                 usage();
335                         cmd = getcmd("rusage");
336                         break;
337                 case 'S':
338                         if (cmd != NULL)
339                                 usage();
340                         cmd = getcmd("cpuset");
341                         break;
342                 case 's':
343                         if (cmd != NULL)
344                                 usage();
345                         cmd = getcmd("credentials");
346                         break;
347                 case 't':
348                         if (cmd != NULL)
349                                 usage();
350                         cmd = getcmd("threads");
351                         break;
352                 case 'v':
353                         if (cmd != NULL)
354                                 usage();
355                         cmd = getcmd("vm");
356                         break;
357                 case 'w':
358                         l = strtol(optarg, &dummy, 10);
359                         if (*dummy != '\0')
360                                 usage();
361                         if (l < 1 || l > INT_MAX)
362                                 usage();
363                         interval = l;
364                         break;
365                 case 'x':
366                         if (cmd != NULL)
367                                 usage();
368                         cmd = getcmd("auxv");
369                         break;
370                 case '?':
371                 default:
372                         usage();
373                 }
374
375         }
376         argc -= optind;
377         argv += optind;
378
379         if (cmd == NULL && argv[0] != NULL && (cmd = getcmd(argv[0])) != NULL) {
380                 if ((procstat_opts & PS_SUBCOMMAND_OPTS) != 0)
381                         usage();
382                 if (cmd->opt != NULL) {
383                         optreset = 1;
384                         optind = 1;
385                         cmd->opt(argc, argv);
386                         argc -= optind;
387                         argv += optind;
388                 } else {
389                         argc -= 1;
390                         argv += 1;
391                 }
392         } else {
393                 if (cmd == NULL)
394                         cmd = getcmd("basic");
395                 if (cmd->cmd != procstat_files &&
396                     (procstat_opts & PS_OPT_CAPABILITIES) != 0)
397                         usage();
398         }
399
400         /* Must specify either the -a flag or a list of pids. */
401         if (!(aflag == 1 && argc == 0) && !(aflag == 0 && argc > 0))
402                 usage();
403
404         if (memf != NULL)
405                 prstat = procstat_open_kvm(nlistf, memf);
406         else
407                 prstat = procstat_open_sysctl();
408         if (prstat == NULL)
409                 xo_errx(1, "procstat_open()");
410         do {
411                 xo_set_version(PROCSTAT_XO_VERSION);
412                 xo_open_container("procstat");
413                 xo_open_container(cmd->xocontainer);
414
415                 if (aflag) {
416                         p = procstat_getprocs(prstat, KERN_PROC_PROC, 0, &cnt);
417                         if (p == NULL)
418                                 xo_errx(1, "procstat_getprocs()");
419                         kinfo_proc_sort(p, cnt);
420                         for (i = 0; i < cnt; i++) {
421                                 procstat(cmd, prstat, &p[i]);
422
423                                 /* Suppress header after first process. */
424                                 procstat_opts |= PS_OPT_NOHEADER;
425                                 xo_flush();
426                         }
427                         procstat_freeprocs(prstat, p);
428                 }
429                 for (i = 0; i < argc; i++) {
430                         l = strtol(argv[i], &dummy, 10);
431                         if (*dummy == '\0') {
432                                 if (l < 0)
433                                         usage();
434                                 pid = l;
435
436                                 p = procstat_getprocs(prstat, KERN_PROC_PID,
437                                     pid, &cnt);
438                                 if (p == NULL)
439                                         xo_errx(1, "procstat_getprocs()");
440                                 if (cnt != 0)
441                                         procstat(cmd, prstat, p);
442                                 procstat_freeprocs(prstat, p);
443                         } else {
444                                 cprstat = procstat_open_core(argv[i]);
445                                 if (cprstat == NULL) {
446                                         warnx("procstat_open()");
447                                         continue;
448                                 }
449                                 p = procstat_getprocs(cprstat, KERN_PROC_PID,
450                                     -1, &cnt);
451                                 if (p == NULL)
452                                         xo_errx(1, "procstat_getprocs()");
453                                 if (cnt != 0)
454                                         procstat(cmd, cprstat, p);
455                                 procstat_freeprocs(cprstat, p);
456                                 procstat_close(cprstat);
457                         }
458                         /* Suppress header after first process. */
459                         procstat_opts |= PS_OPT_NOHEADER;
460                 }
461
462                 xo_close_container(cmd->xocontainer);
463                 xo_close_container("procstat");
464                 xo_finish();
465                 if (interval)
466                         sleep(interval);
467         } while (interval);
468
469         procstat_close(prstat);
470
471         exit(0);
472 }
473
474 void
475 cmdopt_none(int argc, char * const argv[])
476 {
477         int ch;
478
479         while ((ch = getopt(argc, argv, "")) != -1) {
480                 switch (ch) {
481                 case '?':
482                 default:
483                         usage();
484                 }
485         }
486 }
487
488 void
489 cmdopt_verbose(int argc, char * const argv[])
490 {
491         int ch;
492
493         while ((ch = getopt(argc, argv, "v")) != -1) {
494                 switch (ch) {
495                 case 'v':
496                         procstat_opts |= PS_OPT_VERBOSE;
497                         break;
498                 case '?':
499                 default:
500                         usage();
501                 }
502         }
503 }
504
505 void
506 cmdopt_signals(int argc, char * const argv[])
507 {
508         int ch;
509
510         while ((ch = getopt(argc, argv, "n")) != -1) {
511                 switch (ch) {
512                 case 'n':
513                         procstat_opts |= PS_OPT_SIGNUM;
514                         break;
515                 case '?':
516                 default:
517                         usage();
518                 }
519         }
520 }
521
522 void
523 cmdopt_rusage(int argc, char * const argv[])
524 {
525         int ch;
526
527         while ((ch = getopt(argc, argv, "Ht")) != -1) {
528                 switch (ch) {
529                 case 'H':
530                         /* FALLTHROUGH */
531                 case 't':
532                         procstat_opts |= PS_OPT_PERTHREAD;
533                         break;
534                 case '?':
535                 default:
536                         usage();
537                 }
538         }
539 }
540
541 void
542 cmdopt_files(int argc, char * const argv[])
543 {
544         int ch;
545
546         while ((ch = getopt(argc, argv, "C")) != -1) {
547                 switch (ch) {
548                 case 'C':
549                         procstat_opts |= PS_OPT_CAPABILITIES;
550                         break;
551                 case '?':
552                 default:
553                         usage();
554                 }
555         }
556 }
557
558 void
559 cmdopt_cpuset(int argc, char * const argv[])
560 {
561
562         procstat_opts |= PS_OPT_PERTHREAD;
563         cmdopt_none(argc, argv);
564 }