LCOV - code coverage report
Current view: top level - dev - kcov.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 100 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 11 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: kcov.c,v 1.4 2018/08/27 15:57:39 anton Exp $  */
       2             : 
       3             : /*
       4             :  * Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
       5             :  *
       6             :  * Permission to use, copy, modify, and distribute this software for any
       7             :  * purpose with or without fee is hereby granted, provided that the above
       8             :  * copyright notice and this permission notice appear in all copies.
       9             :  *
      10             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      11             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      12             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      13             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      14             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      15             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      16             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      17             :  */
      18             : 
      19             : #include <sys/param.h>
      20             : #include <sys/systm.h>
      21             : #include <sys/proc.h>
      22             : #include <sys/kcov.h>
      23             : #include <sys/malloc.h>
      24             : #include <sys/stdint.h>
      25             : #include <sys/queue.h>
      26             : 
      27             : #include <uvm/uvm_extern.h>
      28             : 
      29             : /* #define KCOV_DEBUG */
      30             : #ifdef KCOV_DEBUG
      31             : #define DPRINTF(x...) do { if (kcov_debug) printf(x); } while (0)
      32             : #else
      33             : #define DPRINTF(x...)
      34             : #endif
      35             : 
      36             : struct kcov_dev {
      37             :         enum {
      38             :                 KCOV_MODE_DISABLED,
      39             :                 KCOV_MODE_INIT,
      40             :                 KCOV_MODE_TRACE_PC,
      41             :                 KCOV_MODE_DYING,
      42             :         }                kd_mode;
      43             :         int              kd_unit;       /* device minor */
      44             :         uintptr_t       *kd_buf;        /* traced coverage */
      45             :         size_t           kd_nmemb;
      46             :         size_t           kd_size;
      47             : 
      48             :         TAILQ_ENTRY(kcov_dev)   kd_entry;
      49             : };
      50             : 
      51             : void kcovattach(int);
      52             : 
      53             : int kd_alloc(struct kcov_dev *, unsigned long);
      54             : void kd_free(struct kcov_dev *);
      55             : struct kcov_dev *kd_lookup(int);
      56             : 
      57             : static inline int inintr(void);
      58             : 
      59             : TAILQ_HEAD(, kcov_dev) kd_list = TAILQ_HEAD_INITIALIZER(kd_list);
      60             : 
      61             : #ifdef KCOV_DEBUG
      62             : int kcov_debug = 1;
      63             : #endif
      64             : 
      65             : /*
      66             :  * Compiling the kernel with the `-fsanitize-coverage=trace-pc' option will
      67             :  * cause the following function to be called upon function entry and before
      68             :  * each block instructions that maps to a single line in the original source
      69             :  * code.
      70             :  *
      71             :  * If kcov is enabled for the current thread, the kernel program counter will
      72             :  * be stored in its corresponding coverage buffer.
      73             :  * The first element in the coverage buffer holds the index of next available
      74             :  * element.
      75             :  */
      76             : void
      77           0 : __sanitizer_cov_trace_pc(void)
      78             : {
      79             :         extern int cold;
      80             :         struct kcov_dev *kd;
      81             :         uint64_t idx;
      82             : 
      83             :         /* Do not trace during boot. */
      84           0 :         if (cold)
      85           0 :                 return;
      86             : 
      87             :         /* Do not trace in interrupts to prevent noisy coverage. */
      88           0 :         if (inintr())
      89           0 :                 return;
      90             : 
      91           0 :         kd = curproc->p_kd;
      92           0 :         if (kd == NULL || kd->kd_mode != KCOV_MODE_TRACE_PC)
      93           0 :                 return;
      94             : 
      95           0 :         idx = kd->kd_buf[0];
      96           0 :         if (idx < kd->kd_nmemb) {
      97           0 :                 kd->kd_buf[idx + 1] = (uintptr_t)__builtin_return_address(0);
      98           0 :                 kd->kd_buf[0] = idx + 1;
      99           0 :         }
     100           0 : }
     101             : 
     102             : void
     103           0 : kcovattach(int count)
     104             : {
     105           0 : }
     106             : 
     107             : int
     108           0 : kcovopen(dev_t dev, int flag, int mode, struct proc *p)
     109             : {
     110             :         struct kcov_dev *kd;
     111             : 
     112           0 :         if (kd_lookup(minor(dev)) != NULL)
     113           0 :                 return (EBUSY);
     114             : 
     115             :         DPRINTF("%s: unit=%d\n", __func__, minor(dev));
     116             : 
     117           0 :         kd = malloc(sizeof(*kd), M_SUBPROC, M_WAITOK | M_ZERO);
     118           0 :         kd->kd_unit = minor(dev);
     119           0 :         TAILQ_INSERT_TAIL(&kd_list, kd, kd_entry);
     120           0 :         return (0);
     121           0 : }
     122             : 
     123             : int
     124           0 : kcovclose(dev_t dev, int flag, int mode, struct proc *p)
     125             : {
     126             :         struct kcov_dev *kd;
     127             : 
     128           0 :         kd = kd_lookup(minor(dev));
     129           0 :         if (kd == NULL)
     130           0 :                 return (EINVAL);
     131             : 
     132             :         DPRINTF("%s: unit=%d\n", __func__, minor(dev));
     133             : 
     134           0 :         if (kd->kd_mode == KCOV_MODE_TRACE_PC)
     135           0 :                 kd->kd_mode = KCOV_MODE_DYING;
     136             :         else
     137           0 :                 kd_free(kd);
     138             : 
     139           0 :         return (0);
     140           0 : }
     141             : 
     142             : int
     143           0 : kcovioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
     144             : {
     145             :         struct kcov_dev *kd;
     146             :         int error = 0;
     147             : 
     148           0 :         kd = kd_lookup(minor(dev));
     149           0 :         if (kd == NULL)
     150           0 :                 return (ENXIO);
     151             : 
     152           0 :         switch (cmd) {
     153             :         case KIOSETBUFSIZE:
     154           0 :                 if (kd->kd_mode != KCOV_MODE_DISABLED) {
     155             :                         error = EBUSY;
     156           0 :                         break;
     157             :                 }
     158           0 :                 error = kd_alloc(kd, *((unsigned long *)data));
     159           0 :                 if (error == 0)
     160           0 :                         kd->kd_mode = KCOV_MODE_INIT;
     161             :                 break;
     162             :         case KIOENABLE:
     163             :                 /* Only one kcov descriptor can be enabled per thread. */
     164           0 :                 if (p->p_kd != NULL || kd->kd_mode != KCOV_MODE_INIT) {
     165             :                         error = EBUSY;
     166           0 :                         break;
     167             :                 }
     168           0 :                 kd->kd_mode = KCOV_MODE_TRACE_PC;
     169           0 :                 p->p_kd = kd;
     170           0 :                 break;
     171             :         case KIODISABLE:
     172             :                 /* Only the enabled thread may disable itself. */
     173           0 :                 if (p->p_kd != kd || kd->kd_mode != KCOV_MODE_TRACE_PC) {
     174             :                         error = EBUSY;
     175           0 :                         break;
     176             :                 }
     177           0 :                 kd->kd_mode = KCOV_MODE_INIT;
     178           0 :                 p->p_kd = NULL;
     179           0 :                 break;
     180             :         default:
     181             :                 error = EINVAL;
     182             :                 DPRINTF("%s: %lu: unknown command\n", __func__, cmd);
     183           0 :         }
     184             : 
     185             :         DPRINTF("%s: unit=%d, mode=%d, error=%d\n",
     186             :             __func__, kd->kd_unit, kd->kd_mode, error);
     187             : 
     188           0 :         return (error);
     189           0 : }
     190             : 
     191             : paddr_t
     192           0 : kcovmmap(dev_t dev, off_t offset, int prot)
     193             : {
     194             :         struct kcov_dev *kd;
     195           0 :         paddr_t pa;
     196             :         vaddr_t va;
     197             : 
     198           0 :         kd = kd_lookup(minor(dev));
     199           0 :         if (kd == NULL)
     200           0 :                 return (paddr_t)(-1);
     201             : 
     202           0 :         if (offset < 0 || offset >= kd->kd_nmemb * sizeof(uintptr_t))
     203           0 :                 return (paddr_t)(-1);
     204             : 
     205           0 :         va = (vaddr_t)kd->kd_buf + offset;
     206           0 :         if (pmap_extract(pmap_kernel(), va, &pa) == FALSE)
     207           0 :                 return (paddr_t)(-1);
     208           0 :         return (pa);
     209           0 : }
     210             : 
     211             : void
     212           0 : kcov_exit(struct proc *p)
     213             : {
     214             :         struct kcov_dev *kd;
     215             : 
     216           0 :         kd = p->p_kd;
     217           0 :         if (kd == NULL)
     218           0 :                 return;
     219             : 
     220             :         DPRINTF("%s: unit=%d\n", __func__, kd->kd_unit);
     221             : 
     222           0 :         if (kd->kd_mode == KCOV_MODE_DYING)
     223           0 :                 kd_free(kd);
     224             :         else
     225           0 :                 kd->kd_mode = KCOV_MODE_INIT;
     226           0 :         p->p_kd = NULL;
     227           0 : }
     228             : 
     229             : struct kcov_dev *
     230           0 : kd_lookup(int unit)
     231             : {
     232             :         struct kcov_dev *kd;
     233             : 
     234           0 :         TAILQ_FOREACH(kd, &kd_list, kd_entry) {
     235           0 :                 if (kd->kd_unit == unit)
     236           0 :                         return (kd);
     237             :         }
     238           0 :         return (NULL);
     239           0 : }
     240             : 
     241             : int
     242           0 : kd_alloc(struct kcov_dev *kd, unsigned long nmemb)
     243             : {
     244             :         size_t size;
     245             : 
     246           0 :         KASSERT(kd->kd_buf == NULL);
     247             : 
     248           0 :         if (nmemb == 0 || nmemb > KCOV_BUF_MAX_NMEMB)
     249           0 :                 return (EINVAL);
     250             : 
     251           0 :         size = roundup(nmemb * sizeof(uintptr_t), PAGE_SIZE);
     252           0 :         kd->kd_buf = malloc(size, M_SUBPROC, M_WAITOK | M_ZERO);
     253             :         /* The first element is reserved to hold the number of used elements. */
     254           0 :         kd->kd_nmemb = nmemb - 1;
     255           0 :         kd->kd_size = size;
     256           0 :         return (0);
     257           0 : }
     258             : 
     259             : void
     260           0 : kd_free(struct kcov_dev *kd)
     261             : {
     262             :         DPRINTF("%s: unit=%d mode=%d\n", __func__, kd->kd_unit, kd->kd_mode);
     263             : 
     264           0 :         TAILQ_REMOVE(&kd_list, kd, kd_entry);
     265           0 :         free(kd->kd_buf, M_SUBPROC, kd->kd_size);
     266           0 :         free(kd, M_SUBPROC, sizeof(*kd));
     267           0 : }
     268             : 
     269             : static inline int
     270           0 : inintr(void)
     271             : {
     272             : #if defined(__amd64__) || defined(__i386__)
     273           0 :         return (curcpu()->ci_idepth > 0);
     274             : #else
     275             :         return (0);
     276             : #endif
     277             : }

Generated by: LCOV version 1.13