1 /* $OpenBSD: dtvar.h,v 1.22 2025/01/23 11:17:32 mpi 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 #ifndef _DT_H_ 20 #define _DT_H_ 21 22 #include <sys/ioccom.h> 23 #include <sys/stacktrace.h> 24 #include <sys/time.h> 25 26 /* 27 * Length of provider/probe/function names, including NUL. 28 */ 29 #define DTNAMESIZE 16 30 31 /* 32 * Length of process name, including NUL. 33 */ 34 #define DTMAXCOMLEN _MAXCOMLEN 35 36 /* 37 * Maximum number of arguments passed to a function. 38 */ 39 #define DTMAXFUNCARGS 10 40 #define DTMAXARGTYPES 5 41 42 /* 43 * Event state: where to store information when a probe fires. 44 */ 45 struct dt_evt { 46 unsigned int dtev_pbn; /* Probe number */ 47 unsigned int dtev_cpu; /* CPU id */ 48 pid_t dtev_pid; /* ID of current process */ 49 pid_t dtev_tid; /* ID of current thread */ 50 struct timespec dtev_tsp; /* timestamp (nsecs) */ 51 52 /* 53 * Recorded if the corresponding flag is set. 54 */ 55 struct stacktrace dtev_kstack; /* kernel stack frame */ 56 struct stacktrace dtev_ustack; /* userland stack frame */ 57 char dtev_comm[DTMAXCOMLEN]; /* current pr. name */ 58 union { 59 register_t E_entry[DTMAXFUNCARGS]; 60 struct { 61 register_t __retval[2]; 62 int __error; 63 } E_return; 64 } _args; 65 #define dtev_args _args.E_entry /* function args. */ 66 #define dtev_retval _args.E_return.__retval /* function retval */ 67 #define dtev_error _args.E_return.__error /* function error */ 68 }; 69 70 /* 71 * States to record when a probe fires. 72 */ 73 #define DTEVT_EXECNAME (1 << 0) /* current process name */ 74 #define DTEVT_USTACK (1 << 1) /* userland stack */ 75 #define DTEVT_KSTACK (1 << 2) /* kernel stack */ 76 #define DTEVT_FUNCARGS (1 << 3) /* function arguments */ 77 78 #define DTEVT_FLAG_BITS \ 79 "\020" \ 80 "\001EXECNAME" \ 81 "\002USTACK" \ 82 "\003KSTACK" \ 83 "\004FUNCARGS" \ 84 85 struct dtioc_probe_info { 86 uint32_t dtpi_pbn; /* probe number */ 87 uint8_t dtpi_nargs; /* # of arguments */ 88 char dtpi_prov[DTNAMESIZE]; 89 char dtpi_func[DTNAMESIZE]; 90 char dtpi_name[DTNAMESIZE]; 91 }; 92 93 struct dtioc_probe { 94 size_t dtpr_size; /* size of the buffer */ 95 struct dtioc_probe_info *dtpr_probes; /* array of probe info */ 96 }; 97 98 struct dtioc_arg_info { 99 uint32_t dtai_pbn; /* probe number */ 100 uint8_t dtai_argn; /* arguments number */ 101 char dtai_argtype[DTNAMESIZE]; 102 }; 103 104 struct dtioc_arg { 105 uint32_t dtar_pbn; /* probe number */ 106 size_t dtar_size; /* size of the buffer */ 107 struct dtioc_arg_info *dtar_args; /* array of arg info */ 108 }; 109 110 struct dtioc_req { 111 uint32_t dtrq_pbn; /* probe number */ 112 uint32_t __unused1; 113 uint64_t dtrq_evtflags; /* states to record */ 114 uint64_t dtrq_nsecs; /* execution period */ 115 }; 116 117 struct dtioc_stat { 118 uint64_t dtst_readevt; /* events read */ 119 uint64_t dtst_dropevt; /* events dropped */ 120 uint64_t dtst_skiptick; /* clock ticks skipped */ 121 uint64_t dtst_recurevt; /* recursive events */ 122 }; 123 124 struct dtioc_getaux { 125 pid_t dtga_pid; /* process to inspect */ 126 unsigned long dtga_auxbase; /* AUX_base value */ 127 }; 128 129 #define DTIOCGPLIST _IOWR('D', 1, struct dtioc_probe) 130 #define DTIOCGSTATS _IOR('D', 2, struct dtioc_stat) 131 #define DTIOCRECORD _IOW('D', 3, int) 132 #define DTIOCPRBENABLE _IOW('D', 4, struct dtioc_req) 133 #define DTIOCPRBDISABLE _IOW('D', 5, struct dtioc_req) 134 #define DTIOCGARGS _IOWR('D', 6, struct dtioc_arg) 135 #define DTIOCGETAUXBASE _IOWR('D', 7, struct dtioc_getaux) 136 137 #ifdef _KERNEL 138 139 #include <sys/mutex.h> 140 #include <sys/queue.h> 141 #include <sys/smr.h> 142 143 /* Flags that make sense for all providers. */ 144 #define DTEVT_COMMON (DTEVT_EXECNAME|DTEVT_KSTACK|DTEVT_USTACK) 145 146 #define M_DT M_DEVBUF /* XXX FIXME */ 147 148 struct dt_softc; 149 150 /* 151 * Probe control block, possibly per-CPU. 152 * 153 * At least a PCB is allocated for each probe enabled via the DTIOCPRBENABLE 154 * ioctl(2). It will hold the events written when the probe fires until 155 * userland read(2)s them. 156 * 157 * Locks used to protect struct members in this file: 158 * D dt_lock 159 * I immutable after creation 160 * K kernel lock 161 * K,S kernel lock for writing and SMR for reading 162 * m per-pcb mutex 163 * c owned (read & modified) by a single CPU 164 */ 165 struct dt_pcb { 166 SMR_SLIST_ENTRY(dt_pcb) dp_pnext; /* [K,S] next PCB per probe */ 167 TAILQ_ENTRY(dt_pcb) dp_snext; /* [K] next PCB per softc */ 168 169 struct dt_softc *dp_sc; /* [I] related softc */ 170 struct dt_probe *dp_dtp; /* [I] related probe */ 171 uint64_t dp_evtflags; /* [I] event states to record */ 172 173 /* Provider specific fields. */ 174 struct clockintr dp_clockintr; /* [D] profiling handle */ 175 uint64_t dp_nsecs; /* [I] profiling period */ 176 struct cpu_info *dp_cpu; /* [I] on which CPU */ 177 }; 178 179 TAILQ_HEAD(dt_pcb_list, dt_pcb); 180 181 struct dt_pcb *dt_pcb_alloc(struct dt_probe *, struct dt_softc *); 182 void dt_pcb_free(struct dt_pcb *); 183 void dt_pcb_purge(struct dt_pcb_list *); 184 185 void dt_pcb_ring_skiptick(struct dt_pcb *, unsigned int); 186 struct dt_evt *dt_pcb_ring_get(struct dt_pcb *, int); 187 void dt_pcb_ring_consume(struct dt_pcb *, struct dt_evt *); 188 189 /* 190 * Probes are entry points in the system where events can be recorded. 191 * 192 * Locks used to protect struct members in this file: 193 * I immutable after creation 194 * K kernel lock 195 * D dt_lock 196 * D,S dt_lock for writing and SMR for reading 197 * M dtp mutex 198 */ 199 struct dt_probe { 200 SIMPLEQ_ENTRY(dt_probe) dtp_next; /* [K] global list of probes */ 201 SMR_SLIST_HEAD(, dt_pcb) dtp_pcbs; /* [D,S] list of enabled PCBs */ 202 struct dt_provider *dtp_prov; /* [I] its to provider */ 203 const char *dtp_func; /* [I] probe function */ 204 const char *dtp_name; /* [I] probe name */ 205 uint32_t dtp_pbn; /* [I] unique ID */ 206 volatile uint32_t dtp_recording; /* [d] is it recording? */ 207 unsigned dtp_ref; /* [m] # of PCBs referencing the probe */ 208 209 /* Provider specific fields. */ 210 int dtp_sysnum; /* [I] related # of syscall */ 211 const char *dtp_argtype[DTMAXARGTYPES]; 212 /* [I] type of arguments */ 213 int dtp_nargs; /* [I] # of arguments */ 214 vaddr_t dtp_addr; /* [I] address of breakpoint */ 215 }; 216 217 218 /* 219 * Providers expose a set of probes and a method to record events. 220 */ 221 struct dt_provider { 222 const char *dtpv_name; /* [I] provider name */ 223 volatile uint32_t dtpv_recording;/* [D] # of recording PCBs */ 224 225 int (*dtpv_alloc)(struct dt_probe *, struct dt_softc *, 226 struct dt_pcb_list *, struct dtioc_req *); 227 int (*dtpv_enter)(struct dt_provider *, ...); 228 void (*dtpv_leave)(struct dt_provider *, ...); 229 int (*dtpv_dealloc)(struct dt_probe *, struct dt_softc *, 230 struct dtioc_req *); 231 }; 232 233 extern struct dt_provider dt_prov_kprobe; 234 235 int dt_prov_profile_init(void); 236 int dt_prov_syscall_init(void); 237 int dt_prov_static_init(void); 238 int dt_prov_kprobe_init(void); 239 240 struct dt_probe *dt_dev_alloc_probe(const char *, const char *, 241 struct dt_provider *); 242 void dt_dev_register_probe(struct dt_probe *); 243 244 void dt_clock(struct clockrequest *, void *, void *); 245 246 extern volatile uint32_t dt_tracing; /* currently tracing? */ 247 248 #define DT_ENTER(provname, args...) do { \ 249 extern struct dt_provider dt_prov_ ## provname ; \ 250 struct dt_provider *dtpv = &dt_prov_ ## provname ; \ 251 \ 252 if (__predict_false(dt_tracing) && \ 253 __predict_false(dtpv->dtpv_recording)) { \ 254 dtpv->dtpv_enter(dtpv, args); \ 255 } \ 256 } while (0) 257 258 #define DT_LEAVE(provname, args...) do { \ 259 extern struct dt_provider dt_prov_ ## provname ; \ 260 struct dt_provider *dtpv = &dt_prov_ ## provname ; \ 261 \ 262 if (__predict_false(dt_tracing) && \ 263 __predict_false(dtpv->dtpv_recording)) { \ 264 dtpv->dtpv_leave(dtpv, args); \ 265 } \ 266 } while (0) 267 268 #define _DT_STATIC_P(func, name) (dt_static_##func##_##name) 269 270 /* 271 * Probe definition for the static provider. 272 */ 273 #define _DT_STATIC_PROBEN(func, name, arg0, arg1, arg2, arg3, arg4, n) \ 274 struct dt_probe _DT_STATIC_P(func, name) = { \ 275 .dtp_next = { NULL }, \ 276 .dtp_pcbs = { NULL }, \ 277 .dtp_prov = &dt_prov_static, \ 278 .dtp_func = #func, \ 279 .dtp_name = #name, \ 280 .dtp_pbn = 0, \ 281 .dtp_sysnum = 0, \ 282 .dtp_argtype = { arg0, arg1, arg2, arg3, arg4 }, \ 283 .dtp_nargs = n, \ 284 } \ 285 286 #define DT_STATIC_PROBE0(func, name) \ 287 _DT_STATIC_PROBEN(func, name, NULL, NULL, NULL, NULL, NULL, 0) 288 289 #define DT_STATIC_PROBE1(func, name, arg0) \ 290 _DT_STATIC_PROBEN(func, name, arg0, NULL, NULL, NULL, NULL, 1) 291 292 #define DT_STATIC_PROBE2(func, name, arg0, arg1) \ 293 _DT_STATIC_PROBEN(func, name, arg0, arg1, NULL, NULL, NULL, 2) 294 295 #define DT_STATIC_PROBE3(func, name, arg0, arg1, arg2) \ 296 _DT_STATIC_PROBEN(func, name, arg0, arg1, arg2, NULL, NULL, 3) 297 298 #define DT_STATIC_PROBE4(func, name, arg0, arg1, arg2, arg3) \ 299 _DT_STATIC_PROBEN(func, name, arg0, arg1, arg2, arg3, NULL, 4) 300 301 #define DT_STATIC_PROBE5(func, name, arg0, arg1, arg2, arg3, arg4) \ 302 _DT_STATIC_PROBEN(func, name, arg0, arg1, arg2, arg3, arg4, 5) 303 304 #define DT_STATIC_ENTER(func, name, args...) do { \ 305 extern struct dt_probe _DT_STATIC_P(func, name); \ 306 struct dt_probe *dtp = &_DT_STATIC_P(func, name); \ 307 \ 308 if (__predict_false(dt_tracing) && \ 309 __predict_false(dtp->dtp_recording)) { \ 310 struct dt_provider *dtpv = dtp->dtp_prov; \ 311 \ 312 dtpv->dtpv_enter(dtpv, dtp, args); \ 313 } \ 314 } while (0) 315 316 #define _DT_INDEX_P(func) (dtps_index_##func) 317 318 #define DT_INDEX_ENTER(func, index, args...) do { \ 319 extern struct dt_probe **_DT_INDEX_P(func); \ 320 \ 321 if (__predict_false(dt_tracing) && \ 322 __predict_false(index > 0) && \ 323 __predict_true(_DT_INDEX_P(func) != NULL)) { \ 324 struct dt_probe *dtp = _DT_INDEX_P(func)[index]; \ 325 \ 326 if(__predict_false(dtp->dtp_recording)) { \ 327 struct dt_provider *dtpv = dtp->dtp_prov; \ 328 \ 329 dtpv->dtpv_enter(dtpv, dtp, args); \ 330 } \ 331 } \ 332 } while (0) 333 334 #endif /* !_KERNEL */ 335 #endif /* !_DT_H_ */ 336