]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/procstat/procstat.c
ldd: clarify format options
[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
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/param.h>
35 #include <sys/sysctl.h>
36 #include <sys/user.h>
37
38 #include <err.h>
39 #include <libprocstat.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sysexits.h>
44 #include <unistd.h>
45
46 #include "procstat.h"
47
48 enum {
49         PS_CMP_NORMAL = 0x00,
50         PS_CMP_PLURAL = 0x01, 
51         PS_CMP_SUBSTR = 0x02
52 };
53
54 struct procstat_cmd {
55         const char *command;
56         const char *xocontainer;
57         const char *usage;
58         void (*cmd)(struct procstat *, struct kinfo_proc *);
59         void (*opt)(int, char * const *);
60         int cmp;
61 };
62
63 int procstat_opts = 0;
64
65 static void cmdopt_none(int argc, char * const argv[]);
66 static void cmdopt_verbose(int argc, char * const argv[]);
67 static void cmdopt_signals(int argc, char * const argv[]);
68 static void cmdopt_rusage(int argc, char * const argv[]);
69 static void cmdopt_files(int argc, char * const argv[]);
70 static void cmdopt_cpuset(int argc, char * const argv[]);
71
72 static const char *progname;
73
74 /* aliased program parameters and arguments
75  * - usage field is abused to hold the pointer to the function
76  *   displaying program usage
77  */
78 static const struct procstat_cmd pacmd_table[] = {
79         /* arguments are the same as for pwdx: pid or core file */
80         { "pargs", "args", NULL, &procstat_pargs, &cmdopt_none,
81             PS_CMP_NORMAL | PS_MODE_COMPAT },
82         { "penv", "env", NULL, &procstat_penv, &cmdopt_none,
83             PS_CMP_NORMAL | PS_MODE_COMPAT },
84         { "pwdx", "pwd", NULL, &procstat_pwdx, &cmdopt_none,
85             PS_CMP_NORMAL | PS_MODE_COMPAT }
86 };
87
88 /* procstat parameters and arguments */
89 static const struct procstat_cmd cmd_table[] = {
90         { "advlock", "advisory_locks", NULL, &procstat_advlocks, &cmdopt_none,
91             PS_CMP_PLURAL | PS_CMP_SUBSTR | PS_MODE_NO_KINFO_PROC },
92         { "argument", "arguments", NULL, &procstat_args, &cmdopt_none,
93             PS_CMP_PLURAL | PS_CMP_SUBSTR },
94         { "auxv", "auxv", NULL, &procstat_auxv, &cmdopt_none, PS_CMP_NORMAL },
95         { "basic", "basic", NULL, &procstat_basic, &cmdopt_none,
96             PS_CMP_NORMAL },
97         { "binary", "binary", NULL, &procstat_bin, &cmdopt_none,
98             PS_CMP_SUBSTR },
99         { "cpuset", "cs", NULL, &procstat_cs, &cmdopt_cpuset, PS_CMP_NORMAL },
100         { "cs", "cs", NULL, &procstat_cs, &cmdopt_cpuset, PS_CMP_NORMAL },
101         { "credential", "credentials", NULL, &procstat_cred, &cmdopt_none,
102             PS_CMP_PLURAL | PS_CMP_SUBSTR },
103         { "environment", "environment", NULL, &procstat_env, &cmdopt_none,
104             PS_CMP_SUBSTR },
105         { "fd", "files", "[-C]", &procstat_files, &cmdopt_files,
106             PS_CMP_PLURAL },
107         { "file", "files", "[-C]", &procstat_files, &cmdopt_files,
108             PS_CMP_PLURAL },
109         { "kstack", "kstack", "[-v]", &procstat_kstack, &cmdopt_verbose,
110             PS_CMP_NORMAL },
111         { "pargs", "args", NULL, &procstat_pargs, &cmdopt_none,
112             PS_CMP_NORMAL },
113         { "penv", "env", NULL, &procstat_penv, &cmdopt_none,
114             PS_CMP_NORMAL },
115         { "ptlwpinfo", "ptlwpinfo", NULL, &procstat_ptlwpinfo, &cmdopt_none,
116             PS_CMP_NORMAL },
117         { "pwdx", "pwd", NULL, &procstat_pwdx, &cmdopt_none,
118             PS_CMP_NORMAL },
119         { "rlimit", "rlimit", NULL, &procstat_rlimit, &cmdopt_none,
120             PS_CMP_NORMAL },
121         { "rusage", "rusage", "[-Ht]", &procstat_rusage, &cmdopt_rusage,
122             PS_CMP_NORMAL },
123         { "sigfastblock", "sigfastblock", NULL, &procstat_sigfastblock,
124             &cmdopt_none, PS_CMP_NORMAL },
125         { "signal", "signals", "[-n]", &procstat_sigs, &cmdopt_signals,
126             PS_CMP_PLURAL | PS_CMP_SUBSTR },
127         { "thread", "threads", NULL, &procstat_threads, &cmdopt_none,
128             PS_CMP_PLURAL },
129         { "tsignal", "thread_signals", "[-n]", &procstat_threads_sigs,
130             &cmdopt_signals, PS_CMP_PLURAL | PS_CMP_SUBSTR },
131         { "vm", "vm", NULL, &procstat_vm, &cmdopt_none, PS_CMP_NORMAL }
132 };
133
134 static void
135 usage(const struct procstat_cmd *cmd)
136 {
137         size_t i, l;
138         int multi;
139
140         if (cmd == NULL || (cmd->cmp & PS_MODE_COMPAT) == 0) {
141                 xo_error("usage: procstat [--libxo] [-h] [-M core] [-N system]"
142                     " [-w interval] command\n"
143                     "                [pid ... | core ...]\n"
144                     "       procstat [--libxo] -a [-h] [-M core] [-N system] "
145                     " [-w interval] command\n"
146                     "       procstat [--libxo] [-h] [-M core] [-N system]"
147                     " [-w interval]\n"
148                     "                [-S | -b | -c | -e | -f [-C] | -i [-n] | "
149                     "-j [-n] | -k [-k] |\n"
150                     "                 -l | -r [-H] | -s | -t | -v | -x] "
151                     "[pid ... | core ...]\n"
152                     "       procstat [--libxo] -a [-h] [-M core] [-N system]"
153                     " [-w interval]\n"
154                     "                [-S | -b | -c | -e | -f [-C] | -i [-n] | "
155                     "-j [-n] | -k [-k] |\n"
156                     "                 -l | -r [-H] | -s | -t | -v | -x]\n"
157                     "       procstat [--libxo] -L [-h] [-M core] [-N system] core ...\n"
158                     "Available commands:\n");
159                 for (i = 0, l = nitems(cmd_table); i < l; i++) {
160                         multi = i + 1 < l && cmd_table[i].cmd ==
161                             cmd_table[i + 1].cmd;
162                         xo_error("       %s%s%s", multi ? "[" : "",
163                             cmd_table[i].command, (cmd_table[i].cmp &
164                             PS_CMP_PLURAL) ? "(s)" : "");
165                         for (; i + 1 < l && cmd_table[i].cmd ==
166                             cmd_table[i + 1].cmd; i++)
167                                 xo_error(" | %s%s", cmd_table[i + 1].command,
168                                     (cmd_table[i].cmp & PS_CMP_PLURAL) ?
169                                     "(s)" : "");
170                         if (multi)
171                                 xo_error("]");
172                         if (cmd_table[i].usage != NULL)
173                                 xo_error(" %s", cmd_table[i].usage);
174                         xo_error("\n");
175                 }
176         } else {
177                 xo_error("usage: %s [--libxo] pid ...\n", progname);
178         }
179         xo_finish();
180         exit(EX_USAGE);
181 }
182
183 static void
184 procstat(const struct procstat_cmd *cmd, struct procstat *prstat,
185     struct kinfo_proc *kipp)
186 {
187         char *pidstr = NULL;
188
189         asprintf(&pidstr, "%d", kipp->ki_pid);
190         if (pidstr == NULL)
191                 xo_errc(1, ENOMEM, "Failed to allocate memory in procstat()");
192         xo_open_container(pidstr);
193         cmd->cmd(prstat, kipp);
194         xo_close_container(pidstr);
195         free(pidstr);
196 }
197
198 /*
199  * Sort processes first by pid and then tid.
200  */
201 static int
202 kinfo_proc_compare(const void *a, const void *b)
203 {
204         int i;
205
206         i = ((const struct kinfo_proc *)a)->ki_pid -
207             ((const struct kinfo_proc *)b)->ki_pid;
208         if (i != 0)
209                 return (i);
210         i = ((const struct kinfo_proc *)a)->ki_tid -
211             ((const struct kinfo_proc *)b)->ki_tid;
212         return (i);
213 }
214
215 void
216 kinfo_proc_sort(struct kinfo_proc *kipp, int count)
217 {
218
219         qsort(kipp, count, sizeof(*kipp), kinfo_proc_compare);
220 }
221
222 const char *
223 kinfo_proc_thread_name(const struct kinfo_proc *kipp)
224 {
225         static char name[MAXCOMLEN+1];
226
227         strlcpy(name, kipp->ki_tdname, sizeof(name));
228         strlcat(name, kipp->ki_moretdname, sizeof(name));
229         if (name[0] == '\0' || strcmp(kipp->ki_comm, name) == 0) {
230                 name[0] = '-';
231                 name[1] = '\0';
232         }
233
234         return (name);
235 }
236
237 static const struct procstat_cmd *
238 getcmdbyprogname(const char *pprogname)
239 {
240         const char *ca;
241         size_t i;
242
243         if (pprogname == NULL)
244                 return (NULL);
245
246         for (i = 0; i < nitems(pacmd_table); i++) {
247                 ca = pacmd_table[i].command;
248                 if (ca != NULL && strcmp(ca, pprogname) == 0)
249                         return (&pacmd_table[i]);
250         }
251
252         return (NULL);
253 }
254
255 static const struct procstat_cmd *
256 getcmd(const char *str)
257 {
258         const struct procstat_cmd *cmd;
259         size_t i, l;
260         int cmp, s;
261
262         if (str == NULL)
263                 return (NULL);
264         cmd = NULL;
265         if ((l = strlen(str)) == 0)
266                 return (getcmd("basic"));
267         s = l > 1 && strcasecmp(str + l - 1, "s") == 0;
268         for (i = 0; i < nitems(cmd_table); i++) {
269                 /*
270                  * After the first match substring matches are disabled,
271                  * allowing subsequent full matches to take precedence.
272                  */
273                 if (cmd == NULL && (cmd_table[i].cmp & PS_CMP_SUBSTR))
274                         cmp = strncasecmp(str, cmd_table[i].command, l -
275                             ((cmd_table[i].cmp & PS_CMP_PLURAL) && s ? 1 : 0));
276                 else if ((cmd_table[i].cmp & PS_CMP_PLURAL) && s &&
277                     l == strlen(cmd_table[i].command) + 1)
278                         cmp = strncasecmp(str, cmd_table[i].command, l - 1);
279                 else
280                         cmp = strcasecmp(str, cmd_table[i].command);
281                 if (cmp == 0)
282                         cmd = &cmd_table[i];
283         }
284         return (cmd);
285 }
286
287 int
288 main(int argc, char *argv[])
289 {
290         struct kinfo_proc *p;
291         const struct procstat_cmd *cmd;
292         struct procstat *prstat, *cprstat;
293         char *dummy, *nlistf, *memf;
294         const char *xocontainer;
295         long l;
296         pid_t pid;
297         int aflag, ch, cnt, i, interval;
298
299         interval = 0;
300         cmd = NULL;
301         memf = nlistf = NULL;
302         aflag = 0;
303         argc = xo_parse_args(argc, argv);
304
305         progname = getprogname();
306         cmd = getcmdbyprogname(progname);
307
308         while ((ch = getopt(argc, argv, "abCcefHhijkLlM:N:nrSstvw:x")) != -1) {
309                 switch (ch) {
310                 case 'a':
311                         aflag++;
312                         break;
313                 case 'b':
314                         if (cmd != NULL)
315                                 usage(cmd);
316                         cmd = getcmd("binary");
317                         break;
318                 case 'C':
319                         procstat_opts |= PS_OPT_CAPABILITIES;
320                         break;
321                 case 'c':
322                         if (cmd != NULL)
323                                 usage(cmd);
324                         cmd = getcmd("arguments");
325                         break;
326                 case 'e':
327                         if (cmd != NULL)
328                                 usage(cmd);
329                         cmd = getcmd("environment");
330                         break;
331                 case 'f':
332                         if (cmd != NULL)
333                                 usage(cmd);
334                         cmd = getcmd("files");
335                         break;
336                 case 'H':
337                         procstat_opts |= PS_OPT_PERTHREAD;
338                         break;
339                 case 'h':
340                         procstat_opts |= PS_OPT_NOHEADER;
341                         break;
342                 case 'i':
343                         if (cmd != NULL)
344                                 usage(cmd);
345                         cmd = getcmd("signals");
346                         break;
347                 case 'j':
348                         if (cmd != NULL)
349                                 usage(cmd);
350                         cmd = getcmd("tsignals");
351                         break;
352                 case 'k':
353                         if (cmd != NULL && cmd->cmd == procstat_kstack) {
354                                 if ((procstat_opts & PS_OPT_VERBOSE) != 0)
355                                         usage(cmd);
356                                 procstat_opts |= PS_OPT_VERBOSE;
357                         } else {
358                                 if (cmd != NULL)
359                                         usage(cmd);
360                                 cmd = getcmd("kstack");
361                         }
362                         break;
363                 case 'L':
364                         if (cmd != NULL)
365                                 usage(cmd);
366                         cmd = getcmd("ptlwpinfo");
367                         break;
368                 case 'l':
369                         if (cmd != NULL)
370                                 usage(cmd);
371                         cmd = getcmd("rlimit");
372                         break;
373                 case 'M':
374                         memf = optarg;
375                         break;
376                 case 'N':
377                         nlistf = optarg;
378                         break;
379                 case 'n':
380                         procstat_opts |= PS_OPT_SIGNUM;
381                         break;
382                 case 'r':
383                         if (cmd != NULL)
384                                 usage(cmd);
385                         cmd = getcmd("rusage");
386                         break;
387                 case 'S':
388                         if (cmd != NULL)
389                                 usage(cmd);
390                         cmd = getcmd("cpuset");
391                         break;
392                 case 's':
393                         if (cmd != NULL)
394                                 usage(cmd);
395                         cmd = getcmd("credentials");
396                         break;
397                 case 't':
398                         if (cmd != NULL)
399                                 usage(cmd);
400                         cmd = getcmd("threads");
401                         break;
402                 case 'v':
403                         if (cmd != NULL)
404                                 usage(cmd);
405                         cmd = getcmd("vm");
406                         break;
407                 case 'w':
408                         l = strtol(optarg, &dummy, 10);
409                         if (*dummy != '\0')
410                                 usage(cmd);
411                         if (l < 1 || l > INT_MAX)
412                                 usage(cmd);
413                         interval = l;
414                         break;
415                 case 'x':
416                         if (cmd != NULL)
417                                 usage(cmd);
418                         cmd = getcmd("auxv");
419                         break;
420                 case '?':
421                 default:
422                         usage(cmd);
423                 }
424
425         }
426         argc -= optind;
427         argv += optind;
428
429         if (cmd == NULL && argv[0] != NULL && (cmd = getcmd(argv[0])) != NULL) {
430                 if ((procstat_opts & PS_SUBCOMMAND_OPTS) != 0)
431                         usage(cmd);
432                 if (cmd->opt != NULL) {
433                         optreset = 1;
434                         optind = 1;
435                         cmd->opt(argc, argv);
436                         if ((cmd->cmp & PS_MODE_COMPAT) == 0) {
437                                 argc -= optind;
438                                 argv += optind;
439                         }
440                 } else {
441                         argc -= 1;
442                         argv += 1;
443                 }
444         } else {
445                 if (cmd == NULL)
446                         cmd = getcmd("basic");
447                 if (cmd->cmd != procstat_files &&
448                     (procstat_opts & PS_OPT_CAPABILITIES) != 0 &&
449                     (cmd->cmp & PS_MODE_COMPAT) == 0)
450                         usage(cmd);
451         }
452
453         /* Must specify either the -a flag or a list of pids. */
454         if (!(aflag == 1 && argc == 0) && !(aflag == 0 && argc > 0) &&
455             (cmd->cmp & PS_MODE_NO_KINFO_PROC) == 0)
456                 usage(cmd);
457
458         if (memf != NULL)
459                 prstat = procstat_open_kvm(nlistf, memf);
460         else
461                 prstat = procstat_open_sysctl();
462         if (prstat == NULL)
463                 xo_errx(1, "procstat_open()");
464         do {
465                 xocontainer = cmd->xocontainer != NULL ? cmd->xocontainer :
466                     cmd->command;
467                 xo_set_version(PROCSTAT_XO_VERSION);
468                 xo_open_container(progname);
469                 xo_open_container(xocontainer);
470
471                 if ((cmd->cmp & PS_MODE_NO_KINFO_PROC) != 0) {
472                         cmd->cmd(prstat, NULL);
473                         goto iter;
474                 }
475
476                 if (aflag) {
477                         p = procstat_getprocs(prstat, KERN_PROC_PROC, 0, &cnt);
478                         if (p == NULL)
479                                 xo_errx(1, "procstat_getprocs()");
480                         kinfo_proc_sort(p, cnt);
481                         for (i = 0; i < cnt; i++) {
482                                 procstat(cmd, prstat, &p[i]);
483
484                                 /* Suppress header after first process. */
485                                 procstat_opts |= PS_OPT_NOHEADER;
486                                 xo_flush();
487                         }
488                         procstat_freeprocs(prstat, p);
489                 }
490                 for (i = 0; i < argc; i++) {
491                         l = strtol(argv[i], &dummy, 10);
492                         if (*dummy == '\0') {
493                                 if (l < 0)
494                                         usage(cmd);
495                                 pid = l;
496
497                                 p = procstat_getprocs(prstat, KERN_PROC_PID,
498                                     pid, &cnt);
499                                 if (p == NULL)
500                                         xo_errx(1, "procstat_getprocs()");
501                                 if (cnt != 0)
502                                         procstat(cmd, prstat, p);
503                                 procstat_freeprocs(prstat, p);
504                         } else {
505                                 if ((cmd->cmp & PS_MODE_COMPAT) == 0) {
506                                         cprstat = procstat_open_core(argv[i]);
507                                         if (cprstat == NULL) {
508                                                 warnx("procstat_open()");
509                                                 continue;
510                                         }
511                                         p = procstat_getprocs(cprstat,
512                                             KERN_PROC_PID, -1, &cnt);
513                                         if (p == NULL) {
514                                                 xo_errx(1,
515                                                     "procstat_getprocs()");
516                                         }
517                                         if (cnt != 0)
518                                                 procstat(cmd, cprstat, p);
519                                         procstat_freeprocs(cprstat, p);
520                                         procstat_close(cprstat);
521                                 } else {
522                                         usage(cmd);
523                                 }
524                         }
525                         if ((cmd->cmp & PS_MODE_COMPAT) == 0) {
526                                 /* Suppress header after first process. */
527                                 procstat_opts |= PS_OPT_NOHEADER;
528                         }
529                 }
530
531 iter:
532                 xo_close_container(xocontainer);
533                 xo_close_container(progname);
534                 xo_finish();
535                 if (interval)
536                         sleep(interval);
537         } while (interval);
538
539         procstat_close(prstat);
540
541         exit(0);
542 }
543
544 void
545 cmdopt_none(int argc, char * const argv[])
546 {
547         int ch;
548
549         while ((ch = getopt(argc, argv, "")) != -1) {
550                 switch (ch) {
551                 case '?':
552                 default:
553                         usage(NULL);
554                 }
555         }
556 }
557
558 void
559 cmdopt_verbose(int argc, char * const argv[])
560 {
561         int ch;
562
563         while ((ch = getopt(argc, argv, "v")) != -1) {
564                 switch (ch) {
565                 case 'v':
566                         procstat_opts |= PS_OPT_VERBOSE;
567                         break;
568                 case '?':
569                 default:
570                         usage(NULL);
571                 }
572         }
573 }
574
575 void
576 cmdopt_signals(int argc, char * const argv[])
577 {
578         int ch;
579
580         while ((ch = getopt(argc, argv, "n")) != -1) {
581                 switch (ch) {
582                 case 'n':
583                         procstat_opts |= PS_OPT_SIGNUM;
584                         break;
585                 case '?':
586                 default:
587                         usage(NULL);
588                 }
589         }
590 }
591
592 void
593 cmdopt_rusage(int argc, char * const argv[])
594 {
595         int ch;
596
597         while ((ch = getopt(argc, argv, "Ht")) != -1) {
598                 switch (ch) {
599                 case 'H':
600                         /* FALLTHROUGH */
601                 case 't':
602                         procstat_opts |= PS_OPT_PERTHREAD;
603                         break;
604                 case '?':
605                 default:
606                         usage(NULL);
607                 }
608         }
609 }
610
611 void
612 cmdopt_files(int argc, char * const argv[])
613 {
614         int ch;
615
616         while ((ch = getopt(argc, argv, "C")) != -1) {
617                 switch (ch) {
618                 case 'C':
619                         procstat_opts |= PS_OPT_CAPABILITIES;
620                         break;
621                 case '?':
622                 default:
623                         usage(NULL);
624                 }
625         }
626 }
627
628 void
629 cmdopt_cpuset(int argc, char * const argv[])
630 {
631
632         procstat_opts |= PS_OPT_PERTHREAD;
633         cmdopt_none(argc, argv);
634 }