[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [UNIKRAFT PATCH 10/18] lib/ukalloc: Allocator statistics
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 +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 +#include <string.h> + +#if CONFIG_LIBUKALLOC_IFSTATS_GLOBAL +extern struct uk_alloc_stats _uk_alloc_stats_global; +#endif /* CONFIG_LIBUKALLOC_IFSTATS_GLOBAL */ + +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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |