]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
libfrog: hoist free space histogram code
authorDarrick J. Wong <djwong@kernel.org>
Wed, 3 Jul 2024 21:21:13 +0000 (14:21 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 9 Jul 2024 22:36:58 +0000 (15:36 -0700)
Combine the two free space histograms in xfs_db and xfs_spaceman into a
single implementation.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
db/freesp.c
libfrog/Makefile
libfrog/histogram.c [new file with mode: 0644]
libfrog/histogram.h [new file with mode: 0644]
spaceman/freesp.c

index 883741e66fee318430c99d6d3642f195605518d4..43520481d5e038251f740167f37279f35d01dd89 100644 (file)
 #include "output.h"
 #include "init.h"
 #include "malloc.h"
-
-typedef struct histent
-{
-       int             low;
-       int             high;
-       long long       count;
-       long long       blocks;
-} histent_t;
+#include "libfrog/histogram.h"
 
 static void    addhistent(int h);
 static void    addtohist(xfs_agnumber_t agno, xfs_agblock_t agbno,
@@ -46,13 +39,10 @@ static int          alignment;
 static int             countflag;
 static int             dumpflag;
 static int             equalsize;
-static histent_t       *hist;
-static int             histcount;
+static struct histogram        freesp_hist;
 static int             multsize;
 static int             seen1;
 static int             summaryflag;
-static long long       totblocks;
-static long long       totexts;
 
 static const cmdinfo_t freesp_cmd =
        { "freesp", NULL, freesp_f, 0, -1, 0,
@@ -93,18 +83,20 @@ freesp_f(
                if (inaglist(agno))
                        scan_ag(agno);
        }
-       if (histcount)
+       if (hist_buckets(&freesp_hist))
                printhist();
        if (summaryflag) {
-               dbprintf(_("total free extents %lld\n"), totexts);
-               dbprintf(_("total free blocks %lld\n"), totblocks);
-               dbprintf(_("average free extent size %g\n"),
-                       (double)totblocks / (double)totexts);
+               struct histogram_strings hstr = {
+                       .sum            = _("total free blocks"),
+                       .observations   = _("total free extents"),
+                       .averages       = _("average free extent size"),
+               };
+
+               hist_summarize(&freesp_hist, &hstr);
        }
        if (aglist)
                xfree(aglist);
-       if (hist)
-               xfree(hist);
+       hist_free(&freesp_hist);
        return 0;
 }
 
@@ -132,10 +124,9 @@ init(
        int             speced = 0;
 
        agcount = countflag = dumpflag = equalsize = multsize = optind = 0;
-       histcount = seen1 = summaryflag = 0;
-       totblocks = totexts = 0;
+       seen1 = summaryflag = 0;
        aglist = NULL;
-       hist = NULL;
+
        while ((c = getopt(argc, argv, "A:a:bcde:h:m:s")) != EOF) {
                switch (c) {
                case 'A':
@@ -163,7 +154,7 @@ init(
                        speced = 1;
                        break;
                case 'h':
-                       if (speced && !histcount)
+                       if (speced && hist_buckets(&freesp_hist) == 0)
                                return usage();
                        addhistent(atoi(optarg));
                        speced = 1;
@@ -339,14 +330,7 @@ static void
 addhistent(
        int     h)
 {
-       hist = xrealloc(hist, (histcount + 1) * sizeof(*hist));
-       if (h == 0)
-               h = 1;
-       hist[histcount].low = h;
-       hist[histcount].count = hist[histcount].blocks = 0;
-       histcount++;
-       if (h == 1)
-               seen1 = 1;
+       hist_add_bucket(&freesp_hist, h);
 }
 
 static void
@@ -355,30 +339,12 @@ addtohist(
        xfs_agblock_t   agbno,
        xfs_extlen_t    len)
 {
-       int             i;
-
        if (alignment && (XFS_AGB_TO_FSB(mp,agno,agbno) % alignment))
                return;
 
        if (dumpflag)
                dbprintf("%8d %8d %8d\n", agno, agbno, len);
-       totexts++;
-       totblocks += len;
-       for (i = 0; i < histcount; i++) {
-               if (hist[i].high >= len) {
-                       hist[i].count++;
-                       hist[i].blocks += len;
-                       break;
-               }
-       }
-}
-
-static int
-hcmp(
-       const void      *a,
-       const void      *b)
-{
-       return ((histent_t *)a)->low - ((histent_t *)b)->low;
+       hist_add(&freesp_hist, len);
 }
 
 static void
@@ -387,6 +353,7 @@ histinit(
 {
        int     i;
 
+       hist_init(&freesp_hist);
        if (equalsize) {
                for (i = 1; i < maxlen; i += equalsize)
                        addhistent(i);
@@ -396,27 +363,17 @@ histinit(
        } else {
                if (!seen1)
                        addhistent(1);
-               qsort(hist, histcount, sizeof(*hist), hcmp);
-       }
-       for (i = 0; i < histcount; i++) {
-               if (i < histcount - 1)
-                       hist[i].high = hist[i + 1].low - 1;
-               else
-                       hist[i].high = maxlen;
        }
+       hist_prepare(&freesp_hist, maxlen);
 }
 
 static void
 printhist(void)
 {
-       int     i;
+       struct histogram_strings hstr = {
+               .sum            = _("blocks"),
+               .observations   = _("extents"),
+       };
 
-       dbprintf("%7s %7s %7s %7s %6s\n",
-               _("from"), _("to"), _("extents"), _("blocks"), _("pct"));
-       for (i = 0; i < histcount; i++) {
-               if (hist[i].count)
-                       dbprintf("%7d %7d %7lld %7lld %6.2f\n", hist[i].low,
-                               hist[i].high, hist[i].count, hist[i].blocks,
-                               hist[i].blocks * 100.0 / totblocks);
-       }
+       hist_print(&freesp_hist, &hstr);
 }
index 53e3c3492377d8a7d346ca29425b285fdd2df988..acfa228bc8ec9c4d8cf29382a6239bd41a0c6296 100644 (file)
@@ -20,6 +20,7 @@ convert.c \
 crc32.c \
 file_exchange.c \
 fsgeom.c \
+histogram.c \
 list_sort.c \
 linux.c \
 logging.c \
@@ -45,6 +46,7 @@ dahashselftest.h \
 div64.h \
 file_exchange.h \
 fsgeom.h \
+histogram.h \
 logging.h \
 paths.h \
 projects.h \
diff --git a/libfrog/histogram.c b/libfrog/histogram.c
new file mode 100644 (file)
index 0000000..c2f344a
--- /dev/null
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2012 Red Hat, Inc.
+ * Copyright (c) 2017-2024 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 histbucket       *buckets;
+
+       if (hs->nr_buckets == INT_MAX)
+               return EFBIG;
+
+       buckets = realloc(hs->buckets,
+                       (hs->nr_buckets + 1) * sizeof(struct histbucket));
+       if (!buckets)
+               return errno;
+
+       hs->buckets = buckets;
+       hs->buckets[hs->nr_buckets].low = bucket_low;
+       hs->buckets[hs->nr_buckets].nr_obs = 0;
+       hs->buckets[hs->nr_buckets].sum = 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->tot_obs++;
+       hs->tot_sum += len;
+       for (i = 0; i < hs->nr_buckets; i++) {
+               if (hs->buckets[i].high >= len) {
+                       hs->buckets[i].nr_obs++;
+                       hs->buckets[i].sum += len;
+                       break;
+               }
+       }
+}
+
+static int
+histbucket_cmp(
+       const void              *a,
+       const void              *b)
+{
+       const struct histbucket *ha = a;
+       const struct histbucket *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 histbucket),
+                       histbucket_cmp);
+
+       for (i = 0; i < hs->nr_buckets - 1; i++)
+               hs->buckets[i].high = hs->buckets[i + 1].low - 1;
+       hs->buckets[hs->nr_buckets - 1].high = maxlen;
+}
+
+/* Free all data associated with a histogram. */
+void
+hist_free(
+       struct histogram        *hs)
+{
+       free(hs->buckets);
+       memset(hs, 0, sizeof(struct histogram));
+}
+
+/* Dump a histogram to stdout. */
+void
+hist_print(
+       const struct histogram          *hs,
+       const struct histogram_strings  *hstr)
+{
+       unsigned int                    obs_w = strlen(hstr->observations);
+       unsigned int                    sum_w = strlen(hstr->sum);
+       unsigned int                    i;
+
+       printf("%7s %7s %*s %*s %6s\n",
+                       _("from"), _("to"),
+                       obs_w, hstr->observations,
+                       sum_w, hstr->sum,
+                       _("pct"));
+
+       for (i = 0; i < hs->nr_buckets; i++) {
+               if (hs->buckets[i].nr_obs == 0)
+                       continue;
+
+               printf("%7lld %7lld %*lld %*lld %6.2f\n",
+                               hs->buckets[i].low, hs->buckets[i].high,
+                               obs_w, hs->buckets[i].nr_obs,
+                               sum_w, hs->buckets[i].sum,
+                               hs->buckets[i].sum * 100.0 / hs->tot_sum);
+       }
+}
+
+/* Summarize the contents of the histogram. */
+void
+hist_summarize(
+       const struct histogram          *hs,
+       const struct histogram_strings  *hstr)
+{
+       printf("%s %lld\n", hstr->observations, hs->tot_obs);
+       printf("%s %lld\n", hstr->sum, hs->tot_sum);
+       printf("%s %g\n", hstr->averages,
+                       (double)hs->tot_sum / (double)hs->tot_obs);
+}
diff --git a/libfrog/histogram.h b/libfrog/histogram.h
new file mode 100644 (file)
index 0000000..68afdeb
--- /dev/null
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2012 Red Hat, Inc.
+ * Copyright (c) 2017-2024 Oracle.
+ * All Rights Reserved.
+ */
+#ifndef __LIBFROG_HISTOGRAM_H__
+#define __LIBFROG_HISTOGRAM_H__
+
+struct histbucket {
+       /* Low and high size of this bucket */
+       long long               low;
+       long long               high;
+
+       /* Count of observations recorded */
+       long long               nr_obs;
+
+       /* Sum of values recorded */
+       long long               sum;
+};
+
+struct histogram {
+       /* Sum of all values recorded */
+       long long               tot_sum;
+
+       /* Count of all observations recorded */
+       long long               tot_obs;
+
+       struct histbucket       *buckets;
+
+       /* Number of buckets */
+       unsigned int            nr_buckets;
+};
+
+int hist_add_bucket(struct histogram *hs, long long bucket_low);
+void hist_add(struct histogram *hs, long long value);
+void hist_init(struct histogram *hs);
+void hist_prepare(struct histogram *hs, long long maxvalue);
+void hist_free(struct histogram *hs);
+
+struct histogram_strings {
+       /* What does each sum represent? ("free blocks") */
+       const char              *sum;
+
+       /* What does each observation represent? ("free extents") */
+       const char              *observations;
+
+       /* What does sum / observation represent? ("average extent length") */
+       const char              *averages;
+};
+
+void hist_print(const struct histogram *hs,
+               const struct histogram_strings *hstr);
+void hist_summarize(const struct histogram *hs,
+               const struct histogram_strings *hstr);
+
+static inline unsigned int hist_buckets(const struct histogram *hs)
+{
+       return hs->nr_buckets;
+}
+
+#endif /* __LIBFROG_HISTOGRAM_H__ */
index f5177cb4ee5d2ab43a82f92e97a20df8d1389492..dfbec52a716035d057a55ce0be4a70ad05e74689 100644 (file)
 #include "libfrog/paths.h"
 #include "space.h"
 #include "input.h"
-
-struct histent
-{
-       long long       low;
-       long long       high;
-       long long       count;
-       long long       blocks;
-};
+#include "libfrog/histogram.h"
 
 static int             agcount;
 static xfs_agnumber_t  *aglist;
-static struct histent  *hist;
+static struct histogram        freesp_hist;
 static int             dumpflag;
 static long long       equalsize;
 static long long       multsize;
-static int             histcount;
 static int             seen1;
 static int             summaryflag;
 static int             gflag;
 static bool            rtflag;
-static long long       totblocks;
-static long long       totexts;
 
 static cmdinfo_t freesp_cmd;
 
-static void
+static inline void
 addhistent(
        long long       h)
 {
-       if (histcount == INT_MAX) {
+       int             error;
+
+       error = hist_add_bucket(&freesp_hist, h);
+       if (error == EFBIG) {
                printf(_("Too many histogram buckets.\n"));
                return;
        }
-       hist = realloc(hist, (histcount + 1) * sizeof(*hist));
+       if (error) {
+               printf("%s\n", strerror(error));
+               return;
+       }
+
        if (h == 0)
                h = 1;
-       hist[histcount].low = h;
-       hist[histcount].count = hist[histcount].blocks = 0;
-       histcount++;
        if (h == 1)
                seen1 = 1;
 }
 
-static void
+static inline void
 addtohist(
        xfs_agnumber_t  agno,
        xfs_agblock_t   agbno,
        off_t           len)
 {
-       long            i;
-
        if (dumpflag)
                printf("%8d %8d %8"PRId64"\n", agno, agbno, len);
-       totexts++;
-       totblocks += len;
-       for (i = 0; i < histcount; i++) {
-               if (hist[i].high >= len) {
-                       hist[i].count++;
-                       hist[i].blocks += len;
-                       break;
-               }
-       }
-}
-
-static int
-hcmp(
-       const void      *a,
-       const void      *b)
-{
-       return ((struct histent *)a)->low - ((struct histent *)b)->low;
+       hist_add(&freesp_hist, len);
 }
 
 static void
@@ -93,6 +69,7 @@ histinit(
 {
        long long       i;
 
+       hist_init(&freesp_hist);
        if (equalsize) {
                for (i = 1; i < maxlen; i += equalsize)
                        addhistent(i);
@@ -102,29 +79,19 @@ histinit(
        } else {
                if (!seen1)
                        addhistent(1);
-               qsort(hist, histcount, sizeof(*hist), hcmp);
-       }
-       for (i = 0; i < histcount; i++) {
-               if (i < histcount - 1)
-                       hist[i].high = hist[i + 1].low - 1;
-               else
-                       hist[i].high = maxlen;
        }
+       hist_prepare(&freesp_hist, maxlen);
 }
 
-static void
+static inline void
 printhist(void)
 {
-       int     i;
-
-       printf("%7s %7s %7s %7s %6s\n",
-               _("from"), _("to"), _("extents"), _("blocks"), _("pct"));
-       for (i = 0; i < histcount; i++) {
-               if (hist[i].count)
-                       printf("%7lld %7lld %7lld %7lld %6.2f\n", hist[i].low,
-                               hist[i].high, hist[i].count, hist[i].blocks,
-                               hist[i].blocks * 100.0 / totblocks);
-       }
+       struct histogram_strings hstr = {
+               .sum            = _("blocks"),
+               .observations   = _("extents"),
+       };
+
+       hist_print(&freesp_hist, &hstr);
 }
 
 static int
@@ -255,10 +222,8 @@ init(
        int                     speced = 0;     /* only one of -b -e -h or -m */
 
        agcount = dumpflag = equalsize = multsize = optind = gflag = 0;
-       histcount = seen1 = summaryflag = 0;
-       totblocks = totexts = 0;
+       seen1 = summaryflag = 0;
        aglist = NULL;
-       hist = NULL;
        rtflag = false;
 
        while ((c = getopt(argc, argv, "a:bde:gh:m:rs")) != EOF) {
@@ -287,7 +252,7 @@ init(
                        gflag++;
                        break;
                case 'h':
-                       if (speced && !histcount)
+                       if (speced && hist_buckets(&freesp_hist) == 0)
                                goto many_spec;
                        /* addhistent increments histcount */
                        x = cvt_s64(optarg, 0);
@@ -345,18 +310,20 @@ freesp_f(
                if (inaglist(agno))
                        scan_ag(agno);
        }
-       if (histcount && !gflag)
+       if (hist_buckets(&freesp_hist) > 0 && !gflag)
                printhist();
        if (summaryflag) {
-               printf(_("total free extents %lld\n"), totexts);
-               printf(_("total free blocks %lld\n"), totblocks);
-               printf(_("average free extent size %g\n"),
-                       (double)totblocks / (double)totexts);
+               struct histogram_strings hstr = {
+                       .sum            = _("total free blocks"),
+                       .observations   = _("total free extents"),
+                       .averages       = _("average free extent size"),
+               };
+
+               hist_summarize(&freesp_hist, &hstr);
        }
        if (aglist)
                free(aglist);
-       if (hist)
-               free(hist);
+       hist_free(&freesp_hist);
        return 0;
 }