1 /* $OpenBSD: dtvar.h,v 1.19 2024/04/06 11:18:02 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 dtrq_rate; /* number of ticks */ 113 uint64_t dtrq_evtflags; /* states to record */ 114 }; 115 116 struct dtioc_stat { 117 uint64_t dtst_readevt; /* events read */ 118 uint64_t dtst_dropevt; /* events dropped */ 119 }; 120 121 struct dtioc_getaux { 122 pid_t dtga_pid; /* process to inspect */ 123 unsigned long dtga_auxbase; /* AUX_base value */ 124 }; 125 126 #define DTIOCGPLIST _IOWR('D', 1, struct dtioc_probe) 127 #define DTIOCGSTATS _IOR('D', 2, struct dtioc_stat) 128 #define DTIOCRECORD _IOW('D', 3, int) 129 #define DTIOCPRBENABLE _IOW('D', 4, struct dtioc_req) 130 #define DTIOCPRBDISABLE _IOW('D', 5, struct dtioc_req) 131 #define DTIOCGARGS _IOWR('D', 6, struct dtioc_arg) 132 #define DTIOCGETAUXBASE _IOWR('D', 7, struct dtioc_getaux) 133 134 #ifdef _KERNEL 135 136 #include <sys/mutex.h> 137 #include <sys/queue.h> 138 #include <sys/smr.h> 139 140 /* Flags that make sense for all providers. */ 141 #define DTEVT_COMMON (DTEVT_EXECNAME|DTEVT_KSTACK|DTEVT_USTACK) 142 143 #define M_DT M_DEVBUF /* XXX FIXME */ 144 145 struct dt_softc; 146 147 /* 148 * Probe control block, possibly per-CPU. 149 * 150 * At least a PCB is allocated for each probe enabled via the DTIOCPRBENABLE 151 * ioctl(2). It will hold the events written when the probe fires until 152 * userland read(2)s them. 153 * 154 * Locks used to protect struct members in this file: 155 * D dt_lock 156 * I immutable after creation 157 * K kernel lock 158 * K,S kernel lock for writing and SMR for reading 159 * m per-pcb mutex 160 * c owned (read & modified) by a single CPU 161 */ 162 struct dt_pcb { 163 SMR_SLIST_ENTRY(dt_pcb) dp_pnext; /* [K,S] next PCB per probe */ 164 TAILQ_ENTRY(dt_pcb) dp_snext; /* [K] next PCB per softc */ 165 166 /* Event states ring */ 167 unsigned int dp_prod; /* [m] read index */ 168 unsigned int dp_cons; /* [m] write index */ 169 struct dt_evt *dp_ring; /* [m] ring of event states */ 170 struct mutex dp_mtx; 171 172 struct dt_softc *dp_sc; /* [I] related softc */ 173 struct dt_probe *dp_dtp; /* [I] related probe */ 174 uint64_t dp_evtflags; /* [I] event states to record */ 175 176 /* Provider specific fields. */ 177 struct clockintr dp_clockintr; /* [D] profiling handle */ 178 uint64_t dp_nsecs; /* [I] profiling period */ 179 struct cpu_info *dp_cpu; /* [I] on which CPU */ 180 181 /* Counters */ 182 uint64_t dp_dropevt; /* [m] # dropped event */ 183 }; 184 185 TAILQ_HEAD(dt_pcb_list, dt_pcb); 186 187 struct dt_pcb *dt_pcb_alloc(struct dt_probe *, struct dt_softc *); 188 void dt_pcb_free(struct dt_pcb *); 189 void dt_pcb_purge(struct dt_pcb_list *); 190 191 struct dt_evt *dt_pcb_ring_get(struct dt_pcb *, int); 192 void dt_pcb_ring_consume(struct dt_pcb *, struct dt_evt *); 193 194 /* 195 * Probes are entry points in the system where events can be recorded. 196 * 197 * Locks used to protect struct members in this file: 198 * I immutable after creation 199 * K kernel lock 200 * D dt_lock 201 * D,S dt_lock for writing and SMR for reading 202 * M dtp mutex 203 */ 204 struct dt_probe { 205 SIMPLEQ_ENTRY(dt_probe) dtp_next; /* [K] global list of probes */ 206 SMR_SLIST_HEAD(, dt_pcb) dtp_pcbs; /* [D,S] list of enabled PCBs */ 207 struct dt_provider *dtp_prov; /* [I] its to provider */ 208 const char *dtp_func; /* [I] probe function */ 209 const char *dtp_name; /* [I] probe name */ 210 uint32_t dtp_pbn; /* [I] unique ID */ 211 volatile uint32_t dtp_recording; /* [d] is it recording? */ 212 unsigned dtp_ref; /* [m] # of PCBs referencing the probe */ 213 214 /* Provider specific fields. */ 215 int dtp_sysnum; /* [I] related # of syscall */ 216 const char *dtp_argtype[DTMAXARGTYPES]; 217 /* [I] type of arguments */ 218 int dtp_nargs; /* [I] # of arguments */ 219 vaddr_t dtp_addr; /* [I] address of breakpoint */ 220 }; 221 222 223 /* 224 * Providers expose a set of probes and a method to record events. 225 */ 226 struct dt_provider { 227 const char *dtpv_name; /* [I] provider name */ 228 volatile uint32_t dtpv_recording;/* [D] # of recording PCBs */ 229 230 int (*dtpv_alloc)(struct dt_probe *, struct dt_softc *, 231 struct dt_pcb_list *, struct dtioc_req *); 232 int (*dtpv_enter)(struct dt_provider *, ...); 233 void (*dtpv_leave)(struct dt_provider *, ...); 234 int (*dtpv_dealloc)(struct dt_probe *, struct dt_softc *, 235 struct dtioc_req *); 236 }; 237 238 extern struct dt_provider dt_prov_kprobe; 239 240 int dt_prov_profile_init(void); 241 int dt_prov_syscall_init(void); 242 int dt_prov_static_init(void); 243 int dt_prov_kprobe_init(void); 244 245 struct dt_probe *dt_dev_alloc_probe(const char *, const char *, 246 struct dt_provider *); 247 void dt_dev_register_probe(struct dt_probe *); 248 249 void dt_clock(struct clockrequest *, void *, void *); 250 251 extern volatile uint32_t dt_tracing; /* currently tracing? */ 252 253 #define DT_ENTER(provname, args...) do { \ 254 extern struct dt_provider dt_prov_ ## provname ; \ 255 struct dt_provider *dtpv = &dt_prov_ ## provname ; \ 256 \ 257 if (__predict_false(dt_tracing) && \ 258 __predict_false(dtpv->dtpv_recording)) { \ 259 dtpv->dtpv_enter(dtpv, args); \ 260 } \ 261 } while (0) 262 263 #define DT_LEAVE(provname, args...) do { \ 264 extern struct dt_provider dt_prov_ ## provname ; \ 265 struct dt_provider *dtpv = &dt_prov_ ## provname ; \ 266 \ 267 if (__predict_false(dt_tracing) && \ 268 __predict_false(dtpv->dtpv_recording)) { \ 269 dtpv->dtpv_leave(dtpv, args); \ 270 } \ 271 } while (0) 272 273 #define _DT_STATIC_P(func, name) (dt_static_##func##_##name) 274 275 /* 276 * Probe definition for the static provider. 277 */ 278 #define _DT_STATIC_PROBEN(func, name, arg0, arg1, arg2, arg3, arg4, n) \ 279 struct dt_probe _DT_STATIC_P(func, name) = { \ 280 .dtp_next = { NULL }, \ 281 .dtp_pcbs = { NULL }, \ 282 .dtp_prov = &dt_prov_static, \ 283 .dtp_func = #func, \ 284 .dtp_name = #name, \ 285 .dtp_pbn = 0, \ 286 .dtp_sysnum = 0, \ 287 .dtp_argtype = { arg0, arg1, arg2, arg3, arg4 }, \ 288 .dtp_nargs = n, \ 289 } \ 290 291 #define DT_STATIC_PROBE0(func, name) \ 292 _DT_STATIC_PROBEN(func, name, NULL, NULL, NULL, NULL, NULL, 0) 293 294 #define DT_STATIC_PROBE1(func, name, arg0) \ 295 _DT_STATIC_PROBEN(func, name, arg0, NULL, NULL, NULL, NULL, 1) 296 297 #define DT_STATIC_PROBE2(func, name, arg0, arg1) \ 298 _DT_STATIC_PROBEN(func, name, arg0, arg1, NULL, NULL, NULL, 2) 299 300 #define DT_STATIC_PROBE3(func, name, arg0, arg1, arg2) \ 301 _DT_STATIC_PROBEN(func, name, arg0, arg1, arg2, NULL, NULL, 3) 302 303 #define DT_STATIC_PROBE4(func, name, arg0, arg1, arg2, arg3) \ 304 _DT_STATIC_PROBEN(func, name, arg0, arg1, arg2, arg3, NULL, 4) 305 306 #define DT_STATIC_PROBE5(func, name, arg0, arg1, arg2, arg3, arg4) \ 307 _DT_STATIC_PROBEN(func, name, arg0, arg1, arg2, arg3, arg4, 5) 308 309 #define DT_STATIC_ENTER(func, name, args...) do { \ 310 extern struct dt_probe _DT_STATIC_P(func, name); \ 311 struct dt_probe *dtp = &_DT_STATIC_P(func, name); \ 312 \ 313 if (__predict_false(dt_tracing) && \ 314 __predict_false(dtp->dtp_recording)) { \ 315 struct dt_provider *dtpv = dtp->dtp_prov; \ 316 \ 317 dtpv->dtpv_enter(dtpv, dtp, args); \ 318 } \ 319 } while (0) 320 321 #define _DT_INDEX_P(func) (dtps_index_##func) 322 323 #define DT_INDEX_ENTER(func, index, args...) do { \ 324 extern struct dt_probe **_DT_INDEX_P(func); \ 325 \ 326 if (__predict_false(dt_tracing) && \ 327 __predict_false(index > 0) && \ 328 __predict_true(_DT_INDEX_P(func) != NULL)) { \ 329 struct dt_probe *dtp = _DT_INDEX_P(func)[index]; \ 330 \ 331 if(__predict_false(dtp->dtp_recording)) { \ 332 struct dt_provider *dtpv = dtp->dtp_prov; \ 333 \ 334 dtpv->dtpv_enter(dtpv, dtp, args); \ 335 } \ 336 } \ 337 } while (0) 338 339 #endif /* !_KERNEL */ 340 #endif /* !_DT_H_ */ 341