1*087e1372Stomee /* 2*087e1372Stomee * CDDL HEADER START 3*087e1372Stomee * 4*087e1372Stomee * The contents of this file are subject to the terms of the 5*087e1372Stomee * Common Development and Distribution License (the "License"). 6*087e1372Stomee * You may not use this file except in compliance with the License. 7*087e1372Stomee * 8*087e1372Stomee * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*087e1372Stomee * or http://www.opensolaris.org/os/licensing. 10*087e1372Stomee * See the License for the specific language governing permissions 11*087e1372Stomee * and limitations under the License. 12*087e1372Stomee * 13*087e1372Stomee * When distributing Covered Code, include this CDDL HEADER in each 14*087e1372Stomee * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*087e1372Stomee * If applicable, add the following below this CDDL HEADER, with the 16*087e1372Stomee * fields enclosed by brackets "[]" replaced with your own identifying 17*087e1372Stomee * information: Portions Copyright [yyyy] [name of copyright owner] 18*087e1372Stomee * 19*087e1372Stomee * CDDL HEADER END 20*087e1372Stomee */ 21*087e1372Stomee /* 22*087e1372Stomee * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23*087e1372Stomee * Use is subject to license terms. 24*087e1372Stomee */ 25*087e1372Stomee 26*087e1372Stomee #pragma ident "%Z%%M% %I% %E% SMI" 27*087e1372Stomee 28*087e1372Stomee #include <mdb/mdb_modapi.h> 29*087e1372Stomee #ifndef _KMDB 30*087e1372Stomee #include <math.h> 31*087e1372Stomee #endif 32*087e1372Stomee 33*087e1372Stomee #include "dist.h" 34*087e1372Stomee 35*087e1372Stomee /* 36*087e1372Stomee * Divides the given range (inclusive at both endpoints) evenly into the given 37*087e1372Stomee * number of buckets, adding one bucket at the end that is one past the end of 38*087e1372Stomee * the range. The returned buckets will be automatically freed when the dcmd 39*087e1372Stomee * completes or is forcibly aborted. 40*087e1372Stomee */ 41*087e1372Stomee const int * 42*087e1372Stomee dist_linear(int buckets, int beg, int end) 43*087e1372Stomee { 44*087e1372Stomee int *out = mdb_alloc((buckets + 1) * sizeof (*out), UM_SLEEP | UM_GC); 45*087e1372Stomee int pos; 46*087e1372Stomee int dist = end - beg + 1; 47*087e1372Stomee 48*087e1372Stomee for (pos = 0; pos < buckets; pos++) 49*087e1372Stomee out[pos] = beg + (pos * dist)/buckets; 50*087e1372Stomee out[buckets] = end + 1; 51*087e1372Stomee 52*087e1372Stomee return (out); 53*087e1372Stomee } 54*087e1372Stomee 55*087e1372Stomee /* 56*087e1372Stomee * We want the bins to be a constant ratio: 57*087e1372Stomee * 58*087e1372Stomee * b_0 = beg; 59*087e1372Stomee * b_idx = b_{idx-1} * r; 60*087e1372Stomee * b_buckets = end + 1; 61*087e1372Stomee * 62*087e1372Stomee * That is: 63*087e1372Stomee * 64*087e1372Stomee * buckets 65*087e1372Stomee * beg * r = end 66*087e1372Stomee * 67*087e1372Stomee * Which reduces to: 68*087e1372Stomee * 69*087e1372Stomee * buckets ___________________ 70*087e1372Stomee * r = -------/ ((end + 1) / beg) 71*087e1372Stomee * 72*087e1372Stomee * log ((end + 1) / beg) 73*087e1372Stomee * log r = --------------------- 74*087e1372Stomee * buckets 75*087e1372Stomee * 76*087e1372Stomee * (log ((end + 1) / beg)) / buckets 77*087e1372Stomee * r = e 78*087e1372Stomee */ 79*087e1372Stomee /* ARGSUSED */ 80*087e1372Stomee const int * 81*087e1372Stomee dist_geometric(int buckets, int beg, int end, int minbucketsize) 82*087e1372Stomee { 83*087e1372Stomee #ifdef _KMDB 84*087e1372Stomee return (dist_linear(buckets, beg, end)); 85*087e1372Stomee #else 86*087e1372Stomee int *out = mdb_alloc((buckets + 1) * sizeof (*out), UM_SLEEP | UM_GC); 87*087e1372Stomee 88*087e1372Stomee double r; 89*087e1372Stomee double b; 90*087e1372Stomee int idx = 0; 91*087e1372Stomee int last; 92*087e1372Stomee int begzero; 93*087e1372Stomee 94*087e1372Stomee if (minbucketsize == 0) 95*087e1372Stomee minbucketsize = 1; 96*087e1372Stomee 97*087e1372Stomee if (buckets == 1) { 98*087e1372Stomee out[0] = beg; 99*087e1372Stomee out[1] = end + 1; 100*087e1372Stomee return (out); 101*087e1372Stomee } 102*087e1372Stomee 103*087e1372Stomee begzero = (beg == 0); 104*087e1372Stomee if (begzero) 105*087e1372Stomee beg = 1; 106*087e1372Stomee 107*087e1372Stomee r = exp(log((double)(end + 1) / beg) / buckets); 108*087e1372Stomee 109*087e1372Stomee /* 110*087e1372Stomee * We've now computed r, using the previously derived formula. We 111*087e1372Stomee * now need to generate the array of bucket bounds. There are 112*087e1372Stomee * two major variables: 113*087e1372Stomee * 114*087e1372Stomee * b holds b_idx, the current index, as a double. 115*087e1372Stomee * last holds the integer which goes into out[idx] 116*087e1372Stomee * 117*087e1372Stomee * Our job is to transform the smooth function b_idx, defined 118*087e1372Stomee * above, into integer-sized buckets, with a specified minimum 119*087e1372Stomee * bucket size. Since b_idx is an exponentially growing function, 120*087e1372Stomee * any inadequate buckets must be at the beginning. To deal 121*087e1372Stomee * with this, we make buckets of minimum size until b catches up 122*087e1372Stomee * with last. 123*087e1372Stomee * 124*087e1372Stomee * A final wrinkle is that beg *can* be zero. We compute r and b 125*087e1372Stomee * as if beg was 1, then start last as 0. This can lead to a bit 126*087e1372Stomee * of oddness around the 0 bucket, but it's mostly reasonable. 127*087e1372Stomee */ 128*087e1372Stomee 129*087e1372Stomee b = last = beg; 130*087e1372Stomee if (begzero) 131*087e1372Stomee last = 0; 132*087e1372Stomee 133*087e1372Stomee for (idx = 0; idx < buckets; idx++) { 134*087e1372Stomee int next; 135*087e1372Stomee 136*087e1372Stomee out[idx] = last; 137*087e1372Stomee 138*087e1372Stomee b *= r; 139*087e1372Stomee next = (int)b; 140*087e1372Stomee 141*087e1372Stomee if (next > last + minbucketsize - 1) 142*087e1372Stomee last = next; 143*087e1372Stomee else 144*087e1372Stomee last += minbucketsize; 145*087e1372Stomee } 146*087e1372Stomee out[buckets] = end + 1; 147*087e1372Stomee 148*087e1372Stomee return (out); 149*087e1372Stomee #endif 150*087e1372Stomee } 151*087e1372Stomee 152*087e1372Stomee #define NCHARS 50 153*087e1372Stomee /* 154*087e1372Stomee * Print the distribution header with the given bucket label. The header is 155*087e1372Stomee * printed on a single line, and the label is assumed to fit within the given 156*087e1372Stomee * width (number of characters). The default label width when unspecified (0) 157*087e1372Stomee * is eleven characters. Optionally, a label other than "count" may be specified 158*087e1372Stomee * for the bucket counts. 159*087e1372Stomee */ 160*087e1372Stomee void 161*087e1372Stomee dist_print_header(const char *label, int width, const char *count) 162*087e1372Stomee { 163*087e1372Stomee int n; 164*087e1372Stomee const char *dist = " Distribution "; 165*087e1372Stomee char dashes[NCHARS + 1]; 166*087e1372Stomee 167*087e1372Stomee if (width == 0) 168*087e1372Stomee width = 11; 169*087e1372Stomee 170*087e1372Stomee if (count == NULL) 171*087e1372Stomee count = "count"; 172*087e1372Stomee 173*087e1372Stomee n = (NCHARS - strlen(dist)) / 2; 174*087e1372Stomee (void) memset(dashes, '-', n); 175*087e1372Stomee dashes[n] = '\0'; 176*087e1372Stomee 177*087e1372Stomee mdb_printf("%*s %s%s%s %s\n", width, label, dashes, dist, dashes, 178*087e1372Stomee count); 179*087e1372Stomee } 180*087e1372Stomee 181*087e1372Stomee /* 182*087e1372Stomee * Print one distribution bucket whose range is from distarray[i] inclusive to 183*087e1372Stomee * distarray[i + 1] exclusive by totalling counts in that index range. The 184*087e1372Stomee * given total is assumed to be the sum of all elements in the counts array. 185*087e1372Stomee * Each bucket is labeled by its range in the form "first-last" (omit "-last" if 186*087e1372Stomee * the range is a single value) where first and last are integers, and last is 187*087e1372Stomee * one less than the first value of the next bucket range. The bucket label is 188*087e1372Stomee * assumed to fit within the given width (number of characters), which should 189*087e1372Stomee * match the width value passed to dist_print_header(). The default width when 190*087e1372Stomee * unspecified (0) is eleven characters. 191*087e1372Stomee */ 192*087e1372Stomee void 193*087e1372Stomee dist_print_bucket(const int *distarray, int i, const uint_t *counts, 194*087e1372Stomee uint64_t total, int width) 195*087e1372Stomee { 196*087e1372Stomee int b; /* bucket range index */ 197*087e1372Stomee int bb = distarray[i]; /* bucket begin */ 198*087e1372Stomee int be = distarray[i + 1] - 1; /* bucket end */ 199*087e1372Stomee uint64_t count = 0; /* bucket value */ 200*087e1372Stomee 201*087e1372Stomee int nats; 202*087e1372Stomee char ats[NCHARS + 1], spaces[NCHARS + 1]; 203*087e1372Stomee char range[40]; 204*087e1372Stomee 205*087e1372Stomee if (width == 0) 206*087e1372Stomee width = 11; 207*087e1372Stomee 208*087e1372Stomee if (total == 0) 209*087e1372Stomee total = 1; /* avoid divide-by-zero */ 210*087e1372Stomee 211*087e1372Stomee for (b = bb; b <= be; b++) 212*087e1372Stomee count += counts[b]; 213*087e1372Stomee 214*087e1372Stomee nats = (NCHARS * count) / total; 215*087e1372Stomee (void) memset(ats, '@', nats); 216*087e1372Stomee ats[nats] = 0; 217*087e1372Stomee (void) memset(spaces, ' ', NCHARS - nats); 218*087e1372Stomee spaces[NCHARS - nats] = 0; 219*087e1372Stomee 220*087e1372Stomee if (bb == be) 221*087e1372Stomee (void) mdb_snprintf(range, sizeof (range), "%d", bb); 222*087e1372Stomee else 223*087e1372Stomee (void) mdb_snprintf(range, sizeof (range), "%d-%d", bb, be); 224*087e1372Stomee mdb_printf("%*s |%s%s %lld\n", width, range, ats, spaces, count); 225*087e1372Stomee } 226*087e1372Stomee #undef NCHARS 227