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