xref: /netbsd-src/sys/kern/kern_history.c (revision 479d8f7d843cc1b22d497efdf1f27a50ee8418d4)
1 /*	$NetBSD: kern_history.c,v 1.11 2017/01/05 03:40:33 pgoyette Exp $	 */
2 
3 /*
4  * Copyright (c) 1997 Charles D. Cranor and Washington University.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  * from: NetBSD: uvm_stat.c,v 1.36 2011/02/02 15:13:34 chuck Exp
28  * from: Id: uvm_stat.c,v 1.1.2.3 1997/12/19 15:01:00 mrg Exp
29  */
30 
31 /*
32  * subr_kernhist.c
33  */
34 
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: kern_history.c,v 1.11 2017/01/05 03:40:33 pgoyette Exp $");
37 
38 #include "opt_ddb.h"
39 #include "opt_kernhist.h"
40 #include "opt_syscall_debug.h"
41 #include "opt_usb.h"
42 #include "opt_uvmhist.h"
43 #include "opt_biohist.h"
44 #include "opt_sysctl.h"
45 
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/cpu.h>
49 #include <sys/sysctl.h>
50 #include <sys/kernhist.h>
51 #include <sys/kmem.h>
52 
53 #ifdef UVMHIST
54 #include <uvm/uvm.h>
55 #endif
56 
57 #ifdef USB_DEBUG
58 #include <dev/usb/usbhist.h>
59 #endif
60 
61 #ifdef BIOHIST
62 #include <sys/biohist.h>
63 #endif
64 
65 #ifdef SYSCALL_DEBUG
66 KERNHIST_DECL(scdebughist);
67 #endif
68 
69 struct addr_xlt {
70 	const char *addr;
71 	size_t len;
72 	uint32_t offset;
73 };
74 
75 /*
76  * globals
77  */
78 
79 struct kern_history_head kern_histories;
80 
81 int kernhist_print_enabled = 1;
82 
83 int sysctl_hist_node;
84 
85 #ifdef DDB
86 
87 /*
88  * prototypes
89  */
90 
91 void kernhist_dump(struct kern_history *,
92     void (*)(const char *, ...) __printflike(1, 2));
93 void kernhist_dumpmask(uint32_t);
94 static void kernhist_dump_histories(struct kern_history *[],
95     void (*)(const char *, ...) __printflike(1, 2));
96 
97 static int sysctl_kernhist_helper(SYSCTLFN_PROTO);
98 
99 /*
100  * call this from ddb
101  *
102  * expects the system to be quiesced, no locking
103  */
104 void
105 kernhist_dump(struct kern_history *l, void (*pr)(const char *, ...))
106 {
107 	int lcv;
108 
109 	lcv = l->f;
110 	do {
111 		if (l->e[lcv].fmt)
112 			kernhist_entry_print(&l->e[lcv], pr);
113 		lcv = (lcv + 1) % l->n;
114 	} while (lcv != l->f);
115 }
116 
117 /*
118  * print a merged list of kern_history structures
119  */
120 static void
121 kernhist_dump_histories(struct kern_history *hists[], void (*pr)(const char *, ...))
122 {
123 	struct bintime	bt;
124 	int	cur[MAXHISTS];
125 	int	lcv, hi;
126 
127 	/* find the first of each list */
128 	for (lcv = 0; hists[lcv]; lcv++)
129 		 cur[lcv] = hists[lcv]->f;
130 
131 	/*
132 	 * here we loop "forever", finding the next earliest
133 	 * history entry and printing it.  cur[X] is the current
134 	 * entry to test for the history in hists[X].  if it is
135 	 * -1, then this history is finished.
136 	 */
137 	for (;;) {
138 		hi = -1;
139 		bt.sec = 0; bt.frac = 0;
140 
141 		/* loop over each history */
142 		for (lcv = 0; hists[lcv]; lcv++) {
143 restart:
144 			if (cur[lcv] == -1)
145 				continue;
146 			if (!hists[lcv]->e)
147 				continue;
148 
149 			/*
150 			 * if the format is empty, go to the next entry
151 			 * and retry.
152 			 */
153 			if (hists[lcv]->e[cur[lcv]].fmt == NULL) {
154 				cur[lcv] = (cur[lcv] + 1) % (hists[lcv]->n);
155 				if (cur[lcv] == hists[lcv]->f)
156 					cur[lcv] = -1;
157 				goto restart;
158 			}
159 
160 			/*
161 			 * if the time hasn't been set yet, or this entry is
162 			 * earlier than the current bt, set the time and history
163 			 * index.
164 			 */
165 			if (bt.sec == 0 ||
166 			    bintimecmp(&hists[lcv]->e[cur[lcv]].bt, &bt, <)) {
167 				bt = hists[lcv]->e[cur[lcv]].bt;
168 				hi = lcv;
169 			}
170 		}
171 
172 		/* if we didn't find any entries, we must be done */
173 		if (hi == -1)
174 			break;
175 
176 		/* print and move to the next entry */
177 		kernhist_entry_print(&hists[hi]->e[cur[hi]], pr);
178 		cur[hi] = (cur[hi] + 1) % (hists[hi]->n);
179 		if (cur[hi] == hists[hi]->f)
180 			cur[hi] = -1;
181 	}
182 }
183 
184 /*
185  * call this from ddb.  `bitmask' is from <sys/kernhist.h>.  it
186  * merges the named histories.
187  *
188  * expects the system to be quiesced, no locking
189  */
190 void
191 kernhist_dumpmask(uint32_t bitmask)	/* XXX only support 32 hists */
192 {
193 	struct kern_history *hists[MAXHISTS + 1];
194 	int i = 0;
195 
196 #ifdef UVMHIST
197 	if ((bitmask & KERNHIST_UVMMAPHIST) || bitmask == 0)
198 		hists[i++] = &maphist;
199 
200 	if ((bitmask & KERNHIST_UVMPDHIST) || bitmask == 0)
201 		hists[i++] = &pdhist;
202 
203 	if ((bitmask & KERNHIST_UVMUBCHIST) || bitmask == 0)
204 		hists[i++] = &ubchist;
205 
206 	if ((bitmask & KERNHIST_UVMLOANHIST) || bitmask == 0)
207 		hists[i++] = &loanhist;
208 #endif
209 
210 #ifdef USB_DEBUG
211 	if ((bitmask & KERNHIST_USBHIST) || bitmask == 0)
212 		hists[i++] = &usbhist;
213 #endif
214 
215 #ifdef SYSCALL_DEBUG
216 	if ((bitmask & KERNHIST_SCDEBUGHIST) || bitmask == 0)
217 		hists[i++] = &scdebughist;
218 #endif
219 
220 #ifdef BIOHIST
221 	if ((bitmask & KERNHIST_BIOHIST) || bitmask == 0)
222 		hists[i++] = &biohist;
223 #endif
224 
225 	hists[i] = NULL;
226 
227 	kernhist_dump_histories(hists, printf);
228 }
229 
230 /*
231  * kernhist_print: ddb hook to print kern history
232  */
233 void
234 kernhist_print(void *addr, void (*pr)(const char *, ...) __printflike(1,2))
235 {
236 	struct kern_history *h;
237 
238 	LIST_FOREACH(h, &kern_histories, list) {
239 		if (h == addr)
240 			break;
241 	}
242 
243 	if (h == NULL) {
244 		struct kern_history *hists[MAXHISTS + 1];
245 		int i = 0;
246 #ifdef UVMHIST
247 		hists[i++] = &maphist;
248 		hists[i++] = &pdhist;
249 		hists[i++] = &ubchist;
250 		hists[i++] = &loanhist;
251 #endif
252 #ifdef USB_DEBUG
253 		hists[i++] = &usbhist;
254 #endif
255 
256 #ifdef SYSCALL_DEBUG
257 		hists[i++] = &scdebughist;
258 #endif
259 #ifdef BIOHIST
260 		hists[i++] = &biohist;
261 #endif
262 		hists[i] = NULL;
263 
264 		kernhist_dump_histories(hists, pr);
265 	} else {
266 		kernhist_dump(h, pr);
267 	}
268 }
269 
270 #endif
271 
272 /*
273  * sysctl interface
274  */
275 
276 /*
277  * sysctl_hist_new()
278  *
279  *	Scan the list of histories;  for any history that does not already
280  *	have a sysctl node (under kern.hist) we create a new one and record
281  *	it's node number.
282  */
283 static void
284 sysctl_hist_new(void)
285 {
286 	int error;
287 	struct kern_history *h;
288 	const struct sysctlnode *rnode = NULL;
289 
290 	LIST_FOREACH(h, &kern_histories, list) {
291 		if (h->s != 0)
292 			continue;
293 		error = sysctl_createv(NULL, 0, NULL, &rnode,
294 			    CTLFLAG_PERMANENT,
295 			    CTLTYPE_STRUCT, h->name,
296 			    SYSCTL_DESCR("history data"),
297 			    sysctl_kernhist_helper, 0, NULL, 0,
298 			    CTL_KERN, sysctl_hist_node, CTL_CREATE, CTL_EOL);
299 		if (error == 0)
300 			h->s = rnode->sysctl_num;
301 	}
302 }
303 
304 /*
305  * sysctl_kerhnist_init()
306  *
307  *	Create the 2nd level "hw.hist" sysctl node
308  */
309 void
310 sysctl_kernhist_init(void)
311 {
312 	const struct sysctlnode *rnode = NULL;
313 
314 	sysctl_createv(NULL, 0, NULL, &rnode,
315 			CTLFLAG_PERMANENT,
316 			CTLTYPE_NODE, "hist",
317 			SYSCTL_DESCR("kernel history tables"),
318 			sysctl_kernhist_helper, 0, NULL, 0,
319 			CTL_KERN, CTL_CREATE, CTL_EOL);
320 	sysctl_hist_node = rnode->sysctl_num;
321 
322 	sysctl_hist_new();
323 }
324 
325 /*
326  * find_string()
327  *
328  *	Search the address-to-offset translation table for matching an
329  *	address and len, and return the index of the entry we found.  If
330  *	not found, returns index 0 which points to the "?" entry.  (We
331  *	start matching at index 1, ignoring any matches of the "?" entry
332  *	itself.)
333  */
334 static int
335 find_string(struct addr_xlt table[], size_t *count, const char *string,
336 	    size_t len)
337 {
338 	int i;
339 
340 	for (i = 1; i < *count; i++)
341 		if (string == table[i].addr && len == table[i].len)
342 			return i;
343 
344 	return 0;
345 }
346 
347 /*
348  * add_string()
349  *
350  *	If the string and len are unique, add a new address-to-offset
351  *	entry in the translation table and set the offset of the next
352  *	entry.
353  */
354 static void
355 add_string(struct addr_xlt table[], size_t *count, const char *string,
356 	   size_t len)
357 {
358 
359 	if (find_string(table, count, string, len) == 0) {
360 		table[*count].addr = string;
361 		table[*count].len = len;
362 		table[*count + 1].offset = table[*count].offset + len + 1;
363 		(*count)++;
364 	}
365 }
366 
367 /*
368  * sysctl_kernhist_helper
369  *
370  *	This helper routine is called for all accesses to the kern.hist
371  *	hierarchy.
372  */
373 static int
374 sysctl_kernhist_helper(SYSCTLFN_ARGS)
375 {
376 	struct kern_history *h;
377 	struct kern_history_ent *in_evt;
378 	struct sysctl_history_event *out_evt;
379 	struct sysctl_history *buf;
380 	struct addr_xlt *xlate_t, *xlt;
381 	size_t bufsize, xlate_s;
382 	size_t xlate_c;
383 	const char *strp;
384 	char *next;
385 	int i, j;
386 	int error;
387 
388 	sysctl_hist_new();	/* make sure we're up to date */
389 
390 	if (namelen == 1 && name[0] == CTL_QUERY)
391 		return sysctl_query(SYSCTLFN_CALL(rnode));
392 
393 	/*
394 	 * Disallow userland updates, verify that we arrived at a
395 	 * valid history rnode
396 	 */
397 	if (newp)
398 		return EPERM;
399 	if (namelen != 1 || name[0] != CTL_EOL)
400 		return EINVAL;
401 
402 	/* Find the correct kernhist for this sysctl node */
403 	LIST_FOREACH(h, &kern_histories, list) {
404 		if (h->s == rnode->sysctl_num)
405 			break;
406 	}
407 	if (h == NULL)
408 		return ENOENT;
409 
410 	/*
411 	 * Worst case is two string pointers per history entry, plus
412 	 * two for the history name and "?" string; allocate an extra
413 	 * entry since we pre-set the "next" entry's offset member.
414 	 */
415 	xlate_s = sizeof(struct addr_xlt) * h->n * 2 + 3;
416 	xlate_t = kmem_alloc(xlate_s, KM_SLEEP);
417 	xlate_c = 0;
418 
419 	/* offset 0 reserved for NULL pointer, ie unused history entry */
420 	xlate_t[0].offset = 1;
421 
422 	/*
423 	 * If the history gets updated and an unexpected string is
424 	 * found later, we'll point it here.  Otherwise, we'd have to
425 	 * repeat this process iteratively, and it could take multiple
426 	 * iterations before terminating.
427 	 */
428 	add_string(xlate_t, &xlate_c, "?", 0);
429 
430 	/* Copy the history name itself to the export structure */
431 	add_string(xlate_t, &xlate_c, h->name, h->namelen);
432 
433 	/*
434 	 * Loop through all used history entries to find the unique
435 	 * fn and fmt strings
436 	 */
437 	for (i = 0, in_evt = h->e; i < h->n; i++, in_evt++) {
438 		if (in_evt->fn == NULL)
439 			continue;
440 		add_string(xlate_t, &xlate_c, in_evt->fn, in_evt->fnlen);
441 		add_string(xlate_t, &xlate_c, in_evt->fmt, in_evt->fmtlen);
442 	}
443 
444 	/* Total buffer size includes header, events, and string table */
445 	bufsize = sizeof(struct sysctl_history) +
446 	    h->n * sizeof(struct sysctl_history_event) +
447 	    xlate_t[xlate_c].offset;
448 	buf = kmem_alloc(bufsize, KM_SLEEP);
449 
450 	/*
451 	 * Copy history header info to the export structure
452 	 */
453 	j = find_string(xlate_t, &xlate_c, h->name, h->namelen);
454 	buf->sh_listentry.shle_nameoffset = xlate_t[j].offset;
455 	buf->sh_listentry.shle_numentries = h->n;
456 	buf->sh_listentry.shle_nextfree = h->f;
457 
458 	/*
459 	 * Loop through the history events again, copying the data to
460 	 * the export structure
461 	 */
462 	for (i = 0, in_evt = h->e, out_evt = buf->sh_events; i < h->n;
463 	    i++, in_evt++, out_evt++) {
464 		if (in_evt->fn == NULL) {	/* skip unused entries */
465 			out_evt->she_funcoffset = 0;
466 			out_evt->she_fmtoffset = 0;
467 			continue;
468 		}
469 		out_evt->she_bintime = in_evt->bt;
470 		out_evt->she_callnumber = in_evt->call;
471 		out_evt->she_cpunum = in_evt->cpunum;
472 		out_evt->she_values[0] = in_evt->v[0];
473 		out_evt->she_values[1] = in_evt->v[1];
474 		out_evt->she_values[2] = in_evt->v[2];
475 		out_evt->she_values[3] = in_evt->v[3];
476 		j = find_string(xlate_t, &xlate_c, in_evt->fn, in_evt->fnlen);
477 		out_evt->she_funcoffset = xlate_t[j].offset;
478 		j = find_string(xlate_t, &xlate_c, in_evt->fmt, in_evt->fmtlen);
479 		out_evt->she_fmtoffset = xlate_t[j].offset;
480 	}
481 
482 	/*
483 	 * Finally, fill the text string area with all the unique
484 	 * strings we found earlier.
485 	 *
486 	 * Skip the initial byte, since we use an offset of 0 to mean
487 	 * a NULL pointer (which means an unused history event).
488 	 */
489 	strp = next = (char *)(&buf->sh_events[h->n]);
490 	*next++ = '\0';
491 
492 	/*
493 	 * Then copy each string into the export structure, making
494 	 * sure to terminate each string with a '\0' character
495 	 */
496 	for (i = 0, xlt = xlate_t; i < xlate_c; i++, xlt++) {
497 		KASSERTMSG((next - strp) == xlt->offset,
498 		    "entry %d at wrong offset %"PRIu32, i, xlt->offset);
499 		memcpy(next, xlt->addr, xlt->len);
500 		next += xlt->len;
501 		*next++ = '\0';
502 	}
503 
504 	/* Copy data to userland */
505 	error = copyout(buf, oldp, min(bufsize, *oldlenp));
506 
507 	/* If copyout was successful but only partial, report ENOMEM */
508 	if (error == 0 && *oldlenp < bufsize)
509 		error = ENOMEM;
510 
511 	*oldlenp = bufsize;	/* inform userland of space requirements */
512 
513 	/* Free up the stuff we allocated */
514 	kmem_free(buf, bufsize);
515 	kmem_free(xlate_t, xlate_s);
516 
517 	return error;
518 }
519