]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/ktrdump/ktrdump.c
Add UPDATING entries and bump version.
[FreeBSD/FreeBSD.git] / usr.bin / ktrdump / ktrdump.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2002 Jake Burkholder
5  * Copyright (c) 2004 Robert Watson
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/types.h>
34 #include <sys/ktr.h>
35 #include <sys/mman.h>
36 #include <sys/stat.h>
37
38 #include <err.h>
39 #include <fcntl.h>
40 #include <kvm.h>
41 #include <limits.h>
42 #include <nlist.h>
43 #include <stdint.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48
49 #define SBUFLEN 128
50 #define USAGE \
51         "usage: ktrdump [-cflqrtH] [-i ktrfile] [-M core] [-N system] [-o outfile]\n"
52
53 static void usage(void);
54
55 static struct nlist nl[] = {
56         { "_ktr_version" },
57         { "_ktr_entries" },
58         { "_ktr_idx" },
59         { "_ktr_buf" },
60         { NULL }
61 };
62
63 static int cflag;
64 static int fflag;
65 static int lflag;
66 static int Mflag;
67 static int Nflag;
68 static int qflag;
69 static int rflag;
70 static int tflag;
71 static int iflag;
72 static int hflag;
73
74 static char corefile[PATH_MAX];
75 static char execfile[PATH_MAX];
76
77 static char desc[SBUFLEN];
78 static char errbuf[_POSIX2_LINE_MAX];
79 static char fbuf[PATH_MAX];
80 static char obuf[PATH_MAX];
81 static char sbuf[KTR_PARMS][SBUFLEN];
82
83 /*
84  * Reads the ktr trace buffer from kernel memory and prints the trace entries.
85  */
86 int
87 main(int ac, char **av)
88 {
89         u_long parms[KTR_PARMS];
90         struct ktr_entry *buf;
91         uintmax_t tlast, tnow;
92         unsigned long bufptr;
93         struct stat sb;
94         kvm_t *kd;
95         FILE *out;
96         char *p;
97         int version;
98         int entries;
99         int count;
100         int index, index2;
101         int parm;
102         int in;
103         int c;
104         int i = 0;
105
106         /*
107          * Parse commandline arguments.
108          */
109         out = stdout;
110         while ((c = getopt(ac, av, "cflqrtHe:i:m:M:N:o:")) != -1)
111                 switch (c) {
112                 case 'c':
113                         cflag = 1;
114                         break;
115                 case 'N':
116                 case 'e':
117                         if (strlcpy(execfile, optarg, sizeof(execfile))
118                             >= sizeof(execfile))
119                                 errx(1, "%s: File name too long", optarg);
120                         Nflag = 1;
121                         break;
122                 case 'f':
123                         fflag = 1;
124                         break;
125                 case 'i':
126                         iflag = 1;
127                         if ((in = open(optarg, O_RDONLY)) == -1)
128                                 err(1, "%s", optarg);
129                         break;
130                 case 'l':
131                         lflag = 1;
132                         break;
133                 case 'M':
134                 case 'm':
135                         if (strlcpy(corefile, optarg, sizeof(corefile))
136                             >= sizeof(corefile))
137                                 errx(1, "%s: File name too long", optarg);
138                         Mflag = 1;
139                         break;
140                 case 'o':
141                         if ((out = fopen(optarg, "w")) == NULL)
142                                 err(1, "%s", optarg);
143                         break;
144                 case 'q':
145                         qflag++;
146                         break;
147                 case 'r':
148                         rflag = 1;
149                         break;
150                 case 't':
151                         tflag = 1;
152                         break;
153                 case 'H':
154                         hflag = 1;
155                         break;
156                 case '?':
157                 default:
158                         usage();
159                 }
160         ac -= optind;
161         av += optind;
162         if (ac != 0)
163                 usage();
164
165         /*
166          * Open our execfile and corefile, resolve needed symbols and read in
167          * the trace buffer.
168          */
169         if ((kd = kvm_openfiles(Nflag ? execfile : NULL,
170             Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL)
171                 errx(1, "%s", errbuf);
172         count = kvm_nlist(kd, nl);
173         if (count == -1)
174                 errx(1, "%s", kvm_geterr(kd));
175         if (count > 0)
176                 errx(1, "failed to resolve ktr symbols");
177         if (kvm_read(kd, nl[0].n_value, &version, sizeof(version)) == -1)
178                 errx(1, "%s", kvm_geterr(kd));
179         if (version != KTR_VERSION)
180                 errx(1, "ktr version mismatch");
181         if (iflag) {
182                 if (fstat(in, &sb) == -1)
183                         errx(1, "stat");
184                 entries = sb.st_size / sizeof(*buf);
185                 index = 0;
186                 buf = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, in, 0);
187                 if (buf == MAP_FAILED)
188                         errx(1, "mmap");
189         } else {
190                 if (kvm_read(kd, nl[1].n_value, &entries, sizeof(entries))
191                     == -1)
192                         errx(1, "%s", kvm_geterr(kd));
193                 if ((buf = malloc(sizeof(*buf) * entries)) == NULL)
194                         err(1, NULL);
195                 if (kvm_read(kd, nl[2].n_value, &index, sizeof(index)) == -1 ||
196                     kvm_read(kd, nl[3].n_value, &bufptr,
197                     sizeof(bufptr)) == -1 ||
198                     kvm_read(kd, bufptr, buf, sizeof(*buf) * entries) == -1 ||
199                     kvm_read(kd, nl[2].n_value, &index2, sizeof(index2)) == -1)
200                         errx(1, "%s", kvm_geterr(kd));
201         }
202
203         /*
204          * Print a nice header.
205          */
206         if (!qflag) {
207                 fprintf(out, "%-6s ", "index");
208                 if (cflag)
209                         fprintf(out, "%-3s ", "cpu");
210                 if (tflag)
211                         fprintf(out, "%-16s ", "timestamp");
212                 if (fflag)
213                         fprintf(out, "%-40s ", "file and line");
214                 if (hflag)
215                         fprintf(out, "%-18s ", "tid");
216                 fprintf(out, "%s", "trace");
217                 fprintf(out, "\n");
218
219                 fprintf(out, "------ ");
220                 if (cflag)
221                         fprintf(out, "--- ");
222                 if (tflag)
223                         fprintf(out, "---------------- ");
224                 if (fflag)
225                         fprintf(out,
226                             "---------------------------------------- ");
227                 if (hflag)
228                         fprintf(out, "------------------ ");
229                 fprintf(out, "----- ");
230                 fprintf(out, "\n");
231         }
232
233         tlast = -1;
234         /*
235          * Now tear through the trace buffer.
236          *
237          * In "live" mode, find the oldest entry (first non-NULL entry
238          * after index2) and walk forward.  Otherwise, start with the
239          * most recent entry and walk backwards.
240          */
241         if (!iflag) {
242                 if (lflag) {
243                         i = index2 + 1 % entries;
244                         while (buf[i].ktr_desc == NULL && i != index) {
245                                 i++;
246                                 if (i == entries)
247                                         i = 0;
248                         }
249                 } else {
250                         i = index - 1;
251                         if (i < 0)
252                                 i = entries - 1;
253                 }
254         }
255 dump_entries:
256         for (;;) {
257                 if (buf[i].ktr_desc == NULL)
258                         break;
259                 if (kvm_read(kd, (u_long)buf[i].ktr_desc, desc,
260                     sizeof(desc)) == -1)
261                         errx(1, "%s", kvm_geterr(kd));
262                 desc[sizeof(desc) - 1] = '\0';
263                 parm = 0;
264                 for (p = desc; (c = *p++) != '\0';) {
265                         if (c != '%')
266                                 continue;
267 next:                   if ((c = *p++) == '\0')
268                                 break;
269                         if (parm == KTR_PARMS)
270                                 errx(1, "too many parameters in \"%s\"", desc);
271                         switch (c) {
272                         case '0': case '1': case '2': case '3': case '4':
273                         case '5': case '6': case '7': case '8': case '9':
274                         case '#': case '-': case ' ': case '+': case '\'':
275                         case 'h': case 'l': case 'j': case 't': case 'z':
276                         case 'q': case 'L': case '.':
277                                 goto next;
278                         case 's':
279                                 if (kvm_read(kd, (u_long)buf[i].ktr_parms[parm],
280                                     sbuf[parm], sizeof(sbuf[parm])) == -1)
281                                         strcpy(sbuf[parm], "(null)");
282                                 sbuf[parm][sizeof(sbuf[0]) - 1] = '\0';
283                                 parms[parm] = (u_long)sbuf[parm];
284                                 parm++;
285                                 break;
286                         default:
287                                 parms[parm] = buf[i].ktr_parms[parm];
288                                 parm++;
289                                 break;
290                         }
291                 }
292                 fprintf(out, "%6d ", i);
293                 if (cflag)
294                         fprintf(out, "%3d ", buf[i].ktr_cpu);
295                 if (tflag) {
296                         tnow = (uintmax_t)buf[i].ktr_timestamp;
297                         if (rflag) {
298                                 if (tlast == -1)
299                                         tlast = tnow;
300                                 fprintf(out, "%16ju ", !iflag ? tlast - tnow :
301                                     tnow - tlast);
302                                 tlast = tnow;
303                         } else
304                                 fprintf(out, "%16ju ", tnow);
305                 }
306                 if (fflag) {
307                         if (kvm_read(kd, (u_long)buf[i].ktr_file, fbuf,
308                             sizeof(fbuf)) == -1)
309                                 strcpy(fbuf, "(null)");
310                         snprintf(obuf, sizeof(obuf), "%s:%d", fbuf,
311                             buf[i].ktr_line);
312                         fprintf(out, "%-40s ", obuf);
313                 }
314                 if (hflag)
315                         fprintf(out, "%p ", buf[i].ktr_thread);
316                 fprintf(out, desc, parms[0], parms[1], parms[2], parms[3],
317                     parms[4], parms[5]);
318                 fprintf(out, "\n");
319                 if (!iflag) {
320                         /*
321                          * 'index' and 'index2' are the values of 'ktr_idx'
322                          * before and after the KTR buffer was copied into
323                          * 'buf'. Since the KTR entries between 'index' and
324                          * 'index2' were in flux while the KTR buffer was
325                          * being copied to userspace we don't dump them.
326                          */
327                         if (lflag) {
328                                 if (++i == entries)
329                                         i = 0;
330                                 if (i == index)
331                                         break;
332                         } else {
333                                 if (i == index2)
334                                         break;
335                                 if (--i < 0)
336                                         i = entries - 1;
337                         }
338                 } else {
339                         if (++i == entries)
340                                 break;
341                 }
342         }
343
344         /*
345          * In "live" mode, poll 'ktr_idx' periodically and dump any
346          * new entries since our last pass through the ring.
347          */
348         if (lflag && !iflag) {
349                 while (index == index2) {
350                         usleep(50 * 1000);
351                         if (kvm_read(kd, nl[2].n_value, &index2,
352                             sizeof(index2)) == -1)
353                                 errx(1, "%s", kvm_geterr(kd));
354                 }
355                 i = index;
356                 index = index2;
357                 if (kvm_read(kd, bufptr, buf, sizeof(*buf) * entries) == -1 ||
358                     kvm_read(kd, nl[2].n_value, &index2, sizeof(index2)) == -1)
359                         errx(1, "%s", kvm_geterr(kd));
360                 goto dump_entries;
361         }
362
363         return (0);
364 }
365
366 static void
367 usage(void)
368 {
369
370         fprintf(stderr, USAGE);
371         exit(1);
372 }