xref: /freebsd/usr.bin/procstat/procstat.c (revision aa0a1e58)
1 /*-
2  * Copyright (c) 2007 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/param.h>
30 #include <sys/sysctl.h>
31 #include <sys/user.h>
32 
33 #include <err.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <sysexits.h>
37 #include <unistd.h>
38 
39 #include "procstat.h"
40 
41 static int aflag, bflag, cflag, fflag, iflag, jflag, kflag, sflag, tflag, vflag;
42 int	hflag, nflag;
43 
44 static void
45 usage(void)
46 {
47 
48 	fprintf(stderr, "usage: procstat [-h] [-n] [-w interval] [-b | -c | -f | "
49 	    "-i | -j | -k | -s | -t | -v]\n");
50 	fprintf(stderr, "                [-a | pid ...]\n");
51 	exit(EX_USAGE);
52 }
53 
54 static void
55 procstat(pid_t pid, struct kinfo_proc *kipp)
56 {
57 
58 	if (bflag)
59 		procstat_bin(pid, kipp);
60 	else if (cflag)
61 		procstat_args(pid, kipp);
62 	else if (fflag)
63 		procstat_files(pid, kipp);
64 	else if (iflag)
65 		procstat_sigs(pid, kipp);
66 	else if (jflag)
67 		procstat_threads_sigs(pid, kipp);
68 	else if (kflag)
69 		procstat_kstack(pid, kipp, kflag);
70 	else if (sflag)
71 		procstat_cred(pid, kipp);
72 	else if (tflag)
73 		procstat_threads(pid, kipp);
74 	else if (vflag)
75 		procstat_vm(pid, kipp);
76 	else
77 		procstat_basic(pid, kipp);
78 }
79 
80 /*
81  * Sort processes first by pid and then tid.
82  */
83 static int
84 kinfo_proc_compare(const void *a, const void *b)
85 {
86 	int i;
87 
88 	i = ((const struct kinfo_proc *)a)->ki_pid -
89 	    ((const struct kinfo_proc *)b)->ki_pid;
90 	if (i != 0)
91 		return (i);
92 	i = ((const struct kinfo_proc *)a)->ki_tid -
93 	    ((const struct kinfo_proc *)b)->ki_tid;
94 	return (i);
95 }
96 
97 void
98 kinfo_proc_sort(struct kinfo_proc *kipp, int count)
99 {
100 
101 	qsort(kipp, count, sizeof(*kipp), kinfo_proc_compare);
102 }
103 
104 int
105 main(int argc, char *argv[])
106 {
107 	int ch, interval, name[4], tmp;
108 	unsigned int i;
109 	struct kinfo_proc *kipp;
110 	size_t len;
111 	long l;
112 	pid_t pid;
113 	char *dummy;
114 
115 	interval = 0;
116 	while ((ch = getopt(argc, argv, "abcfijknhstvw:")) != -1) {
117 		switch (ch) {
118 		case 'a':
119 			aflag++;
120 			break;
121 
122 		case 'b':
123 			bflag++;
124 			break;
125 
126 		case 'c':
127 			cflag++;
128 			break;
129 
130 		case 'f':
131 			fflag++;
132 			break;
133 
134 		case 'i':
135 			iflag++;
136 			break;
137 
138 		case 'j':
139 			jflag++;
140 			break;
141 
142 		case 'k':
143 			kflag++;
144 			break;
145 
146 		case 'n':
147 			nflag++;
148 			break;
149 
150 		case 'h':
151 			hflag++;
152 			break;
153 
154 		case 's':
155 			sflag++;
156 			break;
157 
158 		case 't':
159 			tflag++;
160 			break;
161 
162 		case 'v':
163 			vflag++;
164 			break;
165 
166 		case 'w':
167 			l = strtol(optarg, &dummy, 10);
168 			if (*dummy != '\0')
169 				usage();
170 			if (l < 1 || l > INT_MAX)
171 				usage();
172 			interval = l;
173 			break;
174 
175 		case '?':
176 		default:
177 			usage();
178 		}
179 
180 	}
181 	argc -= optind;
182 	argv += optind;
183 
184 	/* We require that either 0 or 1 mode flags be set. */
185 	tmp = bflag + cflag + fflag + (kflag ? 1 : 0) + sflag + tflag + vflag;
186 	if (!(tmp == 0 || tmp == 1))
187 		usage();
188 
189 	/* We allow -k to be specified up to twice, but not more. */
190 	if (kflag > 2)
191 		usage();
192 
193 	/* Must specify either the -a flag or a list of pids. */
194 	if (!(aflag == 1 && argc == 0) && !(aflag == 0 && argc > 0))
195 		usage();
196 
197 	do {
198 		if (aflag) {
199 			name[0] = CTL_KERN;
200 			name[1] = KERN_PROC;
201 			name[2] = KERN_PROC_PROC;
202 
203 			len = 0;
204 			if (sysctl(name, 3, NULL, &len, NULL, 0) < 0)
205 				err(-1, "sysctl: kern.proc.all");
206 
207 			kipp = malloc(len);
208 			if (kipp == NULL)
209 				err(-1, "malloc");
210 
211 			if (sysctl(name, 3, kipp, &len, NULL, 0) < 0) {
212 				free(kipp);
213 				err(-1, "sysctl: kern.proc.all");
214 			}
215 			if (len % sizeof(*kipp) != 0)
216 				err(-1, "kinfo_proc mismatch");
217 			if (kipp->ki_structsize != sizeof(*kipp))
218 				err(-1, "kinfo_proc structure mismatch");
219 			kinfo_proc_sort(kipp, len / sizeof(*kipp));
220 			for (i = 0; i < len / sizeof(*kipp); i++) {
221 				procstat(kipp[i].ki_pid, &kipp[i]);
222 
223 				/* Suppress header after first process. */
224 				hflag = 1;
225 			}
226 			free(kipp);
227 		}
228 		for (i = 0; i < (unsigned int)argc; i++) {
229 			l = strtol(argv[i], &dummy, 10);
230 			if (*dummy != '\0')
231 				usage();
232 			if (l < 0)
233 				usage();
234 			pid = l;
235 
236 			name[0] = CTL_KERN;
237 			name[1] = KERN_PROC;
238 			name[2] = KERN_PROC_PID;
239 			name[3] = pid;
240 
241 			len = 0;
242 			if (sysctl(name, 4, NULL, &len, NULL, 0) < 0)
243 				err(-1, "sysctl: kern.proc.pid: %d", pid);
244 
245 			kipp = malloc(len);
246 			if (kipp == NULL)
247 				err(-1, "malloc");
248 
249 			if (sysctl(name, 4, kipp, &len, NULL, 0) < 0) {
250 				free(kipp);
251 				err(-1, "sysctl: kern.proc.pid: %d", pid);
252 			}
253 			if (len != sizeof(*kipp))
254 				err(-1, "kinfo_proc mismatch");
255 			if (kipp->ki_structsize != sizeof(*kipp))
256 				errx(-1, "kinfo_proc structure mismatch");
257 			if (kipp->ki_pid != pid)
258 				errx(-1, "kinfo_proc pid mismatch");
259 			procstat(pid, kipp);
260 			free(kipp);
261 
262 			/* Suppress header after first process. */
263 			hflag = 1;
264 		}
265 		if (interval)
266 			sleep(interval);
267 	} while (interval);
268 	exit(0);
269 }
270