|
Packit Service |
cbade1 |
/*
|
|
Packit Service |
cbade1 |
* Copyright (c) 2020 Red Hat, Inc.
|
|
Packit Service |
cbade1 |
*
|
|
Packit Service |
cbade1 |
* This program is free software; you can redistribute it and/or
|
|
Packit Service |
cbade1 |
* modify it under the terms of the GNU General Public License
|
|
Packit Service |
cbade1 |
* as published by the Free Software Foundation; either version 2
|
|
Packit Service |
cbade1 |
* of the License, or (at your option) any later version.
|
|
Packit Service |
cbade1 |
*
|
|
Packit Service |
cbade1 |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
cbade1 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
cbade1 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
cbade1 |
* GNU General Public License for more details.
|
|
Packit Service |
cbade1 |
*
|
|
Packit Service |
cbade1 |
* You should have received a copy of the GNU General Public License
|
|
Packit Service |
cbade1 |
* along with this program; if not, write to the Free Software
|
|
Packit Service |
cbade1 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit Service |
cbade1 |
* 02110-1301, USA.
|
|
Packit Service |
cbade1 |
*
|
|
Packit Service |
cbade1 |
* $Id: //eng/vdo-releases/aluminum/src/c++/vdo/kernel/histogram.c#2 $
|
|
Packit Service |
cbade1 |
*/
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
#include <linux/kobject.h>
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
#include "memoryAlloc.h"
|
|
Packit Service |
cbade1 |
#include "typeDefs.h"
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
#include "histogram.h"
|
|
Packit Service |
cbade1 |
#include "logger.h"
|
|
Packit Service |
cbade1 |
#include "numUtils.h"
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/*
|
|
Packit Service |
cbade1 |
* Set NO_BUCKETS to streamline the histogram code by reducing it to
|
|
Packit Service |
cbade1 |
* tracking just minimum, maximum, mean, etc. Only one bucket counter
|
|
Packit Service |
cbade1 |
* (the final one for "bigger" values) will be used, no range checking
|
|
Packit Service |
cbade1 |
* is needed to find the right bucket, and no histogram will be
|
|
Packit Service |
cbade1 |
* reported. With newer compilers, the histogram output code will be
|
|
Packit Service |
cbade1 |
* optimized out.
|
|
Packit Service |
cbade1 |
*/
|
|
Packit Service |
cbade1 |
enum {
|
|
Packit Service |
cbade1 |
NO_BUCKETS = 1
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/*
|
|
Packit Service |
cbade1 |
* Support histogramming in the VDO code.
|
|
Packit Service |
cbade1 |
*
|
|
Packit Service |
cbade1 |
* This is not a complete and general histogram package. It follows the XP
|
|
Packit Service |
cbade1 |
* practice of implementing the "customer" requirements, and no more. We can
|
|
Packit Service |
cbade1 |
* support other requirements after we know what they are.
|
|
Packit Service |
cbade1 |
*
|
|
Packit Service |
cbade1 |
* The code was originally borrowed from Albireo, and includes both linear and
|
|
Packit Service |
cbade1 |
* logarithmic histograms. VDO only uses the logarithmic histograms.
|
|
Packit Service |
cbade1 |
*
|
|
Packit Service |
cbade1 |
* All samples are uint64_t values.
|
|
Packit Service |
cbade1 |
*
|
|
Packit Service |
cbade1 |
* A unit conversion option is supported internally to allow sample values to
|
|
Packit Service |
cbade1 |
* be supplied in "jiffies" and results to be reported via /sys in
|
|
Packit Service |
cbade1 |
* milliseconds. Depending on the system configuration, this could mean a
|
|
Packit Service |
cbade1 |
* factor of four (a bucket for values of 1 jiffy is reported as 4-7
|
|
Packit Service |
cbade1 |
* milliseconds). In theory it could be a non-integer ratio (including less
|
|
Packit Service |
cbade1 |
* than one), but as the x86-64 platforms we've encountered appear to use 1 or
|
|
Packit Service |
cbade1 |
* 4 milliseconds per jiffy, we don't support non-integer values yet.
|
|
Packit Service |
cbade1 |
*
|
|
Packit Service |
cbade1 |
* All internal processing uses the values as passed to enterHistogramSample.
|
|
Packit Service |
cbade1 |
* Conversions only affect the values seen or input through the /sys interface,
|
|
Packit Service |
cbade1 |
* including possibly rounding a "limit" value entered.
|
|
Packit Service |
cbade1 |
*/
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
struct histogram {
|
|
Packit Service |
cbade1 |
// These fields are ordered so that enterHistogramSample touches
|
|
Packit Service |
cbade1 |
// only the first cache line.
|
|
Packit Service |
cbade1 |
atomic64_t *counters; // Counter for each bucket
|
|
Packit Service |
cbade1 |
uint64_t limit; // We want to know how many samples are larger
|
|
Packit Service |
cbade1 |
atomic64_t sum; // Sum of all the samples
|
|
Packit Service |
cbade1 |
atomic64_t count; // Number of samples
|
|
Packit Service |
cbade1 |
atomic64_t minimum; // Minimum value
|
|
Packit Service |
cbade1 |
atomic64_t maximum; // Maximum value
|
|
Packit Service |
cbade1 |
atomic64_t unacceptable; // Number of samples that exceed the limit
|
|
Packit Service |
cbade1 |
int numBuckets; // The number of buckets
|
|
Packit Service |
cbade1 |
bool logFlag; // True if the y scale should be logarithmic
|
|
Packit Service |
cbade1 |
// These fields are used only when reporting results.
|
|
Packit Service |
cbade1 |
const char *label; // Histogram label
|
|
Packit Service |
cbade1 |
const char *countedItems; // Name for things being counted
|
|
Packit Service |
cbade1 |
const char *metric; // Term for value used to divide into buckets
|
|
Packit Service |
cbade1 |
const char *sampleUnits; // Unit for measuring metric; NULL for count
|
|
Packit Service |
cbade1 |
unsigned int conversionFactor; // Converts input units to reporting units
|
|
Packit Service |
cbade1 |
struct kobject kobj;
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/*
|
|
Packit Service |
cbade1 |
* Fixed table defining the top value for each bucket of a logarithmic
|
|
Packit Service |
cbade1 |
* histogram. We arbitrarily limit the histogram to 12 orders of magnitude.
|
|
Packit Service |
cbade1 |
*/
|
|
Packit Service |
cbade1 |
enum { MAX_LOG_SIZE = 12 };
|
|
Packit Service |
cbade1 |
static const uint64_t bottomValue[1 + 10 * MAX_LOG_SIZE] = {
|
|
Packit Service |
cbade1 |
// 0 to 10 - The first 10 buckets are linear
|
|
Packit Service |
cbade1 |
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
|
Packit Service |
cbade1 |
// 10 to 100 - From this point on, the Nth entry of the table is
|
|
Packit Service |
cbade1 |
// floor(exp10((double)N/10.0)).
|
|
Packit Service |
cbade1 |
12, 15, 19, 25, 31, 39, 50, 63, 79, 100,
|
|
Packit Service |
cbade1 |
// 100 to 1K
|
|
Packit Service |
cbade1 |
125, 158, 199, 251, 316, 398, 501, 630, 794, 1000,
|
|
Packit Service |
cbade1 |
// 1K to 10K
|
|
Packit Service |
cbade1 |
1258, 1584, 1995, 2511, 3162, 3981, 5011, 6309, 7943, 10000,
|
|
Packit Service |
cbade1 |
// 10K to 100K
|
|
Packit Service |
cbade1 |
12589, 15848, 19952, 25118, 31622, 39810, 50118, 63095, 79432, 100000,
|
|
Packit Service |
cbade1 |
// 100K to 1M
|
|
Packit Service |
cbade1 |
125892, 158489, 199526, 251188, 316227,
|
|
Packit Service |
cbade1 |
398107, 501187, 630957, 794328, 1000000,
|
|
Packit Service |
cbade1 |
// 1M to 10M
|
|
Packit Service |
cbade1 |
1258925, 1584893, 1995262, 2511886, 3162277,
|
|
Packit Service |
cbade1 |
3981071, 5011872, 6309573, 7943282, 10000000,
|
|
Packit Service |
cbade1 |
// 10M to 100M
|
|
Packit Service |
cbade1 |
12589254, 15848931, 19952623, 25118864, 31622776,
|
|
Packit Service |
cbade1 |
39810717, 50118723, 63095734, 79432823, 100000000,
|
|
Packit Service |
cbade1 |
// 100M to 1G
|
|
Packit Service |
cbade1 |
125892541, 158489319, 199526231, 251188643, 316227766,
|
|
Packit Service |
cbade1 |
398107170, 501187233, 630957344, 794328234, 1000000000,
|
|
Packit Service |
cbade1 |
// 1G to 10G
|
|
Packit Service |
cbade1 |
1258925411L, 1584893192L, 1995262314L, 2511886431L, 3162277660L,
|
|
Packit Service |
cbade1 |
3981071705L, 5011872336L, 6309573444L, 7943282347L, 10000000000L,
|
|
Packit Service |
cbade1 |
// 10G to 100G
|
|
Packit Service |
cbade1 |
12589254117L, 15848931924L, 19952623149L, 25118864315L, 31622776601L,
|
|
Packit Service |
cbade1 |
39810717055L, 50118723362L, 63095734448L, 79432823472L, 100000000000L,
|
|
Packit Service |
cbade1 |
// 100G to 1T
|
|
Packit Service |
cbade1 |
125892541179L, 158489319246L, 199526231496L, 251188643150L, 316227766016L,
|
|
Packit Service |
cbade1 |
398107170553L, 501187233627L, 630957344480L, 794328234724L, 1000000000000L,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static unsigned int divideRoundingToNearest(uint64_t number, uint64_t divisor)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
number += divisor / 2;
|
|
Packit Service |
cbade1 |
return number / divisor;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static int maxBucket(Histogram *h)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
int max = h->numBuckets;
|
|
Packit Service |
cbade1 |
while ((max >= 0) && (atomic64_read(&h->counters[max]) == 0)) {
|
|
Packit Service |
cbade1 |
max--;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
// max == -1 means that there were no samples
|
|
Packit Service |
cbade1 |
return max;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
typedef struct {
|
|
Packit Service |
cbade1 |
struct attribute attr;
|
|
Packit Service |
cbade1 |
ssize_t (*show)(Histogram *h, char *buf);
|
|
Packit Service |
cbade1 |
ssize_t (*store)(Histogram *h, const char *buf, size_t length);
|
|
Packit Service |
cbade1 |
} HistogramAttribute;
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static void histogramKobjRelease(struct kobject *kobj)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
Histogram *h = container_of(kobj, Histogram, kobj);
|
|
Packit Service |
cbade1 |
FREE(h->counters);
|
|
Packit Service |
cbade1 |
FREE(h);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static ssize_t histogramShow(struct kobject *kobj,
|
|
Packit Service |
cbade1 |
struct attribute *attr,
|
|
Packit Service |
cbade1 |
char *buf)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
HistogramAttribute *ha = container_of(attr, HistogramAttribute, attr);
|
|
Packit Service |
cbade1 |
if (ha->show == NULL) {
|
|
Packit Service |
cbade1 |
return -EINVAL;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
Histogram *h = container_of(kobj, Histogram, kobj);
|
|
Packit Service |
cbade1 |
return ha->show(h, buf);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static ssize_t histogramStore(struct kobject *kobj,
|
|
Packit Service |
cbade1 |
struct attribute *attr,
|
|
Packit Service |
cbade1 |
const char *buf,
|
|
Packit Service |
cbade1 |
size_t length)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
HistogramAttribute *ha = container_of(attr, HistogramAttribute, attr);
|
|
Packit Service |
cbade1 |
if (ha->show == NULL) {
|
|
Packit Service |
cbade1 |
return -EINVAL;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
Histogram *h = container_of(kobj, Histogram, kobj);
|
|
Packit Service |
cbade1 |
return ha->store(h, buf, length);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static ssize_t histogramShowCount(Histogram *h, char *buf)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
int64_t count = atomic64_read(&h->count);
|
|
Packit Service |
cbade1 |
return sprintf(buf, "%" PRId64 "\n", count);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static ssize_t histogramShowHistogram(Histogram *h, char *buffer)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
/*
|
|
Packit Service |
cbade1 |
* We're given one page in which to write. The caller logs a complaint if we
|
|
Packit Service |
cbade1 |
* report that we've written too much, so we'll truncate to PAGE_SIZE-1.
|
|
Packit Service |
cbade1 |
*/
|
|
Packit Service |
cbade1 |
size_t bufferSize = PAGE_SIZE;
|
|
Packit Service |
cbade1 |
bool bars = true;
|
|
Packit Service |
cbade1 |
ssize_t length = 0;
|
|
Packit Service |
cbade1 |
int max = maxBucket(h);
|
|
Packit Service |
cbade1 |
// If max is -1, we'll fall through to reporting the total of zero.
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
enum { BAR_SIZE = 50 };
|
|
Packit Service |
cbade1 |
char bar[BAR_SIZE + 2];
|
|
Packit Service |
cbade1 |
bar[0] = ' ';
|
|
Packit Service |
cbade1 |
memset(bar + 1, '=', BAR_SIZE);
|
|
Packit Service |
cbade1 |
bar[BAR_SIZE + 1] = '\0';
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
uint64_t total = 0;
|
|
Packit Service |
cbade1 |
for (int i = 0; i <= max; i++) {
|
|
Packit Service |
cbade1 |
total += atomic64_read(&h->counters[i]);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
length += snprintf(buffer, bufferSize, "%s Histogram - number of %s by %s",
|
|
Packit Service |
cbade1 |
h->label, h->countedItems, h->metric);
|
|
Packit Service |
cbade1 |
if (length >= (bufferSize - 1)) {
|
|
Packit Service |
cbade1 |
return bufferSize - 1;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
if (h->sampleUnits != NULL) {
|
|
Packit Service |
cbade1 |
length += snprintf(buffer + length, bufferSize - length, " (%s)",
|
|
Packit Service |
cbade1 |
h->sampleUnits);
|
|
Packit Service |
cbade1 |
if (length >= (bufferSize - 1)) {
|
|
Packit Service |
cbade1 |
return bufferSize - 1;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
length += snprintf(buffer + length, bufferSize - length, "\n");
|
|
Packit Service |
cbade1 |
if (length >= (bufferSize - 1)) {
|
|
Packit Service |
cbade1 |
return bufferSize - 1;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
for (int i = 0; i <= max; i++) {
|
|
Packit Service |
cbade1 |
uint64_t value = atomic64_read(&h->counters[i]);
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
unsigned int barLength;
|
|
Packit Service |
cbade1 |
if (bars && (total != 0)) {
|
|
Packit Service |
cbade1 |
// +1 for the space at the beginning
|
|
Packit Service |
cbade1 |
barLength = (divideRoundingToNearest(value * BAR_SIZE, total) + 1);
|
|
Packit Service |
cbade1 |
if (barLength == 1) {
|
|
Packit Service |
cbade1 |
// Don't bother printing just the initial space.
|
|
Packit Service |
cbade1 |
barLength = 0;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
} else {
|
|
Packit Service |
cbade1 |
// 0 means skip the space and the bar
|
|
Packit Service |
cbade1 |
barLength = 0;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
if (h->logFlag) {
|
|
Packit Service |
cbade1 |
if (i == h->numBuckets) {
|
|
Packit Service |
cbade1 |
length += snprintf(buffer + length, bufferSize - length, "%-16s",
|
|
Packit Service |
cbade1 |
"Bigger");
|
|
Packit Service |
cbade1 |
} else {
|
|
Packit Service |
cbade1 |
unsigned int lower = h->conversionFactor * bottomValue[i];
|
|
Packit Service |
cbade1 |
unsigned int upper = h->conversionFactor * bottomValue[i + 1] - 1;
|
|
Packit Service |
cbade1 |
length += snprintf(buffer + length, bufferSize - length, "%6u - %7u",
|
|
Packit Service |
cbade1 |
lower, upper);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
} else {
|
|
Packit Service |
cbade1 |
if (i == h->numBuckets) {
|
|
Packit Service |
cbade1 |
length += snprintf(buffer + length, bufferSize - length, "%6s",
|
|
Packit Service |
cbade1 |
"Bigger");
|
|
Packit Service |
cbade1 |
} else {
|
|
Packit Service |
cbade1 |
length += snprintf(buffer + length, bufferSize - length, "%6d", i);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
if (length >= (bufferSize - 1)) {
|
|
Packit Service |
cbade1 |
return bufferSize - 1;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
length += snprintf(buffer + length, bufferSize - length,
|
|
Packit Service |
cbade1 |
" : %12llu%.*s\n", value, barLength, bar);
|
|
Packit Service |
cbade1 |
if (length >= (bufferSize - 1)) {
|
|
Packit Service |
cbade1 |
return bufferSize - 1;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
length += snprintf(buffer + length, bufferSize - length,
|
|
Packit Service |
cbade1 |
"total %llu\n", total);
|
|
Packit Service |
cbade1 |
return minSizeT(bufferSize - 1, length);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static ssize_t histogramShowMaximum(Histogram *h, char *buf)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
// Maximum is initialized to 0.
|
|
Packit Service |
cbade1 |
unsigned long value = atomic64_read(&h->maximum);
|
|
Packit Service |
cbade1 |
return sprintf(buf, "%lu\n", h->conversionFactor * value);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static ssize_t histogramShowMinimum(Histogram *h, char *buf)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
// Minimum is initialized to -1.
|
|
Packit Service |
cbade1 |
unsigned long value = ((atomic64_read(&h->count) > 0)
|
|
Packit Service |
cbade1 |
? atomic64_read(&h->minimum)
|
|
Packit Service |
cbade1 |
: 0);
|
|
Packit Service |
cbade1 |
return sprintf(buf, "%lu\n", h->conversionFactor * value);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static ssize_t histogramShowLimit(Histogram *h, char *buf)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
// Display the limit in the reporting units
|
|
Packit Service |
cbade1 |
return sprintf(buf, "%u\n", (unsigned int)(h->conversionFactor * h->limit));
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static ssize_t histogramStoreLimit(Histogram *h,
|
|
Packit Service |
cbade1 |
const char *buf,
|
|
Packit Service |
cbade1 |
size_t length)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
unsigned int value;
|
|
Packit Service |
cbade1 |
if ((length > 12) || (sscanf(buf, "%u", &value) != 1)) {
|
|
Packit Service |
cbade1 |
return -EINVAL;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
/*
|
|
Packit Service |
cbade1 |
* Convert input from reporting units (e.g., milliseconds) to internal
|
|
Packit Service |
cbade1 |
* recording units (e.g., jiffies).
|
|
Packit Service |
cbade1 |
*
|
|
Packit Service |
cbade1 |
* computeBucketCount could also be called "divideRoundingUp".
|
|
Packit Service |
cbade1 |
*/
|
|
Packit Service |
cbade1 |
h->limit = computeBucketCount(value, h->conversionFactor);
|
|
Packit Service |
cbade1 |
atomic64_set(&h->unacceptable, 0);
|
|
Packit Service |
cbade1 |
return length;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static ssize_t histogramShowMean(Histogram *h, char *buf)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
uint64_t count = atomic64_read(&h->count);
|
|
Packit Service |
cbade1 |
if (count == 0) {
|
|
Packit Service |
cbade1 |
return sprintf(buf, "0/0\n");
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
// Compute mean, scaled up by 1000, in reporting units
|
|
Packit Service |
cbade1 |
unsigned long sumTimes1000InReportingUnits
|
|
Packit Service |
cbade1 |
= h->conversionFactor * atomic64_read(&h->sum) * 1000;
|
|
Packit Service |
cbade1 |
unsigned int meanTimes1000
|
|
Packit Service |
cbade1 |
= divideRoundingToNearest(sumTimes1000InReportingUnits, count);
|
|
Packit Service |
cbade1 |
// Print mean with fractional part
|
|
Packit Service |
cbade1 |
return sprintf(buf, "%u.%03u\n", meanTimes1000 / 1000,
|
|
Packit Service |
cbade1 |
meanTimes1000 % 1000);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static ssize_t histogramShowUnacceptable(Histogram *h, char *buf)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
int64_t count = atomic64_read(&h->unacceptable);
|
|
Packit Service |
cbade1 |
return sprintf(buf, "%" PRId64 "\n", count);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static ssize_t histogramShowLabel(Histogram *h, char *buf)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
return sprintf(buf, "%s\n", h->label);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static ssize_t histogramShowUnit(Histogram *h, char *buf)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
if (h->sampleUnits != NULL) {
|
|
Packit Service |
cbade1 |
return sprintf(buf, "%s\n", h->sampleUnits);
|
|
Packit Service |
cbade1 |
} else {
|
|
Packit Service |
cbade1 |
*buf = 0;
|
|
Packit Service |
cbade1 |
return 0;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
static struct sysfs_ops histogramSysfsOps = {
|
|
Packit Service |
cbade1 |
.show = histogramShow,
|
|
Packit Service |
cbade1 |
.store = histogramStore,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
static HistogramAttribute countAttribute = {
|
|
Packit Service |
cbade1 |
.attr = { .name = "count", .mode = 0444, },
|
|
Packit Service |
cbade1 |
.show = histogramShowCount,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
static HistogramAttribute histogramAttribute = {
|
|
Packit Service |
cbade1 |
.attr = { .name = "histogram", .mode = 0444, },
|
|
Packit Service |
cbade1 |
.show = histogramShowHistogram,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
static HistogramAttribute labelAttribute = {
|
|
Packit Service |
cbade1 |
.attr = { .name = "label", .mode = 0444, },
|
|
Packit Service |
cbade1 |
.show = histogramShowLabel,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
static HistogramAttribute maximumAttribute = {
|
|
Packit Service |
cbade1 |
.attr = { .name = "maximum", .mode = 0444, },
|
|
Packit Service |
cbade1 |
.show = histogramShowMaximum,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
static HistogramAttribute minimumAttribute = {
|
|
Packit Service |
cbade1 |
.attr = { .name = "minimum", .mode = 0444, },
|
|
Packit Service |
cbade1 |
.show = histogramShowMinimum,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
static HistogramAttribute limitAttribute = {
|
|
Packit Service |
cbade1 |
.attr = { .name = "limit", .mode = 0644, },
|
|
Packit Service |
cbade1 |
.show = histogramShowLimit,
|
|
Packit Service |
cbade1 |
.store = histogramStoreLimit,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
static HistogramAttribute meanAttribute = {
|
|
Packit Service |
cbade1 |
.attr = { .name = "mean", .mode = 0444, },
|
|
Packit Service |
cbade1 |
.show = histogramShowMean,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
static HistogramAttribute unacceptableAttribute = {
|
|
Packit Service |
cbade1 |
.attr = { .name = "unacceptable", .mode = 0444, },
|
|
Packit Service |
cbade1 |
.show = histogramShowUnacceptable,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
static HistogramAttribute unitAttribute = {
|
|
Packit Service |
cbade1 |
.attr = { .name = "unit", .mode = 0444, },
|
|
Packit Service |
cbade1 |
.show = histogramShowUnit,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
// "Real" histogram plotting.
|
|
Packit Service |
cbade1 |
static struct attribute *histogramAttributes[] = {
|
|
Packit Service |
cbade1 |
&countAttribute.attr,
|
|
Packit Service |
cbade1 |
&histogramAttribute.attr,
|
|
Packit Service |
cbade1 |
&labelAttribute.attr,
|
|
Packit Service |
cbade1 |
&limitAttribute.attr,
|
|
Packit Service |
cbade1 |
&maximumAttribute.attr,
|
|
Packit Service |
cbade1 |
&meanAttribute.attr,
|
|
Packit Service |
cbade1 |
&minimumAttribute.attr,
|
|
Packit Service |
cbade1 |
&unacceptableAttribute.attr,
|
|
Packit Service |
cbade1 |
&unitAttribute.attr,
|
|
Packit Service |
cbade1 |
NULL,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
static struct kobj_type histogramKobjType = {
|
|
Packit Service |
cbade1 |
.release = histogramKobjRelease,
|
|
Packit Service |
cbade1 |
.sysfs_ops = &histogramSysfsOps,
|
|
Packit Service |
cbade1 |
.default_attrs = histogramAttributes,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
static struct attribute *bucketlessHistogramAttributes[] = {
|
|
Packit Service |
cbade1 |
&countAttribute.attr,
|
|
Packit Service |
cbade1 |
&labelAttribute.attr,
|
|
Packit Service |
cbade1 |
&maximumAttribute.attr,
|
|
Packit Service |
cbade1 |
&meanAttribute.attr,
|
|
Packit Service |
cbade1 |
&minimumAttribute.attr,
|
|
Packit Service |
cbade1 |
&unitAttribute.attr,
|
|
Packit Service |
cbade1 |
NULL,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
static struct kobj_type bucketlessHistogramKobjType = {
|
|
Packit Service |
cbade1 |
.release = histogramKobjRelease,
|
|
Packit Service |
cbade1 |
.sysfs_ops = &histogramSysfsOps,
|
|
Packit Service |
cbade1 |
.default_attrs = bucketlessHistogramAttributes,
|
|
Packit Service |
cbade1 |
};
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
static Histogram *makeHistogram(struct kobject *parent,
|
|
Packit Service |
cbade1 |
const char *name,
|
|
Packit Service |
cbade1 |
const char *label,
|
|
Packit Service |
cbade1 |
const char *countedItems,
|
|
Packit Service |
cbade1 |
const char *metric,
|
|
Packit Service |
cbade1 |
const char *sampleUnits,
|
|
Packit Service |
cbade1 |
int numBuckets,
|
|
Packit Service |
cbade1 |
unsigned long conversionFactor,
|
|
Packit Service |
cbade1 |
bool logFlag)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
Histogram *h;
|
|
Packit Service |
cbade1 |
if (ALLOCATE(1, Histogram, "histogram", &h) != UDS_SUCCESS) {
|
|
Packit Service |
cbade1 |
return NULL;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
if (NO_BUCKETS) {
|
|
Packit Service |
cbade1 |
numBuckets = 0; // plus 1 for "bigger" bucket
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
if (numBuckets <= 10) {
|
|
Packit Service |
cbade1 |
/*
|
|
Packit Service |
cbade1 |
* The first buckets in a "logarithmic" histogram are still
|
|
Packit Service |
cbade1 |
* linear, but the bucket-search mechanism is a wee bit slower
|
|
Packit Service |
cbade1 |
* than for linear, so change the type.
|
|
Packit Service |
cbade1 |
*/
|
|
Packit Service |
cbade1 |
logFlag = false;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
h->label = label;
|
|
Packit Service |
cbade1 |
h->countedItems = countedItems;
|
|
Packit Service |
cbade1 |
h->metric = metric;
|
|
Packit Service |
cbade1 |
h->sampleUnits = sampleUnits;
|
|
Packit Service |
cbade1 |
h->logFlag = logFlag;
|
|
Packit Service |
cbade1 |
h->numBuckets = numBuckets;
|
|
Packit Service |
cbade1 |
h->conversionFactor = conversionFactor;
|
|
Packit Service |
cbade1 |
atomic64_set(&h->minimum, -1UL);
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
if (ALLOCATE(h->numBuckets + 1, atomic64_t, "histogram counters",
|
|
Packit Service |
cbade1 |
&h->counters) != UDS_SUCCESS) {
|
|
Packit Service |
cbade1 |
histogramKobjRelease(&h->kobj);
|
|
Packit Service |
cbade1 |
return NULL;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
kobject_init(&h->kobj,
|
|
Packit Service |
cbade1 |
((numBuckets > 0)
|
|
Packit Service |
cbade1 |
? &histogramKobjType
|
|
Packit Service |
cbade1 |
: &bucketlessHistogramKobjType));
|
|
Packit Service |
cbade1 |
if (kobject_add(&h->kobj, parent, name) != 0) {
|
|
Packit Service |
cbade1 |
histogramKobjRelease(&h->kobj);
|
|
Packit Service |
cbade1 |
return NULL;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
return h;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
Histogram *makeLinearHistogram(struct kobject *parent,
|
|
Packit Service |
cbade1 |
const char *name,
|
|
Packit Service |
cbade1 |
const char *initLabel,
|
|
Packit Service |
cbade1 |
const char *countedItems,
|
|
Packit Service |
cbade1 |
const char *metric,
|
|
Packit Service |
cbade1 |
const char *sampleUnits,
|
|
Packit Service |
cbade1 |
int size)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
return makeHistogram(parent, name, initLabel, countedItems,
|
|
Packit Service |
cbade1 |
metric, sampleUnits, size, 1, false);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/**
|
|
Packit Service |
cbade1 |
* Intermediate routine for creating logarithmic histograms.
|
|
Packit Service |
cbade1 |
*
|
|
Packit Service |
cbade1 |
* Limits the histogram size, and computes the bucket count from the
|
|
Packit Service |
cbade1 |
* orders-of-magnitude count.
|
|
Packit Service |
cbade1 |
*
|
|
Packit Service |
cbade1 |
* @param parent The parent kobject.
|
|
Packit Service |
cbade1 |
* @param name The short name of the histogram. This label is
|
|
Packit Service |
cbade1 |
* used for the sysfs node.
|
|
Packit Service |
cbade1 |
* @param initLabel The label for the sampled data. This label is used
|
|
Packit Service |
cbade1 |
* when we plot the data.
|
|
Packit Service |
cbade1 |
* @param countedItems A name (plural) for the things being counted.
|
|
Packit Service |
cbade1 |
* @param metric The measure being used to divide samples into
|
|
Packit Service |
cbade1 |
* buckets.
|
|
Packit Service |
cbade1 |
* @param sampleUnits The units (plural) for the metric, or NULL if it's
|
|
Packit Service |
cbade1 |
* a simple counter.
|
|
Packit Service |
cbade1 |
* @param logSize The number of buckets. There are buckets for a
|
|
Packit Service |
cbade1 |
* range of sizes up to 10^logSize, and an extra
|
|
Packit Service |
cbade1 |
* bucket for larger samples.
|
|
Packit Service |
cbade1 |
* @param conversionFactor Unit conversion factor for reporting.
|
|
Packit Service |
cbade1 |
*
|
|
Packit Service |
cbade1 |
* @return the histogram
|
|
Packit Service |
cbade1 |
**/
|
|
Packit Service |
cbade1 |
static Histogram *
|
|
Packit Service |
cbade1 |
makeLogarithmicHistogramWithConversionFactor(struct kobject *parent,
|
|
Packit Service |
cbade1 |
const char *name,
|
|
Packit Service |
cbade1 |
const char *initLabel,
|
|
Packit Service |
cbade1 |
const char *countedItems,
|
|
Packit Service |
cbade1 |
const char *metric,
|
|
Packit Service |
cbade1 |
const char *sampleUnits,
|
|
Packit Service |
cbade1 |
int logSize,
|
|
Packit Service |
cbade1 |
uint64_t conversionFactor)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
if (logSize > MAX_LOG_SIZE) {
|
|
Packit Service |
cbade1 |
logSize = MAX_LOG_SIZE;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
return makeHistogram(parent, name,
|
|
Packit Service |
cbade1 |
initLabel, countedItems, metric, sampleUnits,
|
|
Packit Service |
cbade1 |
10 * logSize, conversionFactor, true);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
Histogram *makeLogarithmicHistogram(struct kobject *parent,
|
|
Packit Service |
cbade1 |
const char *name,
|
|
Packit Service |
cbade1 |
const char *initLabel,
|
|
Packit Service |
cbade1 |
const char *countedItems,
|
|
Packit Service |
cbade1 |
const char *metric,
|
|
Packit Service |
cbade1 |
const char *sampleUnits,
|
|
Packit Service |
cbade1 |
int logSize)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
return makeLogarithmicHistogramWithConversionFactor(parent, name, initLabel,
|
|
Packit Service |
cbade1 |
countedItems,
|
|
Packit Service |
cbade1 |
metric, sampleUnits,
|
|
Packit Service |
cbade1 |
logSize, 1);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
Histogram *makeLogarithmicJiffiesHistogram(struct kobject *parent,
|
|
Packit Service |
cbade1 |
const char *name,
|
|
Packit Service |
cbade1 |
const char *initLabel,
|
|
Packit Service |
cbade1 |
const char *countedItems,
|
|
Packit Service |
cbade1 |
const char *metric,
|
|
Packit Service |
cbade1 |
int logSize)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
/*
|
|
Packit Service |
cbade1 |
* If these fail, we have a jiffy duration that is not an integral number of
|
|
Packit Service |
cbade1 |
* milliseconds, and the unit conversion code needs updating.
|
|
Packit Service |
cbade1 |
*/
|
|
Packit Service |
cbade1 |
STATIC_ASSERT(HZ <= MSEC_PER_SEC);
|
|
Packit Service |
cbade1 |
STATIC_ASSERT((MSEC_PER_SEC % HZ) == 0);
|
|
Packit Service |
cbade1 |
return makeLogarithmicHistogramWithConversionFactor(parent, name, initLabel,
|
|
Packit Service |
cbade1 |
countedItems,
|
|
Packit Service |
cbade1 |
metric, "milliseconds",
|
|
Packit Service |
cbade1 |
logSize,
|
|
Packit Service |
cbade1 |
jiffies_to_msecs(1));
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
void enterHistogramSample(Histogram *h, uint64_t sample)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
int bucket;
|
|
Packit Service |
cbade1 |
if (h->logFlag) {
|
|
Packit Service |
cbade1 |
int lo = 0;
|
|
Packit Service |
cbade1 |
int hi = h->numBuckets;
|
|
Packit Service |
cbade1 |
while (lo < hi) {
|
|
Packit Service |
cbade1 |
int middle = (lo + hi) / 2;
|
|
Packit Service |
cbade1 |
if (sample < bottomValue[middle + 1]) {
|
|
Packit Service |
cbade1 |
hi = middle;
|
|
Packit Service |
cbade1 |
} else {
|
|
Packit Service |
cbade1 |
lo = middle + 1;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
bucket = lo;
|
|
Packit Service |
cbade1 |
} else {
|
|
Packit Service |
cbade1 |
bucket = sample < h->numBuckets ? sample : h->numBuckets;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
atomic64_inc(&h->counters[bucket]);
|
|
Packit Service |
cbade1 |
atomic64_inc(&h->count);
|
|
Packit Service |
cbade1 |
atomic64_add(sample, &h->sum);
|
|
Packit Service |
cbade1 |
if ((h->limit > 0) && (sample > h->limit)) {
|
|
Packit Service |
cbade1 |
atomic64_inc(&h->unacceptable);
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/*
|
|
Packit Service |
cbade1 |
* Theoretically this could loop a lot; in practice it should rarely
|
|
Packit Service |
cbade1 |
* do more than a single read, with no memory barrier, from a cache
|
|
Packit Service |
cbade1 |
* line we've already referenced above.
|
|
Packit Service |
cbade1 |
*/
|
|
Packit Service |
cbade1 |
uint64_t oldMaximum = atomic64_read(&h->maximum);
|
|
Packit Service |
cbade1 |
while (oldMaximum < sample) {
|
|
Packit Service |
cbade1 |
uint64_t readValue = atomic64_cmpxchg(&h->maximum, oldMaximum, sample);
|
|
Packit Service |
cbade1 |
if (readValue == oldMaximum) {
|
|
Packit Service |
cbade1 |
break;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
oldMaximum = readValue;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
uint64_t oldMinimum = atomic64_read(&h->minimum);
|
|
Packit Service |
cbade1 |
while (oldMinimum > sample) {
|
|
Packit Service |
cbade1 |
uint64_t readValue = atomic64_cmpxchg(&h->minimum, oldMinimum, sample);
|
|
Packit Service |
cbade1 |
if (readValue == oldMinimum) {
|
|
Packit Service |
cbade1 |
break;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
oldMinimum = readValue;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
|
|
Packit Service |
cbade1 |
/***********************************************************************/
|
|
Packit Service |
cbade1 |
void freeHistogram(Histogram **hp)
|
|
Packit Service |
cbade1 |
{
|
|
Packit Service |
cbade1 |
if (*hp != NULL) {
|
|
Packit Service |
cbade1 |
Histogram *h = *hp;
|
|
Packit Service |
cbade1 |
kobject_put(&h->kobj);
|
|
Packit Service |
cbade1 |
*hp = NULL;
|
|
Packit Service |
cbade1 |
}
|
|
Packit Service |
cbade1 |
}
|