/*--------------------------------------------------------------- * Copyright (c) 2017 * Broadcom Corporation * All Rights Reserved. *--------------------------------------------------------------- * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, * sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do * so, subject to the following conditions: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and * the following disclaimers. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimers in the documentation and/or other materials * provided with the distribution. * * * Neither the name of Broadcom Coporation, * nor the names of its contributors may be used to endorse * or promote products derived from this Software without * specific prior written permission. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ________________________________________________________________ * * histograms.c * Suppport for histograms * * by Robert J. McMahon (rjmcmahon@rjmcmahon.com, bob.mcmahon@broadcom.com) * ------------------------------------------------------------------- */ #include "headers.h" #include "histogram.h" #include "Locale.h" #include "util.h" #ifdef HAVE_THREAD_DEBUG // needed for thread_debug #include "Thread.h" #endif struct histogram *histogram_init(unsigned int bincount, unsigned int binwidth, float offset, float units,\ double ci_lower, double ci_upper, unsigned int id, char *name, bool Omit) { struct histogram *this = (struct histogram *) malloc(sizeof(struct histogram)); if (!this) { fprintf(stderr,"Malloc failure in histogram init\n"); return(NULL); } this->Omit = Omit; if (!bincount) bincount = 1000; this->mybins = (unsigned int *) malloc(sizeof(unsigned int) * bincount); if (!this->mybins) { fprintf(stderr,"Malloc failure in histogram init b\n"); free(this); return(NULL); } this->myname = (char *) malloc(sizeof(strlen(name))); if (!this->myname) { fprintf(stderr,"Malloc failure in histogram init n\n"); free(this->mybins); free(this); return(NULL); } this->outbuf = (char *) malloc(120 + (32*bincount) + strlen(name)); if (!this->outbuf) { fprintf(stderr,"Malloc failure in histogram init o\n"); free(this->myname); free(this->mybins); free(this); return(NULL); } memset(this->mybins, 0, bincount * sizeof(unsigned int)); strcpy(this->myname, name); this->id = id; this->bincount = bincount; this->binwidth = binwidth; this->populationcnt = 0; this->offset=offset; this->units=units; this->cntloweroutofbounds=0; this->cntupperoutofbounds=0; this->ci_lower = ci_lower; this->ci_upper = ci_upper; this->prev = NULL; this->maxbin = -1; this->fmaxbin = -1; this->maxts.tv_sec = 0; this->maxts.tv_usec = 0; this->fmaxts.tv_sec = 0; this->fmaxts.tv_usec = 0; #ifdef HAVE_THREAD_DEBUG thread_debug("histo create %p", (void *) this); #endif return this; } void histogram_delete(struct histogram *h) { #ifdef HAVE_THREAD_DEBUG thread_debug("histo delete %p", (void *) h); #endif if (h) { if (h->prev) histogram_delete(h->prev); if (h->mybins) free(h->mybins); if (h->myname) free(h->myname); free(h); } } // value is units seconds int histogram_insert(struct histogram *h, float value, struct timeval *ts) { int bin; // calculate the bin, convert the value units from seconds to units of interest bin = (int) (h->units * (value - h->offset) / h->binwidth); h->populationcnt++; if (ts && (value > h->maxval)) { h->maxbin = bin; h->maxval = value; h->maxts.tv_sec = ts->tv_sec; h->maxts.tv_usec = ts->tv_usec; // printf("imax=%ld.%ld %f\n",h->maxts.tv_sec, h->maxts.tv_usec, value); if (value > h->fmaxval) { h->fmaxbin = bin; h->fmaxval = value; h->fmaxts.tv_sec = ts->tv_sec; h->fmaxts.tv_usec = ts->tv_usec; // printf("fmax=%ld.%ld %f\n",h->fmaxts.tv_sec, h->fmaxts.tv_usec, value); } } if (bin < 0) { h->cntloweroutofbounds++; return(-1); } else if (bin > (int) h->bincount) { h->cntupperoutofbounds++; return(-2); } else { h->mybins[bin]++; return(h->mybins[bin]); } } void histogram_clear(struct histogram *h) { memset(h->mybins, 0, (h->bincount * sizeof(unsigned int))); h->populationcnt = 0; h->cntloweroutofbounds=0; h->cntupperoutofbounds=0; h->maxbin = 0; h->maxts.tv_sec = 0; h->maxts.tv_usec = 0; if (h->prev) histogram_clear(h->prev); h->prev = NULL; } void histogram_add(struct histogram *to, struct histogram *from) { int ix; assert(to != NULL); assert(from != NULL); if (to->bincount <= from->bincount) { for (ix=0; ix < to->bincount; ix ++) { to->mybins[ix] += from->mybins[ix]; } to->populationcnt += from->populationcnt; to->cntloweroutofbounds += from->cntloweroutofbounds; to->cntupperoutofbounds += from->cntupperoutofbounds; if (from->maxbin > to->maxbin) { to->maxbin = from->maxbin; } if (from->maxts.tv_sec > to->maxts.tv_sec) { to->maxts.tv_sec = from->maxts.tv_sec; to->maxts.tv_usec = from->maxts.tv_usec; } else if ((from->maxts.tv_sec == to->maxts.tv_sec) && \ (from->maxts.tv_usec > to->maxts.tv_usec)) { to->maxts.tv_usec = from->maxts.tv_usec; } } } void histogram_print(struct histogram *h, double start, double end) { if (h->final && h->prev) { histogram_clear(h->prev); } if (!h->prev) { h->prev = histogram_init(h->bincount, h->binwidth, h->offset, h->units, h->ci_lower, h->ci_upper, h->id, h->myname, h->Omit); } int n = 0, ix, delta, lowerci, upperci, outliercnt, fence_lower, fence_upper, upper3stdev; int running=0; int intervalpopulation, oob_u, oob_l; intervalpopulation = h->populationcnt - h->prev->populationcnt; strcpy(h->outbuf, h->myname); sprintf(h->outbuf, "[%3d] " IPERFTimeFrmt " sec %s%s%s bin(w=%d%s):cnt(%d)=", h->id, start, end, h->myname, (h->final ? "(f)" : ""), "-PDF:",h->binwidth, ((h->units == 1e3) ? "ms" : "us"), intervalpopulation); n = strlen(h->outbuf); lowerci=0; upperci=0; upper3stdev = 0; outliercnt=0; fence_lower = 0; fence_upper = 0; int outside3fences = 0; h->prev->populationcnt = h->populationcnt; oob_l = h->cntloweroutofbounds - h->prev->cntloweroutofbounds; h->prev->cntloweroutofbounds = h->cntloweroutofbounds; oob_u = h->cntupperoutofbounds - h->prev->cntupperoutofbounds; h->prev->cntupperoutofbounds = h->cntupperoutofbounds; for (ix = 0; ix < h->bincount; ix++) { delta = h->mybins[ix] - h->prev->mybins[ix]; if (delta > 0) { running+=delta; if (!lowerci && ((float)running/intervalpopulation > h->ci_lower/100.0)) { lowerci = ix+1; } // use 10% and 90% for inner fence post, then 3 times for outlier if ((float)running/intervalpopulation < 0.1) { fence_lower=ix+1; } if ((float)running/intervalpopulation < 0.9) { fence_upper=ix+1; } else if (!outside3fences) { outside3fences = fence_upper + (3 * (fence_upper - fence_lower)); } else if (ix > outside3fences) { outliercnt += delta; } if (!upperci && ((float)running/intervalpopulation > h->ci_upper/100.0)) { upperci = ix+1; } if (!upper3stdev && ((float)running/intervalpopulation > 99.7/100.0)) { upper3stdev = ix+1; } n += sprintf(h->outbuf + n,"%d:%d,", ix+1, delta); h->prev->mybins[ix] = h->mybins[ix]; } } h->outbuf[strlen(h->outbuf)-1] = '\0'; if (!upperci) upperci=h->bincount; if (!upper3stdev) upper3stdev=h->bincount; if (h->ci_upper > 99.7) fprintf(stdout, "%s (%.2f/99.7/%.2f/%%=%d/%d/%d,Outliers=%d,obl/obu=%d/%d)", \ h->outbuf, h->ci_lower, h->ci_upper, lowerci, upper3stdev, upperci, outliercnt, oob_l, oob_u); else fprintf(stdout, "%s (%.2f/%.2f/99.7%%=%d/%d/%d,Outliers=%d,obl/obu=%d/%d)", \ h->outbuf, h->ci_lower, h->ci_upper, lowerci, upperci, upper3stdev, outliercnt, oob_l, oob_u); if (!h->final && (h->maxval > 0) && ((h->maxts.tv_sec > 0) || h->maxts.tv_usec > 0)) { fprintf(stdout, " (%0.3f ms/%ld.%ld)", (h->maxval * 1e3), (long) h->maxts.tv_sec, (long) h->maxts.tv_usec); if (TimeDifference(h->prev->maxts, h->maxts) > 0) { fprintf(stdout, "(clock_err)"); } h->maxbin = -1; h->maxval = 0; h->prev->maxts.tv_sec = 0; h->prev->maxts.tv_usec = 0; h->maxts.tv_sec = 0; h->maxts.tv_usec = 0; } else if (h->final && (h->fmaxval > 0) && ((h->maxts.tv_sec > 0) || h->maxts.tv_usec > 0)) { fprintf(stdout, " (%0.3f ms/%ld.%ld)", (h->fmaxval * 1e3), (long) h->fmaxts.tv_sec, (long) h->fmaxts.tv_usec); } fprintf(stdout, "%s\n", (h->Omit ? report_omitted : "")); }