aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcvs2svn <cvs2svn@FreeBSD.org>2005-07-24 01:41:48 +0000
committercvs2svn <cvs2svn@FreeBSD.org>2005-07-24 01:41:48 +0000
commite0b04eb627b56112e1b012281f2ad77cc24980c6 (patch)
treef6454b4efe708393e80d8db50fc4fec7e84f4a96
parent44e2176466637e8f586aeb405979083a60467626 (diff)
Notes
-rw-r--r--lib/libmemstat/Makefile25
-rw-r--r--lib/libmemstat/libmemstat.3453
-rw-r--r--lib/libmemstat/memstat.c408
-rw-r--r--lib/libmemstat/memstat.h164
-rw-r--r--lib/libmemstat/memstat_internal.h123
-rw-r--r--lib/libmemstat/memstat_malloc.c228
-rw-r--r--lib/libmemstat/memstat_uma.c221
7 files changed, 1622 insertions, 0 deletions
diff --git a/lib/libmemstat/Makefile b/lib/libmemstat/Makefile
new file mode 100644
index 000000000000..1d3c43d28731
--- /dev/null
+++ b/lib/libmemstat/Makefile
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+WARNS?= 3
+LIB= memstat
+SHLIB_MAJOR= 1
+SRCS+= memstat.c
+SRCS+= memstat_all.c
+SRCS+= memstat_malloc.c
+SRCS+= memstat_uma.c
+INCS= memstat.h
+
+MAN= libmemstat.3
+
+MLINKS+= libmemstat.3 memstat_mtl_alloc.3
+MLINKS+= libmemstat.3 memstat_mtl_first.3
+MLINKS+= libmemstat.3 memstat_mtl_next.3
+MLINKS+= libmemstat.3 memstat_mtl_find.3
+MLINKS+= libmemstat.3 memstat_mtl_free.3
+MLINKS+= libmemstat.3 memstat_mtl_geterror.3
+MLINKS+= libmemstat.3 memstat_strerror.3
+MLINKS+= libmemstat.3 memstat_sysctl_all.3
+MLINKS+= libmemstat.3 memstat_sysctl_malloc.3
+MLINKS+= libmemstat.3 memstat_sysctl_uma.3
+
+.include <bsd.lib.mk>
diff --git a/lib/libmemstat/libmemstat.3 b/lib/libmemstat/libmemstat.3
new file mode 100644
index 000000000000..0bc341035c32
--- /dev/null
+++ b/lib/libmemstat/libmemstat.3
@@ -0,0 +1,453 @@
+.\" Copyright (c) 2005 Robert N. M. Watson
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 27, 2005
+.Os
+.Dt LIBMEMSTAT 3
+.Sh NAME
+.Nm libmemstat
+.Nd "library interface to retrieve kernel memory allocator statistics"
+.Sh LIBRARY
+.Lb libmemstat
+.Sh SYNOPSIS
+.In sys/types.h
+.In memstat.h
+.Ss General Functions
+.Ft const char *
+.Fn memstat_strerror "int error"
+.Ss Memory Type List Management Functions
+.Ft struct memory_type_list *
+.Fn memstat_mtl_alloc "void"
+.Ft struct memory_type *
+.Fn memstat_mtl_first "struct memory_type_list *list"
+.Ft struct memory_type *
+.Fn memstat_mtl_next "struct memory_type *mtp"
+.Ft struct memory_type *
+.Fn memstat_mtl_find "struct memory_type_list *list" "int allocator" "const char *name"
+.Ft void
+.Fn memstat_mtl_free "struct memory_type_list *list"
+.Ft int
+.Fn memstat_mtl_geterror "struct memory_type_list *list"
+.Ss Allocator Query Functions
+.Ft int
+.Fn memstat_sysctl_all "struct memory_type_list *list" "int flags"
+.Ft int
+.Fn memstat_sysctl_malloc "struct memory_type_list *list" "int flags"
+.Ft int
+.Fn memstat_sysctl_uma "struct memory_type_list *list" "int flags"
+.Ss Memory Type Accessor Methods
+.Ft const char *
+.Fn memstat_get_name "const struct memory_type *mtp"
+.Ft int
+.Fn memstat_get_allocator "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_countlimit "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_byteslimit "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_sizemask "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_size "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_memalloced "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_memfreed "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_numallocs "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_numfrees "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_bytes "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_count "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_free "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_failures "const struct memory_type *mtp"
+.Ft void *
+.Fn memstat_get_caller_pointer "const struct memory_type *mtp" "int index"
+.Ft void
+.Fn memstat_set_caller_pointer "struct memory_type *mtp" "int index" "void *value"
+.Ft uint64_t
+.Fn memstat_get_caller_uint64 "const struct memory_type *mtp" "int index"
+.Ft void
+.Fn memstat_set_caller_uint64 "struct memory_type *mtp" "int index" "uint64_t value"
+.Ft uint64_t
+.Fn memstat_get_zonefree "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_kegfree "const struct memory_type *mtp"
+.Ft uint64_t
+.Fn memstat_get_percpu_memalloced "const struct memory_type *mtp" "int cpu"
+.Ft uint64_t
+.Fn memstat_get_percpu_memfreed "const struct memory_type *mtp" "int cpu"
+.Ft uint64_t
+.Fn memstat_get_percpu_numallocs "const struct memory_type *mtp" "int cpu"
+.Ft uint64_t
+.Fn memstat_get_percpu_numfrees "const struct memory_type *mtp" "int cpu"
+.Ft uint64_t
+.Fn memstat_get_percpu_sizemask "const struct memory_type *mtp" "int cpu"
+.Ft void *
+.Fn memstat_get_percpu_caller_pointer "const struct memory_type *mtp" "int cpu" "int index"
+.Ft void
+.Fn memstat_set_percpu_caller_pointer "struct memory_type *mtp" "int cpu" "int index" "void *value"
+.Ft uint64_t
+.Fn memstat_get_percpu_caller_uint64 "const struct memory_type *mtp" "int cpu" "int index"
+.Ft void
+.Fn memstat_set_percpu_caller_uint64 "struct memory_type *mtp" "int cpu" "int index" "uint64_t value"
+.Ft uint64_t
+.Fn memstat_get_percpu_free "const struct memory_type *mtp" "int cpu"
+.Sh DESCRIPTION
+.Nm
+provides an interface to retrieve kernel memory allocator statistics, for
+the purposes of debugging and system monitoring, insulating applications
+from implementation details of the allocators, and allowing a tool to
+transparently support multiple allocators.
+.Nm
+supports both retrieving a single statistics snapshot, as well as
+incrementally updating statistics for long-term monitoring.
+.Pp
+.Nm
+describes each memory type using a
+.Vt struct memory_type ,
+an opaque memory type accessed by the application using accessor functions
+in the library.
+.Nm
+returns and updates chains of
+.Vt struct memory_type
+via a
+.Vt struct memory_type_list ,
+which will be allocated by calling
+.Fn memstat_mtl_alloc ,
+and freed on completion using
+.Fn memstat_mtl_free .
+Lists of memory types are populated via calls that query the kernel for
+statistics information; currently:
+.Fn memstat_sysctl_all ,
+.Fn memstat_sysctl_uma ,
+and
+.Fn memstat_sysctl_malloc .
+Repeated calls will incrementally update the list of memory types, permitting
+tracking over time without recreating all list state.
+If an error is detected during a query call, error condition information may
+be retrieved using
+.Fn memstat_mtl_geterror ,
+and converted to a user-readable string using
+.Fn memstat_strerror .
+.Pp
+Freeing the list will free all memory type data in the list, and so
+invalidates any outstanding pointers to entries in the list.
+.Vt struct memory_type
+entries in the list may be iterated over using
+.Fn memstat_mtl_first
+and
+.fn memstat_mtl_next ,
+which respectively return the first entry in a list, and the next entry in a
+list.
+.Fn memstat_mtl_find ,
+which will return a pointer to the first entry matching the passed
+parameters.
+.Pp
+A series of accessor methods is provided to access fields of the structure,
+including retrieving statistics and properties, as well as setting of caller
+owned fields.
+Direct application access to the data structure fields is not supported.
+.Ss Library memory_type Fields
+Each
+.Vt struct memory_type
+holds a description of the memory type, including its name and the allocator
+it is managed by, as well as current statistics on use.
+Some statistics are directly measured, others are derived from directly
+measured statistics.
+Certain high level statistics are present across all available allocators,
+such as the number of allocation and free operations; other measurements,
+such as the quantity of free items in per-CPU caches, or administrative
+limit on the number of allocations, is available only for specific
+allocators.
+.Ss Caller memory_type Fields
+.Vt struct memory_type
+includes fields to allow the application to store data, in the form of
+pointers and 64-bit integers, with memory types.
+For example, the application author might make use of one of the caller
+pointers to reference a more complex data structure tracking long-term
+behavior of the memory type, or a window system object that is used to
+render the state of the memory type.
+General and per-CPU storage is provided with each
+.Vt struct memory_type
+in the form of an array of pointers and integers.
+The array entries are accessed via the
+.Fa index
+argument to the get and set accessor methods.
+Possible values of
+.Fa index
+range between
+.Dv 0
+and
+.Dv MEMSTAT_MAXCALLER .
+.Pp
+Caller-owned fields are initialized to
+.Dv 0
+or
+.Dv NULL
+when a new
+.Vt struct memory_type
+is allocated and attached to a memory type list; these fields retain their
+values across queries that update library-owned fields.
+.Ss Allocator Types
+Currently,
+.Nm
+supports two kernel allocators:
+.Dv ALLOCATOR_UMA
+for
+.Xr uma 9 ,
+and
+.Dv ALLOCATOR_MALLOC
+for
+.Xr malloc 9 .
+These values may be passed to
+.Fn memstat_mtl_find ,
+and will be returned by
+.Fn memstat_get_allocator .
+Two additional constants in the allocator name space are defined:
+.Dv ALLOCATOR_UNKNOWN ,
+which will only be returned as a result of a library error, and
+.Dv ALLOCATOR_ANY ,
+which can be used to specify that returning types matching any allocator is
+permittible from
+.Fn memstat_mtl_find .
+.Ss Access Method List
+The following accessor methods are defined, of which some will be valid for
+a given memory type:
+.Pp
+.Bl -tag -width "memstat_get_name" -compact -offset wee
+.It memstat_get_name
+Return a pointer to the name of the memory type.
+Memory for the name is owned by
+.Nm
+and will be valid through a call to
+.Fn memstat_mtl_free .
+Note that names will be unique with respect to a single allocator, but that
+the same name might be used by different memory types owned by different
+memory allocators.
+.It memstat_get_allocator
+Return an integer identifier for the memory allocator that owns the memory
+type.
+.It memstat_get_countlimit
+If the memory type has an administrative limit on the number of simultaneous
+allocations, return it.
+.It memstat_get_byteslimit
+If the memory type has an administrative limit on the number of bytes of
+memory that may be simultaenously allocated for the memory type, return it.
+.It memstat_get_sizemask
+If the memory type supports variable allocation sizes, return a bitmask of
+sizes allocated for the memory type.
+.It memstat_get_size
+If the memory type supports a fixed allocation size, return that size.
+.It memstat_get_memalloced
+Return the total number of bytes allocated for the memory type over its
+lifetime.
+.It memstat_get_memfreed
+Return the total number of bytes freed for the memory type over its lifetime.
+.It memstat_get_numallocs
+Return the total number of allocations for the memory type over its lifetime.
+.It memstat_get_numfrees
+Return the total number of frees for the memory type over its lifetime.
+.It memstat_get_bytes
+Return the current number of bytes allocated to the memory type.
+.It memstat_get_count
+Return the current number of allocations for the memory type.
+.It memstat_get_free
+If the memory allocator supports a cache, return the number of items in the
+cache.
+.It memstat_get_failures
+If the memory allocator and type permit allocation failures, return the
+number of allocation failures measured.
+.It memstat_get_caller_pointer
+Return a caller-owned pointer for the memory type.
+.It memstat_set_caller_pointer
+Set a caller-owned pointer for the memory type.
+.It memstat_get_caller_uint64
+Return a caller-owned integer for the memory type.
+.It memstat_set_caller_uint64
+Set a caller-owned integer for the memory type.
+.It memstat_get_zonefree
+If the memory allocator supports a multi-level allocation structure, return
+the number of cached items in the zone.
+These items will be in a fully constructed state available for immediate
+use.
+.It memstat_get_kegfree
+If the memory allocator supports a multi-level allocation structure, return
+the number of cached items in the keg.
+These items may be in a partially constructed state, and may require further
+processing before they can be made available for use.
+.It memstat_get_percpu_memalloced
+If the memory allocator supports per-CPU statistics, return the number of
+bytes of memory allocated for the memory type on the CPU over its lifetime.
+.It memstat_get_percpu_memfreed
+If the memory allocator supports per-CPU statistics, return the number of
+bytes of memory freed from the memory type on the CPU over its lifetime.
+.It memstat_get_percpu_numallocs
+If the memory allocator supports per-CPU statistics, return the number of
+allocations for the memory type on the CPU over its lifetime.
+.It memstat_get_percpu_numfrees
+If the memory allocator supports per-CPU statistics, return the number of
+frees for the memory type on the CPU over its lifetime.
+.It memstat_get_percpu_sizemask
+If the memory allocator supports variable size memory allocation and per-CPU
+statistics, return the size bitmask for the memory type on the CPU.
+.It memstat_get_percpu_caller_pointer
+Return a caller-owned per-CPU pointer for the memory type.
+.It memstat_set_percpu_caller_pointer
+Set a caller-owned per-CPU pointer for the memory type.
+.It memstat_get_percpu_caller_uint64
+Return a caller-owned per-CPU integer for the memory type.
+.It memsttat_set_percpu_caller_uint64
+Set a caller-owned per-CPU integer for the memory type.
+.It memstat_get_percpu_free
+If the memory allocator supports a per-CPU cache, return the number of free
+items in the per-CPU cache of the designated CPU.
+.El
+.Sh RETURN VALUES
+.Nm
+functions fall into three categories: functions returning a pointer to an
+object, functions returning an integer return value, and functions
+implementing accessor methods returning data from a
+.Vt struct memory_type .
+.Pp
+Functions returning a pointer to an object will generally return
+.Dv NULL
+on failure.
+.Fn memstat_mtl_alloc
+will return an error value via
+.Va errno ,
+which will consist of the value
+.Dv ENOMEM .
+Functions
+.Fn memstat_mtl_first ,
+.Fn memstat_mtl_next ,
+and
+.Fn memstat_mtl_find
+will return
+.Dv NULL
+when there is no entry or match in the list; however, this is not considered
+a failure mode and no error value is available.
+.Pp
+Functions returning a integer success valuye will return
+.Dv 0
+on success, or
+.Dv -1
+on failure.
+If a failure is returned, the list error access method,
+.Fn memstat_mtl_geterror ,
+may be used to retrieve the error state.
+The string representation of the error may be retrieved using
+.Fn memstat_strerror .
+Possible error values are:
+.Pp
+.Bl -tag -width "MEMSTAT_ERROR_TOOMANYCPUS" -compact -offset wee
+.It Dv MEMSTAT_ERROR_UNDEFINED
+Undefined error. Occurs if
+.Fn memstat_mtl_geterror
+is called on a list before an error associated with the list has occurred.
+.It Dv MEMSTAT_ERROR_NOMEMORY
+Insufficient memory. Occurs if library calls to
+.Xr malloc 3
+fail, or if a system call to retrieve kernel statistics fails with
+.Er ENOMEM .
+.It Dv MEMSTAT_ERROR_VERSION
+Returned if the current version of
+.Nm
+is unable to interpret the statistics data returned by the kernel due to an
+explicit version mismatch, or to differences in data structures that cannot
+be reconciled.
+.It Dv MEMSTAT_ERROR_PERMISSION
+Returned if a statistics source returns
+.Va errno
+values of
+.Dv EACCES
+or
+.Dv EPERM .
+.It Dv MEMSTAT_ERROR_TOOMANYCPUS
+Returned if the compile-time limit on the number of CPUs in
+.Nm
+is lower than the number of CPUs returned by a statistics data source.
+.It Dv MEMSTAT_ERROR_DATAERROR
+Returned if
+.Nm
+is unable to interpret statistics data returned by the data source, even
+though there does not appear to be a version problem.
+.El
+.Pp
+Finally, functions returning data from a
+.Dt struct memory_type
+pointer are not permitted to fail, and directly return either a statistic
+or pointer to a string.
+.Sh EXAMPLES
+Create a memory type list, query the
+.Xr uma 9
+memory allocator for available statistics, and print out the number of
+allocations performed by the
+.Dv Mbuf
+zone.
+.Bd -literal -offset indent
+struct memory_type_list *mtlp;
+struct memory_type *mtp;
+uint64_t mbuf_count;
+
+mtlp = memstat_mtl_alloc();
+if (mtlp == NULL)
+ err(-1, "memstat_mtl_alloc");
+if (memstat_sysctl_uma(mtlp, 0) < 0)
+ err(-1, "memstat_sysctl_uma");
+mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, "Mbuf");
+if (mtp == NULL)
+ errx(-1, "memstat_mtl_find: Mbuf not found");
+mbuf_count = memstat_get_count(mtp);
+memstat_mtl_free(mtlp);
+
+printf("Mbufs: %llu\\n", (unsigned long long)mbuf_count);
+.Ed
+.Sh SEE ALSO
+.Xr malloc 9 ,
+.Xr uma 9
+.Sh HISTORY
+The
+.Nm libmemstat
+library appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+The kernel memory allocator changes necessary to support a general purpose
+monitoring library, along with the library, were written by
+.An Robert Watson Aq rwatson@FreeBSD.org
+.Sh BUGS
+.Nm
+cannot yet extract statistics from kernel core dumps, although this should be
+straight forward to implement.
+.Pp
+Once a memory type is present on a memory type list, it will not be removed
+even if the kernel no longer presents information on the type via its
+monitoring interfaces.
+In order to flush removed memory types, it is necessary to free the entire
+list and allocate a new one.
diff --git a/lib/libmemstat/memstat.c b/lib/libmemstat/memstat.c
new file mode 100644
index 000000000000..f32541feb55e
--- /dev/null
+++ b/lib/libmemstat/memstat.c
@@ -0,0 +1,408 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "memstat.h"
+#include "memstat_internal.h"
+
+const char *
+memstat_strerror(int error)
+{
+
+ switch (error) {
+ case MEMSTAT_ERROR_NOMEMORY:
+ return ("Cannot allocate memory");
+ case MEMSTAT_ERROR_VERSION:
+ return ("Version mismatch");
+ case MEMSTAT_ERROR_PERMISSION:
+ return ("Permission denied");
+ case MEMSTAT_ERROR_TOOMANYCPUS:
+ return ("Too many CPUs");
+ case MEMSTAT_ERROR_DATAERROR:
+ return ("Data format error");
+ case MEMSTAT_ERROR_UNDEFINED:
+ default:
+ return ("Unknown error");
+ }
+}
+
+struct memory_type_list *
+memstat_mtl_alloc(void)
+{
+ struct memory_type_list *mtlp;
+
+ mtlp = malloc(sizeof(*mtlp));
+ if (mtlp == NULL)
+ return (NULL);
+
+ LIST_INIT(&mtlp->mtl_list);
+ mtlp->mtl_error = MEMSTAT_ERROR_UNDEFINED;
+ return (mtlp);
+}
+
+struct memory_type *
+memstat_mtl_first(struct memory_type_list *list)
+{
+
+ return (LIST_FIRST(&list->mtl_list));
+}
+
+struct memory_type *
+memstat_mtl_next(struct memory_type *mtp)
+{
+
+ return (LIST_NEXT(mtp, mt_list));
+}
+
+void
+memstat_mtl_free(struct memory_type_list *list)
+{
+ struct memory_type *mtp;
+
+ while ((mtp = LIST_FIRST(&list->mtl_list))) {
+ LIST_REMOVE(mtp, mt_list);
+ free(mtp);
+ }
+ free(list);
+}
+
+int
+memstat_mtl_geterror(struct memory_type_list *list)
+{
+
+ return (list->mtl_error);
+}
+
+/*
+ * Look for an existing memory_type entry in a memory_type list, based on the
+ * allocator and name of the type. If not found, return NULL. No errno or
+ * memstat error.
+ */
+struct memory_type *
+memstat_mtl_find(struct memory_type_list *list, int allocator,
+ const char *name)
+{
+ struct memory_type *mtp;
+
+ LIST_FOREACH(mtp, &list->mtl_list, mt_list) {
+ if ((mtp->mt_allocator == allocator ||
+ allocator == ALLOCATOR_ANY) &&
+ strcmp(mtp->mt_name, name) == 0)
+ return (mtp);
+ }
+ return (NULL);
+}
+
+/*
+ * Allocate a new memory_type with the specificed allocator type and name,
+ * then insert into the list. The structure will be zero'd.
+ *
+ * libmemstat(3) internal function.
+ */
+struct memory_type *
+_memstat_mt_allocate(struct memory_type_list *list, int allocator,
+ const char *name)
+{
+ struct memory_type *mtp;
+
+ mtp = malloc(sizeof(*mtp));
+ if (mtp == NULL)
+ return (NULL);
+
+ bzero(mtp, sizeof(*mtp));
+
+ mtp->mt_allocator = allocator;
+ strlcpy(mtp->mt_name, name, MEMTYPE_MAXNAME);
+ LIST_INSERT_HEAD(&list->mtl_list, mtp, mt_list);
+ return (mtp);
+}
+
+/*
+ * Reset any libmemstat(3)-owned statistics in a memory_type record so that
+ * it can be reused without incremental addition problems. Caller-owned
+ * memory is left "as-is", and must be updated by the caller if desired.
+ *
+ * libmemstat(3) internal function.
+ */
+void
+_memstat_mt_reset_stats(struct memory_type *mtp)
+{
+ int i;
+
+ mtp->mt_countlimit = 0;
+ mtp->mt_byteslimit = 0;
+ mtp->mt_sizemask = 0;
+ mtp->mt_size = 0;
+
+ mtp->mt_memalloced = 0;
+ mtp->mt_memfreed = 0;
+ mtp->mt_numallocs = 0;
+ mtp->mt_numfrees = 0;
+ mtp->mt_bytes = 0;
+ mtp->mt_count = 0;
+ mtp->mt_free = 0;
+ mtp->mt_failures = 0;
+
+ mtp->mt_zonefree = 0;
+ mtp->mt_kegfree = 0;
+
+ for (i = 0; i < MEMSTAT_MAXCPU; i++) {
+ mtp->mt_percpu_alloc[i].mtp_memalloced = 0;
+ mtp->mt_percpu_alloc[i].mtp_memfreed = 0;
+ mtp->mt_percpu_alloc[i].mtp_numallocs = 0;
+ mtp->mt_percpu_alloc[i].mtp_numfrees = 0;
+ mtp->mt_percpu_alloc[i].mtp_sizemask = 0;
+ mtp->mt_percpu_cache[i].mtp_free = 0;
+ }
+}
+
+/*
+ * Accessor methods for struct memory_type. Avoids encoding the structure
+ * ABI into the application.
+ */
+const char *
+memstat_get_name(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_name);
+}
+
+int
+memstat_get_allocator(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_allocator);
+}
+
+uint64_t
+memstat_get_countlimit(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_countlimit);
+}
+
+uint64_t
+memstat_get_byteslimit(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_byteslimit);
+}
+
+uint64_t
+memstat_get_sizemask(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_sizemask);
+}
+
+uint64_t
+memstat_get_size(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_size);
+}
+
+uint64_t
+memstat_get_memalloced(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_memalloced);
+}
+
+uint64_t
+memstat_get_memfreed(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_memfreed);
+}
+
+uint64_t
+memstat_get_numallocs(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_numallocs);
+}
+
+uint64_t
+memstat_get_numfrees(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_numfrees);
+}
+
+uint64_t
+memstat_get_bytes(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_bytes);
+}
+
+uint64_t
+memstat_get_count(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_count);
+}
+
+uint64_t
+memstat_get_free(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_free);
+}
+
+uint64_t
+memstat_get_failures(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_failures);
+}
+
+void *
+memstat_get_caller_pointer(const struct memory_type *mtp, int index)
+{
+
+ return (mtp->mt_caller_pointer[index]);
+}
+
+void
+memstat_set_caller_pointer(struct memory_type *mtp, int index, void *value)
+{
+
+ mtp->mt_caller_pointer[index] = value;
+}
+
+uint64_t
+memstat_get_caller_uint64(const struct memory_type *mtp, int index)
+{
+
+ return (mtp->mt_caller_uint64[index]);
+}
+
+void
+memstat_set_caller_uint64(struct memory_type *mtp, int index, uint64_t value)
+{
+
+ mtp->mt_caller_uint64[index] = value;
+}
+
+uint64_t
+memstat_get_zonefree(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_zonefree);
+}
+
+uint64_t
+memstat_get_kegfree(const struct memory_type *mtp)
+{
+
+ return (mtp->mt_kegfree);
+}
+
+uint64_t
+memstat_get_percpu_memalloced(const struct memory_type *mtp, int cpu)
+{
+
+ return (mtp->mt_percpu_alloc[cpu].mtp_memalloced);
+}
+
+uint64_t
+memstat_get_percpu_memfreed(const struct memory_type *mtp, int cpu)
+{
+
+ return (mtp->mt_percpu_alloc[cpu].mtp_memfreed);
+}
+
+uint64_t
+memstat_get_percpu_numallocs(const struct memory_type *mtp, int cpu)
+{
+
+ return (mtp->mt_percpu_alloc[cpu].mtp_numallocs);
+}
+
+uint64_t
+memstat_get_percpu_numfrees(const struct memory_type *mtp, int cpu)
+{
+
+ return (mtp->mt_percpu_alloc[cpu].mtp_numfrees);
+}
+
+uint64_t
+memstat_get_percpu_sizemask(const struct memory_type *mtp, int cpu)
+{
+
+ return (mtp->mt_percpu_alloc[cpu].mtp_sizemask);
+}
+
+void *
+memstat_get_percpu_caller_pointer(const struct memory_type *mtp, int cpu,
+ int index)
+{
+
+ return (mtp->mt_percpu_alloc[cpu].mtp_caller_pointer[index]);
+}
+
+void
+memstat_set_percpu_caller_pointer(struct memory_type *mtp, int cpu,
+ int index, void *value)
+{
+
+ mtp->mt_percpu_alloc[cpu].mtp_caller_pointer[index] = value;
+}
+
+uint64_t
+memstat_get_percpu_caller_uint64(const struct memory_type *mtp, int cpu,
+ int index)
+{
+
+ return (mtp->mt_percpu_alloc[cpu].mtp_caller_uint64[index]);
+}
+
+void
+memstat_set_percpu_caller_uint64(struct memory_type *mtp, int cpu, int index,
+ uint64_t value)
+{
+
+ mtp->mt_percpu_alloc[cpu].mtp_caller_uint64[index] = value;
+}
+
+uint64_t
+memstat_get_percpu_free(const struct memory_type *mtp, int cpu)
+{
+
+ return (mtp->mt_percpu_cache[cpu].mtp_free);
+}
diff --git a/lib/libmemstat/memstat.h b/lib/libmemstat/memstat.h
new file mode 100644
index 000000000000..f46640876caf
--- /dev/null
+++ b/lib/libmemstat/memstat.h
@@ -0,0 +1,164 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MEMSTAT_H_
+#define _MEMSTAT_H_
+
+/*
+ * Number of CPU slots in library-internal data structures. This should be
+ * at least the value of MAXCPU from param.h.
+ */
+#define MEMSTAT_MAXCPU 16
+
+/*
+ * Amount of caller data to maintain for each caller data slot. Applications
+ * must not request more than this number of caller save data, or risk
+ * corrupting internal libmemstat(3) data structures. A compile time check
+ * in the application is probably appropriate.
+ */
+#define MEMSTAT_MAXCALLER 16
+
+/*
+ * libmemstat(3) is able to extract memory data from different allocators;
+ * when it does so, it tags which allocator it got the data from so that
+ * consumers can determine which fields are usable, as data returned varies
+ * some.
+ */
+#define ALLOCATOR_UNKNOWN 0
+#define ALLOCATOR_MALLOC 1
+#define ALLOCATOR_UMA 2
+#define ALLOCATOR_ANY 255
+
+/*
+ * Library maximum type name. Should be max(set of name maximums over
+ * various allocators).
+ */
+#define MEMTYPE_MAXNAME 32
+
+/*
+ * Library error conditions, mostly from the underlying data sources. On
+ * failure, functions typically return (-1) or (NULL); on success, (0) or a
+ * valid data pointer. The error from the last operation is stored in
+ * struct memory_type, and accessed via memstat_get_error(mtp).
+ */
+#define MEMSTAT_ERROR_UNDEFINED 0 /* Initialization value. */
+#define MEMSTAT_ERROR_NOMEMORY 1 /* Out of memory. */
+#define MEMSTAT_ERROR_VERSION 2 /* Unsupported version. */
+#define MEMSTAT_ERROR_PERMISSION 3 /* Permission denied. */
+#define MEMSTAT_ERROR_TOOMANYCPUS 4 /* Too many CPUs. */
+#define MEMSTAT_ERROR_DATAERROR 5 /* Error in stat data. */
+
+/*
+ * Forward declare struct memory_type, which holds per-type properties and
+ * statistics. This is an opaque type, to be frobbed only from within the
+ * library, in order to avoid building ABI assumptions into the application.
+ * Accessor methods should be used to get and sometimes set the fields from
+ * consumers of the library.
+ */
+struct memory_type;
+
+/*
+ * struct memory_type_list is the head of a list of memory types and
+ * statistics.
+ */
+struct memory_type_list;
+
+__BEGIN_DECLS
+/*
+ * Functions that operate without memory type or memory type list context.
+ */
+const char *memstat_strerror(int error);
+
+/*
+ * Functions for managing memory type and statistics data.
+ */
+struct memory_type_list *memstat_mtl_alloc(void);
+struct memory_type *memstat_mtl_first(struct memory_type_list *list);
+struct memory_type *memstat_mtl_next(struct memory_type *mtp);
+struct memory_type *memstat_mtl_find(struct memory_type_list *list,
+ int allocator, const char *name);
+void memstat_mtl_free(struct memory_type_list *list);
+int memstat_mtl_geterror(struct memory_type_list *list);
+
+/*
+ * Functions to retrieve data from a live kernel using sysctl.
+ */
+int memstat_sysctl_all(struct memory_type_list *list, int flags);
+int memstat_sysctl_malloc(struct memory_type_list *list, int flags);
+int memstat_sysctl_uma(struct memory_type_list *list, int flags);
+
+/*
+ * Accessor methods for struct memory_type.
+ */
+const char *memstat_get_name(const struct memory_type *mtp);
+int memstat_get_allocator(const struct memory_type *mtp);
+uint64_t memstat_get_countlimit(const struct memory_type *mtp);
+uint64_t memstat_get_byteslimit(const struct memory_type *mtp);
+uint64_t memstat_get_sizemask(const struct memory_type *mtp);
+uint64_t memstat_get_size(const struct memory_type *mtp);
+uint64_t memstat_get_memalloced(const struct memory_type *mtp);
+uint64_t memstat_get_memfreed(const struct memory_type *mtp);
+uint64_t memstat_get_numallocs(const struct memory_type *mtp);
+uint64_t memstat_get_numfrees(const struct memory_type *mtp);
+uint64_t memstat_get_bytes(const struct memory_type *mtp);
+uint64_t memstat_get_count(const struct memory_type *mtp);
+uint64_t memstat_get_free(const struct memory_type *mtp);
+uint64_t memstat_get_failures(const struct memory_type *mtp);
+void *memstat_get_caller_pointer(const struct memory_type *mtp,
+ int index);
+void memstat_set_caller_pointer(struct memory_type *mtp,
+ int index, void *value);
+uint64_t memstat_get_caller_uint64(const struct memory_type *mtp,
+ int index);
+void memstat_set_caller_uint64(struct memory_type *mtp, int index,
+ uint64_t value);
+uint64_t memstat_get_zonefree(const struct memory_type *mtp);
+uint64_t memstat_get_kegfree(const struct memory_type *mtp);
+uint64_t memstat_get_percpu_memalloced(const struct memory_type *mtp,
+ int cpu);
+uint64_t memstat_get_percpu_memfreed(const struct memory_type *mtp,
+ int cpu);
+uint64_t memstat_get_percpu_numallocs(const struct memory_type *mtp,
+ int cpu);
+uint64_t memstat_get_percpu_numfrees(const struct memory_type *mtp,
+ int cpu);
+uint64_t memstat_get_percpu_sizemask(const struct memory_type *mtp,
+ int cpu);
+void *memstat_get_percpu_caller_pointer(
+ const struct memory_type *mtp, int cpu, int index);
+void memstat_set_percpu_caller_pointer(struct memory_type *mtp,
+ int cpu, int index, void *value);
+uint64_t memstat_get_percpu_caller_uint64(
+ const struct memory_type *mtp, int cpu, int index);
+void memstat_set_percpu_caller_uint64(struct memory_type *mtp,
+ int cpu, int index, uint64_t value);
+uint64_t memstat_get_percpu_free(const struct memory_type *mtp,
+ int cpu);
+__END_DECLS
+
+#endif /* !_MEMSTAT_H_ */
diff --git a/lib/libmemstat/memstat_internal.h b/lib/libmemstat/memstat_internal.h
new file mode 100644
index 000000000000..838eaa60a357
--- /dev/null
+++ b/lib/libmemstat/memstat_internal.h
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MEMSTAT_INTERNAL_H_
+#define _MEMSTAT_INTERNAL_H_
+
+/*
+ * memstat maintains its own internal notion of statistics on each memory
+ * type, common across UMA and kernel malloc. Some fields are straight from
+ * the allocator statistics, others are derived when extracted from the
+ * kernel. A struct memory_type will describe each type supported by an
+ * allocator. memory_type structures can be chained into lists.
+ */
+struct memory_type {
+ /*
+ * Static properties of type.
+ */
+ int mt_allocator; /* malloc(9), uma(9), etc. */
+ char mt_name[MEMTYPE_MAXNAME]; /* name of memory type. */
+
+ /*
+ * (Relatively) static zone settings, that don't uniquely identify
+ * the zone, but also don't change much.
+ */
+ uint64_t mt_countlimit; /* 0, or maximum allocations. */
+ uint64_t mt_byteslimit; /* 0, or maximum bytes. */
+ uint64_t mt_sizemask; /* malloc: allocated size bitmask. */
+ uint64_t mt_size; /* uma: size of objects. */
+
+ /*
+ * Zone or type information that includes all caches and any central
+ * zone state. Depending on the allocator, this may be synthesized
+ * from several sources, or directly measured.
+ */
+ uint64_t mt_memalloced; /* Bytes allocated over life time. */
+ uint64_t mt_memfreed; /* Bytes freed over life time. */
+ uint64_t mt_numallocs; /* Allocations over life time. */
+ uint64_t mt_numfrees; /* Frees over life time. */
+ uint64_t mt_bytes; /* Bytes currently allocated. */
+ uint64_t mt_count; /* Number of current allocations. */
+ uint64_t mt_free; /* Number of cached free items. */
+ uint64_t mt_failures; /* Number of allocation failures. */
+
+ /*
+ * Caller-owned memory.
+ */
+ void *mt_caller_pointer[MEMSTAT_MAXCALLER]; /* Pointers. */
+ uint64_t mt_caller_uint64[MEMSTAT_MAXCALLER]; /* Integers. */
+
+ /*
+ * For allocators making use of per-CPU caches, we also provide raw
+ * statistics from the central allocator and each per-CPU cache,
+ * which (combined) sometimes make up the above general statistics.
+ *
+ * First, central zone/type state, all numbers excluding any items
+ * cached in per-CPU caches.
+ *
+ * XXXRW: Might be desirable to separately expose allocation stats
+ * from zone, which should (combined with per-cpu) add up to the
+ * global stats above.
+ */
+ uint64_t mt_zonefree; /* Free items in zone. */
+ uint64_t mt_kegfree; /* Free items in keg. */
+
+ /*
+ * Per-CPU measurements fall into two categories: per-CPU allocation,
+ * and per-CPU cache state.
+ */
+ struct {
+ uint64_t mtp_memalloced;/* Per-CPU mt_memalloced. */
+ uint64_t mtp_memfreed; /* Per-CPU mt_memfreed. */
+ uint64_t mtp_numallocs; /* Per-CPU mt_numallocs. */
+ uint64_t mtp_numfrees; /* Per-CPU mt_numfrees. */
+ uint64_t mtp_sizemask; /* Per-CPU mt_sizemask. */
+ void *mtp_caller_pointer[MEMSTAT_MAXCALLER];
+ uint64_t mtp_caller_uint64[MEMSTAT_MAXCALLER];
+ } mt_percpu_alloc[MEMSTAT_MAXCPU];
+
+ struct {
+ uint64_t mtp_free; /* Per-CPU cache free items. */
+ } mt_percpu_cache[MEMSTAT_MAXCPU];
+
+ LIST_ENTRY(memory_type) mt_list; /* List of types. */
+};
+
+/*
+ * Description of struct memory_type_list is in memstat.h.
+ */
+struct memory_type_list {
+ LIST_HEAD(, memory_type) mtl_list;
+ int mtl_error;
+};
+
+struct memory_type *_memstat_mt_allocate(struct memory_type_list *list,
+ int allocator, const char *name);
+void _memstat_mt_reset_stats(struct memory_type *mtp);
+
+#endif /* !_MEMSTAT_INTERNAL_H_ */
diff --git a/lib/libmemstat/memstat_malloc.c b/lib/libmemstat/memstat_malloc.c
new file mode 100644
index 000000000000..4170812490ae
--- /dev/null
+++ b/lib/libmemstat/memstat_malloc.c
@@ -0,0 +1,228 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "memstat.h"
+#include "memstat_internal.h"
+
+/*
+ * Extract malloc(9) statistics from the running kernel, and store all memory
+ * type information in the passed list. For each type, check the list for an
+ * existing entry with the right name/allocator -- if present, update that
+ * entry. Otherwise, add a new entry. On error, the entire list will be
+ * cleared, as entries will be in an inconsistent state.
+ *
+ * To reduce the level of work for a list that starts empty, we keep around a
+ * hint as to whether it was empty when we began, so we can avoid searching
+ * the list for entries to update. Updates are O(n^2) due to searching for
+ * each entry before adding it.
+ */
+int
+memstat_sysctl_malloc(struct memory_type_list *list, int flags)
+{
+ struct malloc_type_stream_header *mtshp;
+ struct malloc_type_header *mthp;
+ struct malloc_type_stats *mtsp;
+ struct memory_type *mtp;
+ int count, hint_dontsearch, i, j, maxcpus;
+ char *buffer, *p;
+ size_t size;
+
+ hint_dontsearch = LIST_EMPTY(&list->mtl_list);
+
+ /*
+ * Query the number of CPUs, number of malloc types so that we can
+ * guess an initial buffer size. We loop until we succeed or really
+ * fail. Note that the value of maxcpus we query using sysctl is not
+ * the version we use when processing the real data -- that is read
+ * from the header.
+ */
+retry:
+ size = sizeof(maxcpus);
+ if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) {
+ if (errno == EACCES || errno == EPERM)
+ list->mtl_error = MEMSTAT_ERROR_PERMISSION;
+ else
+ list->mtl_error = MEMSTAT_ERROR_DATAERROR;
+ return (-1);
+ }
+ if (size != sizeof(maxcpus)) {
+ list->mtl_error = MEMSTAT_ERROR_DATAERROR;
+ return (-1);
+ }
+
+ if (maxcpus > MEMSTAT_MAXCPU) {
+ list->mtl_error = MEMSTAT_ERROR_TOOMANYCPUS;
+ return (-1);
+ }
+
+ size = sizeof(count);
+ if (sysctlbyname("kern.malloc_count", &count, &size, NULL, 0) < 0) {
+ if (errno == EACCES || errno == EPERM)
+ list->mtl_error = MEMSTAT_ERROR_PERMISSION;
+ else
+ list->mtl_error = MEMSTAT_ERROR_VERSION;
+ return (-1);
+ }
+ if (size != sizeof(count)) {
+ list->mtl_error = MEMSTAT_ERROR_DATAERROR;
+ return (-1);
+ }
+
+ size = sizeof(*mthp) + count * (sizeof(*mthp) + sizeof(*mtsp) *
+ maxcpus);
+
+ buffer = malloc(size);
+ if (buffer == NULL) {
+ list->mtl_error = MEMSTAT_ERROR_NOMEMORY;
+ return (-1);
+ }
+
+ if (sysctlbyname("kern.malloc_stats", buffer, &size, NULL, 0) < 0) {
+ /*
+ * XXXRW: ENOMEM is an ambiguous return, we should bound the
+ * number of loops, perhaps.
+ */
+ if (errno == ENOMEM) {
+ free(buffer);
+ goto retry;
+ }
+ if (errno == EACCES || errno == EPERM)
+ list->mtl_error = MEMSTAT_ERROR_PERMISSION;
+ else
+ list->mtl_error = MEMSTAT_ERROR_VERSION;
+ free(buffer);
+ return (-1);
+ }
+
+ if (size == 0) {
+ free(buffer);
+ return (0);
+ }
+
+ if (size < sizeof(*mtshp)) {
+ list->mtl_error = MEMSTAT_ERROR_VERSION;
+ free(buffer);
+ return (-1);
+ }
+ p = buffer;
+ mtshp = (struct malloc_type_stream_header *)p;
+ p += sizeof(*mtshp);
+
+ if (mtshp->mtsh_version != MALLOC_TYPE_STREAM_VERSION) {
+ list->mtl_error = MEMSTAT_ERROR_VERSION;
+ free(buffer);
+ return (-1);
+ }
+
+ if (mtshp->mtsh_maxcpus > MEMSTAT_MAXCPU) {
+ list->mtl_error = MEMSTAT_ERROR_TOOMANYCPUS;
+ free(buffer);
+ return (-1);
+ }
+
+ /*
+ * For the remainder of this function, we are quite trusting about
+ * the layout of structures and sizes, since we've determined we have
+ * a matching version and acceptable CPU count.
+ */
+ maxcpus = mtshp->mtsh_maxcpus;
+ count = mtshp->mtsh_count;
+ for (i = 0; i < count; i++) {
+ mthp = (struct malloc_type_header *)p;
+ p += sizeof(*mthp);
+
+ if (hint_dontsearch == 0) {
+ mtp = memstat_mtl_find(list, ALLOCATOR_MALLOC,
+ mthp->mth_name);
+ } else
+ mtp = NULL;
+ if (mtp == NULL)
+ mtp = _memstat_mt_allocate(list, ALLOCATOR_MALLOC,
+ mthp->mth_name);
+ if (mtp == NULL) {
+ memstat_mtl_free(list);
+ free(buffer);
+ list->mtl_error = MEMSTAT_ERROR_NOMEMORY;
+ return (-1);
+ }
+
+ /*
+ * Reset the statistics on a current node.
+ */
+ _memstat_mt_reset_stats(mtp);
+
+ for (j = 0; j < maxcpus; j++) {
+ mtsp = (struct malloc_type_stats *)p;
+ p += sizeof(*mtsp);
+
+ /*
+ * Sumarize raw statistics across CPUs into coalesced
+ * statistics.
+ */
+ mtp->mt_memalloced += mtsp->mts_memalloced;
+ mtp->mt_memfreed += mtsp->mts_memfreed;
+ mtp->mt_numallocs += mtsp->mts_numallocs;
+ mtp->mt_numfrees += mtsp->mts_numfrees;
+ mtp->mt_sizemask |= mtsp->mts_size;
+
+ /*
+ * Copies of per-CPU statistics.
+ */
+ mtp->mt_percpu_alloc[j].mtp_memalloced =
+ mtsp->mts_memalloced;
+ mtp->mt_percpu_alloc[j].mtp_memfreed =
+ mtsp->mts_memfreed;
+ mtp->mt_percpu_alloc[j].mtp_numallocs =
+ mtsp->mts_numallocs;
+ mtp->mt_percpu_alloc[j].mtp_numfrees =
+ mtsp->mts_numfrees;
+ mtp->mt_percpu_alloc[j].mtp_sizemask =
+ mtsp->mts_size;
+ }
+
+ /*
+ * Derived cross-CPU statistics.
+ */
+ mtp->mt_bytes = mtp->mt_memalloced - mtp->mt_memfreed;
+ mtp->mt_count = mtp->mt_numallocs - mtp->mt_numfrees;
+ }
+
+ free(buffer);
+
+ return (0);
+}
diff --git a/lib/libmemstat/memstat_uma.c b/lib/libmemstat/memstat_uma.c
new file mode 100644
index 000000000000..b35c4d90cf8e
--- /dev/null
+++ b/lib/libmemstat/memstat_uma.c
@@ -0,0 +1,221 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#include <vm/uma.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "memstat.h"
+#include "memstat_internal.h"
+
+/*
+ * Extract uma(9) statistics from the running kernel, and store all memory
+ * type information in the passed list. For each type, check the list for an
+ * existing entry with the right name/allocator -- if present, update that
+ * entry. Otherwise, add a new entry. On error, the entire list will be
+ * cleared, as entries will be in an inconsistent state.
+ *
+ * To reduce the level of work for a list that starts empty, we keep around a
+ * hint as to whether it was empty when we began, so we can avoid searching
+ * the list for entries to update. Updates are O(n^2) due to searching for
+ * each entry before adding it.
+ */
+int
+memstat_sysctl_uma(struct memory_type_list *list, int flags)
+{
+ struct uma_stream_header *ushp;
+ struct uma_type_header *uthp;
+ struct uma_percpu_stat *upsp;
+ struct memory_type *mtp;
+ int count, hint_dontsearch, i, j, maxcpus;
+ char *buffer, *p;
+ size_t size;
+
+ hint_dontsearch = LIST_EMPTY(&list->mtl_list);
+
+ /*
+ * Query the number of CPUs, number of malloc types so that we can
+ * guess an initial buffer size. We loop until we succeed or really
+ * fail. Note that the value of maxcpus we query using sysctl is not
+ * the version we use when processing the real data -- that is read
+ * from the header.
+ */
+retry:
+ size = sizeof(maxcpus);
+ if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) {
+ if (errno == EACCES || errno == EPERM)
+ list->mtl_error = MEMSTAT_ERROR_PERMISSION;
+ else
+ list->mtl_error = MEMSTAT_ERROR_DATAERROR;
+ return (-1);
+ }
+ if (size != sizeof(maxcpus)) {
+ list->mtl_error = MEMSTAT_ERROR_DATAERROR;
+ return (-1);
+ }
+
+ if (maxcpus > MEMSTAT_MAXCPU) {
+ list->mtl_error = MEMSTAT_ERROR_TOOMANYCPUS;
+ return (-1);
+ }
+
+ size = sizeof(count);
+ if (sysctlbyname("vm.zone_count", &count, &size, NULL, 0) < 0) {
+ if (errno == EACCES || errno == EPERM)
+ list->mtl_error = MEMSTAT_ERROR_PERMISSION;
+ else
+ list->mtl_error = MEMSTAT_ERROR_VERSION;
+ return (-1);
+ }
+ if (size != sizeof(count)) {
+ list->mtl_error = MEMSTAT_ERROR_DATAERROR;
+ return (-1);
+ }
+
+ size = sizeof(*uthp) + count * (sizeof(*uthp) + sizeof(*upsp) *
+ maxcpus);
+
+ buffer = malloc(size);
+ if (buffer == NULL) {
+ list->mtl_error = MEMSTAT_ERROR_NOMEMORY;
+ return (-1);
+ }
+
+ if (sysctlbyname("vm.zone_stats", buffer, &size, NULL, 0) < 0) {
+ /*
+ * XXXRW: ENOMEM is an ambiguous return, we should bound the
+ * number of loops, perhaps.
+ */
+ if (errno == ENOMEM) {
+ free(buffer);
+ goto retry;
+ }
+ if (errno == EACCES || errno == EPERM)
+ list->mtl_error = MEMSTAT_ERROR_PERMISSION;
+ else
+ list->mtl_error = MEMSTAT_ERROR_VERSION;
+ free(buffer);
+ return (-1);
+ }
+
+ if (size == 0) {
+ free(buffer);
+ return (0);
+ }
+
+ if (size < sizeof(*ushp)) {
+ list->mtl_error = MEMSTAT_ERROR_VERSION;
+ free(buffer);
+ return (-1);
+ }
+ p = buffer;
+ ushp = (struct uma_stream_header *)p;
+ p += sizeof(*ushp);
+
+ if (ushp->ush_version != UMA_STREAM_VERSION) {
+ list->mtl_error = MEMSTAT_ERROR_VERSION;
+ free(buffer);
+ return (-1);
+ }
+
+ if (ushp->ush_maxcpus > MEMSTAT_MAXCPU) {
+ list->mtl_error = MEMSTAT_ERROR_TOOMANYCPUS;
+ free(buffer);
+ return (-1);
+ }
+
+ /*
+ * For the remainder of this function, we are quite trusting about
+ * the layout of structures and sizes, since we've determined we have
+ * a matching version and acceptable CPU count.
+ */
+ maxcpus = ushp->ush_maxcpus;
+ count = ushp->ush_count;
+ for (i = 0; i < count; i++) {
+ uthp = (struct uma_type_header *)p;
+ p += sizeof(*uthp);
+
+ if (hint_dontsearch == 0) {
+ mtp = memstat_mtl_find(list, ALLOCATOR_UMA,
+ uthp->uth_name);
+ } else
+ mtp = NULL;
+ if (mtp == NULL)
+ mtp = _memstat_mt_allocate(list, ALLOCATOR_UMA,
+ uthp->uth_name);
+ if (mtp == NULL) {
+ memstat_mtl_free(list);
+ free(buffer);
+ list->mtl_error = MEMSTAT_ERROR_NOMEMORY;
+ return (-1);
+ }
+
+ /*
+ * Reset the statistics on a current node.
+ */
+ _memstat_mt_reset_stats(mtp);
+
+ mtp->mt_numallocs = uthp->uth_allocs;
+ mtp->mt_numfrees = uthp->uth_frees;
+ mtp->mt_failures = uthp->uth_fails;
+
+ for (j = 0; j < maxcpus; j++) {
+ upsp = (struct uma_percpu_stat *)p;
+ p += sizeof(*upsp);
+
+ mtp->mt_percpu_cache[j].mtp_free =
+ upsp->ups_cache_free;
+ mtp->mt_free += upsp->ups_cache_free;
+ mtp->mt_numallocs += upsp->ups_allocs;
+ mtp->mt_numfrees += upsp->ups_frees;
+ }
+
+ mtp->mt_size = uthp->uth_size;
+ mtp->mt_memalloced = mtp->mt_numallocs * uthp->uth_size;
+ mtp->mt_memfreed = mtp->mt_numfrees * uthp->uth_size;
+ mtp->mt_bytes = mtp->mt_memalloced - mtp->mt_memfreed;
+ mtp->mt_countlimit = uthp->uth_limit;
+ mtp->mt_byteslimit = uthp->uth_limit * uthp->uth_size;
+
+ mtp->mt_count = mtp->mt_numallocs - mtp->mt_numfrees;
+ mtp->mt_zonefree = uthp->uth_zone_free;
+ mtp->mt_kegfree = uthp->uth_keg_free;
+ mtp->mt_free += mtp->mt_zonefree;
+ }
+
+ free(buffer);
+
+ return (0);
+}