summaryrefslogtreecommitdiff
path: root/usr/src/cmd/mdb/common/modules/genunix/dist.c
blob: 37b6ad553fe18d90d1679c0103944d4cedda3f34 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <mdb/mdb_modapi.h>
#ifndef	_KMDB
#include <math.h>
#endif

#include "dist.h"

/*
 * Divides the given range (inclusive at both endpoints) evenly into the given
 * number of buckets, adding one bucket at the end that is one past the end of
 * the range. The returned buckets will be automatically freed when the dcmd
 * completes or is forcibly aborted.
 */
const int *
dist_linear(int buckets, int beg, int end)
{
	int *out = mdb_alloc((buckets + 1) * sizeof (*out), UM_SLEEP | UM_GC);
	int pos;
	int dist = end - beg + 1;

	for (pos = 0; pos < buckets; pos++)
		out[pos] = beg + (pos * dist)/buckets;
	out[buckets] = end + 1;

	return (out);
}

/*
 * We want the bins to be a constant ratio:
 *
 *	b_0	  = beg;
 *	b_idx	  = b_{idx-1} * r;
 *	b_buckets = end + 1;
 *
 * That is:
 *
 *	       buckets
 *	beg * r        = end
 *
 * Which reduces to:
 *
 *		  buckets ___________________
 *	      r = -------/ ((end + 1) / beg)
 *
 *		  log ((end + 1) / beg)
 *	  log r = ---------------------
 *		         buckets
 *
 *		   (log ((end + 1) / beg)) / buckets
 *	      r = e
 */
/* ARGSUSED */
const int *
dist_geometric(int buckets, int beg, int end, int minbucketsize)
{
#ifdef	_KMDB
	return (dist_linear(buckets, beg, end));
#else
	int *out = mdb_alloc((buckets + 1) * sizeof (*out), UM_SLEEP | UM_GC);

	double r;
	double b;
	int idx = 0;
	int last;
	int begzero;

	if (minbucketsize == 0)
		minbucketsize = 1;

	if (buckets == 1) {
		out[0] = beg;
		out[1] = end + 1;
		return (out);
	}

	begzero = (beg == 0);
	if (begzero)
		beg = 1;

	r = exp(log((double)(end + 1) / beg) / buckets);

	/*
	 * We've now computed r, using the previously derived formula.  We
	 * now need to generate the array of bucket bounds.  There are
	 * two major variables:
	 *
	 *	b	holds b_idx, the current index, as a double.
	 *	last	holds the integer which goes into out[idx]
	 *
	 * Our job is to transform the smooth function b_idx, defined
	 * above, into integer-sized buckets, with a specified minimum
	 * bucket size.  Since b_idx is an exponentially growing function,
	 * any inadequate buckets must be at the beginning.  To deal
	 * with this, we make buckets of minimum size until b catches up
	 * with last.
	 *
	 * A final wrinkle is that beg *can* be zero.  We compute r and b
	 * as if beg was 1, then start last as 0.  This can lead to a bit
	 * of oddness around the 0 bucket, but it's mostly reasonable.
	 */

	b = last = beg;
	if (begzero)
		last = 0;

	for (idx = 0; idx < buckets; idx++) {
		int next;

		out[idx] = last;

		b *= r;
		next = (int)b;

		if (next > last + minbucketsize - 1)
			last = next;
		else
			last += minbucketsize;
	}
	out[buckets] = end + 1;

	return (out);
#endif
}

#define	NCHARS	50
/*
 * Print the distribution header with the given bucket label. The header is
 * printed on a single line, and the label is assumed to fit within the given
 * width (number of characters). The default label width when unspecified (0)
 * is eleven characters. Optionally, a label other than "count" may be specified
 * for the bucket counts.
 */
void
dist_print_header(const char *label, int width, const char *count)
{
	int n;
	const char *dist = " Distribution ";
	char dashes[NCHARS + 1];

	if (width == 0)
		width = 11;

	if (count == NULL)
		count = "count";

	n = (NCHARS - strlen(dist)) / 2;
	(void) memset(dashes, '-', n);
	dashes[n] = '\0';

	mdb_printf("%*s  %s%s%s %s\n", width, label, dashes, dist, dashes,
	    count);
}

/*
 * Print one distribution bucket whose range is from distarray[i] inclusive to
 * distarray[i + 1] exclusive by totalling counts in that index range.  The
 * given total is assumed to be the sum of all elements in the counts array.
 * Each bucket is labeled by its range in the form "first-last" (omit "-last" if
 * the range is a single value) where first and last are integers, and last is
 * one less than the first value of the next bucket range. The bucket label is
 * assumed to fit within the given width (number of characters), which should
 * match the width value passed to dist_print_header(). The default width when
 * unspecified (0) is eleven characters.
 */
void
dist_print_bucket(const int *distarray, int i, const uint_t *counts,
    uint64_t total, int width)
{
	int b;				/* bucket range index */
	int bb = distarray[i];		/* bucket begin */
	int be = distarray[i + 1] - 1;	/* bucket end */
	uint64_t count = 0;		/* bucket value */

	int nats;
	char ats[NCHARS + 1], spaces[NCHARS + 1];
	char range[40];

	if (width == 0)
		width = 11;

	if (total == 0)
		total = 1;		/* avoid divide-by-zero */

	for (b = bb; b <= be; b++)
		count += counts[b];

	nats = (NCHARS * count) / total;
	(void) memset(ats, '@', nats);
	ats[nats] = 0;
	(void) memset(spaces, ' ', NCHARS - nats);
	spaces[NCHARS - nats] = 0;

	if (bb == be)
		(void) mdb_snprintf(range, sizeof (range), "%d", bb);
	else
		(void) mdb_snprintf(range, sizeof (range), "%d-%d", bb, be);
	mdb_printf("%*s |%s%s %lld\n", width, range, ats, spaces, count);
}
#undef NCHARS