| File: | dev/dt/dt_dev.c |
| Warning: | line 608, column 18 The left operand of '==' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: dt_dev.c,v 1.19 2022/01/09 05:42:37 jsg Exp $ */ | |||
| 2 | ||||
| 3 | /* | |||
| 4 | * Copyright (c) 2019 Martin Pieuchot <mpi@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/types.h> | |||
| 20 | #include <sys/systm.h> | |||
| 21 | #include <sys/param.h> | |||
| 22 | #include <sys/device.h> | |||
| 23 | #include <sys/malloc.h> | |||
| 24 | #include <sys/proc.h> | |||
| 25 | ||||
| 26 | #include <dev/dt/dtvar.h> | |||
| 27 | ||||
| 28 | /* | |||
| 29 | * Number of frames to skip in stack traces. | |||
| 30 | * | |||
| 31 | * The number of frames required to execute dt(4) profiling code | |||
| 32 | * depends on the probe, context, architecture and possibly the | |||
| 33 | * compiler. | |||
| 34 | * | |||
| 35 | * Static probes (tracepoints) are executed in the context of the | |||
| 36 | * current thread and only need to skip frames up to the recording | |||
| 37 | * function. For example the syscall provider: | |||
| 38 | * | |||
| 39 | * dt_prov_syscall_entry+0x141 | |||
| 40 | * syscall+0x205 <--- start here | |||
| 41 | * Xsyscall+0x128 | |||
| 42 | * | |||
| 43 | * Probes executed in their own context, like the profile provider, | |||
| 44 | * need to skip the frames of that context which are different for | |||
| 45 | * every architecture. For example the profile provider executed | |||
| 46 | * from hardclock(9) on amd64: | |||
| 47 | * | |||
| 48 | * dt_prov_profile_enter+0x6e | |||
| 49 | * hardclock+0x1a9 | |||
| 50 | * lapic_clockintr+0x3f | |||
| 51 | * Xresume_lapic_ltimer+0x26 | |||
| 52 | * acpicpu_idle+0x1d2 <---- start here. | |||
| 53 | * sched_idle+0x225 | |||
| 54 | * proc_trampoline+0x1c | |||
| 55 | */ | |||
| 56 | #if defined(__amd64__1) | |||
| 57 | #define DT_FA_PROFILE5 5 | |||
| 58 | #define DT_FA_STATIC2 2 | |||
| 59 | #elif defined(__powerpc64__) | |||
| 60 | #define DT_FA_PROFILE5 6 | |||
| 61 | #define DT_FA_STATIC2 2 | |||
| 62 | #elif defined(__sparc64__) | |||
| 63 | #define DT_FA_PROFILE5 5 | |||
| 64 | #define DT_FA_STATIC2 1 | |||
| 65 | #else | |||
| 66 | #define DT_FA_STATIC2 0 | |||
| 67 | #define DT_FA_PROFILE5 0 | |||
| 68 | #endif | |||
| 69 | ||||
| 70 | #define DT_EVTRING_SIZE16 16 /* # of slots in per PCB event ring */ | |||
| 71 | ||||
| 72 | #define DPRINTF(x...) /* nothing */ | |||
| 73 | ||||
| 74 | /* | |||
| 75 | * Descriptor associated with each program opening /dev/dt. It is used | |||
| 76 | * to keep track of enabled PCBs. | |||
| 77 | * | |||
| 78 | * Locks used to protect struct members in this file: | |||
| 79 | * m per-softc mutex | |||
| 80 | * K kernel lock | |||
| 81 | */ | |||
| 82 | struct dt_softc { | |||
| 83 | SLIST_ENTRY(dt_softc)struct { struct dt_softc *sle_next; } ds_next; /* [K] descriptor list */ | |||
| 84 | int ds_unit; /* [I] D_CLONE unique unit */ | |||
| 85 | pid_t ds_pid; /* [I] PID of tracing program */ | |||
| 86 | ||||
| 87 | struct mutex ds_mtx; | |||
| 88 | ||||
| 89 | struct dt_pcb_list ds_pcbs; /* [K] list of enabled PCBs */ | |||
| 90 | struct dt_evt *ds_bufqueue; /* [K] copy evts to userland */ | |||
| 91 | size_t ds_bufqlen; /* [K] length of the queue */ | |||
| 92 | int ds_recording; /* [K] currently recording? */ | |||
| 93 | int ds_evtcnt; /* [m] # of readable evts */ | |||
| 94 | ||||
| 95 | /* Counters */ | |||
| 96 | uint64_t ds_readevt; /* [m] # of events read */ | |||
| 97 | uint64_t ds_dropevt; /* [m] # of events dropped */ | |||
| 98 | }; | |||
| 99 | ||||
| 100 | SLIST_HEAD(, dt_softc)struct { struct dt_softc *slh_first; } dtdev_list; /* [K] list of open /dev/dt nodes */ | |||
| 101 | ||||
| 102 | /* | |||
| 103 | * Probes are created during dt_attach() and never modified/freed during | |||
| 104 | * the lifetime of the system. That's why we consider them as [I]mmutable. | |||
| 105 | */ | |||
| 106 | unsigned int dt_nprobes; /* [I] # of probes available */ | |||
| 107 | SIMPLEQ_HEAD(, dt_probe)struct { struct dt_probe *sqh_first; struct dt_probe **sqh_last ; } dt_probe_list; /* [I] list of probes */ | |||
| 108 | ||||
| 109 | struct rwlock dt_lock = RWLOCK_INITIALIZER("dtlk"){ 0, "dtlk" }; | |||
| 110 | volatile uint32_t dt_tracing = 0; /* [K] # of processes tracing */ | |||
| 111 | ||||
| 112 | int allowdt; | |||
| 113 | ||||
| 114 | void dtattach(struct device *, struct device *, void *); | |||
| 115 | int dtopen(dev_t, int, int, struct proc *); | |||
| 116 | int dtclose(dev_t, int, int, struct proc *); | |||
| 117 | int dtread(dev_t, struct uio *, int); | |||
| 118 | int dtioctl(dev_t, u_long, caddr_t, int, struct proc *); | |||
| 119 | ||||
| 120 | struct dt_softc *dtlookup(int); | |||
| 121 | ||||
| 122 | int dt_ioctl_list_probes(struct dt_softc *, struct dtioc_probe *); | |||
| 123 | int dt_ioctl_get_stats(struct dt_softc *, struct dtioc_stat *); | |||
| 124 | int dt_ioctl_record_start(struct dt_softc *); | |||
| 125 | void dt_ioctl_record_stop(struct dt_softc *); | |||
| 126 | int dt_ioctl_probe_enable(struct dt_softc *, struct dtioc_req *); | |||
| 127 | int dt_ioctl_probe_disable(struct dt_softc *, struct dtioc_req *); | |||
| 128 | ||||
| 129 | int dt_pcb_ring_copy(struct dt_pcb *, struct dt_evt *, size_t, uint64_t *); | |||
| 130 | ||||
| 131 | void | |||
| 132 | dtattach(struct device *parent, struct device *self, void *aux) | |||
| 133 | { | |||
| 134 | SLIST_INIT(&dtdev_list){ ((&dtdev_list)->slh_first) = ((void *)0); }; | |||
| 135 | SIMPLEQ_INIT(&dt_probe_list)do { (&dt_probe_list)->sqh_first = ((void *)0); (& dt_probe_list)->sqh_last = &(&dt_probe_list)->sqh_first ; } while (0); | |||
| 136 | ||||
| 137 | /* Init providers */ | |||
| 138 | dt_nprobes += dt_prov_profile_init(); | |||
| 139 | dt_nprobes += dt_prov_syscall_init(); | |||
| 140 | dt_nprobes += dt_prov_static_init(); | |||
| 141 | #ifdef DDBPROF | |||
| 142 | dt_nprobes += dt_prov_kprobe_init(); | |||
| 143 | #endif | |||
| 144 | } | |||
| 145 | ||||
| 146 | int | |||
| 147 | dtopen(dev_t dev, int flags, int mode, struct proc *p) | |||
| 148 | { | |||
| 149 | struct dt_softc *sc; | |||
| 150 | int unit = minor(dev)((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >> 8)); | |||
| 151 | ||||
| 152 | if (!allowdt) | |||
| 153 | return EPERM1; | |||
| 154 | ||||
| 155 | KASSERT(dtlookup(unit) == NULL)((dtlookup(unit) == ((void *)0)) ? (void)0 : __assert("diagnostic " , "/usr/src/sys/dev/dt/dt_dev.c", 155, "dtlookup(unit) == NULL" )); | |||
| 156 | ||||
| 157 | sc = malloc(sizeof(*sc), M_DEVBUF2, M_WAITOK0x0001|M_CANFAIL0x0004|M_ZERO0x0008); | |||
| 158 | if (sc == NULL((void *)0)) | |||
| 159 | return ENOMEM12; | |||
| 160 | ||||
| 161 | /* | |||
| 162 | * Enough space to empty 2 full rings of events in a single read. | |||
| 163 | */ | |||
| 164 | sc->ds_bufqlen = 2 * DT_EVTRING_SIZE16; | |||
| 165 | sc->ds_bufqueue = mallocarray(sc->ds_bufqlen, sizeof(*sc->ds_bufqueue), | |||
| 166 | M_DEVBUF2, M_WAITOK0x0001|M_CANFAIL0x0004); | |||
| 167 | if (sc->ds_bufqueue == NULL((void *)0)) | |||
| 168 | goto bad; | |||
| 169 | ||||
| 170 | sc->ds_unit = unit; | |||
| 171 | sc->ds_pid = p->p_p->ps_pid; | |||
| 172 | TAILQ_INIT(&sc->ds_pcbs)do { (&sc->ds_pcbs)->tqh_first = ((void *)0); (& sc->ds_pcbs)->tqh_last = &(&sc->ds_pcbs)-> tqh_first; } while (0); | |||
| 173 | mtx_init(&sc->ds_mtx, IPL_HIGH)do { (void)(((void *)0)); (void)(0); __mtx_init((&sc-> ds_mtx), ((((0xd)) > 0x0 && ((0xd)) < 0x9) ? 0x9 : ((0xd)))); } while (0); | |||
| 174 | sc->ds_evtcnt = 0; | |||
| 175 | sc->ds_readevt = 0; | |||
| 176 | sc->ds_dropevt = 0; | |||
| 177 | ||||
| 178 | SLIST_INSERT_HEAD(&dtdev_list, sc, ds_next)do { (sc)->ds_next.sle_next = (&dtdev_list)->slh_first ; (&dtdev_list)->slh_first = (sc); } while (0); | |||
| 179 | ||||
| 180 | DPRINTF("dt%d: pid %d open\n", sc->ds_unit, sc->ds_pid); | |||
| 181 | ||||
| 182 | return 0; | |||
| 183 | ||||
| 184 | bad: | |||
| 185 | free(sc, M_DEVBUF2, sizeof(*sc)); | |||
| 186 | return ENOMEM12; | |||
| 187 | } | |||
| 188 | ||||
| 189 | int | |||
| 190 | dtclose(dev_t dev, int flags, int mode, struct proc *p) | |||
| 191 | { | |||
| 192 | struct dt_softc *sc; | |||
| 193 | int unit = minor(dev)((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >> 8)); | |||
| 194 | ||||
| 195 | sc = dtlookup(unit); | |||
| 196 | KASSERT(sc != NULL)((sc != ((void *)0)) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/dev/dt/dt_dev.c" , 196, "sc != NULL")); | |||
| 197 | ||||
| 198 | DPRINTF("dt%d: pid %d close\n", sc->ds_unit, sc->ds_pid); | |||
| 199 | ||||
| 200 | SLIST_REMOVE(&dtdev_list, sc, dt_softc, ds_next)do { if ((&dtdev_list)->slh_first == (sc)) { do { ((& dtdev_list))->slh_first = ((&dtdev_list))->slh_first ->ds_next.sle_next; } while (0); } else { struct dt_softc * curelm = (&dtdev_list)->slh_first; while (curelm->ds_next .sle_next != (sc)) curelm = curelm->ds_next.sle_next; curelm ->ds_next.sle_next = curelm->ds_next.sle_next->ds_next .sle_next; } ((sc)->ds_next.sle_next) = ((void *)-1); } while (0); | |||
| 201 | dt_ioctl_record_stop(sc); | |||
| 202 | dt_pcb_purge(&sc->ds_pcbs); | |||
| 203 | ||||
| 204 | free(sc->ds_bufqueue, M_DEVBUF2, | |||
| 205 | sc->ds_bufqlen * sizeof(*sc->ds_bufqueue)); | |||
| 206 | free(sc, M_DEVBUF2, sizeof(*sc)); | |||
| 207 | ||||
| 208 | return 0; | |||
| 209 | } | |||
| 210 | ||||
| 211 | int | |||
| 212 | dtread(dev_t dev, struct uio *uio, int flags) | |||
| 213 | { | |||
| 214 | struct sleep_state sls; | |||
| 215 | struct dt_softc *sc; | |||
| 216 | struct dt_evt *estq; | |||
| 217 | struct dt_pcb *dp; | |||
| 218 | int error = 0, unit = minor(dev)((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >> 8)); | |||
| 219 | size_t qlen, count, read = 0; | |||
| 220 | uint64_t dropped = 0; | |||
| 221 | ||||
| 222 | sc = dtlookup(unit); | |||
| 223 | KASSERT(sc != NULL)((sc != ((void *)0)) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/dev/dt/dt_dev.c" , 223, "sc != NULL")); | |||
| 224 | ||||
| 225 | count = howmany(uio->uio_resid, sizeof(struct dt_evt))(((uio->uio_resid) + ((sizeof(struct dt_evt)) - 1)) / (sizeof (struct dt_evt))); | |||
| 226 | if (count < 1) | |||
| 227 | return (EMSGSIZE40); | |||
| 228 | ||||
| 229 | while (!sc->ds_evtcnt) { | |||
| 230 | sleep_setup(&sls, sc, PWAIT32 | PCATCH0x100, "dtread", 0); | |||
| 231 | error = sleep_finish(&sls, !sc->ds_evtcnt); | |||
| 232 | if (error == EINTR4 || error == ERESTART-1) | |||
| 233 | break; | |||
| 234 | } | |||
| 235 | if (error) | |||
| 236 | return error; | |||
| 237 | ||||
| 238 | estq = sc->ds_bufqueue; | |||
| 239 | qlen = MIN(sc->ds_bufqlen, count)(((sc->ds_bufqlen)<(count))?(sc->ds_bufqlen):(count) ); | |||
| 240 | ||||
| 241 | KERNEL_ASSERT_LOCKED()((_kernel_lock_held()) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/dev/dt/dt_dev.c" , 241, "_kernel_lock_held()")); | |||
| 242 | TAILQ_FOREACH(dp, &sc->ds_pcbs, dp_snext)for((dp) = ((&sc->ds_pcbs)->tqh_first); (dp) != ((void *)0); (dp) = ((dp)->dp_snext.tqe_next)) { | |||
| 243 | count = dt_pcb_ring_copy(dp, estq, qlen, &dropped); | |||
| 244 | read += count; | |||
| 245 | estq += count; /* pointer arithmetic */ | |||
| 246 | qlen -= count; | |||
| 247 | if (qlen == 0) | |||
| 248 | break; | |||
| 249 | } | |||
| 250 | if (read > 0) | |||
| 251 | uiomove(sc->ds_bufqueue, read * sizeof(struct dt_evt), uio); | |||
| 252 | ||||
| 253 | mtx_enter(&sc->ds_mtx); | |||
| 254 | sc->ds_evtcnt -= read; | |||
| 255 | sc->ds_readevt += read; | |||
| 256 | sc->ds_dropevt += dropped; | |||
| 257 | mtx_leave(&sc->ds_mtx); | |||
| 258 | ||||
| 259 | return 0; | |||
| 260 | } | |||
| 261 | ||||
| 262 | int | |||
| 263 | dtioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) | |||
| 264 | { | |||
| 265 | struct dt_softc *sc; | |||
| 266 | int unit = minor(dev)((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >> 8)); | |||
| 267 | int on, error = 0; | |||
| 268 | ||||
| 269 | sc = dtlookup(unit); | |||
| 270 | KASSERT(sc != NULL)((sc != ((void *)0)) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/dev/dt/dt_dev.c" , 270, "sc != NULL")); | |||
| 271 | ||||
| 272 | switch (cmd) { | |||
| 273 | case DTIOCGPLIST(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct dtioc_probe) & 0x1fff) << 16) | ((('D')) << 8) | ((1))): | |||
| 274 | return dt_ioctl_list_probes(sc, (struct dtioc_probe *)addr); | |||
| 275 | case DTIOCGSTATS((unsigned long)0x40000000 | ((sizeof(struct dtioc_stat) & 0x1fff) << 16) | ((('D')) << 8) | ((2))): | |||
| 276 | return dt_ioctl_get_stats(sc, (struct dtioc_stat *)addr); | |||
| 277 | case DTIOCRECORD((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('D')) << 8) | ((3))): | |||
| 278 | case DTIOCPRBENABLE((unsigned long)0x80000000 | ((sizeof(struct dtioc_req) & 0x1fff) << 16) | ((('D')) << 8) | ((4))): | |||
| 279 | case DTIOCPRBDISABLE((unsigned long)0x80000000 | ((sizeof(struct dtioc_req) & 0x1fff) << 16) | ((('D')) << 8) | ((5))): | |||
| 280 | /* root only ioctl(2) */ | |||
| 281 | break; | |||
| 282 | default: | |||
| 283 | return ENOTTY25; | |||
| 284 | } | |||
| 285 | ||||
| 286 | if ((error = suser(p)) != 0) | |||
| 287 | return error; | |||
| 288 | ||||
| 289 | switch (cmd) { | |||
| 290 | case DTIOCRECORD((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('D')) << 8) | ((3))): | |||
| 291 | on = *(int *)addr; | |||
| 292 | if (on) | |||
| 293 | error = dt_ioctl_record_start(sc); | |||
| 294 | else | |||
| 295 | dt_ioctl_record_stop(sc); | |||
| 296 | break; | |||
| 297 | case DTIOCPRBENABLE((unsigned long)0x80000000 | ((sizeof(struct dtioc_req) & 0x1fff) << 16) | ((('D')) << 8) | ((4))): | |||
| 298 | error = dt_ioctl_probe_enable(sc, (struct dtioc_req *)addr); | |||
| 299 | break; | |||
| 300 | case DTIOCPRBDISABLE((unsigned long)0x80000000 | ((sizeof(struct dtioc_req) & 0x1fff) << 16) | ((('D')) << 8) | ((5))): | |||
| 301 | error = dt_ioctl_probe_disable(sc, (struct dtioc_req *)addr); | |||
| 302 | break; | |||
| 303 | default: | |||
| 304 | KASSERT(0)((0) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/dev/dt/dt_dev.c" , 304, "0")); | |||
| 305 | } | |||
| 306 | ||||
| 307 | return error; | |||
| 308 | } | |||
| 309 | ||||
| 310 | struct dt_softc * | |||
| 311 | dtlookup(int unit) | |||
| 312 | { | |||
| 313 | struct dt_softc *sc; | |||
| 314 | ||||
| 315 | KERNEL_ASSERT_LOCKED()((_kernel_lock_held()) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/dev/dt/dt_dev.c" , 315, "_kernel_lock_held()")); | |||
| 316 | ||||
| 317 | SLIST_FOREACH(sc, &dtdev_list, ds_next)for((sc) = ((&dtdev_list)->slh_first); (sc) != ((void * )0); (sc) = ((sc)->ds_next.sle_next)) { | |||
| 318 | if (sc->ds_unit == unit) | |||
| 319 | break; | |||
| 320 | } | |||
| 321 | ||||
| 322 | return sc; | |||
| 323 | } | |||
| 324 | ||||
| 325 | int | |||
| 326 | dtioc_req_isvalid(struct dtioc_req *dtrq) | |||
| 327 | { | |||
| 328 | switch (dtrq->dtrq_filter.dtf_operand) { | |||
| 329 | case DT_OP_NONE: | |||
| 330 | case DT_OP_EQ: | |||
| 331 | case DT_OP_NE: | |||
| 332 | break; | |||
| 333 | default: | |||
| 334 | return 0; | |||
| 335 | } | |||
| 336 | ||||
| 337 | switch (dtrq->dtrq_filter.dtf_variable) { | |||
| 338 | case DT_FV_NONE: | |||
| 339 | case DT_FV_PID: | |||
| 340 | case DT_FV_TID: | |||
| 341 | break; | |||
| 342 | default: | |||
| 343 | return 0; | |||
| 344 | } | |||
| 345 | ||||
| 346 | return 1; | |||
| 347 | } | |||
| 348 | ||||
| 349 | int | |||
| 350 | dt_ioctl_list_probes(struct dt_softc *sc, struct dtioc_probe *dtpr) | |||
| 351 | { | |||
| 352 | struct dtioc_probe_info info, *dtpi; | |||
| 353 | struct dt_probe *dtp; | |||
| 354 | size_t size; | |||
| 355 | int error = 0; | |||
| 356 | ||||
| 357 | size = dtpr->dtpr_size; | |||
| 358 | dtpr->dtpr_size = dt_nprobes * sizeof(*dtpi); | |||
| 359 | if (size == 0) | |||
| 360 | return 0; | |||
| 361 | ||||
| 362 | dtpi = dtpr->dtpr_probes; | |||
| 363 | memset(&info, 0, sizeof(info))__builtin_memset((&info), (0), (sizeof(info))); | |||
| 364 | SIMPLEQ_FOREACH(dtp, &dt_probe_list, dtp_next)for((dtp) = ((&dt_probe_list)->sqh_first); (dtp) != (( void *)0); (dtp) = ((dtp)->dtp_next.sqe_next)) { | |||
| 365 | if (size < sizeof(*dtpi)) { | |||
| 366 | error = ENOSPC28; | |||
| 367 | break; | |||
| 368 | } | |||
| 369 | info.dtpi_pbn = dtp->dtp_pbn; | |||
| 370 | info.dtpi_nargs = dtp->dtp_nargs; | |||
| 371 | strlcpy(info.dtpi_prov, dtp->dtp_prov->dtpv_name, | |||
| 372 | sizeof(info.dtpi_prov)); | |||
| 373 | strlcpy(info.dtpi_func, dtp->dtp_func, sizeof(info.dtpi_func)); | |||
| 374 | strlcpy(info.dtpi_name, dtp->dtp_name, sizeof(info.dtpi_name)); | |||
| 375 | error = copyout(&info, dtpi, sizeof(*dtpi)); | |||
| 376 | if (error) | |||
| 377 | break; | |||
| 378 | size -= sizeof(*dtpi); | |||
| 379 | dtpi++; | |||
| 380 | }; | |||
| 381 | ||||
| 382 | return error; | |||
| 383 | } | |||
| 384 | ||||
| 385 | int | |||
| 386 | dt_ioctl_get_stats(struct dt_softc *sc, struct dtioc_stat *dtst) | |||
| 387 | { | |||
| 388 | mtx_enter(&sc->ds_mtx); | |||
| 389 | dtst->dtst_readevt = sc->ds_readevt; | |||
| 390 | dtst->dtst_dropevt = sc->ds_dropevt; | |||
| 391 | mtx_leave(&sc->ds_mtx); | |||
| 392 | ||||
| 393 | return 0; | |||
| 394 | } | |||
| 395 | ||||
| 396 | int | |||
| 397 | dt_ioctl_record_start(struct dt_softc *sc) | |||
| 398 | { | |||
| 399 | struct dt_pcb *dp; | |||
| 400 | ||||
| 401 | if (sc->ds_recording) | |||
| 402 | return EBUSY16; | |||
| 403 | ||||
| 404 | KERNEL_ASSERT_LOCKED()((_kernel_lock_held()) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/dev/dt/dt_dev.c" , 404, "_kernel_lock_held()")); | |||
| 405 | if (TAILQ_EMPTY(&sc->ds_pcbs)(((&sc->ds_pcbs)->tqh_first) == ((void *)0))) | |||
| 406 | return ENOENT2; | |||
| 407 | ||||
| 408 | rw_enter_write(&dt_lock); | |||
| 409 | TAILQ_FOREACH(dp, &sc->ds_pcbs, dp_snext)for((dp) = ((&sc->ds_pcbs)->tqh_first); (dp) != ((void *)0); (dp) = ((dp)->dp_snext.tqe_next)) { | |||
| 410 | struct dt_probe *dtp = dp->dp_dtp; | |||
| 411 | ||||
| 412 | SMR_SLIST_INSERT_HEAD_LOCKED(&dtp->dtp_pcbs, dp, dp_pnext)do { (dp)->dp_pnext.smr_sle_next = (&dtp->dtp_pcbs) ->smr_slh_first; do { __asm volatile("" ::: "memory"); } while (0); (&dtp->dtp_pcbs)->smr_slh_first = (dp); } while (0); | |||
| 413 | dtp->dtp_recording++; | |||
| 414 | dtp->dtp_prov->dtpv_recording++; | |||
| 415 | } | |||
| 416 | rw_exit_write(&dt_lock); | |||
| 417 | ||||
| 418 | sc->ds_recording = 1; | |||
| 419 | dt_tracing++; | |||
| 420 | ||||
| 421 | return 0; | |||
| 422 | } | |||
| 423 | ||||
| 424 | void | |||
| 425 | dt_ioctl_record_stop(struct dt_softc *sc) | |||
| 426 | { | |||
| 427 | struct dt_pcb *dp; | |||
| 428 | ||||
| 429 | if (!sc->ds_recording) | |||
| 430 | return; | |||
| 431 | ||||
| 432 | DPRINTF("dt%d: pid %d disable\n", sc->ds_unit, sc->ds_pid); | |||
| 433 | ||||
| 434 | dt_tracing--; | |||
| 435 | sc->ds_recording = 0; | |||
| 436 | ||||
| 437 | rw_enter_write(&dt_lock); | |||
| 438 | TAILQ_FOREACH(dp, &sc->ds_pcbs, dp_snext)for((dp) = ((&sc->ds_pcbs)->tqh_first); (dp) != ((void *)0); (dp) = ((dp)->dp_snext.tqe_next)) { | |||
| 439 | struct dt_probe *dtp = dp->dp_dtp; | |||
| 440 | ||||
| 441 | dtp->dtp_recording--; | |||
| 442 | dtp->dtp_prov->dtpv_recording--; | |||
| 443 | SMR_SLIST_REMOVE_LOCKED(&dtp->dtp_pcbs, dp, dt_pcb, dp_pnext)do { if ((&dtp->dtp_pcbs)->smr_slh_first == (dp)) { do { ((&dtp->dtp_pcbs))->smr_slh_first = ((&dtp ->dtp_pcbs))->smr_slh_first->dp_pnext.smr_sle_next;} while (0); } else { struct dt_pcb *curelm = (&dtp->dtp_pcbs )->smr_slh_first; while (curelm->dp_pnext.smr_sle_next != (dp)) curelm = curelm->dp_pnext.smr_sle_next; curelm-> dp_pnext.smr_sle_next = curelm->dp_pnext.smr_sle_next-> dp_pnext.smr_sle_next; } } while (0); | |||
| 444 | } | |||
| 445 | rw_exit_write(&dt_lock); | |||
| 446 | ||||
| 447 | /* Wait until readers cannot access the PCBs. */ | |||
| 448 | smr_barrier()smr_barrier_impl(0); | |||
| 449 | } | |||
| 450 | ||||
| 451 | int | |||
| 452 | dt_ioctl_probe_enable(struct dt_softc *sc, struct dtioc_req *dtrq) | |||
| 453 | { | |||
| 454 | struct dt_pcb_list plist; | |||
| 455 | struct dt_probe *dtp; | |||
| 456 | int error; | |||
| 457 | ||||
| 458 | if (!dtioc_req_isvalid(dtrq)) | |||
| 459 | return EINVAL22; | |||
| 460 | ||||
| 461 | SIMPLEQ_FOREACH(dtp, &dt_probe_list, dtp_next)for((dtp) = ((&dt_probe_list)->sqh_first); (dtp) != (( void *)0); (dtp) = ((dtp)->dtp_next.sqe_next)) { | |||
| 462 | if (dtp->dtp_pbn == dtrq->dtrq_pbn) | |||
| 463 | break; | |||
| 464 | } | |||
| 465 | if (dtp == NULL((void *)0)) | |||
| 466 | return ENOENT2; | |||
| 467 | ||||
| 468 | TAILQ_INIT(&plist)do { (&plist)->tqh_first = ((void *)0); (&plist)-> tqh_last = &(&plist)->tqh_first; } while (0); | |||
| 469 | error = dtp->dtp_prov->dtpv_alloc(dtp, sc, &plist, dtrq); | |||
| 470 | if (error) | |||
| 471 | return error; | |||
| 472 | ||||
| 473 | DPRINTF("dt%d: pid %d enable %u : %b\n", sc->ds_unit, sc->ds_pid, | |||
| 474 | dtrq->dtrq_pbn, (unsigned int)dtrq->dtrq_evtflags, DTEVT_FLAG_BITS); | |||
| 475 | ||||
| 476 | /* Append all PCBs to this instance */ | |||
| 477 | TAILQ_CONCAT(&sc->ds_pcbs, &plist, dp_snext)do { if (!(((&plist)->tqh_first) == ((void *)0))) { *( &sc->ds_pcbs)->tqh_last = (&plist)->tqh_first ; (&plist)->tqh_first->dp_snext.tqe_prev = (&sc ->ds_pcbs)->tqh_last; (&sc->ds_pcbs)->tqh_last = (&plist)->tqh_last; do { ((&plist))->tqh_first = ((void *)0); ((&plist))->tqh_last = &((&plist ))->tqh_first; } while (0); } } while (0); | |||
| 478 | ||||
| 479 | return 0; | |||
| 480 | } | |||
| 481 | ||||
| 482 | int | |||
| 483 | dt_ioctl_probe_disable(struct dt_softc *sc, struct dtioc_req *dtrq) | |||
| 484 | { | |||
| 485 | struct dt_probe *dtp; | |||
| 486 | int error; | |||
| 487 | ||||
| 488 | if (!dtioc_req_isvalid(dtrq)) | |||
| 489 | return EINVAL22; | |||
| 490 | ||||
| 491 | SIMPLEQ_FOREACH(dtp, &dt_probe_list, dtp_next)for((dtp) = ((&dt_probe_list)->sqh_first); (dtp) != (( void *)0); (dtp) = ((dtp)->dtp_next.sqe_next)) { | |||
| 492 | if (dtp->dtp_pbn == dtrq->dtrq_pbn) | |||
| 493 | break; | |||
| 494 | } | |||
| 495 | if (dtp == NULL((void *)0)) | |||
| 496 | return ENOENT2; | |||
| 497 | ||||
| 498 | if (dtp->dtp_prov->dtpv_dealloc) { | |||
| 499 | error = dtp->dtp_prov->dtpv_dealloc(dtp, sc, dtrq); | |||
| 500 | if (error) | |||
| 501 | return error; | |||
| 502 | } | |||
| 503 | ||||
| 504 | DPRINTF("dt%d: pid %d dealloc\n", sc->ds_unit, sc->ds_pid, | |||
| 505 | dtrq->dtrq_pbn); | |||
| 506 | ||||
| 507 | return 0; | |||
| 508 | } | |||
| 509 | ||||
| 510 | struct dt_probe * | |||
| 511 | dt_dev_alloc_probe(const char *func, const char *name, struct dt_provider *dtpv) | |||
| 512 | { | |||
| 513 | struct dt_probe *dtp; | |||
| 514 | ||||
| 515 | dtp = malloc(sizeof(*dtp), M_DT2, M_NOWAIT0x0002|M_ZERO0x0008); | |||
| 516 | if (dtp == NULL((void *)0)) | |||
| 517 | return NULL((void *)0); | |||
| 518 | ||||
| 519 | SMR_SLIST_INIT(&dtp->dtp_pcbs)do { (&dtp->dtp_pcbs)->smr_slh_first = ((void *)0); } while (0); | |||
| 520 | dtp->dtp_prov = dtpv; | |||
| 521 | dtp->dtp_func = func; | |||
| 522 | dtp->dtp_name = name; | |||
| 523 | dtp->dtp_sysnum = -1; | |||
| 524 | dtp->dtp_ref = 0; | |||
| 525 | ||||
| 526 | return dtp; | |||
| 527 | } | |||
| 528 | ||||
| 529 | void | |||
| 530 | dt_dev_register_probe(struct dt_probe *dtp) | |||
| 531 | { | |||
| 532 | static uint64_t probe_nb; | |||
| 533 | ||||
| 534 | dtp->dtp_pbn = ++probe_nb; | |||
| 535 | SIMPLEQ_INSERT_TAIL(&dt_probe_list, dtp, dtp_next)do { (dtp)->dtp_next.sqe_next = ((void *)0); *(&dt_probe_list )->sqh_last = (dtp); (&dt_probe_list)->sqh_last = & (dtp)->dtp_next.sqe_next; } while (0); | |||
| 536 | } | |||
| 537 | ||||
| 538 | struct dt_pcb * | |||
| 539 | dt_pcb_alloc(struct dt_probe *dtp, struct dt_softc *sc) | |||
| 540 | { | |||
| 541 | struct dt_pcb *dp; | |||
| 542 | ||||
| 543 | dp = malloc(sizeof(*dp), M_DT2, M_WAITOK0x0001|M_CANFAIL0x0004|M_ZERO0x0008); | |||
| 544 | if (dp == NULL((void *)0)) | |||
| 545 | goto bad; | |||
| 546 | ||||
| 547 | dp->dp_ring = mallocarray(DT_EVTRING_SIZE16, sizeof(*dp->dp_ring), M_DT2, | |||
| 548 | M_WAITOK0x0001|M_CANFAIL0x0004|M_ZERO0x0008); | |||
| 549 | if (dp->dp_ring == NULL((void *)0)) | |||
| 550 | goto bad; | |||
| 551 | ||||
| 552 | mtx_init(&dp->dp_mtx, IPL_HIGH)do { (void)(((void *)0)); (void)(0); __mtx_init((&dp-> dp_mtx), ((((0xd)) > 0x0 && ((0xd)) < 0x9) ? 0x9 : ((0xd)))); } while (0); | |||
| 553 | dp->dp_sc = sc; | |||
| 554 | dp->dp_dtp = dtp; | |||
| 555 | return dp; | |||
| 556 | bad: | |||
| 557 | dt_pcb_free(dp); | |||
| 558 | return NULL((void *)0); | |||
| 559 | } | |||
| 560 | ||||
| 561 | void | |||
| 562 | dt_pcb_free(struct dt_pcb *dp) | |||
| 563 | { | |||
| 564 | if (dp == NULL((void *)0)) | |||
| 565 | return; | |||
| 566 | free(dp->dp_ring, M_DT2, DT_EVTRING_SIZE16 * sizeof(*dp->dp_ring)); | |||
| 567 | free(dp, M_DT2, sizeof(*dp)); | |||
| 568 | } | |||
| 569 | ||||
| 570 | void | |||
| 571 | dt_pcb_purge(struct dt_pcb_list *plist) | |||
| 572 | { | |||
| 573 | struct dt_pcb *dp; | |||
| 574 | ||||
| 575 | while ((dp = TAILQ_FIRST(plist)((plist)->tqh_first)) != NULL((void *)0)) { | |||
| 576 | TAILQ_REMOVE(plist, dp, dp_snext)do { if (((dp)->dp_snext.tqe_next) != ((void *)0)) (dp)-> dp_snext.tqe_next->dp_snext.tqe_prev = (dp)->dp_snext.tqe_prev ; else (plist)->tqh_last = (dp)->dp_snext.tqe_prev; *(dp )->dp_snext.tqe_prev = (dp)->dp_snext.tqe_next; ((dp)-> dp_snext.tqe_prev) = ((void *)-1); ((dp)->dp_snext.tqe_next ) = ((void *)-1); } while (0); | |||
| 577 | dt_pcb_free(dp); | |||
| 578 | } | |||
| 579 | } | |||
| 580 | ||||
| 581 | int | |||
| 582 | dt_pcb_filter(struct dt_pcb *dp) | |||
| 583 | { | |||
| 584 | struct dt_filter *dtf = &dp->dp_filter; | |||
| 585 | struct proc *p = curproc({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc; | |||
| 586 | unsigned int var; | |||
| 587 | int match = 1; | |||
| 588 | ||||
| 589 | /* Filter out tracing program. */ | |||
| 590 | if (dp->dp_sc->ds_pid == p->p_p->ps_pid) | |||
| 591 | return 1; | |||
| 592 | ||||
| 593 | switch (dtf->dtf_variable) { | |||
| 594 | case DT_FV_PID: | |||
| 595 | var = p->p_p->ps_pid; | |||
| 596 | break; | |||
| 597 | case DT_FV_TID: | |||
| 598 | var = p->p_tid + THREAD_PID_OFFSET100000; | |||
| 599 | break; | |||
| 600 | case DT_FV_NONE: | |||
| 601 | break; | |||
| 602 | default: | |||
| 603 | KASSERT(0)((0) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/dev/dt/dt_dev.c" , 603, "0")); | |||
| 604 | } | |||
| 605 | ||||
| 606 | switch (dtf->dtf_operand) { | |||
| 607 | case DT_OP_EQ: | |||
| 608 | match = !!(var == dtf->dtf_value); | |||
| ||||
| 609 | break; | |||
| 610 | case DT_OP_NE: | |||
| 611 | match = !!(var != dtf->dtf_value); | |||
| 612 | break; | |||
| 613 | case DT_OP_NONE: | |||
| 614 | break; | |||
| 615 | default: | |||
| 616 | KASSERT(0)((0) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/dev/dt/dt_dev.c" , 616, "0")); | |||
| 617 | } | |||
| 618 | ||||
| 619 | return !match; | |||
| 620 | } | |||
| 621 | ||||
| 622 | ||||
| 623 | /* | |||
| 624 | * Get a reference to the next free event state from the ring. | |||
| 625 | */ | |||
| 626 | struct dt_evt * | |||
| 627 | dt_pcb_ring_get(struct dt_pcb *dp, int profiling) | |||
| 628 | { | |||
| 629 | struct proc *p = curproc({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc; | |||
| 630 | struct dt_evt *dtev; | |||
| 631 | int distance; | |||
| 632 | ||||
| 633 | if (dt_pcb_filter(dp)) | |||
| ||||
| 634 | return NULL((void *)0); | |||
| 635 | ||||
| 636 | mtx_enter(&dp->dp_mtx); | |||
| 637 | distance = dp->dp_prod - dp->dp_cons; | |||
| 638 | if (distance == 1 || distance == (1 - DT_EVTRING_SIZE16)) { | |||
| 639 | /* read(2) isn't finished */ | |||
| 640 | dp->dp_dropevt++; | |||
| 641 | mtx_leave(&dp->dp_mtx); | |||
| 642 | return NULL((void *)0); | |||
| 643 | } | |||
| 644 | ||||
| 645 | /* | |||
| 646 | * Save states in next free event slot. | |||
| 647 | */ | |||
| 648 | dtev = &dp->dp_ring[dp->dp_cons]; | |||
| 649 | memset(dtev, 0, sizeof(*dtev))__builtin_memset((dtev), (0), (sizeof(*dtev))); | |||
| 650 | ||||
| 651 | dtev->dtev_pbn = dp->dp_dtp->dtp_pbn; | |||
| 652 | dtev->dtev_cpu = cpu_number()(({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_cpuid); | |||
| 653 | dtev->dtev_pid = p->p_p->ps_pid; | |||
| 654 | dtev->dtev_tid = p->p_tid + THREAD_PID_OFFSET100000; | |||
| 655 | nanotime(&dtev->dtev_tsp); | |||
| 656 | ||||
| 657 | if (ISSET(dp->dp_evtflags, DTEVT_EXECNAME)((dp->dp_evtflags) & ((1 << 0)))) | |||
| 658 | memcpy(dtev->dtev_comm, p->p_p->ps_comm, DTMAXCOMLEN - 1)__builtin_memcpy((dtev->dtev_comm), (p->p_p->ps_comm ), (16 - 1)); | |||
| 659 | ||||
| 660 | if (ISSET(dp->dp_evtflags, DTEVT_KSTACK|DTEVT_USTACK)((dp->dp_evtflags) & ((1 << 2)|(1 << 1)))) { | |||
| 661 | if (profiling) | |||
| 662 | stacktrace_save_at(&dtev->dtev_kstack, DT_FA_PROFILE5); | |||
| 663 | else | |||
| 664 | stacktrace_save_at(&dtev->dtev_kstack, DT_FA_STATIC2); | |||
| 665 | } | |||
| 666 | ||||
| 667 | return dtev; | |||
| 668 | } | |||
| 669 | ||||
| 670 | void | |||
| 671 | dt_pcb_ring_consume(struct dt_pcb *dp, struct dt_evt *dtev) | |||
| 672 | { | |||
| 673 | MUTEX_ASSERT_LOCKED(&dp->dp_mtx)do { if (((&dp->dp_mtx)->mtx_owner != ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof (struct cpu_info, ci_self))); __ci;})) && !(panicstr || db_active)) panic("mutex %p not held in %s", (&dp->dp_mtx ), __func__); } while (0); | |||
| 674 | KASSERT(dtev == &dp->dp_ring[dp->dp_cons])((dtev == &dp->dp_ring[dp->dp_cons]) ? (void)0 : __assert ("diagnostic ", "/usr/src/sys/dev/dt/dt_dev.c", 674, "dtev == &dp->dp_ring[dp->dp_cons]" )); | |||
| 675 | ||||
| 676 | dp->dp_cons = (dp->dp_cons + 1) % DT_EVTRING_SIZE16; | |||
| 677 | mtx_leave(&dp->dp_mtx); | |||
| 678 | ||||
| 679 | mtx_enter(&dp->dp_sc->ds_mtx); | |||
| 680 | dp->dp_sc->ds_evtcnt++; | |||
| 681 | mtx_leave(&dp->dp_sc->ds_mtx); | |||
| 682 | wakeup(dp->dp_sc); | |||
| 683 | } | |||
| 684 | ||||
| 685 | /* | |||
| 686 | * Copy at most `qlen' events from `dp', producing the same amount | |||
| 687 | * of free slots. | |||
| 688 | */ | |||
| 689 | int | |||
| 690 | dt_pcb_ring_copy(struct dt_pcb *dp, struct dt_evt *estq, size_t qlen, | |||
| 691 | uint64_t *dropped) | |||
| 692 | { | |||
| 693 | size_t count, copied = 0; | |||
| 694 | unsigned int cons, prod; | |||
| 695 | ||||
| 696 | KASSERT(qlen > 0)((qlen > 0) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/dev/dt/dt_dev.c" , 696, "qlen > 0")); | |||
| 697 | ||||
| 698 | mtx_enter(&dp->dp_mtx); | |||
| 699 | cons = dp->dp_cons; | |||
| 700 | prod = dp->dp_prod; | |||
| 701 | ||||
| 702 | if (cons < prod) | |||
| 703 | count = DT_EVTRING_SIZE16 - prod; | |||
| 704 | else | |||
| 705 | count = cons - prod; | |||
| 706 | ||||
| 707 | if (count == 0) | |||
| 708 | goto out; | |||
| 709 | ||||
| 710 | *dropped += dp->dp_dropevt; | |||
| 711 | dp->dp_dropevt = 0; | |||
| 712 | ||||
| 713 | count = MIN(count, qlen)(((count)<(qlen))?(count):(qlen)); | |||
| 714 | ||||
| 715 | memcpy(&estq[0], &dp->dp_ring[prod], count * sizeof(*estq))__builtin_memcpy((&estq[0]), (&dp->dp_ring[prod]), (count * sizeof(*estq))); | |||
| 716 | copied += count; | |||
| 717 | ||||
| 718 | /* Produce */ | |||
| 719 | prod = (prod + count) % DT_EVTRING_SIZE16; | |||
| 720 | ||||
| 721 | /* If the queue is full or the ring didn't wrap, stop here. */ | |||
| 722 | if (qlen == copied || prod != 0 || cons == 0) | |||
| 723 | goto out; | |||
| 724 | ||||
| 725 | count = MIN(cons, (qlen - copied))(((cons)<((qlen - copied)))?(cons):((qlen - copied))); | |||
| 726 | memcpy(&estq[copied], &dp->dp_ring[0], count * sizeof(*estq))__builtin_memcpy((&estq[copied]), (&dp->dp_ring[0] ), (count * sizeof(*estq))); | |||
| 727 | copied += count; | |||
| 728 | prod += count; | |||
| 729 | ||||
| 730 | out: | |||
| 731 | dp->dp_prod = prod; | |||
| 732 | mtx_leave(&dp->dp_mtx); | |||
| 733 | return copied; | |||
| 734 | } |