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