[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [UNIKRAFT PATCH 10/18] lib/ukalloc: Allocator statistics



I have 3 inline comments for this patch:

On Fri, Nov 20, 2020 at 5:47 PM Simon Kuenzer <simon.kuenzer@xxxxxxxxx> wrote:
This commit introduces an implementation for the configuration option
`LIBUKALLOC_IFSTATS`. When enabled, allocators keep allocation statistics,
like number of current allocations, maximum memory usage due to
allocations, or how often ENOMEM was returned.
The statistics are kept on the `uk_alloc` struct while the instrumentation
of an allocator is minimal: An allocator has only to be modified with
the following macros:
 - `uk_alloc_stats_count_alloc()`, `uk_alloc_stats_count_palloc()`
 - `uk_alloc_stats_count_free()`, `uk_alloc_stats_count_pfree()`
 - `uk_alloc_stats_count_enomem()`, `uk_alloc_stats_count_penomem()`
Current statistics can be queried with `uk_alloc_stats()`.

Signed-off-by: Simon Kuenzer <simon.kuenzer@xxxxxxxxx>
---
 lib/ukalloc/Config.uk               |   6 ++
 lib/ukalloc/Makefile.uk             |   1 +
 lib/ukalloc/exportsyms.uk           |   1 +
 lib/ukalloc/include/uk/alloc.h      |  29 ++++++++
 lib/ukalloc/include/uk/alloc_impl.h | 108 ++++++++++++++++++++++++++++
 lib/ukalloc/stats.c                 |  47 ++++++++++++
 6 files changed, 192 insertions(+)
 create mode 100644 lib/ukalloc/stats.c

diff --git a/lib/ukalloc/Config.uk b/lib/ukalloc/Config.uk
index 59a6aa78..fc64a13b 100644
--- a/lib/ukalloc/Config.uk
+++ b/lib/ukalloc/Config.uk
@@ -10,4 +10,10 @@ if LIBUKALLOC
                default n
                help
                        Provide helpers for allocators defining exclusively malloc and free
+
+       config LIBUKALLOC_IFSTATS
+               bool "Allocator statistics interface"
+               default n
+               help
+                       Provide interfaces for querying allocator statistics.
 endif
diff --git a/lib/ukalloc/Makefile.uk b/lib/ukalloc/Makefile.uk
index 30636462..d56aabb6 100644
--- a/lib/ukalloc/Makefile.uk
+++ b/lib/ukalloc/Makefile.uk
@@ -4,3 +4,4 @@ CINCLUDES-$(CONFIG_LIBUKALLOC)          += -I$(LIBUKALLOC_BASE)/include
 CXXINCLUDES-$(CONFIG_LIBUKALLOC)       += -I$(LIBUKALLOC_BASE)/include

 LIBUKALLOC_SRCS-y += $(LIBUKALLOC_BASE)/alloc.c
+LIBUKALLOC_SRCS-$(CONFIG_LIBUKALLOC_IFSTATS) += $(LIBUKALLOC_BASE)/stats.c
diff --git a/lib/ukalloc/exportsyms.uk b/lib/ukalloc/exportsyms.uk
index 7c25a70c..48de908c 100644
--- a/lib/ukalloc/exportsyms.uk
+++ b/lib/ukalloc/exportsyms.uk
@@ -20,3 +20,4 @@ uk_alloc_pavail_compat
 uk_alloc_availmem_total
 uk_alloc_pavail_total
 _uk_alloc_head
+uk_alloc_stats
diff --git a/lib/ukalloc/include/uk/alloc.h b/lib/ukalloc/include/uk/alloc.h
index fc19551e..59155d8c 100644
--- a/lib/ukalloc/include/uk/alloc.h
+++ b/lib/ukalloc/include/uk/alloc.h
@@ -71,6 +71,24 @@ typedef ssize_t (*uk_alloc_availmem_func_t)
 typedef long  (*uk_alloc_pavail_func_t)
                (struct uk_alloc *a);

+#if CONFIG_LIBUKALLOC_IFSTATS
Honestly I don't know if there is a convention for this, but I found a bit confusing the
struct `uk_alloc_stats` and the function with the same name, `uk_alloc_stats`. 
+struct uk_alloc_stats {
+       size_t last_alloc_size; /* size of the last allocation */
+       size_t max_alloc_size; /* biggest satisfied allocation size */
+       size_t min_alloc_size; /* smallest satisfied allocation size */
+
+       uint64_t tot_nb_allocs; /* total number of satisfied allocations */
+       uint64_t tot_nb_frees;  /* total number of satisfied free operations */
+       uint64_t cur_nb_allocs; /* current number of active allocations */
+       uint64_t max_nb_allocs; /* maximum number of active allocations */
+
+       size_t cur_mem_use; /* current used memory by allocations */
+       size_t max_mem_use; /* maximum amount of memory used by allocations */
+
+       uint64_t nb_enomem; /* number of times failing allocation requests */
+};
+#endif /* CONFIG_LIBUKALLOC_IFSTATS */
+
 struct uk_alloc {
        /* memory allocation */
        uk_alloc_malloc_func_t malloc;
@@ -96,6 +114,10 @@ struct uk_alloc {
        /* optional interface */
        uk_alloc_addmem_func_t addmem;

+#if CONFIG_LIBUKALLOC_IFSTATS
+       struct uk_alloc_stats _stats;
+#endif
+
        /* internal */
        struct uk_alloc *next;
        int8_t priv[];
@@ -283,6 +305,13 @@ size_t uk_alloc_availmem_total(void);

 unsigned long uk_alloc_pavail_total(void);

+#if CONFIG_LIBUKALLOC_IFSTATS
+/*
+ * Memory allocation statistics
+ */
+void uk_alloc_stats(struct uk_alloc *a, struct uk_alloc_stats *dst);
+#endif /* CONFIG_LIBUKALLOC_IFSTATS */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/ukalloc/include/uk/alloc_impl.h b/lib/ukalloc/include/uk/alloc_impl.h
index 4811558e..9ef6df13 100644
--- a/lib/ukalloc/include/uk/alloc_impl.h
+++ b/lib/ukalloc/include/uk/alloc_impl.h
@@ -40,6 +40,7 @@
 #define __UK_ALLOC_IMPL_H__

 #include <uk/alloc.h>
+#include <uk/preempt.h>

 #ifdef __cplusplus
 extern "C" {
@@ -81,6 +82,110 @@ void uk_pfree_compat(struct uk_alloc *a, void *ptr, unsigned long num_pages);
 long uk_alloc_pavail_compat(struct uk_alloc *a);
 long uk_alloc_pmaxalloc_compat(struct uk_alloc *a);

+#if CONFIG_LIBUKALLOC_IFSTATS
Another thing is that `<uk/preempt.h>` is used only under `#if CONFIG_LIBUKALLOC_IFSTATS`,
so we can include it here too. 
 
+#include <string.h>
+
+#if CONFIG_LIBUKALLOC_IFSTATS_GLOBAL
+extern struct uk_alloc_stats _uk_alloc_stats_global;
+#endif /* CONFIG_LIBUKALLOC_IFSTATS_GLOBAL */
+
Just for consistency reasons, I don't know why the `__uk_alloc_stats_refresh_minmax` function
has an extra  `_` in the prefix, compared to the other functions. 
+static inline void __uk_alloc_stats_refresh_minmax(struct uk_alloc_stats *stats)
+{
+       if (stats->cur_nb_allocs > stats->max_nb_allocs)
+               stats->max_nb_allocs = stats->cur_nb_allocs;
+
+       if (stats->cur_mem_use > stats->max_mem_use)
+               stats->max_mem_use = stats->cur_mem_use;
+
+       if (stats->last_alloc_size) {
+               /* Force storing the minimum allocation size
+                * with the first allocation request
+                */
+               if ((stats->tot_nb_allocs == 1)
+                   || (stats->last_alloc_size < stats->min_alloc_size))
+                       stats->min_alloc_size = stats->last_alloc_size;
+               if (stats->last_alloc_size > stats->max_alloc_size)
+                       stats->max_alloc_size = stats->last_alloc_size;
+       }
+}
+
+static inline void _uk_alloc_stats_count_alloc(struct uk_alloc_stats *stats,
+                                              void *ptr, size_t size)
+{
+       /* TODO: SMP safety */
+       uk_preempt_disable();
+       if (likely(ptr)) {
+               stats->tot_nb_allocs++;
+
+               stats->cur_nb_allocs++;
+               stats->cur_mem_use += size;
+               stats->last_alloc_size = size;
+               __uk_alloc_stats_refresh_minmax(stats);
+       } else {
+               stats->nb_enomem++;
+       }
+       uk_preempt_enable();
+}
+
+static inline void _uk_alloc_stats_count_free(struct uk_alloc_stats *stats,
+                                             void *ptr, size_t size)
+{
+       uk_preempt_disable();
+       if (likely(ptr)) {
+               stats->tot_nb_frees++;
+
+               stats->cur_nb_allocs--;
+               stats->cur_mem_use -= size;
+               __uk_alloc_stats_refresh_minmax(stats);
+       }
+       uk_preempt_enable();
+}
+
+/*
+ * The following macros should be used to instrument an allocator for
+ * statistics:
+ */
+/* NOTE: If ptr is NULL, an ENOMEM event is counted */
+#define uk_alloc_stats_count_alloc(a, ptr, size)                       \
+       do {                                                            \
+               _uk_alloc_stats_count_alloc(&((a)->_stats),             \
+                                           (ptr), (size));             \
+       } while (0)
+#define uk_alloc_stats_count_palloc(a, ptr, num_pages)                 \
+       uk_alloc_stats_count_alloc((a), (ptr),                          \
+                                  ((__sz) (num_pages)) << __PAGE_SHIFT)
+
+#define uk_alloc_stats_count_enomem(a, size)                   \
+       uk_alloc_stats_count_alloc((a), NULL, (size))
+#define uk_alloc_stats_count_penomem(a, num_pages)                     \
+       uk_alloc_stats_count_enomem((a),                                \
+                                   ((__sz) (num_pages)) << __PAGE_SHIFT)
+
+/* Note: if ptr is NULL, nothing is counted */
+#define uk_alloc_stats_count_free(a, ptr, freed_size)                  \
+       do {                                                            \
+               _uk_alloc_stats_count_free(&((a)->_stats),              \
+                                          (ptr), (freed_size));        \
+       } while (0)
+#define uk_alloc_stats_count_pfree(a, ptr, num_pages)                  \
+       uk_alloc_stats_count_free((a), (ptr),                           \
+                                 ((__sz) (num_pages)) << __PAGE_SHIFT)
+
+#define uk_alloc_stats_reset(a)                                                \
+       do {                                                            \
+               memset(&(a)->_stats, 0, sizeof((a)->_stats));           \
+       } while (0)
+
+#else /* !CONFIG_LIBUKALLOC_IFSTATS */
+#define uk_alloc_stats_count_alloc(a, ptr, size) do {} while (0)
+#define uk_alloc_stats_count_palloc(a, ptr, num_pages) do {} while (0)
+#define uk_alloc_stats_count_enomem(a, size) do {} while (0)
+#define uk_alloc_stats_count_penomem(a, num_pages) do {} while (0)
+#define uk_alloc_stats_count_free(a, ptr, freed_size) do {} while (0)
+#define uk_alloc_stats_count_pfree(a, ptr, num_pages) do {} while (0)
+#define uk_alloc_stats_reset(a) do {} while (0)
+#endif /* !CONFIG_LIBUKALLOC_IFSTATS */
+
 /* Shortcut for doing a registration of an allocator that does not implement
  * palloc() or pfree()
  */
@@ -104,6 +209,7 @@ long uk_alloc_pmaxalloc_compat(struct uk_alloc *a);
                                      ? uk_alloc_pmaxalloc_compat : NULL; \
                (a)->addmem         = (addmem_f);                       \
                                                                        \
+               uk_alloc_stats_reset((a));                              \
                uk_alloc_register((a));                                 \
        } while (0)

@@ -129,6 +235,7 @@ long uk_alloc_pmaxalloc_compat(struct uk_alloc *a);
                                      ? uk_alloc_pmaxalloc_compat : NULL; \
                (a)->addmem         = (addmem_f);                       \
                                                                        \
+               uk_alloc_stats_reset((a));                              \
                uk_alloc_register((a));                                 \
        } while (0)
 #endif
@@ -155,6 +262,7 @@ long uk_alloc_pmaxalloc_compat(struct uk_alloc *a);
                                      ? uk_alloc_maxalloc_ifpages : NULL; \
                (a)->addmem         = (addmem_func);                    \
                                                                        \
+               uk_alloc_stats_reset((a));                              \
                uk_alloc_register((a));                                 \
        } while (0)

diff --git a/lib/ukalloc/stats.c b/lib/ukalloc/stats.c
new file mode 100644
index 00000000..26dcc405
--- /dev/null
+++ b/lib/ukalloc/stats.c
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Simon Kuenzer <simon.kuenzer@xxxxxxxxx>
+ *
+ * Copyright (c) 2020, NEC Laboratories Europe GmbH, NEC Corporation.
+ *                     All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <uk/preempt.h>
+#include <uk/alloc_impl.h>
+
+void uk_alloc_stats(struct uk_alloc *a,
+                   struct uk_alloc_stats *dst)
+{
+       UK_ASSERT(a);
+       UK_ASSERT(dst);
+
+       uk_preempt_disable();
+       memcpy(dst, &a->_stats, sizeof(*dst));
+       uk_preempt_enable();
+}
--
2.20.1


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.