xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/cyclic.c (revision e00bdde3c6d406f40f53f3025defadc22f7ec31a)
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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Copyright 2023 Oxide Computer Company
28  */
29 
30 #include "cyclic.h"
31 
32 #define	CYCLIC_TRACE
33 
34 #include <mdb/mdb_modapi.h>
35 #include <sys/timer.h>
36 #include <sys/cyclic_impl.h>
37 #include <sys/sysmacros.h>
38 #include <stdio.h>
39 
40 int
41 cyccpu_vread(cyc_cpu_t *cpu, uintptr_t addr)
42 {
43 	static int inited = 0;
44 	static int cyc_trace_enabled = 0;
45 	static size_t cyccpu_size;
46 
47 	if (!inited) {
48 		inited = 1;
49 		(void) mdb_readvar(&cyc_trace_enabled, "cyc_trace_enabled");
50 		cyccpu_size = (cyc_trace_enabled) ? sizeof (*cpu) :
51 		    OFFSETOF(cyc_cpu_t, cyp_trace);
52 	}
53 
54 	if (mdb_vread(cpu, cyccpu_size, addr) == -1)
55 		return (-1);
56 
57 	if (!cyc_trace_enabled)
58 		bzero(cpu->cyp_trace, sizeof (cpu->cyp_trace));
59 
60 	return (0);
61 }
62 
63 int
64 cyccpu_walk_init(mdb_walk_state_t *wsp)
65 {
66 	if (mdb_layered_walk("cpu", wsp) == -1) {
67 		mdb_warn("couldn't walk 'cpu'");
68 		return (WALK_ERR);
69 	}
70 
71 	return (WALK_NEXT);
72 }
73 
74 int
75 cyccpu_walk_step(mdb_walk_state_t *wsp)
76 {
77 	uintptr_t addr = (uintptr_t)((cpu_t *)wsp->walk_layer)->cpu_cyclic;
78 	cyc_cpu_t cpu;
79 
80 	if (cyccpu_vread(&cpu, addr) == -1) {
81 		mdb_warn("couldn't read cyc_cpu at %p", addr);
82 		return (WALK_ERR);
83 	}
84 
85 	return (wsp->walk_callback(addr, &cpu, wsp->walk_cbdata));
86 }
87 
88 int
89 cycomni_walk_init(mdb_walk_state_t *wsp)
90 {
91 	cyc_id_t id;
92 
93 	if (wsp->walk_addr == 0) {
94 		mdb_warn("must provide a cyclic id\n");
95 		return (WALK_ERR);
96 	}
97 
98 	if (mdb_vread(&id, sizeof (id), wsp->walk_addr) == -1) {
99 		mdb_warn("couldn't read cyc_id_t at %p", wsp->walk_addr);
100 		return (WALK_ERR);
101 	}
102 
103 	if (id.cyi_cpu != NULL || id.cyi_omni_list == NULL ||
104 	    id.cyi_omni_hdlr.cyo_online == NULL) {
105 		mdb_warn("%p is not an omnipresent cyclic.\n", wsp->walk_addr);
106 		return (WALK_ERR);
107 	}
108 
109 	wsp->walk_addr = (uintptr_t)id.cyi_omni_list;
110 
111 	return (WALK_NEXT);
112 }
113 
114 int
115 cycomni_walk_step(mdb_walk_state_t *wsp)
116 {
117 	uintptr_t addr = wsp->walk_addr;
118 	cyc_omni_cpu_t omni;
119 
120 	if (addr == 0)
121 		return (WALK_DONE);
122 
123 	if (mdb_vread(&omni, sizeof (omni), addr) == -1) {
124 		mdb_warn("couldn't read cyc_omni_cpu at %p", addr);
125 		return (WALK_ERR);
126 	}
127 
128 	wsp->walk_addr = (uintptr_t)omni.cyo_next;
129 
130 	return (wsp->walk_callback(addr, &omni, wsp->walk_cbdata));
131 }
132 
133 void
134 cyclic_dump_node(cyc_cpu_t *cpu, cyc_index_t *heap, char **c, size_t w,
135     int ndx, int l, int r, int depth)
136 {
137 	int heap_left, heap_right;
138 	int me;
139 	int i, x = l + (r - l) / 2;
140 	size_t n = w - (x - 1); /* n bytes left for snprintf after c[][x - 1] */
141 
142 	heap_left = CYC_HEAP_LEFT(ndx);
143 	heap_right = CYC_HEAP_RIGHT(ndx);
144 	me = heap[ndx];
145 
146 	if (ndx >= cpu->cyp_nelems)
147 		return;
148 
149 	if (me < 10) {
150 		(void) mdb_snprintf(&c[depth][x - 1], n, " %d", me);
151 	} else if (me >= 100) {
152 		(void) mdb_snprintf(&c[depth][x - 1], n, "%3d", me);
153 	} else {
154 		(void) mdb_snprintf(&c[depth][x - 1], n, "%s%2d%s",
155 		    CYC_HEAP_LEFT(CYC_HEAP_PARENT(ndx)) == ndx ? " " : "", me,
156 		    CYC_HEAP_LEFT(CYC_HEAP_PARENT(ndx)) == ndx ? "" : " ");
157 	}
158 
159 	if (r - l > 5) {
160 		c[++depth][x] = '|';
161 		depth++;
162 
163 		for (i = l + (r - l) / 4; i < r - (r - l) / 4; i++)
164 			c[depth][i] = '-';
165 		c[depth][l + (r - l) / 4] = '+';
166 		c[depth][r - (r - l) / 4 - 1] = '+';
167 		c[depth][x] = '+';
168 	} else {
169 
170 		if (heap_left >= cpu->cyp_nelems)
171 			return;
172 
173 		(void) mdb_snprintf(&c[++depth][x - 1], n, "L%d",
174 		    heap[heap_left]);
175 
176 		if (heap_right >= cpu->cyp_nelems)
177 			return;
178 
179 		(void) mdb_snprintf(&c[++depth][x - 1], n, "R%d",
180 		    heap[heap_right]);
181 		return;
182 	}
183 
184 	if (heap_left < cpu->cyp_nelems)
185 		cyclic_dump_node(cpu, heap, c, w, heap_left, l, x, depth + 1);
186 
187 	if (heap_right < cpu->cyp_nelems)
188 		cyclic_dump_node(cpu, heap, c, w, heap_right, x, r, depth + 1);
189 }
190 
191 #define	LINES_PER_LEVEL 3
192 
193 void
194 cyclic_pretty_dump(cyc_cpu_t *cpu)
195 {
196 	char **c;
197 	int i, j;
198 	int width = 80;
199 	int depth;
200 	cyc_index_t *heap;
201 	size_t hsize = sizeof (cyc_index_t) * cpu->cyp_size;
202 
203 	heap = mdb_alloc(hsize, UM_SLEEP | UM_GC);
204 
205 	if (mdb_vread(heap, hsize, (uintptr_t)cpu->cyp_heap) == -1) {
206 		mdb_warn("couldn't read heap at %p", (uintptr_t)cpu->cyp_heap);
207 		return;
208 	}
209 
210 	for (depth = 0; (1 << depth) < cpu->cyp_nelems; depth++)
211 		continue;
212 	depth++;
213 	depth = (depth + 1) * LINES_PER_LEVEL;
214 
215 	c = mdb_zalloc(sizeof (char *) * depth, UM_SLEEP|UM_GC);
216 
217 	for (i = 0; i < depth; i++)
218 		c[i] = mdb_zalloc(width, UM_SLEEP|UM_GC);
219 
220 	cyclic_dump_node(cpu, heap, c, width, 0, 1, width - 2, 0);
221 
222 	for (i = 0; i < depth; i++) {
223 		int dump = 0;
224 		for (j = 0; j < width - 1; j++) {
225 			if (c[i][j] == '\0')
226 				c[i][j] = ' ';
227 			else
228 				dump = 1;
229 		}
230 		c[i][width - 2] = '\n';
231 
232 		if (dump)
233 			mdb_printf(c[i]);
234 	}
235 }
236 
237 int
238 cycinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
239 {
240 	cyc_cpu_t cpu;
241 	cpu_t c;
242 	cyc_index_t root, i, *heap;
243 	size_t hsize;
244 	cyclic_t *cyc;
245 	uintptr_t caddr;
246 	uint_t verbose = FALSE, Verbose = FALSE;
247 	int header = 0;
248 	cyc_level_t lev;
249 
250 	if (!(flags & DCMD_ADDRSPEC)) {
251 		if (mdb_walk_dcmd("cyccpu", "cycinfo", argc, argv) == -1) {
252 			mdb_warn("can't walk 'cyccpu'");
253 			return (DCMD_ERR);
254 		}
255 		return (DCMD_OK);
256 	}
257 
258 	if (mdb_getopts(argc, argv,
259 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
260 	    'V', MDB_OPT_SETBITS, TRUE, &Verbose, NULL) != argc)
261 		return (DCMD_USAGE);
262 
263 	if (!DCMD_HDRSPEC(flags) && (verbose || Verbose))
264 		mdb_printf("\n\n");
265 
266 	if (DCMD_HDRSPEC(flags) || verbose || Verbose)
267 		mdb_printf("%3s %?s %7s %6s %15s %s\n", "CPU",
268 		    "CYC_CPU", "STATE", "NELEMS", "FIRE", "HANDLER");
269 
270 	if (cyccpu_vread(&cpu, addr) == -1) {
271 		mdb_warn("couldn't read cyc_cpu at %p", addr);
272 		return (DCMD_ERR);
273 	}
274 
275 	if (mdb_vread(&c, sizeof (c), (uintptr_t)cpu.cyp_cpu) == -1) {
276 		mdb_warn("couldn't read cpu at %p", cpu.cyp_cpu);
277 		return (DCMD_ERR);
278 	}
279 
280 	cyc = mdb_alloc(sizeof (cyclic_t) * cpu.cyp_size, UM_SLEEP | UM_GC);
281 	caddr = (uintptr_t)cpu.cyp_cyclics;
282 
283 	if (mdb_vread(cyc, sizeof (cyclic_t) * cpu.cyp_size, caddr) == -1) {
284 		mdb_warn("couldn't read cyclic at %p", caddr);
285 		return (DCMD_ERR);
286 	}
287 
288 	hsize = sizeof (cyc_index_t) * cpu.cyp_size;
289 	heap = mdb_alloc(hsize, UM_SLEEP | UM_GC);
290 
291 	if (mdb_vread(heap, hsize, (uintptr_t)cpu.cyp_heap) == -1) {
292 		mdb_warn("couldn't read heap at %p", cpu.cyp_heap);
293 		return (DCMD_ERR);
294 	}
295 
296 	root = heap[0];
297 
298 	mdb_printf("%3d %0?p %7s %6d ", c.cpu_id, addr,
299 	    cpu.cyp_state == CYS_ONLINE ? "online" :
300 	    cpu.cyp_state == CYS_OFFLINE ? "offline" :
301 	    cpu.cyp_state == CYS_EXPANDING ? "expand" :
302 	    cpu.cyp_state == CYS_REMOVING ? "remove" :
303 	    cpu.cyp_state == CYS_SUSPENDED ? "suspend" : "????",
304 	    cpu.cyp_nelems);
305 
306 	if (cpu.cyp_nelems > 0)
307 		mdb_printf("%15llx %a\n",
308 		    cyc[root].cy_expire, cyc[root].cy_handler);
309 	else
310 		mdb_printf("%15s %s\n", "-", "-");
311 
312 	if (!verbose && !Verbose)
313 		return (DCMD_OK);
314 
315 	mdb_printf("\n");
316 
317 	cyclic_pretty_dump(&cpu);
318 
319 	mdb_inc_indent(2);
320 
321 	for (i = 0; i < cpu.cyp_size; i++) {
322 		int j;
323 
324 		for (j = 0; j < cpu.cyp_size; j++) {
325 			if (heap[j] == i)
326 				break;
327 		}
328 
329 		if (!Verbose && j >= cpu.cyp_nelems)
330 			continue;
331 
332 		if (!header) {
333 			header = 1;
334 			mdb_printf("\n%?s %3s %3s %3s %5s %14s %s\n",
335 			    "ADDR", "NDX", "HPX", "LVL",
336 			    "PEND", "FIRE", "HANDLER");
337 		}
338 
339 		mdb_printf("%0?p %3d ", caddr + i * sizeof (cyclic_t), i);
340 
341 		mdb_printf("%3d ", j);
342 
343 		if (j >= cpu.cyp_nelems) {
344 			mdb_printf("%3s %5s %14s %s\n", "-", "-", "-", "-");
345 			continue;
346 		}
347 
348 		mdb_printf("%3s %5d ",
349 		    cyc[i].cy_level == CY_HIGH_LEVEL ? "hgh" :
350 		    cyc[i].cy_level == CY_LOCK_LEVEL ? "lck" :
351 		    cyc[i].cy_level == CY_LOW_LEVEL ? "low" : "????",
352 		    cyc[i].cy_pend);
353 
354 		if (cyc[i].cy_expire != INT64_MAX)
355 			mdb_printf("%14llx ", cyc[i].cy_expire);
356 		else
357 			mdb_printf("%14s ", "-");
358 
359 		mdb_printf("%a\n", cyc[i].cy_handler);
360 	}
361 
362 
363 	if (!Verbose)
364 		goto out;
365 
366 	for (lev = CY_LOW_LEVEL; lev < CY_LOW_LEVEL + CY_SOFT_LEVELS; lev++) {
367 		cyc_softbuf_t *softbuf = &cpu.cyp_softbuf[lev];
368 		char which = softbuf->cys_hard, shared = 1;
369 		cyc_pcbuffer_t *pc;
370 		size_t bufsiz;
371 		cyc_index_t *buf;
372 
373 		if (softbuf->cys_hard != softbuf->cys_soft)
374 			shared = 0;
375 
376 again:
377 		pc = &softbuf->cys_buf[which];
378 		bufsiz = (pc->cypc_sizemask + 1) * sizeof (cyc_index_t);
379 		buf = mdb_alloc(bufsiz, UM_SLEEP | UM_GC);
380 
381 		if (mdb_vread(buf, bufsiz, (uintptr_t)pc->cypc_buf) == -1) {
382 			mdb_warn("couldn't read cypc_buf at %p", pc->cypc_buf);
383 			continue;
384 		}
385 
386 		mdb_printf("\n%3s %4s %4s %4s %?s %4s %?s\n", "CPU",
387 		    "LEVL", "USER", "NDX", "ADDR", "CYC", "CYC_ADDR", "PEND");
388 
389 		for (i = 0; i <= pc->cypc_sizemask &&
390 		    i <= pc->cypc_prodndx; i++) {
391 			uintptr_t cyc_addr = caddr + buf[i] * sizeof (cyclic_t);
392 
393 			mdb_printf("%3d %4s %4s ", c.cpu_id,
394 			    lev == CY_HIGH_LEVEL ? "high" :
395 			    lev == CY_LOCK_LEVEL ? "lock" :
396 			    lev == CY_LOW_LEVEL ? "low" : "????",
397 			    shared ? "shrd" : which == softbuf->cys_hard ?
398 			    "hard" : "soft");
399 
400 			mdb_printf("%4d %0?p ", i,
401 			    (uintptr_t)&buf[i] - (uintptr_t)&buf[0] +
402 			    (uintptr_t)pc->cypc_buf, buf[i],
403 			    caddr + buf[i] * sizeof (cyclic_t));
404 
405 			if (i >= pc->cypc_prodndx)
406 				mdb_printf("%4s %?s %5s  ", "-", "-", "-");
407 			else {
408 				cyclic_t c;
409 
410 				if (mdb_vread(&c, sizeof (c), cyc_addr) == -1) {
411 					mdb_warn("\ncouldn't read cyclic at "
412 					    "%p", cyc_addr);
413 					continue;
414 				}
415 
416 				mdb_printf("%4d %0?p %5d  ", buf[i],
417 				    cyc_addr, c.cy_pend);
418 			}
419 
420 			if (i == (pc->cypc_consndx & pc->cypc_sizemask)) {
421 				mdb_printf("<-- c");
422 				if (i == (pc->cypc_prodndx & pc->cypc_sizemask))
423 					mdb_printf(",p");
424 				mdb_printf("\n");
425 				continue;
426 			}
427 
428 			if (i == (pc->cypc_prodndx & pc->cypc_sizemask)) {
429 				mdb_printf("<-- p\n");
430 				continue;
431 			}
432 			mdb_printf("\n");
433 
434 			if (i >= pc->cypc_prodndx)
435 				break;
436 		}
437 
438 		if (!shared && which == softbuf->cys_hard) {
439 			which = softbuf->cys_soft;
440 			goto again;
441 		}
442 	}
443 
444 out:
445 	mdb_dec_indent(2);
446 	return (DCMD_OK);
447 }
448 
449 int
450 cyctrace_walk_init(mdb_walk_state_t *wsp)
451 {
452 	cyc_cpu_t *cpu;
453 	int i;
454 
455 	cpu = mdb_zalloc(sizeof (cyc_cpu_t), UM_SLEEP);
456 
457 	if (wsp->walk_addr == 0) {
458 		/*
459 		 * If an address isn't provided, we'll use the passive buffer.
460 		 */
461 		GElf_Sym sym;
462 		cyc_tracebuf_t *tr = &cpu->cyp_trace[0];
463 		uintptr_t addr;
464 
465 		if (mdb_lookup_by_name("cyc_ptrace", &sym) == -1) {
466 			mdb_warn("couldn't find passive buffer");
467 			return (-1);
468 		}
469 
470 		addr = (uintptr_t)sym.st_value;
471 
472 		if (mdb_vread(tr, sizeof (cyc_tracebuf_t), addr) == -1) {
473 			mdb_warn("couldn't read passive buffer");
474 			return (-1);
475 		}
476 
477 		wsp->walk_addr = addr - offsetof(cyc_cpu_t, cyp_trace[0]);
478 	} else {
479 		if (cyccpu_vread(cpu, wsp->walk_addr) == -1) {
480 			mdb_warn("couldn't read cyc_cpu at %p", wsp->walk_addr);
481 			mdb_free(cpu, sizeof (cyc_cpu_t));
482 			return (-1);
483 		}
484 	}
485 
486 	for (i = 0; i < CY_LEVELS; i++) {
487 		if (cpu->cyp_trace[i].cyt_ndx-- == 0)
488 			cpu->cyp_trace[i].cyt_ndx = CY_NTRACEREC - 1;
489 	}
490 
491 	wsp->walk_data = cpu;
492 
493 	return (0);
494 }
495 
496 int
497 cyctrace_walk_step(mdb_walk_state_t *wsp)
498 {
499 	cyc_cpu_t *cpu = wsp->walk_data;
500 	cyc_tracebuf_t *buf = cpu->cyp_trace;
501 	hrtime_t latest = 0;
502 	int i, ndx, new_ndx, lev, rval;
503 	uintptr_t addr;
504 
505 	for (i = 0; i < CY_LEVELS; i++) {
506 		if ((ndx = buf[i].cyt_ndx) == -1)
507 			continue;
508 
509 		/*
510 		 * Account for NPT.
511 		 */
512 		buf[i].cyt_buf[ndx].cyt_tstamp <<= 1;
513 		buf[i].cyt_buf[ndx].cyt_tstamp >>= 1;
514 
515 		if (buf[i].cyt_buf[ndx].cyt_tstamp > latest) {
516 			latest = buf[i].cyt_buf[ndx].cyt_tstamp;
517 			lev = i;
518 		}
519 	}
520 
521 	/*
522 	 * If we didn't find one, we're done.
523 	 */
524 	if (latest == 0)
525 		return (-1);
526 
527 	buf = &buf[lev];
528 	ndx = buf->cyt_ndx;
529 	addr = wsp->walk_addr +
530 	    (uintptr_t)&(buf->cyt_buf[ndx]) - (uintptr_t)cpu;
531 
532 	rval = wsp->walk_callback(addr, &buf->cyt_buf[ndx], wsp->walk_cbdata);
533 
534 	new_ndx = ndx == 0 ? CY_NTRACEREC - 1 : ndx - 1;
535 
536 	if (buf->cyt_buf[new_ndx].cyt_tstamp != 0 &&
537 	    buf->cyt_buf[new_ndx].cyt_tstamp > buf->cyt_buf[ndx].cyt_tstamp)
538 		new_ndx = -1;
539 
540 	buf->cyt_ndx = new_ndx;
541 
542 	return (rval);
543 }
544 
545 void
546 cyctrace_walk_fini(mdb_walk_state_t *wsp)
547 {
548 	cyc_cpu_t *cpu = wsp->walk_data;
549 
550 	mdb_free(cpu, sizeof (cyc_cpu_t));
551 }
552 
553 #define	WHYLEN	17
554 
555 int
556 cyctrace_walk(uintptr_t addr, const cyc_tracerec_t *rec, cyc_cpu_t *cpu)
557 {
558 	int i;
559 	char c[WHYLEN];
560 
561 	for (i = 0; cpu != NULL && i < CY_LEVELS; i++)
562 		if (addr < (uintptr_t)&cpu->cyp_trace[i + 1].cyt_buf[0])
563 			break;
564 
565 	(void) mdb_readstr(c, WHYLEN, (uintptr_t)rec->cyt_why);
566 
567 	mdb_printf("%08p %4s %15llx %-*s %15llx %15llx\n",
568 	    addr & UINT_MAX, cpu == NULL ? "pasv" :
569 	    i == CY_HIGH_LEVEL ? "high" : i == CY_LOCK_LEVEL ? "lock" :
570 	    i == CY_LOW_LEVEL ? "low" : "????", rec->cyt_tstamp, WHYLEN, c,
571 	    rec->cyt_arg0, rec->cyt_arg1);
572 
573 	return (0);
574 }
575 
576 /*ARGSUSED*/
577 int
578 cyctrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
579 {
580 	if (!(flags & DCMD_ADDRSPEC) || argc != 0)
581 		addr = 0;
582 
583 	if (mdb_pwalk("cyctrace", (mdb_walk_cb_t)cyctrace_walk,
584 	    (void *)addr, addr) == -1) {
585 		mdb_warn("couldn't walk cyctrace");
586 		return (DCMD_ERR);
587 	}
588 
589 	return (DCMD_OK);
590 }
591 
592 int
593 cyccover_comp(const void *l, const void *r)
594 {
595 	cyc_coverage_t *lhs = (cyc_coverage_t *)l;
596 	cyc_coverage_t *rhs = (cyc_coverage_t *)r;
597 
598 	char ly[WHYLEN], ry[WHYLEN];
599 
600 	if (rhs->cyv_why == lhs->cyv_why)
601 		return (0);
602 
603 	if (rhs->cyv_why == NULL)
604 		return (-1);
605 
606 	if (lhs->cyv_why == NULL)
607 		return (1);
608 
609 	(void) mdb_readstr(ly, WHYLEN, (uintptr_t)lhs->cyv_why);
610 	(void) mdb_readstr(ry, WHYLEN, (uintptr_t)rhs->cyv_why);
611 
612 	return (strcmp(ly, ry));
613 }
614 
615 /*ARGSUSED*/
616 int
617 cyccover(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
618 {
619 	cyc_coverage_t cv[CY_NCOVERAGE];
620 	char c[WHYLEN];
621 	GElf_Sym sym;
622 	int i;
623 
624 	if ((flags & DCMD_ADDRSPEC) || argc != 0)
625 		return (DCMD_USAGE);
626 
627 	if (mdb_lookup_by_name("cyc_coverage", &sym) == -1) {
628 		mdb_warn("couldn't find coverage information");
629 		return (DCMD_ABORT);
630 	}
631 
632 	addr = (uintptr_t)sym.st_value;
633 
634 	if (mdb_vread(cv, sizeof (cyc_coverage_t) * CY_NCOVERAGE, addr) == -1) {
635 		mdb_warn("couldn't read coverage array at %p", addr);
636 		return (DCMD_ABORT);
637 	}
638 
639 	mdb_printf("%-*s %8s %8s %8s %15s %15s\n",
640 	    WHYLEN, "POINT", "HIGH", "LOCK", "LOW/PASV", "ARG0", "ARG1");
641 
642 	qsort(cv, CY_NCOVERAGE, sizeof (cyc_coverage_t), cyccover_comp);
643 
644 	for (i = 0; i < CY_NCOVERAGE; i++) {
645 		if (cv[i].cyv_why != NULL) {
646 			(void) mdb_readstr(c, WHYLEN, (uintptr_t)cv[i].cyv_why);
647 			mdb_printf("%-*s %8d %8d %8d %15llx %15llx\n",
648 			    WHYLEN, c,
649 			    cv[i].cyv_count[CY_HIGH_LEVEL],
650 			    cv[i].cyv_count[CY_LOCK_LEVEL],
651 			    cv[i].cyv_passive_count != 0 ?
652 			    cv[i].cyv_passive_count :
653 			    cv[i].cyv_count[CY_LOW_LEVEL],
654 			    cv[i].cyv_arg0, cv[i].cyv_arg1);
655 		}
656 	}
657 
658 	return (DCMD_OK);
659 }
660 
661 /*ARGSUSED*/
662 int
663 cyclic(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
664 {
665 	cyclic_t cyc;
666 
667 	if (!(flags & DCMD_ADDRSPEC) || argc != 0)
668 		return (DCMD_USAGE);
669 
670 	if (DCMD_HDRSPEC(flags))
671 		mdb_printf("%?s %4s %5s %5s %15s %7s %s\n", "ADDR", "LEVL",
672 		    "PEND", "FLAGS", "FIRE", "USECINT", "HANDLER");
673 
674 	if (mdb_vread(&cyc, sizeof (cyclic_t), addr) == -1) {
675 		mdb_warn("couldn't read cyclic at %p", addr);
676 		return (DCMD_ERR);
677 	}
678 
679 	mdb_printf("%0?p %4s %5d  %04x %15llx %7lld %a\n", addr,
680 	    cyc.cy_level == CY_HIGH_LEVEL ? "high" :
681 	    cyc.cy_level == CY_LOCK_LEVEL ? "lock" :
682 	    cyc.cy_level == CY_LOW_LEVEL ? "low" : "????",
683 	    cyc.cy_pend, cyc.cy_flags, cyc.cy_expire,
684 	    cyc.cy_interval / (uint64_t)(NANOSEC / MICROSEC),
685 	    cyc.cy_handler);
686 
687 	return (DCMD_OK);
688 }
689 
690 static int
691 cycid_cpu(cyc_cpu_t *addr, int ndx)
692 {
693 	cyc_cpu_t cpu;
694 	cpu_t c;
695 	uintptr_t caddr;
696 	cyclic_t cyc;
697 
698 	if (cyccpu_vread(&cpu, (uintptr_t)addr) == -1) {
699 		mdb_warn("couldn't read cyc_cpu at %p", addr);
700 		return (DCMD_ERR);
701 	}
702 
703 	if (mdb_vread(&c, sizeof (c), (uintptr_t)cpu.cyp_cpu) == -1) {
704 		mdb_warn("couldn't read cpu at %p", cpu.cyp_cpu);
705 		return (DCMD_ERR);
706 	}
707 
708 	caddr = (uintptr_t)cpu.cyp_cyclics + ndx * sizeof (cyclic_t);
709 
710 	if (mdb_vread(&cyc, sizeof (cyc), caddr) == -1) {
711 		mdb_warn("couldn't read cyclic at %p", caddr);
712 		return (DCMD_ERR);
713 	}
714 
715 	mdb_printf("%4d %3d %?p %a\n", c.cpu_id, ndx, caddr, cyc.cy_handler);
716 
717 	return (DCMD_OK);
718 }
719 
720 /*ARGSUSED*/
721 static int
722 cycid_walk_omni(uintptr_t addr, const cyc_omni_cpu_t *omni, int *ignored)
723 {
724 	mdb_printf("%?s        ");
725 	cycid_cpu(omni->cyo_cpu, omni->cyo_ndx);
726 
727 	return (WALK_NEXT);
728 }
729 
730 /*ARGSUSED*/
731 int
732 cycid(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
733 {
734 	cyc_id_t id;
735 
736 	if (!(flags & DCMD_ADDRSPEC)) {
737 		if (mdb_walk_dcmd("cyclic_id_cache", "cycid", ac, av) == -1) {
738 			mdb_warn("can't walk cyclic_id_cache");
739 			return (DCMD_ERR);
740 		}
741 
742 		return (DCMD_OK);
743 	}
744 
745 	if (DCMD_HDRSPEC(flags)) {
746 		mdb_printf("%?s %4s %3s %?s %s\n", "ADDR", "CPU", "NDX",
747 		    "CYCLIC", "HANDLER");
748 	}
749 
750 	if (mdb_vread(&id, sizeof (id), addr) == -1) {
751 		mdb_warn("couldn't read cyc_id_t at %p", addr);
752 		return (DCMD_ERR);
753 	}
754 
755 	if (id.cyi_cpu == NULL) {
756 		/*
757 		 * This is an omnipresent cyclic.
758 		 */
759 		mdb_printf("%?p %4s %3s %?s %a\n", addr, "omni", "-", "-",
760 		    id.cyi_omni_hdlr.cyo_online);
761 		mdb_printf("%?s    |\n", "");
762 		mdb_printf("%?s    +-->%4s %3s %?s %s\n", "",
763 		    "CPU", "NDX", "CYCLIC", "HANDLER");
764 
765 		if (mdb_pwalk("cycomni",
766 		    (mdb_walk_cb_t)cycid_walk_omni, NULL, addr) == -1) {
767 			mdb_warn("couldn't walk cycomni for %p", addr);
768 			return (DCMD_ERR);
769 		}
770 
771 		mdb_printf("\n");
772 
773 		return (DCMD_OK);
774 	}
775 
776 	mdb_printf("%?p ", addr);
777 
778 	return (cycid_cpu(id.cyi_cpu, id.cyi_ndx));
779 }
780