File: | dev/dt/dt_dev.c |
Warning: | line 611, 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 | } |