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