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