The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_history.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 0946f360b7ff762b3a3e37842714f8b9


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.