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 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <mdb/mdb_modapi.h>
27#include <sys/bitset.h>
28
29#include "bitset.h"		/* XXX work out ifdef in include file... */
30
31void
32bitset_help(void)
33{
34	mdb_printf("Print the bitset at the address given\n");
35}
36
37static void
38bitset_free(bitset_t *bs)
39{
40	if (bs == NULL)
41		return;
42	if (bs->bs_set && bs->bs_words)
43		mdb_free(bs->bs_set, bs->bs_words * sizeof (ulong_t));
44	mdb_free(bs, sizeof (*bs));
45}
46
47static bitset_t *
48bitset_get(uintptr_t bsaddr)
49{
50	bitset_t	*bs;
51
52	bs = mdb_zalloc(sizeof (*bs), UM_SLEEP);
53	if (mdb_vread(bs, sizeof (*bs), bsaddr) == -1) {
54		mdb_warn("couldn't read bitset 0x%p", bsaddr);
55		bitset_free(bs);
56		return (NULL);
57	}
58
59	bsaddr = (uintptr_t)bs->bs_set;
60	bs->bs_set = mdb_alloc(bs->bs_words * sizeof (ulong_t), UM_SLEEP);
61	if (mdb_vread(bs->bs_set,
62	    bs->bs_words * sizeof (ulong_t), bsaddr) == -1) {
63		mdb_warn("couldn't read bitset bs_set 0x%p", bsaddr);
64		bitset_free(bs);
65		return (NULL);
66	}
67	return (bs);
68
69}
70
71static int
72bitset_highbit(bitset_t *bs)
73{
74	int	high;
75	int	i;
76
77	if ((bs->bs_set == NULL) || (bs->bs_words == 0))
78		return (-1);
79
80	/* move backwards through words */
81	for (i = bs->bs_words; i >= 0; i--)
82		if (bs->bs_set[i])
83			break;
84	if (i < 0)
85		return (-1);
86
87	/* move backwards through bits */
88	high = i << BT_ULSHIFT;
89	for (i = BT_NBIPUL - 1; i; i--)
90		if (BT_TEST(bs->bs_set, high + i))
91			break;
92	return (high + i + 1);
93}
94
95static int
96pow10(int exp)
97{
98	int	res;
99
100	for (res = 1; exp; exp--)
101		res *= 10;
102	return (res);
103}
104
105static int
106log10(int val)
107{
108	int	res = 0;
109
110	do {
111		res++;
112		val /= 10;
113	} while (val);
114	return (res);
115}
116
117/*
118 * The following prints a bitset with a 'ruler' that look like this
119 *
120 *              11111111112222222222333333333344444444445555555555666666666677
121 *    012345678901234567890123456789012345678901234567890123456789012345678901
122 * xx:........................................................................
123 *                                11111111111111111111111111111111111111111111
124 *    777777778888888888999999999900000000001111111111222222222233333333334444
125 *    234567890123456789012345678901234567890123456789012345678901234567890123
126 *    ........................................................................
127 *    111111111111111111111111111111111111111111111111111111112222222222222222
128 *    444444555555555566666666667777777777888888888899999999990000000000111111
129 *    456789012345678901234567890123456789012345678901234567890123456789012345
130 *    ........................................................................
131 *    2222222222
132 *    1111222222
133 *    6789012345
134 *    ..........
135 *
136 * to identify individual bits that are set.
137 */
138static void
139bitset_print(bitset_t *bs, char *label, int width)
140{
141	int	val_start;
142	int	val_max;
143	int	label_width;
144	int	ruler_width;
145	int	v, vm, vi;
146	int	nl, l;
147	int	i;
148	int	p;
149	char	c;
150
151	val_start = 0;
152	val_max = bitset_highbit(bs) + 1;
153	if (val_max <= val_start) {
154		mdb_printf("%s: empty-set", label);
155		return;
156	}
157
158	label_width = strlen(label) + 1;
159	ruler_width = width - label_width;
160
161	for (v = val_start; v < val_max; v = vm) {
162		if ((v + ruler_width) < val_max)
163			vm = v + ruler_width;
164		else
165			vm = val_max;
166
167		nl = log10(vm) - 1;
168		for (l = nl; l >= 0; l--) {
169			p = pow10(l);
170			for (i = 0; i < label_width; i++)
171				mdb_printf(" ");
172
173			for (vi = v; vi < vm; vi++) {
174				c = '0' + ((vi / p) % 10);
175				if ((l == nl) && (c == '0'))
176					c = ' ';
177				mdb_printf("%c", c);
178			}
179
180			mdb_printf("\n");
181		}
182
183		if (v == val_start) {
184			mdb_printf("%s:", label);
185		} else {
186			for (i = 0; i < label_width; i++)
187				mdb_printf(" ");
188		}
189		for (vi = v; vi < vm; vi++) {
190			if (BT_TEST(bs->bs_set, vi))
191				mdb_printf("X");
192			else
193				mdb_printf(".");
194		}
195		mdb_printf("\n");
196	}
197}
198
199/*ARGSUSED*/
200int
201bitset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
202{
203	bitset_t	*bs;
204
205	bs = bitset_get(addr);
206	if (bs == NULL)
207		return (DCMD_ERR);
208
209	bitset_print(bs, "label", 80);
210	bitset_free(bs);
211	return (DCMD_OK);
212}
213