kern_history.c revision 1.10 1 /* $NetBSD: kern_history.c,v 1.10 2017/01/04 01:05:58 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.10 2017/01/04 01:05:58 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 timeval tv;
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 tv.tv_sec = tv.tv_usec = 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 tv, set the time and history
163 * index.
164 */
165 if (tv.tv_sec == 0 ||
166 timercmp(&hists[lcv]->e[cur[lcv]].tv, &tv, <)) {
167 tv = hists[lcv]->e[cur[lcv]].tv;
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_time_sec = in_evt->tv.tv_sec;
470 out_evt->she_time_usec = in_evt->tv.tv_usec;
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