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