xref: /freebsd/usr.bin/procstat/procstat.c (revision 6ff6d951ade3f3379932df7f878ef3ea272cfc59)
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/types.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, kflag, sflag, tflag, vflag;
42 int	hflag;
43 
44 static void
45 usage(void)
46 {
47 
48 	fprintf(stderr, "usage: procstat [-h] [-w interval] [-b | -c | -f | "
49 	    "-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 (kflag)
65 		procstat_kstack(pid, kipp, kflag);
66 	else if (sflag)
67 		procstat_cred(pid, kipp);
68 	else if (tflag)
69 		procstat_threads(pid, kipp);
70 	else if (vflag)
71 		procstat_vm(pid, kipp);
72 	else
73 		procstat_basic(pid, kipp);
74 }
75 
76 /*
77  * Sort processes first by pid and then tid.
78  */
79 static int
80 kinfo_proc_compare(const void *a, const void *b)
81 {
82 	int i;
83 
84 	i = ((const struct kinfo_proc *)a)->ki_pid -
85 	    ((const struct kinfo_proc *)b)->ki_pid;
86 	if (i != 0)
87 		return (i);
88 	i = ((const struct kinfo_proc *)a)->ki_tid -
89 	    ((const struct kinfo_proc *)b)->ki_tid;
90 	return (i);
91 }
92 
93 void
94 kinfo_proc_sort(struct kinfo_proc *kipp, int count)
95 {
96 
97 	qsort(kipp, count, sizeof(*kipp), kinfo_proc_compare);
98 }
99 
100 int
101 main(int argc, char *argv[])
102 {
103 	int ch, interval, name[4], tmp;
104 	unsigned int i;
105 	struct kinfo_proc *kipp;
106 	size_t len;
107 	long l;
108 	pid_t pid;
109 	char *dummy;
110 
111 	interval = 0;
112 	while ((ch = getopt(argc, argv, "abcfkhstvw:")) != -1) {
113 		switch (ch) {
114 		case 'a':
115 			aflag++;
116 			break;
117 
118 		case 'b':
119 			bflag++;
120 			break;
121 
122 		case 'c':
123 			cflag++;
124 			break;
125 
126 		case 'f':
127 			fflag++;
128 			break;
129 
130 		case 'k':
131 			kflag++;
132 			break;
133 
134 		case 'h':
135 			hflag++;
136 			break;
137 
138 		case 's':
139 			sflag++;
140 			break;
141 
142 		case 't':
143 			tflag++;
144 			break;
145 
146 		case 'v':
147 			vflag++;
148 			break;
149 
150 		case 'w':
151 			l = strtol(optarg, &dummy, 10);
152 			if (*dummy != '\0')
153 				usage();
154 			if (l < 1 || l > INT_MAX)
155 				usage();
156 			interval = l;
157 			break;
158 
159 		case '?':
160 		default:
161 			usage();
162 		}
163 
164 	}
165 	argc -= optind;
166 	argv += optind;
167 
168 	/* We require that either 0 or 1 mode flags be set. */
169 	tmp = bflag + cflag + fflag + (kflag ? 1 : 0) + sflag + tflag + vflag;
170 	if (!(tmp == 0 || tmp == 1))
171 		usage();
172 
173 	/* We allow -k to be specified up to twice, but not more. */
174 	if (kflag > 2)
175 		usage();
176 
177 	/* Must specify either the -a flag or a list of pids. */
178 	if (!(aflag == 1 && argc == 0) && !(aflag == 0 && argc > 0))
179 		usage();
180 
181 	do {
182 		if (aflag) {
183 			name[0] = CTL_KERN;
184 			name[1] = KERN_PROC;
185 			name[2] = KERN_PROC_PROC;
186 
187 			len = 0;
188 			if (sysctl(name, 3, NULL, &len, NULL, 0) < 0)
189 				err(-1, "sysctl: kern.proc.all");
190 
191 			kipp = malloc(len);
192 			if (kipp == NULL)
193 				err(-1, "malloc");
194 
195 			if (sysctl(name, 3, kipp, &len, NULL, 0) < 0) {
196 				free(kipp);
197 				err(-1, "sysctl: kern.proc.all");
198 			}
199 			if (len % sizeof(*kipp) != 0)
200 				err(-1, "kinfo_proc mismatch");
201 			if (kipp->ki_structsize != sizeof(*kipp))
202 				err(-1, "kinfo_proc structure mismatch");
203 			kinfo_proc_sort(kipp, len / sizeof(*kipp));
204 			for (i = 0; i < len / sizeof(*kipp); i++) {
205 				procstat(kipp[i].ki_pid, &kipp[i]);
206 
207 				/* Suppress header after first process. */
208 				hflag = 1;
209 			}
210 			free(kipp);
211 		}
212 		for (i = 0; i < (unsigned int)argc; i++) {
213 			l = strtol(argv[i], &dummy, 10);
214 			if (*dummy != '\0')
215 				usage();
216 			if (l < 0)
217 				usage();
218 			pid = l;
219 
220 			name[0] = CTL_KERN;
221 			name[1] = KERN_PROC;
222 			name[2] = KERN_PROC_PID;
223 			name[3] = pid;
224 
225 			len = 0;
226 			if (sysctl(name, 4, NULL, &len, NULL, 0) < 0)
227 				err(-1, "sysctl: kern.proc.pid: %d", pid);
228 
229 			kipp = malloc(len);
230 			if (kipp == NULL)
231 				err(-1, "malloc");
232 
233 			if (sysctl(name, 4, kipp, &len, NULL, 0) < 0) {
234 				free(kipp);
235 				err(-1, "sysctl: kern.proc.pid: %d", pid);
236 			}
237 			if (len != sizeof(*kipp))
238 				err(-1, "kinfo_proc mismatch");
239 			if (kipp->ki_structsize != sizeof(*kipp))
240 				errx(-1, "kinfo_proc structure mismatch");
241 			if (kipp->ki_pid != pid)
242 				errx(-1, "kinfo_proc pid mismatch");
243 			procstat(pid, kipp);
244 			free(kipp);
245 
246 			/* Suppress header after first process. */
247 			hflag = 1;
248 		}
249 		if (interval)
250 			sleep(interval);
251 	} while (interval);
252 	exit(0);
253 }
254