1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * explicitly define DTRACE_ERRDEBUG to pull in definition of dtrace_errhash_t
28 * explicitly define _STDARG_H to avoid stdarg.h/varargs.h u/k defn conflict
29 */
30 #define DTRACE_ERRDEBUG
31 #define _STDARG_H
32
33 #include <mdb/mdb_param.h>
34 #include <mdb/mdb_modapi.h>
35 #include <mdb/mdb_ks.h>
36 #include <sys/dtrace_impl.h>
37 #include <sys/vmem_impl.h>
38 #include <sys/ddi_impldefs.h>
39 #include <sys/sysmacros.h>
40 #include <sys/kobj.h>
41 #include <dtrace.h>
42 #include <alloca.h>
43 #include <ctype.h>
44 #include <errno.h>
45 #include <math.h>
46
47 /*ARGSUSED*/
48 int
id2probe(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)49 id2probe(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
50 {
51 uintptr_t probe = NULL;
52 uintptr_t probes;
53
54 if (!(flags & DCMD_ADDRSPEC))
55 return (DCMD_USAGE);
56
57 if (addr == DTRACE_IDNONE || addr > UINT32_MAX)
58 goto out;
59
60 if (mdb_readvar(&probes, "dtrace_probes") == -1) {
61 mdb_warn("failed to read 'dtrace_probes'");
62 return (DCMD_ERR);
63 }
64
65 probes += (addr - 1) * sizeof (dtrace_probe_t *);
66
67 if (mdb_vread(&probe, sizeof (uintptr_t), probes) == -1) {
68 mdb_warn("failed to read dtrace_probes[%d]", addr - 1);
69 return (DCMD_ERR);
70 }
71
72 out:
73 mdb_printf("%p\n", probe);
74 return (DCMD_OK);
75 }
76
77 void
dtrace_help(void)78 dtrace_help(void)
79 {
80
81 mdb_printf("Given a dtrace_state_t structure that represents a "
82 "DTrace consumer, prints\n"
83 "dtrace(1M)-like output for in-kernel DTrace data. (The "
84 "dtrace_state_t\n"
85 "structures for all DTrace consumers may be obtained by running "
86 "the \n"
87 "::dtrace_state dcmd.) When data is present on multiple CPUs, "
88 "data are\n"
89 "presented in CPU order, with records within each CPU ordered "
90 "oldest to \n"
91 "youngest. Options:\n\n"
92 "-c cpu Only provide output for specified CPU.\n");
93 }
94
95 static int
dtracemdb_eprobe(dtrace_state_t * state,dtrace_eprobedesc_t * epd)96 dtracemdb_eprobe(dtrace_state_t *state, dtrace_eprobedesc_t *epd)
97 {
98 dtrace_epid_t epid = epd->dtepd_epid;
99 dtrace_probe_t probe;
100 dtrace_ecb_t ecb;
101 uintptr_t addr, paddr, ap;
102 dtrace_action_t act;
103 int nactions, nrecs;
104
105 addr = (uintptr_t)state->dts_ecbs +
106 (epid - 1) * sizeof (dtrace_ecb_t *);
107
108 if (mdb_vread(&addr, sizeof (addr), addr) == -1) {
109 mdb_warn("failed to read ecb for epid %d", epid);
110 return (-1);
111 }
112
113 if (addr == NULL) {
114 mdb_warn("epid %d doesn't match an ecb\n", epid);
115 return (-1);
116 }
117
118 if (mdb_vread(&ecb, sizeof (ecb), addr) == -1) {
119 mdb_warn("failed to read ecb at %p", addr);
120 return (-1);
121 }
122
123 paddr = (uintptr_t)ecb.dte_probe;
124
125 if (mdb_vread(&probe, sizeof (probe), paddr) == -1) {
126 mdb_warn("failed to read probe for ecb %p", addr);
127 return (-1);
128 }
129
130 /*
131 * This is a little painful: in order to find the number of actions,
132 * we need to first walk through them.
133 */
134 for (ap = (uintptr_t)ecb.dte_action, nactions = 0; ap != NULL; ) {
135 if (mdb_vread(&act, sizeof (act), ap) == -1) {
136 mdb_warn("failed to read action %p on ecb %p",
137 ap, addr);
138 return (-1);
139 }
140
141 if (!DTRACEACT_ISAGG(act.dta_kind) && !act.dta_intuple)
142 nactions++;
143
144 ap = (uintptr_t)act.dta_next;
145 }
146
147 nrecs = epd->dtepd_nrecs;
148 epd->dtepd_nrecs = nactions;
149 epd->dtepd_probeid = probe.dtpr_id;
150 epd->dtepd_uarg = ecb.dte_uarg;
151 epd->dtepd_size = ecb.dte_size;
152
153 for (ap = (uintptr_t)ecb.dte_action, nactions = 0; ap != NULL; ) {
154 if (mdb_vread(&act, sizeof (act), ap) == -1) {
155 mdb_warn("failed to read action %p on ecb %p",
156 ap, addr);
157 return (-1);
158 }
159
160 if (!DTRACEACT_ISAGG(act.dta_kind) && !act.dta_intuple) {
161 if (nrecs-- == 0)
162 break;
163
164 epd->dtepd_rec[nactions++] = act.dta_rec;
165 }
166
167 ap = (uintptr_t)act.dta_next;
168 }
169
170 return (0);
171 }
172
173 /*ARGSUSED*/
174 static int
dtracemdb_probe(dtrace_state_t * state,dtrace_probedesc_t * pd)175 dtracemdb_probe(dtrace_state_t *state, dtrace_probedesc_t *pd)
176 {
177 uintptr_t base, addr, paddr, praddr;
178 int nprobes, i;
179 dtrace_probe_t probe;
180 dtrace_provider_t prov;
181
182 if (pd->dtpd_id == DTRACE_IDNONE)
183 pd->dtpd_id++;
184
185 if (mdb_readvar(&base, "dtrace_probes") == -1) {
186 mdb_warn("failed to read 'dtrace_probes'");
187 return (-1);
188 }
189
190 if (mdb_readvar(&nprobes, "dtrace_nprobes") == -1) {
191 mdb_warn("failed to read 'dtrace_nprobes'");
192 return (-1);
193 }
194
195 for (i = pd->dtpd_id; i <= nprobes; i++) {
196 addr = base + (i - 1) * sizeof (dtrace_probe_t *);
197
198 if (mdb_vread(&paddr, sizeof (paddr), addr) == -1) {
199 mdb_warn("couldn't read probe pointer at %p", addr);
200 return (-1);
201 }
202
203 if (paddr != NULL)
204 break;
205 }
206
207 if (paddr == NULL) {
208 errno = ESRCH;
209 return (-1);
210 }
211
212 if (mdb_vread(&probe, sizeof (probe), paddr) == -1) {
213 mdb_warn("couldn't read probe at %p", paddr);
214 return (-1);
215 }
216
217 pd->dtpd_id = probe.dtpr_id;
218
219 if (mdb_vread(pd->dtpd_name, DTRACE_NAMELEN,
220 (uintptr_t)probe.dtpr_name) == -1) {
221 mdb_warn("failed to read probe name for probe %p", paddr);
222 return (-1);
223 }
224
225 if (mdb_vread(pd->dtpd_func, DTRACE_FUNCNAMELEN,
226 (uintptr_t)probe.dtpr_func) == -1) {
227 mdb_warn("failed to read function name for probe %p", paddr);
228 return (-1);
229 }
230
231 if (mdb_vread(pd->dtpd_mod, DTRACE_MODNAMELEN,
232 (uintptr_t)probe.dtpr_mod) == -1) {
233 mdb_warn("failed to read module name for probe %p", paddr);
234 return (-1);
235 }
236
237 praddr = (uintptr_t)probe.dtpr_provider;
238
239 if (mdb_vread(&prov, sizeof (prov), praddr) == -1) {
240 mdb_warn("failed to read provider for probe %p", paddr);
241 return (-1);
242 }
243
244 if (mdb_vread(pd->dtpd_provider, DTRACE_PROVNAMELEN,
245 (uintptr_t)prov.dtpv_name) == -1) {
246 mdb_warn("failed to read provider name for probe %p", paddr);
247 return (-1);
248 }
249
250 return (0);
251 }
252
253 /*ARGSUSED*/
254 static int
dtracemdb_aggdesc(dtrace_state_t * state,dtrace_aggdesc_t * agd)255 dtracemdb_aggdesc(dtrace_state_t *state, dtrace_aggdesc_t *agd)
256 {
257 dtrace_aggid_t aggid = agd->dtagd_id;
258 dtrace_aggregation_t agg;
259 dtrace_ecb_t ecb;
260 uintptr_t addr, eaddr, ap, last;
261 dtrace_action_t act;
262 dtrace_recdesc_t *lrec;
263 int nactions, nrecs;
264
265 addr = (uintptr_t)state->dts_aggregations +
266 (aggid - 1) * sizeof (dtrace_aggregation_t *);
267
268 if (mdb_vread(&addr, sizeof (addr), addr) == -1) {
269 mdb_warn("failed to read aggregation for aggid %d", aggid);
270 return (-1);
271 }
272
273 if (addr == NULL) {
274 mdb_warn("aggid %d doesn't match an aggregation\n", aggid);
275 return (-1);
276 }
277
278 if (mdb_vread(&agg, sizeof (agg), addr) == -1) {
279 mdb_warn("failed to read aggregation at %p", addr);
280 return (-1);
281 }
282
283 eaddr = (uintptr_t)agg.dtag_ecb;
284
285 if (mdb_vread(&ecb, sizeof (ecb), eaddr) == -1) {
286 mdb_warn("failed to read ecb for aggregation %p", addr);
287 return (-1);
288 }
289
290 last = (uintptr_t)addr + offsetof(dtrace_aggregation_t, dtag_action);
291
292 /*
293 * This is a little painful: in order to find the number of actions,
294 * we need to first walk through them.
295 */
296 ap = (uintptr_t)agg.dtag_first;
297 nactions = 0;
298
299 for (;;) {
300 if (mdb_vread(&act, sizeof (act), ap) == -1) {
301 mdb_warn("failed to read action %p on aggregation %p",
302 ap, addr);
303 return (-1);
304 }
305
306 nactions++;
307
308 if (ap == last)
309 break;
310
311 ap = (uintptr_t)act.dta_next;
312 }
313
314 lrec = &act.dta_rec;
315 agd->dtagd_size = lrec->dtrd_offset + lrec->dtrd_size - agg.dtag_base;
316
317 nrecs = agd->dtagd_nrecs;
318 agd->dtagd_nrecs = nactions;
319 agd->dtagd_epid = ecb.dte_epid;
320
321 ap = (uintptr_t)agg.dtag_first;
322 nactions = 0;
323
324 for (;;) {
325 dtrace_recdesc_t rec;
326
327 if (mdb_vread(&act, sizeof (act), ap) == -1) {
328 mdb_warn("failed to read action %p on aggregation %p",
329 ap, addr);
330 return (-1);
331 }
332
333 if (nrecs-- == 0)
334 break;
335
336 rec = act.dta_rec;
337 rec.dtrd_offset -= agg.dtag_base;
338 rec.dtrd_uarg = 0;
339 agd->dtagd_rec[nactions++] = rec;
340
341 if (ap == last)
342 break;
343
344 ap = (uintptr_t)act.dta_next;
345 }
346
347 return (0);
348 }
349
350 static int
dtracemdb_bufsnap(dtrace_buffer_t * which,dtrace_bufdesc_t * desc)351 dtracemdb_bufsnap(dtrace_buffer_t *which, dtrace_bufdesc_t *desc)
352 {
353 uintptr_t addr;
354 size_t bufsize;
355 dtrace_buffer_t buf;
356 caddr_t data = desc->dtbd_data;
357 processorid_t max_cpuid, cpu = desc->dtbd_cpu;
358
359 if (mdb_readvar(&max_cpuid, "max_cpuid") == -1) {
360 mdb_warn("failed to read 'max_cpuid'");
361 errno = EIO;
362 return (-1);
363 }
364
365 if (cpu < 0 || cpu > max_cpuid) {
366 errno = EINVAL;
367 return (-1);
368 }
369
370 addr = (uintptr_t)which + cpu * sizeof (dtrace_buffer_t);
371
372 if (mdb_vread(&buf, sizeof (buf), addr) == -1) {
373 mdb_warn("failed to read buffer description at %p", addr);
374 errno = EIO;
375 return (-1);
376 }
377
378 if (buf.dtb_tomax == NULL) {
379 errno = ENOENT;
380 return (-1);
381 }
382
383 if (buf.dtb_flags & DTRACEBUF_WRAPPED) {
384 bufsize = buf.dtb_size;
385 } else {
386 bufsize = buf.dtb_offset;
387 }
388
389 if (mdb_vread(data, bufsize, (uintptr_t)buf.dtb_tomax) == -1) {
390 mdb_warn("couldn't read buffer for CPU %d", cpu);
391 errno = EIO;
392 return (-1);
393 }
394
395 if (buf.dtb_offset > buf.dtb_size) {
396 mdb_warn("buffer for CPU %d has corrupt offset\n", cpu);
397 errno = EIO;
398 return (-1);
399 }
400
401 if (buf.dtb_flags & DTRACEBUF_WRAPPED) {
402 if (buf.dtb_xamot_offset > buf.dtb_size) {
403 mdb_warn("ringbuffer for CPU %d has corrupt "
404 "wrapped offset\n", cpu);
405 errno = EIO;
406 return (-1);
407 }
408
409 /*
410 * If the ring buffer has wrapped, it needs to be polished.
411 * See the comment in dtrace_buffer_polish() for details.
412 */
413 if (buf.dtb_offset < buf.dtb_xamot_offset) {
414 bzero(data + buf.dtb_offset,
415 buf.dtb_xamot_offset - buf.dtb_offset);
416 }
417
418 if (buf.dtb_offset > buf.dtb_xamot_offset) {
419 bzero(data + buf.dtb_offset,
420 buf.dtb_size - buf.dtb_offset);
421 bzero(data, buf.dtb_xamot_offset);
422 }
423
424 desc->dtbd_oldest = buf.dtb_xamot_offset;
425 } else {
426 desc->dtbd_oldest = 0;
427 }
428
429 desc->dtbd_size = bufsize;
430 desc->dtbd_drops = buf.dtb_drops;
431 desc->dtbd_errors = buf.dtb_errors;
432
433 return (0);
434 }
435
436 /*
437 * This is essentially identical to its cousin in the kernel -- with the
438 * notable exception that we automatically set DTRACEOPT_GRABANON if this
439 * state is an anonymous enabling.
440 */
441 static dof_hdr_t *
dtracemdb_dof_create(dtrace_state_t * state,int isanon)442 dtracemdb_dof_create(dtrace_state_t *state, int isanon)
443 {
444 dof_hdr_t *dof;
445 dof_sec_t *sec;
446 dof_optdesc_t *opt;
447 int i, len = sizeof (dof_hdr_t) +
448 roundup(sizeof (dof_sec_t), sizeof (uint64_t)) +
449 sizeof (dof_optdesc_t) * DTRACEOPT_MAX;
450
451 dof = mdb_zalloc(len, UM_SLEEP);
452 dof->dofh_ident[DOF_ID_MAG0] = DOF_MAG_MAG0;
453 dof->dofh_ident[DOF_ID_MAG1] = DOF_MAG_MAG1;
454 dof->dofh_ident[DOF_ID_MAG2] = DOF_MAG_MAG2;
455 dof->dofh_ident[DOF_ID_MAG3] = DOF_MAG_MAG3;
456
457 dof->dofh_ident[DOF_ID_MODEL] = DOF_MODEL_NATIVE;
458 dof->dofh_ident[DOF_ID_ENCODING] = DOF_ENCODE_NATIVE;
459 dof->dofh_ident[DOF_ID_VERSION] = DOF_VERSION;
460 dof->dofh_ident[DOF_ID_DIFVERS] = DIF_VERSION;
461 dof->dofh_ident[DOF_ID_DIFIREG] = DIF_DIR_NREGS;
462 dof->dofh_ident[DOF_ID_DIFTREG] = DIF_DTR_NREGS;
463
464 dof->dofh_flags = 0;
465 dof->dofh_hdrsize = sizeof (dof_hdr_t);
466 dof->dofh_secsize = sizeof (dof_sec_t);
467 dof->dofh_secnum = 1; /* only DOF_SECT_OPTDESC */
468 dof->dofh_secoff = sizeof (dof_hdr_t);
469 dof->dofh_loadsz = len;
470 dof->dofh_filesz = len;
471 dof->dofh_pad = 0;
472
473 /*
474 * Fill in the option section header...
475 */
476 sec = (dof_sec_t *)((uintptr_t)dof + sizeof (dof_hdr_t));
477 sec->dofs_type = DOF_SECT_OPTDESC;
478 sec->dofs_align = sizeof (uint64_t);
479 sec->dofs_flags = DOF_SECF_LOAD;
480 sec->dofs_entsize = sizeof (dof_optdesc_t);
481
482 opt = (dof_optdesc_t *)((uintptr_t)sec +
483 roundup(sizeof (dof_sec_t), sizeof (uint64_t)));
484
485 sec->dofs_offset = (uintptr_t)opt - (uintptr_t)dof;
486 sec->dofs_size = sizeof (dof_optdesc_t) * DTRACEOPT_MAX;
487
488 for (i = 0; i < DTRACEOPT_MAX; i++) {
489 opt[i].dofo_option = i;
490 opt[i].dofo_strtab = DOF_SECIDX_NONE;
491 opt[i].dofo_value = state->dts_options[i];
492 }
493
494 if (isanon)
495 opt[DTRACEOPT_GRABANON].dofo_value = 1;
496
497 return (dof);
498 }
499
500 static int
dtracemdb_format(dtrace_state_t * state,dtrace_fmtdesc_t * desc)501 dtracemdb_format(dtrace_state_t *state, dtrace_fmtdesc_t *desc)
502 {
503 uintptr_t addr, faddr;
504 char c;
505 int len = 0;
506
507 if (desc->dtfd_format == 0 || desc->dtfd_format > state->dts_nformats) {
508 errno = EINVAL;
509 return (-1);
510 }
511
512 faddr = (uintptr_t)state->dts_formats +
513 (desc->dtfd_format - 1) * sizeof (char *);
514
515 if (mdb_vread(&addr, sizeof (addr), faddr) == -1) {
516 mdb_warn("failed to read format string pointer at %p", faddr);
517 return (-1);
518 }
519
520 do {
521 if (mdb_vread(&c, sizeof (c), addr + len++) == -1) {
522 mdb_warn("failed to read format string at %p", addr);
523 return (-1);
524 }
525 } while (c != '\0');
526
527 if (len > desc->dtfd_length) {
528 desc->dtfd_length = len;
529 return (0);
530 }
531
532 if (mdb_vread(desc->dtfd_string, len, addr) == -1) {
533 mdb_warn("failed to reread format string at %p", addr);
534 return (-1);
535 }
536
537 return (0);
538 }
539
540 static int
dtracemdb_status(dtrace_state_t * state,dtrace_status_t * status)541 dtracemdb_status(dtrace_state_t *state, dtrace_status_t *status)
542 {
543 dtrace_dstate_t *dstate;
544 int i, j;
545 uint64_t nerrs;
546 uintptr_t addr;
547 int ncpu;
548
549 if (mdb_readvar(&ncpu, "_ncpu") == -1) {
550 mdb_warn("failed to read '_ncpu'");
551 return (DCMD_ERR);
552 }
553
554 bzero(status, sizeof (dtrace_status_t));
555
556 if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE) {
557 errno = ENOENT;
558 return (-1);
559 }
560
561 /*
562 * For the MDB backend, we never set dtst_exiting or dtst_filled. This
563 * is by design: we don't want the library to try to stop tracing,
564 * because it doesn't particularly mean anything.
565 */
566 nerrs = state->dts_errors;
567 dstate = &state->dts_vstate.dtvs_dynvars;
568
569 for (i = 0; i < ncpu; i++) {
570 dtrace_dstate_percpu_t dcpu;
571 dtrace_buffer_t buf;
572
573 addr = (uintptr_t)&dstate->dtds_percpu[i];
574
575 if (mdb_vread(&dcpu, sizeof (dcpu), addr) == -1) {
576 mdb_warn("failed to read per-CPU dstate at %p", addr);
577 return (-1);
578 }
579
580 status->dtst_dyndrops += dcpu.dtdsc_drops;
581 status->dtst_dyndrops_dirty += dcpu.dtdsc_dirty_drops;
582 status->dtst_dyndrops_rinsing += dcpu.dtdsc_rinsing_drops;
583
584 addr = (uintptr_t)&state->dts_buffer[i];
585
586 if (mdb_vread(&buf, sizeof (buf), addr) == -1) {
587 mdb_warn("failed to read per-CPU buffer at %p", addr);
588 return (-1);
589 }
590
591 nerrs += buf.dtb_errors;
592
593 for (j = 0; j < state->dts_nspeculations; j++) {
594 dtrace_speculation_t spec;
595
596 addr = (uintptr_t)&state->dts_speculations[j];
597
598 if (mdb_vread(&spec, sizeof (spec), addr) == -1) {
599 mdb_warn("failed to read "
600 "speculation at %p", addr);
601 return (-1);
602 }
603
604 addr = (uintptr_t)&spec.dtsp_buffer[i];
605
606 if (mdb_vread(&buf, sizeof (buf), addr) == -1) {
607 mdb_warn("failed to read "
608 "speculative buffer at %p", addr);
609 return (-1);
610 }
611
612 status->dtst_specdrops += buf.dtb_xamot_drops;
613 }
614 }
615
616 status->dtst_specdrops_busy = state->dts_speculations_busy;
617 status->dtst_specdrops_unavail = state->dts_speculations_unavail;
618 status->dtst_errors = nerrs;
619
620 return (0);
621 }
622
623 typedef struct dtracemdb_data {
624 dtrace_state_t *dtmd_state;
625 char *dtmd_symstr;
626 char *dtmd_modstr;
627 uintptr_t dtmd_addr;
628 int dtmd_isanon;
629 } dtracemdb_data_t;
630
631 static int
dtracemdb_ioctl(void * varg,int cmd,void * arg)632 dtracemdb_ioctl(void *varg, int cmd, void *arg)
633 {
634 dtracemdb_data_t *data = varg;
635 dtrace_state_t *state = data->dtmd_state;
636
637 switch (cmd) {
638 case DTRACEIOC_CONF: {
639 dtrace_conf_t *conf = arg;
640
641 bzero(conf, sizeof (conf));
642 conf->dtc_difversion = DIF_VERSION;
643 conf->dtc_difintregs = DIF_DIR_NREGS;
644 conf->dtc_diftupregs = DIF_DTR_NREGS;
645 conf->dtc_ctfmodel = CTF_MODEL_NATIVE;
646
647 return (0);
648 }
649
650 case DTRACEIOC_DOFGET: {
651 dof_hdr_t *hdr = arg, *dof;
652
653 dof = dtracemdb_dof_create(state, data->dtmd_isanon);
654 bcopy(dof, hdr, MIN(hdr->dofh_loadsz, dof->dofh_loadsz));
655 mdb_free(dof, dof->dofh_loadsz);
656
657 return (0);
658 }
659
660 case DTRACEIOC_BUFSNAP:
661 return (dtracemdb_bufsnap(state->dts_buffer, arg));
662
663 case DTRACEIOC_AGGSNAP:
664 return (dtracemdb_bufsnap(state->dts_aggbuffer, arg));
665
666 case DTRACEIOC_AGGDESC:
667 return (dtracemdb_aggdesc(state, arg));
668
669 case DTRACEIOC_EPROBE:
670 return (dtracemdb_eprobe(state, arg));
671
672 case DTRACEIOC_PROBES:
673 return (dtracemdb_probe(state, arg));
674
675 case DTRACEIOC_FORMAT:
676 return (dtracemdb_format(state, arg));
677
678 case DTRACEIOC_STATUS:
679 return (dtracemdb_status(state, arg));
680
681 case DTRACEIOC_GO:
682 *(processorid_t *)arg = -1;
683 return (0);
684
685 case DTRACEIOC_ENABLE:
686 errno = ENOTTY; /* see dt_open.c:dtrace_go() */
687 return (-1);
688
689 case DTRACEIOC_PROVIDER:
690 case DTRACEIOC_PROBEMATCH:
691 errno = ESRCH;
692 return (-1);
693
694 default:
695 mdb_warn("unexpected ioctl 0x%x (%s)\n", cmd,
696 cmd == DTRACEIOC_PROVIDER ? "DTRACEIOC_PROVIDER" :
697 cmd == DTRACEIOC_PROBES ? "DTRACEIOC_PROBES" :
698 cmd == DTRACEIOC_BUFSNAP ? "DTRACEIOC_BUFSNAP" :
699 cmd == DTRACEIOC_PROBEMATCH ? "DTRACEIOC_PROBEMATCH" :
700 cmd == DTRACEIOC_ENABLE ? "DTRACEIOC_ENABLE" :
701 cmd == DTRACEIOC_AGGSNAP ? "DTRACEIOC_AGGSNAP" :
702 cmd == DTRACEIOC_EPROBE ? "DTRACEIOC_EPROBE" :
703 cmd == DTRACEIOC_PROBEARG ? "DTRACEIOC_PROBEARG" :
704 cmd == DTRACEIOC_CONF ? "DTRACEIOC_CONF" :
705 cmd == DTRACEIOC_STATUS ? "DTRACEIOC_STATUS" :
706 cmd == DTRACEIOC_GO ? "DTRACEIOC_GO" :
707 cmd == DTRACEIOC_STOP ? "DTRACEIOC_STOP" :
708 cmd == DTRACEIOC_AGGDESC ? "DTRACEIOC_AGGDESC" :
709 cmd == DTRACEIOC_FORMAT ? "DTRACEIOC_FORMAT" :
710 cmd == DTRACEIOC_DOFGET ? "DTRACEIOC_DOFGET" :
711 cmd == DTRACEIOC_REPLICATE ? "DTRACEIOC_REPLICATE" :
712 "???");
713 errno = ENXIO;
714 return (-1);
715 }
716 }
717
718 static int
dtracemdb_modctl(uintptr_t addr,const struct modctl * m,dtracemdb_data_t * data)719 dtracemdb_modctl(uintptr_t addr, const struct modctl *m, dtracemdb_data_t *data)
720 {
721 struct module mod;
722
723 if (m->mod_mp == NULL)
724 return (WALK_NEXT);
725
726 if (mdb_vread(&mod, sizeof (mod), (uintptr_t)m->mod_mp) == -1) {
727 mdb_warn("couldn't read modctl %p's module", addr);
728 return (WALK_NEXT);
729 }
730
731 if ((uintptr_t)mod.text > data->dtmd_addr)
732 return (WALK_NEXT);
733
734 if ((uintptr_t)mod.text + mod.text_size <= data->dtmd_addr)
735 return (WALK_NEXT);
736
737 if (mdb_readstr(data->dtmd_modstr, MDB_SYM_NAMLEN,
738 (uintptr_t)m->mod_modname) == -1)
739 return (WALK_ERR);
740
741 return (WALK_DONE);
742 }
743
744 static int
dtracemdb_lookup_by_addr(void * varg,GElf_Addr addr,GElf_Sym * symp,dtrace_syminfo_t * sip)745 dtracemdb_lookup_by_addr(void *varg, GElf_Addr addr, GElf_Sym *symp,
746 dtrace_syminfo_t *sip)
747 {
748 dtracemdb_data_t *data = varg;
749
750 if (data->dtmd_symstr == NULL) {
751 data->dtmd_symstr = mdb_zalloc(MDB_SYM_NAMLEN,
752 UM_SLEEP | UM_GC);
753 }
754
755 if (data->dtmd_modstr == NULL) {
756 data->dtmd_modstr = mdb_zalloc(MDB_SYM_NAMLEN,
757 UM_SLEEP | UM_GC);
758 }
759
760 if (symp != NULL) {
761 if (mdb_lookup_by_addr(addr, MDB_SYM_FUZZY, data->dtmd_symstr,
762 MDB_SYM_NAMLEN, symp) == -1)
763 return (-1);
764 }
765
766 if (sip != NULL) {
767 data->dtmd_addr = addr;
768
769 (void) strcpy(data->dtmd_modstr, "???");
770
771 if (mdb_walk("modctl",
772 (mdb_walk_cb_t)dtracemdb_modctl, varg) == -1) {
773 mdb_warn("couldn't walk 'modctl'");
774 return (-1);
775 }
776
777 sip->dts_object = data->dtmd_modstr;
778 sip->dts_id = 0;
779 sip->dts_name = symp != NULL ? data->dtmd_symstr : NULL;
780 }
781
782 return (0);
783 }
784
785 /*ARGSUSED*/
786 static int
dtracemdb_stat(void * varg,processorid_t cpu)787 dtracemdb_stat(void *varg, processorid_t cpu)
788 {
789 GElf_Sym sym;
790 cpu_t c;
791 uintptr_t caddr, addr;
792
793 if (mdb_lookup_by_name("cpu", &sym) == -1) {
794 mdb_warn("failed to find symbol for 'cpu'");
795 return (-1);
796 }
797
798 if (cpu * sizeof (uintptr_t) > sym.st_size)
799 return (-1);
800
801 addr = (uintptr_t)sym.st_value + cpu * sizeof (uintptr_t);
802
803 if (mdb_vread(&caddr, sizeof (caddr), addr) == -1) {
804 mdb_warn("failed to read cpu[%d]", cpu);
805 return (-1);
806 }
807
808 if (caddr == NULL)
809 return (-1);
810
811 if (mdb_vread(&c, sizeof (c), caddr) == -1) {
812 mdb_warn("failed to read cpu at %p", caddr);
813 return (-1);
814 }
815
816 if (c.cpu_flags & CPU_POWEROFF) {
817 return (P_POWEROFF);
818 } else if (c.cpu_flags & CPU_SPARE) {
819 return (P_SPARE);
820 } else if (c.cpu_flags & CPU_FAULTED) {
821 return (P_FAULTED);
822 } else if ((c.cpu_flags & (CPU_READY | CPU_OFFLINE)) != CPU_READY) {
823 return (P_OFFLINE);
824 } else if (c.cpu_flags & CPU_ENABLE) {
825 return (P_ONLINE);
826 } else {
827 return (P_NOINTR);
828 }
829 }
830
831 /*ARGSUSED*/
832 static long
dtracemdb_sysconf(void * varg,int name)833 dtracemdb_sysconf(void *varg, int name)
834 {
835 int max_ncpus;
836 processorid_t max_cpuid;
837
838 switch (name) {
839 case _SC_CPUID_MAX:
840 if (mdb_readvar(&max_cpuid, "max_cpuid") == -1) {
841 mdb_warn("failed to read 'max_cpuid'");
842 return (-1);
843 }
844
845 return (max_cpuid);
846
847 case _SC_NPROCESSORS_MAX:
848 if (mdb_readvar(&max_ncpus, "max_ncpus") == -1) {
849 mdb_warn("failed to read 'max_ncpus'");
850 return (-1);
851 }
852
853 return (max_ncpus);
854
855 default:
856 mdb_warn("unexpected sysconf code %d\n", name);
857 return (-1);
858 }
859 }
860
861 const dtrace_vector_t dtrace_mdbops = {
862 dtracemdb_ioctl,
863 dtracemdb_lookup_by_addr,
864 dtracemdb_stat,
865 dtracemdb_sysconf
866 };
867
868 typedef struct dtrace_dcmddata {
869 dtrace_hdl_t *dtdd_dtp;
870 int dtdd_cpu;
871 int dtdd_quiet;
872 int dtdd_flowindent;
873 int dtdd_heading;
874 } dtrace_dcmddata_t;
875
876 /*ARGSUSED*/
877 static int
dtrace_dcmdrec(const dtrace_probedata_t * data,const dtrace_recdesc_t * rec,void * arg)878 dtrace_dcmdrec(const dtrace_probedata_t *data,
879 const dtrace_recdesc_t *rec, void *arg)
880 {
881 dtrace_dcmddata_t *dd = arg;
882
883 if (rec == NULL) {
884 /*
885 * We have processed the final record; output the newline if
886 * we're not in quiet mode.
887 */
888 if (!dd->dtdd_quiet)
889 mdb_printf("\n");
890
891 return (DTRACE_CONSUME_NEXT);
892 }
893
894 return (DTRACE_CONSUME_THIS);
895 }
896
897 /*ARGSUSED*/
898 static int
dtrace_dcmdprobe(const dtrace_probedata_t * data,void * arg)899 dtrace_dcmdprobe(const dtrace_probedata_t *data, void *arg)
900 {
901 dtrace_probedesc_t *pd = data->dtpda_pdesc;
902 processorid_t cpu = data->dtpda_cpu;
903 dtrace_dcmddata_t *dd = arg;
904 char name[DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 2];
905
906 if (dd->dtdd_cpu != -1UL && dd->dtdd_cpu != cpu)
907 return (DTRACE_CONSUME_NEXT);
908
909 if (dd->dtdd_heading == 0) {
910 if (!dd->dtdd_flowindent) {
911 if (!dd->dtdd_quiet) {
912 mdb_printf("%3s %6s %32s\n",
913 "CPU", "ID", "FUNCTION:NAME");
914 }
915 } else {
916 mdb_printf("%3s %-41s\n", "CPU", "FUNCTION");
917 }
918 dd->dtdd_heading = 1;
919 }
920
921 if (!dd->dtdd_flowindent) {
922 if (!dd->dtdd_quiet) {
923 (void) mdb_snprintf(name, sizeof (name), "%s:%s",
924 pd->dtpd_func, pd->dtpd_name);
925
926 mdb_printf("%3d %6d %32s ", cpu, pd->dtpd_id, name);
927 }
928 } else {
929 int indent = data->dtpda_indent;
930
931 if (data->dtpda_flow == DTRACEFLOW_NONE) {
932 (void) mdb_snprintf(name, sizeof (name), "%*s%s%s:%s",
933 indent, "", data->dtpda_prefix, pd->dtpd_func,
934 pd->dtpd_name);
935 } else {
936 (void) mdb_snprintf(name, sizeof (name), "%*s%s%s",
937 indent, "", data->dtpda_prefix, pd->dtpd_func);
938 }
939
940 mdb_printf("%3d %-41s ", cpu, name);
941 }
942
943 return (DTRACE_CONSUME_THIS);
944 }
945
946 /*ARGSUSED*/
947 static int
dtrace_dcmderr(const dtrace_errdata_t * data,void * arg)948 dtrace_dcmderr(const dtrace_errdata_t *data, void *arg)
949 {
950 mdb_warn(data->dteda_msg);
951 return (DTRACE_HANDLE_OK);
952 }
953
954 /*ARGSUSED*/
955 static int
dtrace_dcmddrop(const dtrace_dropdata_t * data,void * arg)956 dtrace_dcmddrop(const dtrace_dropdata_t *data, void *arg)
957 {
958 mdb_warn(data->dtdda_msg);
959 return (DTRACE_HANDLE_OK);
960 }
961
962 /*ARGSUSED*/
963 static int
dtrace_dcmdbuffered(const dtrace_bufdata_t * bufdata,void * arg)964 dtrace_dcmdbuffered(const dtrace_bufdata_t *bufdata, void *arg)
965 {
966 mdb_printf("%s", bufdata->dtbda_buffered);
967 return (DTRACE_HANDLE_OK);
968 }
969
970 /*ARGSUSED*/
971 int
dtrace(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)972 dtrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
973 {
974 dtrace_state_t state;
975 dtrace_hdl_t *dtp;
976 int ncpu, err;
977 uintptr_t c = -1UL;
978 dtrace_dcmddata_t dd;
979 dtrace_optval_t val;
980 dtracemdb_data_t md;
981 int rval = DCMD_ERR;
982 dtrace_anon_t anon;
983
984 if (!(flags & DCMD_ADDRSPEC))
985 return (DCMD_USAGE);
986
987 if (mdb_getopts(argc, argv, 'c', MDB_OPT_UINTPTR, &c, NULL) != argc)
988 return (DCMD_USAGE);
989
990 if (mdb_readvar(&ncpu, "_ncpu") == -1) {
991 mdb_warn("failed to read '_ncpu'");
992 return (DCMD_ERR);
993 }
994
995 if (mdb_vread(&state, sizeof (state), addr) == -1) {
996 mdb_warn("couldn't read dtrace_state_t at %p", addr);
997 return (DCMD_ERR);
998 }
999
1000 if (state.dts_anon != NULL) {
1001 addr = (uintptr_t)state.dts_anon;
1002
1003 if (mdb_vread(&state, sizeof (state), addr) == -1) {
1004 mdb_warn("couldn't read anonymous state at %p", addr);
1005 return (DCMD_ERR);
1006 }
1007 }
1008
1009 bzero(&md, sizeof (md));
1010 md.dtmd_state = &state;
1011
1012 if ((dtp = dtrace_vopen(DTRACE_VERSION, DTRACE_O_NOSYS, &err,
1013 &dtrace_mdbops, &md)) == NULL) {
1014 mdb_warn("failed to initialize dtrace: %s\n",
1015 dtrace_errmsg(NULL, err));
1016 return (DCMD_ERR);
1017 }
1018
1019 /*
1020 * If this is the anonymous enabling, we need to set a bit indicating
1021 * that DTRACEOPT_GRABANON should be set.
1022 */
1023 if (mdb_readvar(&anon, "dtrace_anon") == -1) {
1024 mdb_warn("failed to read 'dtrace_anon'");
1025 return (DCMD_ERR);
1026 }
1027
1028 md.dtmd_isanon = ((uintptr_t)anon.dta_state == addr);
1029
1030 if (dtrace_go(dtp) != 0) {
1031 mdb_warn("failed to initialize dtrace: %s\n",
1032 dtrace_errmsg(dtp, dtrace_errno(dtp)));
1033 goto err;
1034 }
1035
1036 bzero(&dd, sizeof (dd));
1037 dd.dtdd_dtp = dtp;
1038 dd.dtdd_cpu = c;
1039
1040 if (dtrace_getopt(dtp, "flowindent", &val) == -1) {
1041 mdb_warn("couldn't get 'flowindent' option: %s\n",
1042 dtrace_errmsg(dtp, dtrace_errno(dtp)));
1043 goto err;
1044 }
1045
1046 dd.dtdd_flowindent = (val != DTRACEOPT_UNSET);
1047
1048 if (dtrace_getopt(dtp, "quiet", &val) == -1) {
1049 mdb_warn("couldn't get 'quiet' option: %s\n",
1050 dtrace_errmsg(dtp, dtrace_errno(dtp)));
1051 goto err;
1052 }
1053
1054 dd.dtdd_quiet = (val != DTRACEOPT_UNSET);
1055
1056 if (dtrace_handle_err(dtp, dtrace_dcmderr, NULL) == -1) {
1057 mdb_warn("couldn't add err handler: %s\n",
1058 dtrace_errmsg(dtp, dtrace_errno(dtp)));
1059 goto err;
1060 }
1061
1062 if (dtrace_handle_drop(dtp, dtrace_dcmddrop, NULL) == -1) {
1063 mdb_warn("couldn't add drop handler: %s\n",
1064 dtrace_errmsg(dtp, dtrace_errno(dtp)));
1065 goto err;
1066 }
1067
1068 if (dtrace_handle_buffered(dtp, dtrace_dcmdbuffered, NULL) == -1) {
1069 mdb_warn("couldn't add buffered handler: %s\n",
1070 dtrace_errmsg(dtp, dtrace_errno(dtp)));
1071 goto err;
1072 }
1073
1074 if (dtrace_status(dtp) == -1) {
1075 mdb_warn("couldn't get status: %s\n",
1076 dtrace_errmsg(dtp, dtrace_errno(dtp)));
1077 goto err;
1078 }
1079
1080 if (dtrace_aggregate_snap(dtp) == -1) {
1081 mdb_warn("couldn't snapshot aggregation: %s\n",
1082 dtrace_errmsg(dtp, dtrace_errno(dtp)));
1083 goto err;
1084 }
1085
1086 if (dtrace_consume(dtp, NULL,
1087 dtrace_dcmdprobe, dtrace_dcmdrec, &dd) == -1) {
1088 mdb_warn("couldn't consume DTrace buffers: %s\n",
1089 dtrace_errmsg(dtp, dtrace_errno(dtp)));
1090 }
1091
1092 if (dtrace_aggregate_print(dtp, NULL, NULL) == -1) {
1093 mdb_warn("couldn't print aggregation: %s\n",
1094 dtrace_errmsg(dtp, dtrace_errno(dtp)));
1095 goto err;
1096 }
1097
1098 rval = DCMD_OK;
1099 err:
1100 dtrace_close(dtp);
1101 return (rval);
1102 }
1103
1104 static int
dtrace_errhash_cmp(const void * l,const void * r)1105 dtrace_errhash_cmp(const void *l, const void *r)
1106 {
1107 uintptr_t lhs = *((uintptr_t *)l);
1108 uintptr_t rhs = *((uintptr_t *)r);
1109 dtrace_errhash_t lerr, rerr;
1110 char lmsg[256], rmsg[256];
1111
1112 (void) mdb_vread(&lerr, sizeof (lerr), lhs);
1113 (void) mdb_vread(&rerr, sizeof (rerr), rhs);
1114
1115 if (lerr.dter_msg == NULL)
1116 return (-1);
1117
1118 if (rerr.dter_msg == NULL)
1119 return (1);
1120
1121 (void) mdb_readstr(lmsg, sizeof (lmsg), (uintptr_t)lerr.dter_msg);
1122 (void) mdb_readstr(rmsg, sizeof (rmsg), (uintptr_t)rerr.dter_msg);
1123
1124 return (strcmp(lmsg, rmsg));
1125 }
1126
1127 int
dtrace_errhash_init(mdb_walk_state_t * wsp)1128 dtrace_errhash_init(mdb_walk_state_t *wsp)
1129 {
1130 GElf_Sym sym;
1131 uintptr_t *hash, addr;
1132 int i;
1133
1134 if (wsp->walk_addr != NULL) {
1135 mdb_warn("dtrace_errhash walk only supports global walks\n");
1136 return (WALK_ERR);
1137 }
1138
1139 if (mdb_lookup_by_name("dtrace_errhash", &sym) == -1) {
1140 mdb_warn("couldn't find 'dtrace_errhash' (non-DEBUG kernel?)");
1141 return (WALK_ERR);
1142 }
1143
1144 addr = (uintptr_t)sym.st_value;
1145 hash = mdb_alloc(DTRACE_ERRHASHSZ * sizeof (uintptr_t),
1146 UM_SLEEP | UM_GC);
1147
1148 for (i = 0; i < DTRACE_ERRHASHSZ; i++)
1149 hash[i] = addr + i * sizeof (dtrace_errhash_t);
1150
1151 qsort(hash, DTRACE_ERRHASHSZ, sizeof (uintptr_t), dtrace_errhash_cmp);
1152
1153 wsp->walk_addr = 0;
1154 wsp->walk_data = hash;
1155
1156 return (WALK_NEXT);
1157 }
1158
1159 int
dtrace_errhash_step(mdb_walk_state_t * wsp)1160 dtrace_errhash_step(mdb_walk_state_t *wsp)
1161 {
1162 int ndx = (int)wsp->walk_addr;
1163 uintptr_t *hash = wsp->walk_data;
1164 dtrace_errhash_t err;
1165 uintptr_t addr;
1166
1167 if (ndx >= DTRACE_ERRHASHSZ)
1168 return (WALK_DONE);
1169
1170 wsp->walk_addr = ndx + 1;
1171 addr = hash[ndx];
1172
1173 if (mdb_vread(&err, sizeof (err), addr) == -1) {
1174 mdb_warn("failed to read dtrace_errhash_t at %p", addr);
1175 return (WALK_DONE);
1176 }
1177
1178 if (err.dter_msg == NULL)
1179 return (WALK_NEXT);
1180
1181 return (wsp->walk_callback(addr, &err, wsp->walk_cbdata));
1182 }
1183
1184 /*ARGSUSED*/
1185 int
dtrace_errhash(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1186 dtrace_errhash(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1187 {
1188 dtrace_errhash_t err;
1189 char msg[256];
1190
1191 if (!(flags & DCMD_ADDRSPEC)) {
1192 if (mdb_walk_dcmd("dtrace_errhash", "dtrace_errhash",
1193 argc, argv) == -1) {
1194 mdb_warn("can't walk 'dtrace_errhash'");
1195 return (DCMD_ERR);
1196 }
1197
1198 return (DCMD_OK);
1199 }
1200
1201 if (DCMD_HDRSPEC(flags))
1202 mdb_printf("%8s %s\n", "COUNT", "ERROR");
1203
1204 if (mdb_vread(&err, sizeof (err), addr) == -1) {
1205 mdb_warn("failed to read dtrace_errhash_t at %p", addr);
1206 return (DCMD_ERR);
1207 }
1208
1209 addr = (uintptr_t)err.dter_msg;
1210
1211 if (mdb_readstr(msg, sizeof (msg), addr) == -1) {
1212 mdb_warn("failed to read error msg at %p", addr);
1213 return (DCMD_ERR);
1214 }
1215
1216 mdb_printf("%8d %s", err.dter_count, msg);
1217
1218 /*
1219 * Some error messages include a newline -- only print the newline
1220 * if the message doesn't have one.
1221 */
1222 if (msg[strlen(msg) - 1] != '\n')
1223 mdb_printf("\n");
1224
1225 return (DCMD_OK);
1226 }
1227
1228 int
dtrace_helptrace_init(mdb_walk_state_t * wsp)1229 dtrace_helptrace_init(mdb_walk_state_t *wsp)
1230 {
1231 uint32_t next;
1232 int enabled;
1233
1234 if (wsp->walk_addr != NULL) {
1235 mdb_warn("dtrace_helptrace only supports global walks\n");
1236 return (WALK_ERR);
1237 }
1238
1239 if (mdb_readvar(&enabled, "dtrace_helptrace_enabled") == -1) {
1240 mdb_warn("couldn't read 'dtrace_helptrace_enabled'");
1241 return (WALK_ERR);
1242 }
1243
1244 if (!enabled) {
1245 mdb_warn("helper tracing is not enabled\n");
1246 return (WALK_ERR);
1247 }
1248
1249 if (mdb_readvar(&next, "dtrace_helptrace_next") == -1) {
1250 mdb_warn("couldn't read 'dtrace_helptrace_next'");
1251 return (WALK_ERR);
1252 }
1253
1254 wsp->walk_addr = next;
1255
1256 return (WALK_NEXT);
1257 }
1258
1259 int
dtrace_helptrace_step(mdb_walk_state_t * wsp)1260 dtrace_helptrace_step(mdb_walk_state_t *wsp)
1261 {
1262 uint32_t next, size, nlocals, bufsize;
1263 uintptr_t buffer, addr;
1264 dtrace_helptrace_t *ht;
1265 int rval;
1266
1267 if (mdb_readvar(&next, "dtrace_helptrace_next") == -1) {
1268 mdb_warn("couldn't read 'dtrace_helptrace_next'");
1269 return (WALK_ERR);
1270 }
1271
1272 if (mdb_readvar(&bufsize, "dtrace_helptrace_bufsize") == -1) {
1273 mdb_warn("couldn't read 'dtrace_helptrace_bufsize'");
1274 return (WALK_ERR);
1275 }
1276
1277 if (mdb_readvar(&buffer, "dtrace_helptrace_buffer") == -1) {
1278 mdb_warn("couldn't read 'dtrace_helptrace_buffer'");
1279 return (WALK_ERR);
1280 }
1281
1282 if (mdb_readvar(&nlocals, "dtrace_helptrace_nlocals") == -1) {
1283 mdb_warn("couldn't read 'dtrace_helptrace_nlocals'");
1284 return (WALK_ERR);
1285 }
1286
1287 size = sizeof (dtrace_helptrace_t) +
1288 nlocals * sizeof (uint64_t) - sizeof (uint64_t);
1289
1290 if (wsp->walk_addr + size > bufsize) {
1291 if (next == 0)
1292 return (WALK_DONE);
1293
1294 wsp->walk_addr = 0;
1295 }
1296
1297 addr = buffer + wsp->walk_addr;
1298 ht = alloca(size);
1299
1300 if (mdb_vread(ht, size, addr) == -1) {
1301 mdb_warn("couldn't read entry at %p", addr);
1302 return (WALK_ERR);
1303 }
1304
1305 if (ht->dtht_helper != NULL) {
1306 rval = wsp->walk_callback(addr, ht, wsp->walk_cbdata);
1307
1308 if (rval != WALK_NEXT)
1309 return (rval);
1310 }
1311
1312 if (wsp->walk_addr < next && wsp->walk_addr + size >= next)
1313 return (WALK_DONE);
1314
1315 wsp->walk_addr += size;
1316 return (WALK_NEXT);
1317 }
1318
1319 int
dtrace_helptrace(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1320 dtrace_helptrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1321 {
1322 dtrace_helptrace_t help;
1323 dtrace_helper_action_t helper;
1324 char where[30];
1325 uint_t opt_v = FALSE;
1326 uintptr_t haddr;
1327
1328 if (!(flags & DCMD_ADDRSPEC)) {
1329 if (mdb_walk_dcmd("dtrace_helptrace", "dtrace_helptrace",
1330 argc, argv) == -1) {
1331 mdb_warn("can't walk 'dtrace_helptrace'");
1332 return (DCMD_ERR);
1333 }
1334
1335 return (DCMD_OK);
1336 }
1337
1338 if (mdb_getopts(argc, argv, 'v',
1339 MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
1340 return (DCMD_USAGE);
1341
1342 if (DCMD_HDRSPEC(flags)) {
1343 mdb_printf(" %?s %?s %12s %s\n",
1344 "ADDR", "HELPER", "WHERE", "DIFO");
1345 }
1346
1347 if (mdb_vread(&help, sizeof (help), addr) == -1) {
1348 mdb_warn("failed to read dtrace_helptrace_t at %p", addr);
1349 return (DCMD_ERR);
1350 }
1351
1352 switch (help.dtht_where) {
1353 case 0:
1354 (void) mdb_snprintf(where, sizeof (where), "predicate");
1355 break;
1356
1357 case DTRACE_HELPTRACE_NEXT:
1358 (void) mdb_snprintf(where, sizeof (where), "next");
1359 break;
1360
1361 case DTRACE_HELPTRACE_DONE:
1362 (void) mdb_snprintf(where, sizeof (where), "done");
1363 break;
1364
1365 case DTRACE_HELPTRACE_ERR:
1366 (void) mdb_snprintf(where, sizeof (where), "err");
1367 break;
1368
1369 default:
1370 (void) mdb_snprintf(where, sizeof (where),
1371 "action #%d", help.dtht_where);
1372 break;
1373 }
1374
1375 mdb_printf(" %?p %?p %12s ", addr, help.dtht_helper, where);
1376
1377 haddr = (uintptr_t)help.dtht_helper;
1378
1379 if (mdb_vread(&helper, sizeof (helper), haddr) == -1) {
1380 /*
1381 * We're not going to warn in this case -- we're just not going
1382 * to print anything exciting.
1383 */
1384 mdb_printf("???\n");
1385 } else {
1386 switch (help.dtht_where) {
1387 case 0:
1388 mdb_printf("%p\n", helper.dtha_predicate);
1389 break;
1390
1391 case DTRACE_HELPTRACE_NEXT:
1392 case DTRACE_HELPTRACE_DONE:
1393 case DTRACE_HELPTRACE_ERR:
1394 mdb_printf("-\n");
1395 break;
1396
1397 default:
1398 haddr = (uintptr_t)helper.dtha_actions +
1399 (help.dtht_where - 1) * sizeof (uintptr_t);
1400
1401 if (mdb_vread(&haddr, sizeof (haddr), haddr) == -1) {
1402 mdb_printf("???\n");
1403 } else {
1404 mdb_printf("%p\n", haddr);
1405 }
1406 }
1407 }
1408
1409 if (opt_v) {
1410 int i;
1411
1412 if (help.dtht_where == DTRACE_HELPTRACE_ERR) {
1413 int f = help.dtht_fault;
1414
1415 mdb_printf("%?s| %?s %10s |\n", "", "", "");
1416 mdb_printf("%?s| %?s %10s +-> fault: %s\n", "", "", "",
1417 f == DTRACEFLT_BADADDR ? "BADADDR" :
1418 f == DTRACEFLT_BADALIGN ? "BADALIGN" :
1419 f == DTRACEFLT_ILLOP ? "ILLOP" :
1420 f == DTRACEFLT_DIVZERO ? "DIVZERO" :
1421 f == DTRACEFLT_NOSCRATCH ? "NOSCRATCH" :
1422 f == DTRACEFLT_KPRIV ? "KPRIV" :
1423 f == DTRACEFLT_UPRIV ? "UPRIV" :
1424 f == DTRACEFLT_TUPOFLOW ? "TUPOFLOW" :
1425 f == DTRACEFLT_BADSTACK ? "BADSTACK" :
1426 "DTRACEFLT_UNKNOWN");
1427 mdb_printf("%?s| %?s %12s addr: 0x%x\n", "", "", "",
1428 help.dtht_illval);
1429 mdb_printf("%?s| %?s %12s offset: %d\n", "", "", "",
1430 help.dtht_fltoffs);
1431 }
1432
1433 mdb_printf("%?s|\n%?s+--> %?s %4s %s\n", "", "",
1434 "ADDR", "NDX", "VALUE");
1435 addr += sizeof (help) - sizeof (uint64_t);
1436
1437 for (i = 0; i < help.dtht_nlocals; i++) {
1438 uint64_t val;
1439
1440 if (mdb_vread(&val, sizeof (val), addr) == -1) {
1441 mdb_warn("couldn't read local at %p", addr);
1442 continue;
1443 }
1444
1445 mdb_printf("%?s %?p %4d %p\n", "", addr, i, val);
1446 addr += sizeof (uint64_t);
1447 }
1448
1449 mdb_printf("\n");
1450 }
1451
1452 return (DCMD_OK);
1453 }
1454
1455 /*ARGSUSED*/
1456 static int
dtrace_state_walk(uintptr_t addr,const vmem_seg_t * seg,minor_t * highest)1457 dtrace_state_walk(uintptr_t addr, const vmem_seg_t *seg, minor_t *highest)
1458 {
1459 if (seg->vs_end > *highest)
1460 *highest = seg->vs_end;
1461
1462 return (WALK_NEXT);
1463 }
1464
1465 typedef struct dtrace_state_walk {
1466 uintptr_t dtsw_softstate;
1467 minor_t dtsw_max;
1468 minor_t dtsw_current;
1469 } dtrace_state_walk_t;
1470
1471 int
dtrace_state_init(mdb_walk_state_t * wsp)1472 dtrace_state_init(mdb_walk_state_t *wsp)
1473 {
1474 uintptr_t dtrace_minor;
1475 minor_t max = 0;
1476 dtrace_state_walk_t *dw;
1477
1478 if (wsp->walk_addr != NULL) {
1479 mdb_warn("dtrace_state only supports global walks\n");
1480 return (WALK_ERR);
1481 }
1482
1483 /*
1484 * Find the dtrace_minor vmem arena and walk it to get the maximum
1485 * minor number.
1486 */
1487 if (mdb_readvar(&dtrace_minor, "dtrace_minor") == -1) {
1488 mdb_warn("failed to read 'dtrace_minor'");
1489 return (WALK_ERR);
1490 }
1491
1492 if (mdb_pwalk("vmem_alloc", (mdb_walk_cb_t)dtrace_state_walk,
1493 &max, dtrace_minor) == -1) {
1494 mdb_warn("couldn't walk 'vmem_alloc'");
1495 return (WALK_ERR);
1496 }
1497
1498 dw = mdb_zalloc(sizeof (dtrace_state_walk_t), UM_SLEEP | UM_GC);
1499 dw->dtsw_current = 0;
1500 dw->dtsw_max = max;
1501
1502 if (mdb_readvar(&dw->dtsw_softstate, "dtrace_softstate") == -1) {
1503 mdb_warn("failed to read 'dtrace_softstate'");
1504 return (DCMD_ERR);
1505 }
1506
1507 wsp->walk_data = dw;
1508
1509 return (WALK_NEXT);
1510 }
1511
1512 int
dtrace_state_step(mdb_walk_state_t * wsp)1513 dtrace_state_step(mdb_walk_state_t *wsp)
1514 {
1515 dtrace_state_walk_t *dw = wsp->walk_data;
1516 uintptr_t statep;
1517 dtrace_state_t state;
1518 int rval;
1519
1520 while (mdb_get_soft_state_byaddr(dw->dtsw_softstate, dw->dtsw_current,
1521 &statep, NULL, 0) == -1) {
1522 if (dw->dtsw_current >= dw->dtsw_max)
1523 return (WALK_DONE);
1524
1525 dw->dtsw_current++;
1526 }
1527
1528 if (mdb_vread(&state, sizeof (state), statep) == -1) {
1529 mdb_warn("couldn't read dtrace_state_t at %p", statep);
1530 return (WALK_NEXT);
1531 }
1532
1533 rval = wsp->walk_callback(statep, &state, wsp->walk_cbdata);
1534 dw->dtsw_current++;
1535
1536 return (rval);
1537 }
1538
1539 typedef struct dtrace_state_data {
1540 int dtsd_major;
1541 uintptr_t dtsd_proc;
1542 uintptr_t dtsd_softstate;
1543 uintptr_t dtsd_state;
1544 } dtrace_state_data_t;
1545
1546 static int
dtrace_state_file(uintptr_t addr,struct file * f,dtrace_state_data_t * data)1547 dtrace_state_file(uintptr_t addr, struct file *f, dtrace_state_data_t *data)
1548 {
1549 vnode_t vnode;
1550 proc_t proc;
1551 minor_t minor;
1552 uintptr_t statep;
1553
1554 if (mdb_vread(&vnode, sizeof (vnode), (uintptr_t)f->f_vnode) == -1) {
1555 mdb_warn("couldn't read vnode at %p", (uintptr_t)f->f_vnode);
1556 return (WALK_NEXT);
1557 }
1558
1559 if (getmajor(vnode.v_rdev) != data->dtsd_major)
1560 return (WALK_NEXT);
1561
1562 minor = getminor(vnode.v_rdev);
1563
1564 if (mdb_vread(&proc, sizeof (proc), data->dtsd_proc) == -1) {
1565 mdb_warn("failed to read proc at %p", data->dtsd_proc);
1566 return (WALK_NEXT);
1567 }
1568
1569 if (mdb_get_soft_state_byaddr(data->dtsd_softstate, minor,
1570 &statep, NULL, 0) == -1) {
1571 mdb_warn("failed to read softstate for minor %d", minor);
1572 return (WALK_NEXT);
1573 }
1574
1575 if (statep != data->dtsd_state)
1576 return (WALK_NEXT);
1577
1578 mdb_printf("%?p %5d %?p %-*s %?p\n", statep, minor,
1579 data->dtsd_proc, MAXCOMLEN, proc.p_user.u_comm, addr);
1580
1581 return (WALK_NEXT);
1582 }
1583
1584 /*ARGSUSED*/
1585 static int
dtrace_state_proc(uintptr_t addr,void * ignored,dtrace_state_data_t * data)1586 dtrace_state_proc(uintptr_t addr, void *ignored, dtrace_state_data_t *data)
1587 {
1588 data->dtsd_proc = addr;
1589
1590 if (mdb_pwalk("file",
1591 (mdb_walk_cb_t)dtrace_state_file, data, addr) == -1) {
1592 mdb_warn("couldn't walk 'file' for proc %p", addr);
1593 return (WALK_ERR);
1594 }
1595
1596 return (WALK_NEXT);
1597 }
1598
1599 void
dtrace_state_help(void)1600 dtrace_state_help(void)
1601 {
1602 mdb_printf("Given a dtrace_state_t structure, displays all "
1603 /*CSTYLED*/
1604 "consumers, or \"<anonymous>\"\nif the consumer is anonymous. If "
1605 "no state structure is provided, iterates\nover all state "
1606 "structures.\n\n"
1607 "Addresses in ADDR column may be provided to ::dtrace to obtain\n"
1608 "dtrace(1M)-like output for in-kernel DTrace data.\n");
1609 }
1610
1611 int
dtrace_state(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1612 dtrace_state(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1613 {
1614 uintptr_t devi;
1615 struct dev_info info;
1616 dtrace_state_data_t data;
1617 dtrace_anon_t anon;
1618 dtrace_state_t state;
1619
1620 if (!(flags & DCMD_ADDRSPEC)) {
1621 if (mdb_walk_dcmd("dtrace_state",
1622 "dtrace_state", argc, argv) == -1) {
1623 mdb_warn("can't walk dtrace_state");
1624 return (DCMD_ERR);
1625 }
1626 return (DCMD_OK);
1627 }
1628
1629 if (DCMD_HDRSPEC(flags)) {
1630 mdb_printf("%?s %5s %?s %-*s %?s\n", "ADDR", "MINOR", "PROC",
1631 MAXCOMLEN, "NAME", "FILE");
1632 }
1633
1634 /*
1635 * First determine if this is anonymous state.
1636 */
1637 if (mdb_readvar(&anon, "dtrace_anon") == -1) {
1638 mdb_warn("failed to read 'dtrace_anon'");
1639 return (DCMD_ERR);
1640 }
1641
1642 if ((uintptr_t)anon.dta_state == addr) {
1643 if (mdb_vread(&state, sizeof (state), addr) == -1) {
1644 mdb_warn("failed to read anon at %p", addr);
1645 return (DCMD_ERR);
1646 }
1647
1648 mdb_printf("%?p %5d %?s %-*s %?s\n", addr,
1649 getminor(state.dts_dev), "-", MAXCOMLEN,
1650 "<anonymous>", "-");
1651
1652 return (DCMD_OK);
1653 }
1654
1655 if (mdb_readvar(&devi, "dtrace_devi") == -1) {
1656 mdb_warn("failed to read 'dtrace_devi'");
1657 return (DCMD_ERR);
1658 }
1659
1660 if (mdb_vread(&info, sizeof (struct dev_info), devi) == -1) {
1661 mdb_warn("failed to read 'dev_info'");
1662 return (DCMD_ERR);
1663 }
1664
1665 data.dtsd_major = info.devi_major;
1666
1667 if (mdb_readvar(&data.dtsd_softstate, "dtrace_softstate") == -1) {
1668 mdb_warn("failed to read 'dtrace_softstate'");
1669 return (DCMD_ERR);
1670 }
1671
1672 data.dtsd_state = addr;
1673
1674 /*
1675 * Walk through all processes and all open files looking for this
1676 * state. It must be open somewhere...
1677 */
1678 if (mdb_walk("proc", (mdb_walk_cb_t)dtrace_state_proc, &data) == -1) {
1679 mdb_warn("couldn't walk 'proc'");
1680 return (DCMD_ERR);
1681 }
1682
1683 return (DCMD_OK);
1684 }
1685
1686 typedef struct dtrace_aggkey_data {
1687 uintptr_t *dtakd_hash;
1688 uintptr_t dtakd_hashsize;
1689 uintptr_t dtakd_next;
1690 uintptr_t dtakd_ndx;
1691 } dtrace_aggkey_data_t;
1692
1693 int
dtrace_aggkey_init(mdb_walk_state_t * wsp)1694 dtrace_aggkey_init(mdb_walk_state_t *wsp)
1695 {
1696 dtrace_buffer_t buf;
1697 uintptr_t addr;
1698 dtrace_aggbuffer_t agb;
1699 dtrace_aggkey_data_t *data;
1700 size_t hsize;
1701
1702 if ((addr = wsp->walk_addr) == NULL) {
1703 mdb_warn("dtrace_aggkey walk needs aggregation buffer\n");
1704 return (WALK_ERR);
1705 }
1706
1707 if (mdb_vread(&buf, sizeof (buf), addr) == -1) {
1708 mdb_warn("failed to read aggregation buffer at %p", addr);
1709 return (WALK_ERR);
1710 }
1711
1712 addr = (uintptr_t)buf.dtb_tomax +
1713 buf.dtb_size - sizeof (dtrace_aggbuffer_t);
1714
1715 if (mdb_vread(&agb, sizeof (agb), addr) == -1) {
1716 mdb_warn("failed to read dtrace_aggbuffer_t at %p", addr);
1717 return (WALK_ERR);
1718 }
1719
1720 data = mdb_zalloc(sizeof (dtrace_aggkey_data_t), UM_SLEEP);
1721
1722 data->dtakd_hashsize = agb.dtagb_hashsize;
1723 hsize = agb.dtagb_hashsize * sizeof (dtrace_aggkey_t *);
1724 data->dtakd_hash = mdb_alloc(hsize, UM_SLEEP);
1725
1726 if (mdb_vread(data->dtakd_hash, hsize,
1727 (uintptr_t)agb.dtagb_hash) == -1) {
1728 mdb_warn("failed to read hash at %p",
1729 (uintptr_t)agb.dtagb_hash);
1730 mdb_free(data->dtakd_hash, hsize);
1731 mdb_free(data, sizeof (dtrace_aggkey_data_t));
1732 return (WALK_ERR);
1733 }
1734
1735 wsp->walk_data = data;
1736 return (WALK_NEXT);
1737 }
1738
1739 int
dtrace_aggkey_step(mdb_walk_state_t * wsp)1740 dtrace_aggkey_step(mdb_walk_state_t *wsp)
1741 {
1742 dtrace_aggkey_data_t *data = wsp->walk_data;
1743 dtrace_aggkey_t key;
1744 uintptr_t addr;
1745
1746 while ((addr = data->dtakd_next) == NULL) {
1747 if (data->dtakd_ndx == data->dtakd_hashsize)
1748 return (WALK_DONE);
1749
1750 data->dtakd_next = data->dtakd_hash[data->dtakd_ndx++];
1751 }
1752
1753 if (mdb_vread(&key, sizeof (key), addr) == -1) {
1754 mdb_warn("failed to read dtrace_aggkey_t at %p", addr);
1755 return (WALK_ERR);
1756 }
1757
1758 data->dtakd_next = (uintptr_t)key.dtak_next;
1759
1760 return (wsp->walk_callback(addr, &key, wsp->walk_cbdata));
1761 }
1762
1763 void
dtrace_aggkey_fini(mdb_walk_state_t * wsp)1764 dtrace_aggkey_fini(mdb_walk_state_t *wsp)
1765 {
1766 dtrace_aggkey_data_t *data = wsp->walk_data;
1767 size_t hsize;
1768
1769 hsize = data->dtakd_hashsize * sizeof (dtrace_aggkey_t *);
1770 mdb_free(data->dtakd_hash, hsize);
1771 mdb_free(data, sizeof (dtrace_aggkey_data_t));
1772 }
1773
1774 typedef struct dtrace_dynvar_data {
1775 dtrace_dynhash_t *dtdvd_hash;
1776 uintptr_t dtdvd_hashsize;
1777 uintptr_t dtdvd_next;
1778 uintptr_t dtdvd_ndx;
1779 uintptr_t dtdvd_sink;
1780 } dtrace_dynvar_data_t;
1781
1782 int
dtrace_dynvar_init(mdb_walk_state_t * wsp)1783 dtrace_dynvar_init(mdb_walk_state_t *wsp)
1784 {
1785 uintptr_t addr;
1786 dtrace_dstate_t dstate;
1787 dtrace_dynvar_data_t *data;
1788 size_t hsize;
1789 GElf_Sym sym;
1790
1791 if ((addr = wsp->walk_addr) == NULL) {
1792 mdb_warn("dtrace_dynvar walk needs dtrace_dstate_t\n");
1793 return (WALK_ERR);
1794 }
1795
1796 if (mdb_vread(&dstate, sizeof (dstate), addr) == -1) {
1797 mdb_warn("failed to read dynamic state at %p", addr);
1798 return (WALK_ERR);
1799 }
1800
1801 if (mdb_lookup_by_name("dtrace_dynhash_sink", &sym) == -1) {
1802 mdb_warn("couldn't find 'dtrace_dynhash_sink'");
1803 return (WALK_ERR);
1804 }
1805
1806 data = mdb_zalloc(sizeof (dtrace_dynvar_data_t), UM_SLEEP);
1807
1808 data->dtdvd_hashsize = dstate.dtds_hashsize;
1809 hsize = dstate.dtds_hashsize * sizeof (dtrace_dynhash_t);
1810 data->dtdvd_hash = mdb_alloc(hsize, UM_SLEEP);
1811 data->dtdvd_sink = (uintptr_t)sym.st_value;
1812
1813 if (mdb_vread(data->dtdvd_hash, hsize,
1814 (uintptr_t)dstate.dtds_hash) == -1) {
1815 mdb_warn("failed to read hash at %p",
1816 (uintptr_t)dstate.dtds_hash);
1817 mdb_free(data->dtdvd_hash, hsize);
1818 mdb_free(data, sizeof (dtrace_dynvar_data_t));
1819 return (WALK_ERR);
1820 }
1821
1822 data->dtdvd_next = (uintptr_t)data->dtdvd_hash[0].dtdh_chain;
1823
1824 wsp->walk_data = data;
1825 return (WALK_NEXT);
1826 }
1827
1828 int
dtrace_dynvar_step(mdb_walk_state_t * wsp)1829 dtrace_dynvar_step(mdb_walk_state_t *wsp)
1830 {
1831 dtrace_dynvar_data_t *data = wsp->walk_data;
1832 dtrace_dynvar_t dynvar, *dvar;
1833 size_t dvarsize;
1834 uintptr_t addr;
1835 int nkeys;
1836
1837 while ((addr = data->dtdvd_next) == data->dtdvd_sink) {
1838 if (data->dtdvd_ndx == data->dtdvd_hashsize)
1839 return (WALK_DONE);
1840
1841 data->dtdvd_next =
1842 (uintptr_t)data->dtdvd_hash[data->dtdvd_ndx++].dtdh_chain;
1843 }
1844
1845 if (mdb_vread(&dynvar, sizeof (dynvar), addr) == -1) {
1846 mdb_warn("failed to read dtrace_dynvar_t at %p", addr);
1847 return (WALK_ERR);
1848 }
1849
1850 /*
1851 * Now we need to allocate the correct size.
1852 */
1853 nkeys = dynvar.dtdv_tuple.dtt_nkeys;
1854 dvarsize = (uintptr_t)&dynvar.dtdv_tuple.dtt_key[nkeys] -
1855 (uintptr_t)&dynvar;
1856
1857 dvar = alloca(dvarsize);
1858
1859 if (mdb_vread(dvar, dvarsize, addr) == -1) {
1860 mdb_warn("failed to read dtrace_dynvar_t at %p", addr);
1861 return (WALK_ERR);
1862 }
1863
1864 data->dtdvd_next = (uintptr_t)dynvar.dtdv_next;
1865
1866 return (wsp->walk_callback(addr, dvar, wsp->walk_cbdata));
1867 }
1868
1869 void
dtrace_dynvar_fini(mdb_walk_state_t * wsp)1870 dtrace_dynvar_fini(mdb_walk_state_t *wsp)
1871 {
1872 dtrace_dynvar_data_t *data = wsp->walk_data;
1873 size_t hsize;
1874
1875 hsize = data->dtdvd_hashsize * sizeof (dtrace_dynvar_t *);
1876 mdb_free(data->dtdvd_hash, hsize);
1877 mdb_free(data, sizeof (dtrace_dynvar_data_t));
1878 }
1879
1880 typedef struct dtrace_hashstat_data {
1881 size_t *dthsd_counts;
1882 size_t dthsd_hashsize;
1883 char *dthsd_data;
1884 size_t dthsd_size;
1885 int dthsd_header;
1886 } dtrace_hashstat_data_t;
1887
1888 typedef void (*dtrace_hashstat_func_t)(dtrace_hashstat_data_t *);
1889
1890 static void
dtrace_hashstat_additive(dtrace_hashstat_data_t * data)1891 dtrace_hashstat_additive(dtrace_hashstat_data_t *data)
1892 {
1893 int i;
1894 int hval = 0;
1895
1896 for (i = 0; i < data->dthsd_size; i++)
1897 hval += data->dthsd_data[i];
1898
1899 data->dthsd_counts[hval % data->dthsd_hashsize]++;
1900 }
1901
1902 static void
dtrace_hashstat_shifty(dtrace_hashstat_data_t * data)1903 dtrace_hashstat_shifty(dtrace_hashstat_data_t *data)
1904 {
1905 uint64_t hval = 0;
1906 int i;
1907
1908 if (data->dthsd_size < sizeof (uint64_t)) {
1909 dtrace_hashstat_additive(data);
1910 return;
1911 }
1912
1913 for (i = 0; i < data->dthsd_size; i += sizeof (uint64_t)) {
1914 /* LINTED - alignment */
1915 uint64_t val = *((uint64_t *)&data->dthsd_data[i]);
1916
1917 hval += (val & ((1 << NBBY) - 1)) +
1918 ((val >> NBBY) & ((1 << NBBY) - 1)) +
1919 ((val >> (NBBY << 1)) & ((1 << NBBY) - 1)) +
1920 ((val >> (NBBY << 2)) & ((1 << NBBY) - 1)) +
1921 (val & USHRT_MAX) + (val >> (NBBY << 1) & USHRT_MAX);
1922 }
1923
1924 data->dthsd_counts[hval % data->dthsd_hashsize]++;
1925 }
1926
1927 static void
dtrace_hashstat_knuth(dtrace_hashstat_data_t * data)1928 dtrace_hashstat_knuth(dtrace_hashstat_data_t *data)
1929 {
1930 int i;
1931 int hval = data->dthsd_size;
1932
1933 for (i = 0; i < data->dthsd_size; i++)
1934 hval = (hval << 4) ^ (hval >> 28) ^ data->dthsd_data[i];
1935
1936 data->dthsd_counts[hval % data->dthsd_hashsize]++;
1937 }
1938
1939 static void
dtrace_hashstat_oneatatime(dtrace_hashstat_data_t * data)1940 dtrace_hashstat_oneatatime(dtrace_hashstat_data_t *data)
1941 {
1942 int i;
1943 uint32_t hval = 0;
1944
1945 for (i = 0; i < data->dthsd_size; i++) {
1946 hval += data->dthsd_data[i];
1947 hval += (hval << 10);
1948 hval ^= (hval >> 6);
1949 }
1950
1951 hval += (hval << 3);
1952 hval ^= (hval >> 11);
1953 hval += (hval << 15);
1954
1955 data->dthsd_counts[hval % data->dthsd_hashsize]++;
1956 }
1957
1958 static void
dtrace_hashstat_fnv(dtrace_hashstat_data_t * data)1959 dtrace_hashstat_fnv(dtrace_hashstat_data_t *data)
1960 {
1961 static const uint32_t prime = 0x01000193;
1962 uint32_t hval = 0;
1963 int i;
1964
1965 for (i = 0; i < data->dthsd_size; i++) {
1966 hval *= prime;
1967 hval ^= data->dthsd_data[i];
1968 }
1969
1970 data->dthsd_counts[hval % data->dthsd_hashsize]++;
1971 }
1972
1973 static void
dtrace_hashstat_stats(char * name,dtrace_hashstat_data_t * data)1974 dtrace_hashstat_stats(char *name, dtrace_hashstat_data_t *data)
1975 {
1976 size_t nz = 0, i;
1977 int longest = 0;
1978 size_t ttl = 0;
1979 double sum = 0.0;
1980 double avg;
1981 uint_t util, stddev;
1982
1983 if (!data->dthsd_header) {
1984 mdb_printf("%15s %11s %11s %11s %11s %11s\n", "NAME",
1985 "HASHSIZE", "%UTIL", "LONGEST", "AVERAGE", "STDDEV");
1986 data->dthsd_header = 1;
1987 }
1988
1989 for (i = 0; i < data->dthsd_hashsize; i++) {
1990 if (data->dthsd_counts[i] != 0) {
1991 nz++;
1992
1993 if (data->dthsd_counts[i] > longest)
1994 longest = data->dthsd_counts[i];
1995
1996 ttl += data->dthsd_counts[i];
1997 }
1998 }
1999
2000 if (nz == 0) {
2001 mdb_printf("%15s %11d %11s %11s %11s %11s\n", name,
2002 data->dthsd_hashsize, "-", "-", "-", "-");
2003 return;
2004 }
2005
2006 avg = (double)ttl / (double)nz;
2007
2008 for (i = 0; i < data->dthsd_hashsize; i++) {
2009 double delta = (double)data->dthsd_counts[i] - avg;
2010
2011 if (data->dthsd_counts[i] == 0)
2012 continue;
2013
2014 sum += delta * delta;
2015 }
2016
2017 util = (nz * 1000) / data->dthsd_hashsize;
2018 stddev = (uint_t)sqrt(sum / (double)nz) * 10;
2019
2020 mdb_printf("%15s %11d %9u.%1u %11d %11d %9u.%1u\n", name,
2021 data->dthsd_hashsize, util / 10, util % 10, longest, ttl / nz,
2022 stddev / 10, stddev % 10);
2023 }
2024
2025 static struct dtrace_hashstat {
2026 char *dths_name;
2027 dtrace_hashstat_func_t dths_func;
2028 } _dtrace_hashstat[] = {
2029 { "<actual>", NULL },
2030 { "additive", dtrace_hashstat_additive },
2031 { "shifty", dtrace_hashstat_shifty },
2032 { "knuth", dtrace_hashstat_knuth },
2033 { "one-at-a-time", dtrace_hashstat_oneatatime },
2034 { "fnv", dtrace_hashstat_fnv },
2035 { NULL, 0 }
2036 };
2037
2038 typedef struct dtrace_aggstat_data {
2039 dtrace_hashstat_data_t dtagsd_hash;
2040 dtrace_hashstat_func_t dtagsd_func;
2041 } dtrace_aggstat_data_t;
2042
2043 static int
dtrace_aggstat_walk(uintptr_t addr,dtrace_aggkey_t * key,dtrace_aggstat_data_t * data)2044 dtrace_aggstat_walk(uintptr_t addr, dtrace_aggkey_t *key,
2045 dtrace_aggstat_data_t *data)
2046 {
2047 dtrace_hashstat_data_t *hdata = &data->dtagsd_hash;
2048 size_t size;
2049
2050 if (data->dtagsd_func == NULL) {
2051 size_t bucket = key->dtak_hashval % hdata->dthsd_hashsize;
2052
2053 hdata->dthsd_counts[bucket]++;
2054 return (WALK_NEXT);
2055 }
2056
2057 /*
2058 * We need to read the data.
2059 */
2060 size = key->dtak_size - sizeof (dtrace_aggid_t);
2061 addr = (uintptr_t)key->dtak_data + sizeof (dtrace_aggid_t);
2062 hdata->dthsd_data = alloca(size);
2063 hdata->dthsd_size = size;
2064
2065 if (mdb_vread(hdata->dthsd_data, size, addr) == -1) {
2066 mdb_warn("couldn't read data at %p", addr);
2067 return (WALK_ERR);
2068 }
2069
2070 data->dtagsd_func(hdata);
2071
2072 return (WALK_NEXT);
2073 }
2074
2075 /*ARGSUSED*/
2076 int
dtrace_aggstat(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2077 dtrace_aggstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2078 {
2079 dtrace_buffer_t buf;
2080 uintptr_t aaddr;
2081 dtrace_aggbuffer_t agb;
2082 size_t hsize, i, actual, prime, evenpow;
2083 dtrace_aggstat_data_t data;
2084 dtrace_hashstat_data_t *hdata = &data.dtagsd_hash;
2085
2086 bzero(&data, sizeof (data));
2087
2088 if (!(flags & DCMD_ADDRSPEC))
2089 return (DCMD_USAGE);
2090
2091 if (mdb_vread(&buf, sizeof (buf), addr) == -1) {
2092 mdb_warn("failed to read aggregation buffer at %p", addr);
2093 return (DCMD_ERR);
2094 }
2095
2096 aaddr = (uintptr_t)buf.dtb_tomax +
2097 buf.dtb_size - sizeof (dtrace_aggbuffer_t);
2098
2099 if (mdb_vread(&agb, sizeof (agb), aaddr) == -1) {
2100 mdb_warn("failed to read dtrace_aggbuffer_t at %p", aaddr);
2101 return (DCMD_ERR);
2102 }
2103
2104 hsize = (actual = agb.dtagb_hashsize) * sizeof (size_t);
2105 hdata->dthsd_counts = mdb_alloc(hsize, UM_SLEEP | UM_GC);
2106
2107 /*
2108 * Now pick the largest prime smaller than the hash size. (If the
2109 * existing size is prime, we'll pick a smaller prime just for the
2110 * hell of it.)
2111 */
2112 for (prime = agb.dtagb_hashsize - 1; prime > 7; prime--) {
2113 size_t limit = prime / 7;
2114
2115 for (i = 2; i < limit; i++) {
2116 if ((prime % i) == 0)
2117 break;
2118 }
2119
2120 if (i == limit)
2121 break;
2122 }
2123
2124 /*
2125 * And now we want to pick the largest power of two smaller than the
2126 * hashsize.
2127 */
2128 for (i = 0; (1 << i) < agb.dtagb_hashsize; i++)
2129 continue;
2130
2131 evenpow = (1 << (i - 1));
2132
2133 for (i = 0; _dtrace_hashstat[i].dths_name != NULL; i++) {
2134 data.dtagsd_func = _dtrace_hashstat[i].dths_func;
2135
2136 hdata->dthsd_hashsize = actual;
2137 hsize = hdata->dthsd_hashsize * sizeof (size_t);
2138 bzero(hdata->dthsd_counts, hsize);
2139
2140 if (mdb_pwalk("dtrace_aggkey",
2141 (mdb_walk_cb_t)dtrace_aggstat_walk, &data, addr) == -1) {
2142 mdb_warn("failed to walk dtrace_aggkey at %p", addr);
2143 return (DCMD_ERR);
2144 }
2145
2146 dtrace_hashstat_stats(_dtrace_hashstat[i].dths_name, hdata);
2147
2148 /*
2149 * If we were just printing the actual value, we won't try
2150 * any of the sizing experiments.
2151 */
2152 if (data.dtagsd_func == NULL)
2153 continue;
2154
2155 hdata->dthsd_hashsize = prime;
2156 hsize = hdata->dthsd_hashsize * sizeof (size_t);
2157 bzero(hdata->dthsd_counts, hsize);
2158
2159 if (mdb_pwalk("dtrace_aggkey",
2160 (mdb_walk_cb_t)dtrace_aggstat_walk, &data, addr) == -1) {
2161 mdb_warn("failed to walk dtrace_aggkey at %p", addr);
2162 return (DCMD_ERR);
2163 }
2164
2165 dtrace_hashstat_stats(_dtrace_hashstat[i].dths_name, hdata);
2166
2167 hdata->dthsd_hashsize = evenpow;
2168 hsize = hdata->dthsd_hashsize * sizeof (size_t);
2169 bzero(hdata->dthsd_counts, hsize);
2170
2171 if (mdb_pwalk("dtrace_aggkey",
2172 (mdb_walk_cb_t)dtrace_aggstat_walk, &data, addr) == -1) {
2173 mdb_warn("failed to walk dtrace_aggkey at %p", addr);
2174 return (DCMD_ERR);
2175 }
2176
2177 dtrace_hashstat_stats(_dtrace_hashstat[i].dths_name, hdata);
2178 }
2179
2180 return (DCMD_OK);
2181 }
2182
2183 /*ARGSUSED*/
2184 static int
dtrace_dynstat_walk(uintptr_t addr,dtrace_dynvar_t * dynvar,dtrace_aggstat_data_t * data)2185 dtrace_dynstat_walk(uintptr_t addr, dtrace_dynvar_t *dynvar,
2186 dtrace_aggstat_data_t *data)
2187 {
2188 dtrace_hashstat_data_t *hdata = &data->dtagsd_hash;
2189 dtrace_tuple_t *tuple = &dynvar->dtdv_tuple;
2190 dtrace_key_t *key = tuple->dtt_key;
2191 size_t size = 0, offs = 0;
2192 int i, nkeys = tuple->dtt_nkeys;
2193 char *buf;
2194
2195 if (data->dtagsd_func == NULL) {
2196 size_t bucket = dynvar->dtdv_hashval % hdata->dthsd_hashsize;
2197
2198 hdata->dthsd_counts[bucket]++;
2199 return (WALK_NEXT);
2200 }
2201
2202 /*
2203 * We want to hand the hashing algorithm a contiguous buffer. First
2204 * run through the tuple and determine the size.
2205 */
2206 for (i = 0; i < nkeys; i++) {
2207 if (key[i].dttk_size == 0) {
2208 size += sizeof (uint64_t);
2209 } else {
2210 size += key[i].dttk_size;
2211 }
2212 }
2213
2214 buf = alloca(size);
2215
2216 /*
2217 * Now go back through the tuple and copy the data into the buffer.
2218 */
2219 for (i = 0; i < nkeys; i++) {
2220 if (key[i].dttk_size == 0) {
2221 bcopy(&key[i].dttk_value, &buf[offs],
2222 sizeof (uint64_t));
2223 offs += sizeof (uint64_t);
2224 } else {
2225 if (mdb_vread(&buf[offs], key[i].dttk_size,
2226 key[i].dttk_value) == -1) {
2227 mdb_warn("couldn't read tuple data at %p",
2228 key[i].dttk_value);
2229 return (WALK_ERR);
2230 }
2231
2232 offs += key[i].dttk_size;
2233 }
2234 }
2235
2236 hdata->dthsd_data = buf;
2237 hdata->dthsd_size = size;
2238
2239 data->dtagsd_func(hdata);
2240
2241 return (WALK_NEXT);
2242 }
2243
2244 /*ARGSUSED*/
2245 int
dtrace_dynstat(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2246 dtrace_dynstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2247 {
2248 dtrace_dstate_t dstate;
2249 size_t hsize, i, actual, prime;
2250 dtrace_aggstat_data_t data;
2251 dtrace_hashstat_data_t *hdata = &data.dtagsd_hash;
2252
2253 bzero(&data, sizeof (data));
2254
2255 if (!(flags & DCMD_ADDRSPEC))
2256 return (DCMD_USAGE);
2257
2258 if (mdb_vread(&dstate, sizeof (dstate), addr) == -1) {
2259 mdb_warn("failed to read dynamic variable state at %p", addr);
2260 return (DCMD_ERR);
2261 }
2262
2263 hsize = (actual = dstate.dtds_hashsize) * sizeof (size_t);
2264 hdata->dthsd_counts = mdb_alloc(hsize, UM_SLEEP | UM_GC);
2265
2266 /*
2267 * Now pick the largest prime smaller than the hash size. (If the
2268 * existing size is prime, we'll pick a smaller prime just for the
2269 * hell of it.)
2270 */
2271 for (prime = dstate.dtds_hashsize - 1; prime > 7; prime--) {
2272 size_t limit = prime / 7;
2273
2274 for (i = 2; i < limit; i++) {
2275 if ((prime % i) == 0)
2276 break;
2277 }
2278
2279 if (i == limit)
2280 break;
2281 }
2282
2283 for (i = 0; _dtrace_hashstat[i].dths_name != NULL; i++) {
2284 data.dtagsd_func = _dtrace_hashstat[i].dths_func;
2285
2286 hdata->dthsd_hashsize = actual;
2287 hsize = hdata->dthsd_hashsize * sizeof (size_t);
2288 bzero(hdata->dthsd_counts, hsize);
2289
2290 if (mdb_pwalk("dtrace_dynvar",
2291 (mdb_walk_cb_t)dtrace_dynstat_walk, &data, addr) == -1) {
2292 mdb_warn("failed to walk dtrace_dynvar at %p", addr);
2293 return (DCMD_ERR);
2294 }
2295
2296 dtrace_hashstat_stats(_dtrace_hashstat[i].dths_name, hdata);
2297
2298 /*
2299 * If we were just printing the actual value, we won't try
2300 * any of the sizing experiments.
2301 */
2302 if (data.dtagsd_func == NULL)
2303 continue;
2304
2305 hdata->dthsd_hashsize = prime;
2306 hsize = hdata->dthsd_hashsize * sizeof (size_t);
2307 bzero(hdata->dthsd_counts, hsize);
2308
2309 if (mdb_pwalk("dtrace_dynvar",
2310 (mdb_walk_cb_t)dtrace_dynstat_walk, &data, addr) == -1) {
2311 mdb_warn("failed to walk dtrace_aggkey at %p", addr);
2312 return (DCMD_ERR);
2313 }
2314
2315 dtrace_hashstat_stats(_dtrace_hashstat[i].dths_name, hdata);
2316 }
2317
2318 return (DCMD_OK);
2319 }
2320
2321 typedef struct dtrace_ecb_walk {
2322 dtrace_ecb_t **dtew_ecbs;
2323 int dtew_necbs;
2324 int dtew_curecb;
2325 } dtrace_ecb_walk_t;
2326
2327 static int
dtrace_ecb_init(mdb_walk_state_t * wsp)2328 dtrace_ecb_init(mdb_walk_state_t *wsp)
2329 {
2330 uintptr_t addr;
2331 dtrace_state_t state;
2332 dtrace_ecb_walk_t *ecbwp;
2333
2334 if ((addr = wsp->walk_addr) == NULL) {
2335 mdb_warn("dtrace_ecb walk needs dtrace_state_t\n");
2336 return (WALK_ERR);
2337 }
2338
2339 if (mdb_vread(&state, sizeof (state), addr) == -1) {
2340 mdb_warn("failed to read dtrace state pointer at %p", addr);
2341 return (WALK_ERR);
2342 }
2343
2344 ecbwp = mdb_zalloc(sizeof (dtrace_ecb_walk_t), UM_SLEEP | UM_GC);
2345
2346 ecbwp->dtew_ecbs = state.dts_ecbs;
2347 ecbwp->dtew_necbs = state.dts_necbs;
2348 ecbwp->dtew_curecb = 0;
2349
2350 wsp->walk_data = ecbwp;
2351
2352 return (WALK_NEXT);
2353 }
2354
2355 static int
dtrace_ecb_step(mdb_walk_state_t * wsp)2356 dtrace_ecb_step(mdb_walk_state_t *wsp)
2357 {
2358 uintptr_t ecbp, addr;
2359 dtrace_ecb_walk_t *ecbwp = wsp->walk_data;
2360
2361 addr = (uintptr_t)ecbwp->dtew_ecbs +
2362 ecbwp->dtew_curecb * sizeof (dtrace_ecb_t *);
2363
2364 if (ecbwp->dtew_curecb++ == ecbwp->dtew_necbs)
2365 return (WALK_DONE);
2366
2367 if (mdb_vread(&ecbp, sizeof (addr), addr) == -1) {
2368 mdb_warn("failed to read ecb at entry %d\n",
2369 ecbwp->dtew_curecb);
2370 return (WALK_ERR);
2371 }
2372
2373 if (ecbp == NULL)
2374 return (WALK_NEXT);
2375
2376 return (wsp->walk_callback(ecbp, NULL, wsp->walk_cbdata));
2377 }
2378
2379 static void
dtrace_options_numtostr(uint64_t num,char * buf,size_t len)2380 dtrace_options_numtostr(uint64_t num, char *buf, size_t len)
2381 {
2382 uint64_t n = num;
2383 int index = 0;
2384 char u;
2385
2386 while (n >= 1024) {
2387 n = (n + (1024 / 2)) / 1024; /* Round up or down */
2388 index++;
2389 }
2390
2391 u = " KMGTPE"[index];
2392
2393 if (index == 0) {
2394 (void) mdb_snprintf(buf, len, "%llu", (u_longlong_t)n);
2395 } else if (n < 10 && (num & (num - 1)) != 0) {
2396 (void) mdb_snprintf(buf, len, "%.2f%c",
2397 (double)num / (1ULL << 10 * index), u);
2398 } else if (n < 100 && (num & (num - 1)) != 0) {
2399 (void) mdb_snprintf(buf, len, "%.1f%c",
2400 (double)num / (1ULL << 10 * index), u);
2401 } else {
2402 (void) mdb_snprintf(buf, len, "%llu%c", (u_longlong_t)n, u);
2403 }
2404 }
2405
2406 static void
dtrace_options_numtohz(uint64_t num,char * buf,size_t len)2407 dtrace_options_numtohz(uint64_t num, char *buf, size_t len)
2408 {
2409 (void) mdb_snprintf(buf, len, "%dhz", NANOSEC/num);
2410 }
2411
2412 static void
dtrace_options_numtobufpolicy(uint64_t num,char * buf,size_t len)2413 dtrace_options_numtobufpolicy(uint64_t num, char *buf, size_t len)
2414 {
2415 char *policy = "unknown";
2416
2417 switch (num) {
2418 case DTRACEOPT_BUFPOLICY_RING:
2419 policy = "ring";
2420 break;
2421
2422 case DTRACEOPT_BUFPOLICY_FILL:
2423 policy = "fill";
2424 break;
2425
2426 case DTRACEOPT_BUFPOLICY_SWITCH:
2427 policy = "switch";
2428 break;
2429 }
2430
2431 (void) mdb_snprintf(buf, len, "%s", policy);
2432 }
2433
2434 static void
dtrace_options_numtocpu(uint64_t cpu,char * buf,size_t len)2435 dtrace_options_numtocpu(uint64_t cpu, char *buf, size_t len)
2436 {
2437 if (cpu == DTRACE_CPUALL)
2438 (void) mdb_snprintf(buf, len, "%7s", "unbound");
2439 else
2440 (void) mdb_snprintf(buf, len, "%d", cpu);
2441 }
2442
2443 typedef void (*dtrace_options_func_t)(uint64_t, char *, size_t);
2444
2445 static struct dtrace_options {
2446 char *dtop_optstr;
2447 dtrace_options_func_t dtop_func;
2448 } _dtrace_options[] = {
2449 { "bufsize", dtrace_options_numtostr },
2450 { "bufpolicy", dtrace_options_numtobufpolicy },
2451 { "dynvarsize", dtrace_options_numtostr },
2452 { "aggsize", dtrace_options_numtostr },
2453 { "specsize", dtrace_options_numtostr },
2454 { "nspec", dtrace_options_numtostr },
2455 { "strsize", dtrace_options_numtostr },
2456 { "cleanrate", dtrace_options_numtohz },
2457 { "cpu", dtrace_options_numtocpu },
2458 { "bufresize", dtrace_options_numtostr },
2459 { "grabanon", dtrace_options_numtostr },
2460 { "flowindent", dtrace_options_numtostr },
2461 { "quiet", dtrace_options_numtostr },
2462 { "stackframes", dtrace_options_numtostr },
2463 { "ustackframes", dtrace_options_numtostr },
2464 { "aggrate", dtrace_options_numtohz },
2465 { "switchrate", dtrace_options_numtohz },
2466 { "statusrate", dtrace_options_numtohz },
2467 { "destructive", dtrace_options_numtostr },
2468 { "stackindent", dtrace_options_numtostr },
2469 { "rawbytes", dtrace_options_numtostr },
2470 { "jstackframes", dtrace_options_numtostr },
2471 { "jstackstrsize", dtrace_options_numtostr },
2472 { "aggsortkey", dtrace_options_numtostr },
2473 { "aggsortrev", dtrace_options_numtostr },
2474 { "aggsortpos", dtrace_options_numtostr },
2475 { "aggsortkeypos", dtrace_options_numtostr }
2476 };
2477
2478 static void
dtrace_options_help(void)2479 dtrace_options_help(void)
2480 {
2481 mdb_printf("Given a dtrace_state_t structure, displays the "
2482 "current tunable option\nsettings.\n");
2483 }
2484
2485 /*ARGSUSED*/
2486 static int
dtrace_options(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2487 dtrace_options(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2488 {
2489 dtrace_state_t state;
2490 int i = 0;
2491 dtrace_optval_t *options;
2492 char val[32];
2493
2494 if (!(flags & DCMD_ADDRSPEC))
2495 return (DCMD_USAGE);
2496
2497 if (mdb_vread(&state, sizeof (dtrace_state_t), (uintptr_t)addr) == -1) {
2498 mdb_warn("failed to read state pointer at %p\n", addr);
2499 return (DCMD_ERR);
2500 }
2501
2502 options = &state.dts_options[0];
2503
2504 mdb_printf("%<u>%-25s %s%</u>\n", "OPTION", "VALUE");
2505 for (i = 0; i < DTRACEOPT_MAX; i++) {
2506 if (options[i] == DTRACEOPT_UNSET) {
2507 mdb_printf("%-25s %s\n",
2508 _dtrace_options[i].dtop_optstr, "UNSET");
2509 } else {
2510 (void) _dtrace_options[i].dtop_func(options[i],
2511 val, 32);
2512 mdb_printf("%-25s %s\n",
2513 _dtrace_options[i].dtop_optstr, val);
2514 }
2515 }
2516
2517 return (DCMD_OK);
2518 }
2519
2520 static int
pid2state_init(mdb_walk_state_t * wsp)2521 pid2state_init(mdb_walk_state_t *wsp)
2522 {
2523 dtrace_state_data_t *data;
2524 uintptr_t devi;
2525 uintptr_t proc;
2526 struct dev_info info;
2527 pid_t pid = (pid_t)wsp->walk_addr;
2528
2529 if (wsp->walk_addr == NULL) {
2530 mdb_warn("pid2state walk requires PID\n");
2531 return (WALK_ERR);
2532 }
2533
2534 data = mdb_zalloc(sizeof (dtrace_state_data_t), UM_SLEEP | UM_GC);
2535
2536 if (mdb_readvar(&data->dtsd_softstate, "dtrace_softstate") == -1) {
2537 mdb_warn("failed to read 'dtrace_softstate'");
2538 return (DCMD_ERR);
2539 }
2540
2541 if ((proc = mdb_pid2proc(pid, NULL)) == NULL) {
2542 mdb_warn("PID 0t%d not found\n", pid);
2543 return (DCMD_ERR);
2544 }
2545
2546 if (mdb_readvar(&devi, "dtrace_devi") == -1) {
2547 mdb_warn("failed to read 'dtrace_devi'");
2548 return (DCMD_ERR);
2549 }
2550
2551 if (mdb_vread(&info, sizeof (struct dev_info), devi) == -1) {
2552 mdb_warn("failed to read 'dev_info'");
2553 return (DCMD_ERR);
2554 }
2555
2556 data->dtsd_major = info.devi_major;
2557 data->dtsd_proc = proc;
2558
2559 wsp->walk_data = data;
2560
2561 return (WALK_NEXT);
2562 }
2563
2564 /*ARGSUSED*/
2565 static int
pid2state_file(uintptr_t addr,struct file * f,dtrace_state_data_t * data)2566 pid2state_file(uintptr_t addr, struct file *f, dtrace_state_data_t *data)
2567 {
2568 vnode_t vnode;
2569 minor_t minor;
2570 uintptr_t statep;
2571
2572 /* Get the vnode for this file */
2573 if (mdb_vread(&vnode, sizeof (vnode), (uintptr_t)f->f_vnode) == -1) {
2574 mdb_warn("couldn't read vnode at %p", (uintptr_t)f->f_vnode);
2575 return (WALK_NEXT);
2576 }
2577
2578
2579 /* Is this the dtrace device? */
2580 if (getmajor(vnode.v_rdev) != data->dtsd_major)
2581 return (WALK_NEXT);
2582
2583 /* Get the minor number for this device entry */
2584 minor = getminor(vnode.v_rdev);
2585
2586 if (mdb_get_soft_state_byaddr(data->dtsd_softstate, minor,
2587 &statep, NULL, 0) == -1) {
2588 mdb_warn("failed to read softstate for minor %d", minor);
2589 return (WALK_NEXT);
2590 }
2591
2592 mdb_printf("%p\n", statep);
2593
2594 return (WALK_NEXT);
2595 }
2596
2597 static int
pid2state_step(mdb_walk_state_t * wsp)2598 pid2state_step(mdb_walk_state_t *wsp)
2599 {
2600 dtrace_state_data_t *ds = wsp->walk_data;
2601
2602 if (mdb_pwalk("file",
2603 (mdb_walk_cb_t)pid2state_file, ds, ds->dtsd_proc) == -1) {
2604 mdb_warn("couldn't walk 'file' for proc %p", ds->dtsd_proc);
2605 return (WALK_ERR);
2606 }
2607
2608 return (WALK_DONE);
2609 }
2610
2611 /*ARGSUSED*/
2612 static int
dtrace_probes_walk(uintptr_t addr,void * ignored,uintptr_t * target)2613 dtrace_probes_walk(uintptr_t addr, void *ignored, uintptr_t *target)
2614 {
2615 dtrace_ecb_t ecb;
2616 dtrace_probe_t probe;
2617 dtrace_probedesc_t pd;
2618
2619 if (addr == NULL)
2620 return (WALK_ERR);
2621
2622 if (mdb_vread(&ecb, sizeof (dtrace_ecb_t), addr) == -1) {
2623 mdb_warn("failed to read ecb %p\n", addr);
2624 return (WALK_ERR);
2625 }
2626
2627 if (ecb.dte_probe == NULL)
2628 return (WALK_ERR);
2629
2630 if (mdb_vread(&probe, sizeof (dtrace_probe_t),
2631 (uintptr_t)ecb.dte_probe) == -1) {
2632 mdb_warn("failed to read probe %p\n", ecb.dte_probe);
2633 return (WALK_ERR);
2634 }
2635
2636 pd.dtpd_id = probe.dtpr_id;
2637 dtracemdb_probe(NULL, &pd);
2638
2639 mdb_printf("%5d %10s %17s %33s %s\n", pd.dtpd_id, pd.dtpd_provider,
2640 pd.dtpd_mod, pd.dtpd_func, pd.dtpd_name);
2641
2642 return (WALK_NEXT);
2643 }
2644
2645 static void
dtrace_probes_help(void)2646 dtrace_probes_help(void)
2647 {
2648 mdb_printf("Given a dtrace_state_t structure, displays all "
2649 "its active enablings. If no\nstate structure is provided, "
2650 "all available probes are listed.\n");
2651 }
2652
2653 /*ARGSUSED*/
2654 static int
dtrace_probes(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2655 dtrace_probes(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2656 {
2657 dtrace_probedesc_t pd;
2658 uintptr_t caddr, base, paddr;
2659 int nprobes, i;
2660
2661 mdb_printf("%5s %10s %17s %33s %s\n",
2662 "ID", "PROVIDER", "MODULE", "FUNCTION", "NAME");
2663
2664 if (!(flags & DCMD_ADDRSPEC)) {
2665 /*
2666 * If no argument is provided just display all available
2667 * probes.
2668 */
2669 if (mdb_readvar(&base, "dtrace_probes") == -1) {
2670 mdb_warn("failed to read 'dtrace_probes'");
2671 return (-1);
2672 }
2673
2674 if (mdb_readvar(&nprobes, "dtrace_nprobes") == -1) {
2675 mdb_warn("failed to read 'dtrace_nprobes'");
2676 return (-1);
2677 }
2678
2679 for (i = 0; i < nprobes; i++) {
2680 caddr = base + i * sizeof (dtrace_probe_t *);
2681
2682 if (mdb_vread(&paddr, sizeof (paddr), caddr) == -1) {
2683 mdb_warn("couldn't read probe pointer at %p",
2684 caddr);
2685 continue;
2686 }
2687
2688 if (paddr == NULL)
2689 continue;
2690
2691 pd.dtpd_id = i + 1;
2692 if (dtracemdb_probe(NULL, &pd) == 0) {
2693 mdb_printf("%5d %10s %17s %33s %s\n",
2694 pd.dtpd_id, pd.dtpd_provider,
2695 pd.dtpd_mod, pd.dtpd_func, pd.dtpd_name);
2696 }
2697 }
2698 } else {
2699 if (mdb_pwalk("dtrace_ecb", (mdb_walk_cb_t)dtrace_probes_walk,
2700 NULL, addr) == -1) {
2701 mdb_warn("couldn't walk 'dtrace_ecb'");
2702 return (DCMD_ERR);
2703 }
2704 }
2705
2706 return (DCMD_OK);
2707 }
2708
2709 const mdb_dcmd_t kernel_dcmds[] = {
2710 { "id2probe", ":", "translate a dtrace_id_t to a dtrace_probe_t",
2711 id2probe },
2712 { "dtrace", ":[-c cpu]", "print dtrace(1M)-like output",
2713 dtrace, dtrace_help },
2714 { "dtrace_errhash", ":", "print DTrace error hash", dtrace_errhash },
2715 { "dtrace_helptrace", ":", "print DTrace helper trace",
2716 dtrace_helptrace },
2717 { "dtrace_state", ":", "print active DTrace consumers", dtrace_state,
2718 dtrace_state_help },
2719 { "dtrace_aggstat", ":",
2720 "print DTrace aggregation hash statistics", dtrace_aggstat },
2721 { "dtrace_dynstat", ":",
2722 "print DTrace dynamic variable hash statistics", dtrace_dynstat },
2723 { "dtrace_options", ":",
2724 "print a DTrace consumer's current tuneable options",
2725 dtrace_options, dtrace_options_help },
2726 { "dtrace_probes", "?", "print a DTrace consumer's enabled probes",
2727 dtrace_probes, dtrace_probes_help },
2728 { NULL }
2729 };
2730
2731 const mdb_walker_t kernel_walkers[] = {
2732 { "dtrace_errhash", "walk hash of DTrace error messasges",
2733 dtrace_errhash_init, dtrace_errhash_step },
2734 { "dtrace_helptrace", "walk DTrace helper trace entries",
2735 dtrace_helptrace_init, dtrace_helptrace_step },
2736 { "dtrace_state", "walk DTrace per-consumer softstate",
2737 dtrace_state_init, dtrace_state_step },
2738 { "dtrace_aggkey", "walk DTrace aggregation keys",
2739 dtrace_aggkey_init, dtrace_aggkey_step, dtrace_aggkey_fini },
2740 { "dtrace_dynvar", "walk DTrace dynamic variables",
2741 dtrace_dynvar_init, dtrace_dynvar_step, dtrace_dynvar_fini },
2742 { "dtrace_ecb", "walk a DTrace consumer's enabling control blocks",
2743 dtrace_ecb_init, dtrace_ecb_step },
2744 { "pid2state", "walk a processes dtrace_state structures",
2745 pid2state_init, pid2state_step },
2746 { NULL }
2747 };
2748