blob: 6fece03a37897608150caf324e01fd6fed614627 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
* Copyright (c) 2012 Red Hat, Inc.
* Copyright (c) 2017-2023 Oracle.
* All Rights Reserved.
*/
#include "xfs.h"
#include <stdlib.h>
#include <string.h>
#include "platform_defs.h"
#include "libfrog/histogram.h"
/* Create a new bucket with the given low value. */
int
hist_add_bucket(
struct histogram *hs,
long long bucket_low)
{
struct histent *buckets;
if (hs->nr_buckets == INT_MAX)
return EFBIG;
buckets = realloc(hs->buckets,
(hs->nr_buckets + 1) * sizeof(struct histent));
if (!buckets)
return errno;
hs->buckets = buckets;
hs->buckets[hs->nr_buckets].low = bucket_low;
hs->buckets[hs->nr_buckets].count = buckets[hs->nr_buckets].blocks = 0;
hs->nr_buckets++;
return 0;
}
/* Add an observation to the histogram. */
void
hist_add(
struct histogram *hs,
long long len)
{
unsigned int i;
hs->totexts++;
hs->totblocks += len;
for (i = 0; i < hs->nr_buckets; i++) {
if (hs->buckets[i].high >= len) {
hs->buckets[i].count++;
hs->buckets[i].blocks += len;
break;
}
}
}
static int
histent_cmp(
const void *a,
const void *b)
{
const struct histent *ha = a;
const struct histent *hb = b;
if (ha->low < hb->low)
return -1;
if (ha->low > hb->low)
return 1;
return 0;
}
/* Prepare a histogram for bucket configuration. */
void
hist_init(
struct histogram *hs)
{
memset(hs, 0, sizeof(struct histogram));
}
/* Prepare a histogram to receive data observations. */
void
hist_prepare(
struct histogram *hs,
long long maxlen)
{
unsigned int i;
qsort(hs->buckets, hs->nr_buckets, sizeof(struct histent), histent_cmp);
for (i = 0; i < hs->nr_buckets; i++) {
if (i < hs->nr_buckets - 1)
hs->buckets[i].high = hs->buckets[i + 1].low - 1;
else
hs->buckets[i].high = maxlen;
}
}
/* Free all data associated with a histogram. */
void
hist_free(
struct histogram *hs)
{
free(hs->buckets);
memset(hs, 0, sizeof(struct histogram));
}
/*
* Compute the CDF of the free space in decreasing order of extent length.
* This enables users to determine how much free space is not in the long tail
* of small extents, e.g. 98% of the free space extents are larger than 31
* blocks.
*/
int
hist_cdf(
const struct histogram *hs,
struct histogram *cdf)
{
struct histent *buckets;
int i = hs->nr_buckets - 1;
ASSERT(cdf->nr_buckets == 0);
ASSERT(hs->nr_buckets < INT_MAX);
if (hs->nr_buckets == 0)
return 0;
buckets = calloc(hs->nr_buckets, sizeof(struct histent));
if (!buckets)
return errno;
memset(cdf, 0, sizeof(struct histogram));
cdf->buckets = buckets;
cdf->buckets[i].count = hs->buckets[i].count;
cdf->buckets[i].blocks = hs->buckets[i].blocks;
i--;
while (i >= 0) {
cdf->buckets[i].count = hs->buckets[i].count +
cdf->buckets[i + 1].count;
cdf->buckets[i].blocks = hs->buckets[i].blocks +
cdf->buckets[i + 1].blocks;
i--;
}
return 0;
}
/* Dump a histogram to stdout. */
void
hist_print(
const struct histogram *hs)
{
struct histogram cdf = { };
unsigned int from_w, to_w, extents_w, blocks_w;
unsigned int i;
int error;
error = hist_cdf(hs, &cdf);
if (error) {
printf(_("histogram cdf: %s\n"), strerror(error));
return;
}
from_w = to_w = extents_w = blocks_w = 7;
for (i = 0; i < hs->nr_buckets; i++) {
char buf[256];
if (!hs->buckets[i].count)
continue;
snprintf(buf, sizeof(buf) - 1, "%lld", hs->buckets[i].low);
from_w = max(from_w, strlen(buf));
snprintf(buf, sizeof(buf) - 1, "%lld", hs->buckets[i].high);
to_w = max(to_w, strlen(buf));
snprintf(buf, sizeof(buf) - 1, "%lld", hs->buckets[i].count);
extents_w = max(extents_w, strlen(buf));
snprintf(buf, sizeof(buf) - 1, "%lld", hs->buckets[i].blocks);
blocks_w = max(blocks_w, strlen(buf));
}
printf("%*s %*s %*s %*s %6s %6s %6s\n",
from_w, _("from"), to_w, _("to"), extents_w, _("extents"),
blocks_w, _("blocks"), _("pct"), _("blkcdf"), _("extcdf"));
for (i = 0; i < hs->nr_buckets; i++) {
if (hs->buckets[i].count == 0)
continue;
printf("%*lld %*lld %*lld %*lld %6.2f %6.2f %6.2f\n",
from_w, hs->buckets[i].low,
to_w, hs->buckets[i].high,
extents_w, hs->buckets[i].count,
blocks_w, hs->buckets[i].blocks,
hs->buckets[i].blocks * 100.0 / hs->totblocks,
cdf.buckets[i].blocks * 100.0 / hs->totblocks,
cdf.buckets[i].count * 100.0 / hs->totexts);
}
hist_free(&cdf);
}
/* Summarize the contents of the histogram. */
void
hist_summarize(
const struct histogram *hs)
{
printf(_("total free extents %lld\n"), hs->totexts);
printf(_("total free blocks %lld\n"), hs->totblocks);
printf(_("average free extent size %g\n"),
(double)hs->totblocks / (double)hs->totexts);
}
/* Copy the contents of src to dest. */
void
hist_import(
struct histogram *dest,
const struct histogram *src)
{
unsigned int i;
ASSERT(dest->nr_buckets == src->nr_buckets);
dest->totblocks += src->totblocks;
dest->totexts += src->totexts;
for (i = 0; i < dest->nr_buckets; i++) {
ASSERT(dest->buckets[i].low == src->buckets[i].low);
ASSERT(dest->buckets[i].high == src->buckets[i].high);
dest->buckets[i].count += src->buckets[i].count;
dest->buckets[i].blocks += src->buckets[i].blocks;
}
}
/*
* Move the contents of src to dest and reinitialize src. dst must not
* contain any observations or buckets.
*/
void
hist_move(
struct histogram *dest,
struct histogram *src)
{
ASSERT(dest->nr_buckets == 0);
ASSERT(dest->totexts == 0);
memcpy(dest, src, sizeof(struct histogram));
hist_init(src);
}