kern_history.c revision 1.12 1 /* $NetBSD: kern_history.c,v 1.12 2017/01/08 19:49:25 christos 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.12 2017/01/08 19:49:25 christos 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 static int sysctl_kernhist_helper(SYSCTLFN_PROTO);
86
87 #ifdef DDB
88
89 /*
90 * prototypes
91 */
92
93 void kernhist_dump(struct kern_history *,
94 void (*)(const char *, ...) __printflike(1, 2));
95 void kernhist_dumpmask(uint32_t);
96 static void kernhist_dump_histories(struct kern_history *[],
97 void (*)(const char *, ...) __printflike(1, 2));
98
99
100 /*
101 * call this from ddb
102 *
103 * expects the system to be quiesced, no locking
104 */
105 void
106 kernhist_dump(struct kern_history *l, void (*pr)(const char *, ...))
107 {
108 int lcv;
109
110 lcv = l->f;
111 do {
112 if (l->e[lcv].fmt)
113 kernhist_entry_print(&l->e[lcv], pr);
114 lcv = (lcv + 1) % l->n;
115 } while (lcv != l->f);
116 }
117
118 /*
119 * print a merged list of kern_history structures
120 */
121 static void
122 kernhist_dump_histories(struct kern_history *hists[], void (*pr)(const char *, ...))
123 {
124 struct bintime bt;
125 int cur[MAXHISTS];
126 int lcv, hi;
127
128 /* find the first of each list */
129 for (lcv = 0; hists[lcv]; lcv++)
130 cur[lcv] = hists[lcv]->f;
131
132 /*
133 * here we loop "forever", finding the next earliest
134 * history entry and printing it. cur[X] is the current
135 * entry to test for the history in hists[X]. if it is
136 * -1, then this history is finished.
137 */
138 for (;;) {
139 hi = -1;
140 bt.sec = 0; bt.frac = 0;
141
142 /* loop over each history */
143 for (lcv = 0; hists[lcv]; lcv++) {
144 restart:
145 if (cur[lcv] == -1)
146 continue;
147 if (!hists[lcv]->e)
148 continue;
149
150 /*
151 * if the format is empty, go to the next entry
152 * and retry.
153 */
154 if (hists[lcv]->e[cur[lcv]].fmt == NULL) {
155 cur[lcv] = (cur[lcv] + 1) % (hists[lcv]->n);
156 if (cur[lcv] == hists[lcv]->f)
157 cur[lcv] = -1;
158 goto restart;
159 }
160
161 /*
162 * if the time hasn't been set yet, or this entry is
163 * earlier than the current bt, set the time and history
164 * index.
165 */
166 if (bt.sec == 0 ||
167 bintimecmp(&hists[lcv]->e[cur[lcv]].bt, &bt, <)) {
168 bt = hists[lcv]->e[cur[lcv]].bt;
169 hi = lcv;
170 }
171 }
172
173 /* if we didn't find any entries, we must be done */
174 if (hi == -1)
175 break;
176
177 /* print and move to the next entry */
178 kernhist_entry_print(&hists[hi]->e[cur[hi]], pr);
179 cur[hi] = (cur[hi] + 1) % (hists[hi]->n);
180 if (cur[hi] == hists[hi]->f)
181 cur[hi] = -1;
182 }
183 }
184
185 /*
186 * call this from ddb. `bitmask' is from <sys/kernhist.h>. it
187 * merges the named histories.
188 *
189 * expects the system to be quiesced, no locking
190 */
191 void
192 kernhist_dumpmask(uint32_t bitmask) /* XXX only support 32 hists */
193 {
194 struct kern_history *hists[MAXHISTS + 1];
195 int i = 0;
196
197 #ifdef UVMHIST
198 if ((bitmask & KERNHIST_UVMMAPHIST) || bitmask == 0)
199 hists[i++] = &maphist;
200
201 if ((bitmask & KERNHIST_UVMPDHIST) || bitmask == 0)
202 hists[i++] = &pdhist;
203
204 if ((bitmask & KERNHIST_UVMUBCHIST) || bitmask == 0)
205 hists[i++] = &ubchist;
206
207 if ((bitmask & KERNHIST_UVMLOANHIST) || bitmask == 0)
208 hists[i++] = &loanhist;
209 #endif
210
211 #ifdef USB_DEBUG
212 if ((bitmask & KERNHIST_USBHIST) || bitmask == 0)
213 hists[i++] = &usbhist;
214 #endif
215
216 #ifdef SYSCALL_DEBUG
217 if ((bitmask & KERNHIST_SCDEBUGHIST) || bitmask == 0)
218 hists[i++] = &scdebughist;
219 #endif
220
221 #ifdef BIOHIST
222 if ((bitmask & KERNHIST_BIOHIST) || bitmask == 0)
223 hists[i++] = &biohist;
224 #endif
225
226 hists[i] = NULL;
227
228 kernhist_dump_histories(hists, printf);
229 }
230
231 /*
232 * kernhist_print: ddb hook to print kern history
233 */
234 void
235 kernhist_print(void *addr, void (*pr)(const char *, ...) __printflike(1,2))
236 {
237 struct kern_history *h;
238
239 LIST_FOREACH(h, &kern_histories, list) {
240 if (h == addr)
241 break;
242 }
243
244 if (h == NULL) {
245 struct kern_history *hists[MAXHISTS + 1];
246 int i = 0;
247 #ifdef UVMHIST
248 hists[i++] = &maphist;
249 hists[i++] = &pdhist;
250 hists[i++] = &ubchist;
251 hists[i++] = &loanhist;
252 #endif
253 #ifdef USB_DEBUG
254 hists[i++] = &usbhist;
255 #endif
256
257 #ifdef SYSCALL_DEBUG
258 hists[i++] = &scdebughist;
259 #endif
260 #ifdef BIOHIST
261 hists[i++] = &biohist;
262 #endif
263 hists[i] = NULL;
264
265 kernhist_dump_histories(hists, pr);
266 } else {
267 kernhist_dump(h, pr);
268 }
269 }
270
271 #endif
272
273 /*
274 * sysctl interface
275 */
276
277 /*
278 * sysctl_hist_new()
279 *
280 * Scan the list of histories; for any history that does not already
281 * have a sysctl node (under kern.hist) we create a new one and record
282 * it's node number.
283 */
284 static void
285 sysctl_hist_new(void)
286 {
287 int error;
288 struct kern_history *h;
289 const struct sysctlnode *rnode = NULL;
290
291 LIST_FOREACH(h, &kern_histories, list) {
292 if (h->s != 0)
293 continue;
294 error = sysctl_createv(NULL, 0, NULL, &rnode,
295 CTLFLAG_PERMANENT,
296 CTLTYPE_STRUCT, h->name,
297 SYSCTL_DESCR("history data"),
298 sysctl_kernhist_helper, 0, NULL, 0,
299 CTL_KERN, sysctl_hist_node, CTL_CREATE, CTL_EOL);
300 if (error == 0)
301 h->s = rnode->sysctl_num;
302 }
303 }
304
305 /*
306 * sysctl_kerhnist_init()
307 *
308 * Create the 2nd level "hw.hist" sysctl node
309 */
310 void
311 sysctl_kernhist_init(void)
312 {
313 const struct sysctlnode *rnode = NULL;
314
315 sysctl_createv(NULL, 0, NULL, &rnode,
316 CTLFLAG_PERMANENT,
317 CTLTYPE_NODE, "hist",
318 SYSCTL_DESCR("kernel history tables"),
319 sysctl_kernhist_helper, 0, NULL, 0,
320 CTL_KERN, CTL_CREATE, CTL_EOL);
321 sysctl_hist_node = rnode->sysctl_num;
322
323 sysctl_hist_new();
324 }
325
326 /*
327 * find_string()
328 *
329 * Search the address-to-offset translation table for matching an
330 * address and len, and return the index of the entry we found. If
331 * not found, returns index 0 which points to the "?" entry. (We
332 * start matching at index 1, ignoring any matches of the "?" entry
333 * itself.)
334 */
335 static int
336 find_string(struct addr_xlt table[], size_t *count, const char *string,
337 size_t len)
338 {
339 int i;
340
341 for (i = 1; i < *count; i++)
342 if (string == table[i].addr && len == table[i].len)
343 return i;
344
345 return 0;
346 }
347
348 /*
349 * add_string()
350 *
351 * If the string and len are unique, add a new address-to-offset
352 * entry in the translation table and set the offset of the next
353 * entry.
354 */
355 static void
356 add_string(struct addr_xlt table[], size_t *count, const char *string,
357 size_t len)
358 {
359
360 if (find_string(table, count, string, len) == 0) {
361 table[*count].addr = string;
362 table[*count].len = len;
363 table[*count + 1].offset = table[*count].offset + len + 1;
364 (*count)++;
365 }
366 }
367
368 /*
369 * sysctl_kernhist_helper
370 *
371 * This helper routine is called for all accesses to the kern.hist
372 * hierarchy.
373 */
374 static int
375 sysctl_kernhist_helper(SYSCTLFN_ARGS)
376 {
377 struct kern_history *h;
378 struct kern_history_ent *in_evt;
379 struct sysctl_history_event *out_evt;
380 struct sysctl_history *buf;
381 struct addr_xlt *xlate_t, *xlt;
382 size_t bufsize, xlate_s;
383 size_t xlate_c;
384 const char *strp __diagused;
385 char *next;
386 int i, j;
387 int error;
388
389 sysctl_hist_new(); /* make sure we're up to date */
390
391 if (namelen == 1 && name[0] == CTL_QUERY)
392 return sysctl_query(SYSCTLFN_CALL(rnode));
393
394 /*
395 * Disallow userland updates, verify that we arrived at a
396 * valid history rnode
397 */
398 if (newp)
399 return EPERM;
400 if (namelen != 1 || name[0] != CTL_EOL)
401 return EINVAL;
402
403 /* Find the correct kernhist for this sysctl node */
404 LIST_FOREACH(h, &kern_histories, list) {
405 if (h->s == rnode->sysctl_num)
406 break;
407 }
408 if (h == NULL)
409 return ENOENT;
410
411 /*
412 * Worst case is two string pointers per history entry, plus
413 * two for the history name and "?" string; allocate an extra
414 * entry since we pre-set the "next" entry's offset member.
415 */
416 xlate_s = sizeof(struct addr_xlt) * h->n * 2 + 3;
417 xlate_t = kmem_alloc(xlate_s, KM_SLEEP);
418 xlate_c = 0;
419
420 /* offset 0 reserved for NULL pointer, ie unused history entry */
421 xlate_t[0].offset = 1;
422
423 /*
424 * If the history gets updated and an unexpected string is
425 * found later, we'll point it here. Otherwise, we'd have to
426 * repeat this process iteratively, and it could take multiple
427 * iterations before terminating.
428 */
429 add_string(xlate_t, &xlate_c, "?", 0);
430
431 /* Copy the history name itself to the export structure */
432 add_string(xlate_t, &xlate_c, h->name, h->namelen);
433
434 /*
435 * Loop through all used history entries to find the unique
436 * fn and fmt strings
437 */
438 for (i = 0, in_evt = h->e; i < h->n; i++, in_evt++) {
439 if (in_evt->fn == NULL)
440 continue;
441 add_string(xlate_t, &xlate_c, in_evt->fn, in_evt->fnlen);
442 add_string(xlate_t, &xlate_c, in_evt->fmt, in_evt->fmtlen);
443 }
444
445 /* Total buffer size includes header, events, and string table */
446 bufsize = sizeof(struct sysctl_history) +
447 h->n * sizeof(struct sysctl_history_event) +
448 xlate_t[xlate_c].offset;
449 buf = kmem_alloc(bufsize, KM_SLEEP);
450
451 /*
452 * Copy history header info to the export structure
453 */
454 j = find_string(xlate_t, &xlate_c, h->name, h->namelen);
455 buf->sh_listentry.shle_nameoffset = xlate_t[j].offset;
456 buf->sh_listentry.shle_numentries = h->n;
457 buf->sh_listentry.shle_nextfree = h->f;
458
459 /*
460 * Loop through the history events again, copying the data to
461 * the export structure
462 */
463 for (i = 0, in_evt = h->e, out_evt = buf->sh_events; i < h->n;
464 i++, in_evt++, out_evt++) {
465 if (in_evt->fn == NULL) { /* skip unused entries */
466 out_evt->she_funcoffset = 0;
467 out_evt->she_fmtoffset = 0;
468 continue;
469 }
470 out_evt->she_bintime = in_evt->bt;
471 out_evt->she_callnumber = in_evt->call;
472 out_evt->she_cpunum = in_evt->cpunum;
473 out_evt->she_values[0] = in_evt->v[0];
474 out_evt->she_values[1] = in_evt->v[1];
475 out_evt->she_values[2] = in_evt->v[2];
476 out_evt->she_values[3] = in_evt->v[3];
477 j = find_string(xlate_t, &xlate_c, in_evt->fn, in_evt->fnlen);
478 out_evt->she_funcoffset = xlate_t[j].offset;
479 j = find_string(xlate_t, &xlate_c, in_evt->fmt, in_evt->fmtlen);
480 out_evt->she_fmtoffset = xlate_t[j].offset;
481 }
482
483 /*
484 * Finally, fill the text string area with all the unique
485 * strings we found earlier.
486 *
487 * Skip the initial byte, since we use an offset of 0 to mean
488 * a NULL pointer (which means an unused history event).
489 */
490 strp = next = (char *)(&buf->sh_events[h->n]);
491 *next++ = '\0';
492
493 /*
494 * Then copy each string into the export structure, making
495 * sure to terminate each string with a '\0' character
496 */
497 for (i = 0, xlt = xlate_t; i < xlate_c; i++, xlt++) {
498 KASSERTMSG((next - strp) == xlt->offset,
499 "entry %d at wrong offset %"PRIu32, i, xlt->offset);
500 memcpy(next, xlt->addr, xlt->len);
501 next += xlt->len;
502 *next++ = '\0';
503 }
504
505 /* Copy data to userland */
506 error = copyout(buf, oldp, min(bufsize, *oldlenp));
507
508 /* If copyout was successful but only partial, report ENOMEM */
509 if (error == 0 && *oldlenp < bufsize)
510 error = ENOMEM;
511
512 *oldlenp = bufsize; /* inform userland of space requirements */
513
514 /* Free up the stuff we allocated */
515 kmem_free(buf, bufsize);
516 kmem_free(xlate_t, xlate_s);
517
518 return error;
519 }
520