Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 10 Oct 2019 21:20:44 -0700
From:      Cy Schubert <Cy.Schubert@cschubert.com>
To:        Conrad Meyer <cem@FreeBSD.org>
Cc:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   Re: svn commit: r353429 - in head: share/man/man4 sys/kern sys/vm
Message-ID:  <201910110420.x9B4KinB006128@slippy.cwsent.com>
In-Reply-To: <201910110131.x9B1VV1R047982@repo.freebsd.org>
References:  <201910110131.x9B1VV1R047982@repo.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
In message <201910110131.x9B1VV1R047982@repo.freebsd.org>, Conrad Meyer 
writes:
> Author: cem
> Date: Fri Oct 11 01:31:31 2019
> New Revision: 353429
> URL: https://svnweb.freebsd.org/changeset/base/353429
>
> Log:
>   ddb: Add CSV option, sorting to 'show (malloc|uma)'
>   
>   Add /i option for machine-parseable CSV output.  This allows ready copy/
>   pasting into more sophisticated tooling outside of DDB.
>   
>   Add total zone size ("Memory Use") as a new column for UMA.
>   
>   For both, sort the displayed list on size (print the largest zones/types
>   first).  This is handy for quickly diagnosing "where has my memory gone?" a
> t
>   a high level.
>   
>   Submitted by:	Emily Pettigrew <Emily.Pettigrew AT isilon.com> (earlie
> r version)
>   Sponsored by:	Dell EMC Isilon
>
> Modified:
>   head/share/man/man4/ddb.4
>   head/sys/kern/kern_malloc.c
>   head/sys/vm/uma_core.c
>
> Modified: head/share/man/man4/ddb.4
> =============================================================================
> =
> --- head/share/man/man4/ddb.4	Fri Oct 11 00:02:00 2019	(r353428)
> +++ head/share/man/man4/ddb.4	Fri Oct 11 01:31:31 2019	(r353429)
> @@ -60,7 +60,7 @@
>  .\"
>  .\" $FreeBSD$
>  .\"
> -.Dd September 9, 2019
> +.Dd October 10, 2019
>  .Dt DDB 4
>  .Os
>  .Sh NAME
> @@ -806,11 +806,15 @@ is included in the kernel.
>  .It Ic show Cm locktree
>  .\"
>  .Pp
> -.It Ic show Cm malloc
> +.It Ic show Cm malloc Ns Op Li / Ns Cm i
>  Prints
>  .Xr malloc 9
>  memory allocator statistics.
> -The output format is as follows:
> +If the
> +.Cm i
> +modifier is specified, format output as machine-parseable comma-separated
> +values ("CSV").
> +The output columns are as follows:
>  .Pp
>  .Bl -tag -compact -offset indent -width "Requests"
>  .It Ic Type
> @@ -1076,11 +1080,15 @@ Currently, those are:
>  .Xr rmlock 9 .
>  .\"
>  .Pp
> -.It Ic show Cm uma
> +.It Ic show Cm uma Ns Op Li / Ns Cm i
>  Show UMA allocator statistics.
> -Output consists five columns:
> +If the
> +.Cm i
> +modifier is specified, format output as machine-parseable comma-separated
> +values ("CSV").
> +The output contains the following columns:
>  .Pp
> -.Bl -tag -compact -offset indent -width "Requests"
> +.Bl -tag -compact -offset indent -width "Total Mem"
>  .It Cm "Zone"
>  Name of the UMA zone.
>  The same string that was passed to
> @@ -1094,9 +1102,18 @@ Number of slabs being currently used.
>  Number of free slabs within the UMA zone.
>  .It Cm "Requests"
>  Number of allocations requests to the given zone.
> +.It Cm "Total Mem"
> +Total memory in use (either allocated or free) by a zone, in bytes.
> +.It Cm "XFree"
> +Number of free slabs within the UMA zone that were freed on a different NUMA
> +domain than allocated.
> +(The count in the
> +.Cm "Free"
> +column is inclusive of
> +.Cm "XFree" . )
>  .El
>  .Pp
> -The very same information might be gathered in the userspace
> +The same information might be gathered in the userspace
>  with the help of
>  .Dq Nm vmstat Fl z .
>  .\"
>
> Modified: head/sys/kern/kern_malloc.c
> =============================================================================
> =
> --- head/sys/kern/kern_malloc.c	Fri Oct 11 00:02:00 2019	(r35342
> 8)
> +++ head/sys/kern/kern_malloc.c	Fri Oct 11 01:31:31 2019	(r35342
> 9)
> @@ -1205,35 +1205,90 @@ restart:
>  }
>  
>  #ifdef DDB
> +static int64_t
> +get_malloc_stats(const struct malloc_type_internal *mtip, uint64_t *allocs,
> +    uint64_t *inuse)
> +{
> +	const struct malloc_type_stats *mtsp;
> +	uint64_t frees, alloced, freed;
> +	int i;
> +
> +	*allocs = 0;
> +	frees = 0;
> +	alloced = 0;
> +	freed = 0;
> +	for (i = 0; i <= mp_maxid; i++) {
> +		mtsp = zpcpu_get_cpu(mtip->mti_stats, i);
> +
> +		*allocs += mtsp->mts_numallocs;
> +		frees += mtsp->mts_numfrees;
> +		alloced += mtsp->mts_memalloced;
> +		freed += mtsp->mts_memfreed;
> +	}
> +	*inuse = *allocs - frees;
> +	return (alloced - freed);
> +}
> +
>  DB_SHOW_COMMAND(malloc, db_show_malloc)
>  {
> -	struct malloc_type_internal *mtip;
> -	struct malloc_type_stats *mtsp;
> +	const char *fmt_hdr, *fmt_entry;
>  	struct malloc_type *mtp;
> -	uint64_t allocs, frees;
> -	uint64_t alloced, freed;
> -	int i;
> +	uint64_t allocs, inuse;
> +	int64_t size;
> +	/* variables for sorting */
> +	struct malloc_type *last_mtype, *cur_mtype;
> +	int64_t cur_size, last_size;
> +	int ties;
>  
> -	db_printf("%18s %12s  %12s %12s\n", "Type", "InUse", "MemUse",
> -	    "Requests");
> -	for (mtp = kmemstatistics; mtp != NULL; mtp = mtp->ks_next) {
> -		mtip = (struct malloc_type_internal *)mtp->ks_handle;
> -		allocs = 0;
> -		frees = 0;
> -		alloced = 0;
> -		freed = 0;
> -		for (i = 0; i <= mp_maxid; i++) {
> -			mtsp = zpcpu_get_cpu(mtip->mti_stats, i);
> -			allocs += mtsp->mts_numallocs;
> -			frees += mtsp->mts_numfrees;
> -			alloced += mtsp->mts_memalloced;
> -			freed += mtsp->mts_memfreed;
> +	if (modif[0] == 'i') {
> +		fmt_hdr = "%s,%s,%s,%s\n";
> +		fmt_entry = "\"%s\",%ju,%jdK,%ju\n";
> +	} else {
> +		fmt_hdr = "%18s %12s  %12s %12s\n";
> +		fmt_entry = "%18s %12ju %12jdK %12ju\n";
> +	}
> +
> +	db_printf(fmt_hdr, "Type", "InUse", "MemUse", "Requests");
> +
> +	/* Select sort, largest size first. */
> +	last_mtype = NULL;
> +	last_size = INT64_MAX;
> +	for (;;) {
> +		cur_mtype = NULL;
> +		cur_size = -1;
> +		ties = 0;
> +
> +		for (mtp = kmemstatistics; mtp != NULL; mtp = mtp->ks_next) {
> +			/*
> +			 * In the case of size ties, print out mtypes
> +			 * in the order they are encountered.  That is,
> +			 * when we encounter the most recently output
> +			 * mtype, we have already printed all preceding
> +			 * ties, and we must print all following ties.
> +			 */
> +			if (mtp == last_mtype) {
> +				ties = 1;
> +				continue;
> +			}
> +			size = get_malloc_stats(mtp->ks_handle, &allocs,
> +			    &inuse);
> +			if (size > cur_size && size < last_size + ties) {
> +				cur_size = size;
> +				cur_mtype = mtp;
> +			}
>  		}
> -		db_printf("%18s %12ju %12juK %12ju\n",
> -		    mtp->ks_shortdesc, allocs - frees,
> -		    (alloced - freed + 1023) / 1024, allocs);
> +		if (cur_mtype == NULL)
> +			break;
> +
> +		size = get_malloc_stats(cur_mtype->ks_handle, &allocs, &inuse);
> +		db_printf(fmt_entry, cur_mtype->ks_shortdesc, inuse,
> +		    howmany(size, 1024), allocs);
> +
>  		if (db_pager_quit)
>  			break;
> +
> +		last_mtype = cur_mtype;
> +		last_size = cur_size;
>  	}
>  }
>  
>
> Modified: head/sys/vm/uma_core.c
> =============================================================================
> =
> --- head/sys/vm/uma_core.c	Fri Oct 11 00:02:00 2019	(r353428)
> +++ head/sys/vm/uma_core.c	Fri Oct 11 01:31:31 2019	(r353429)
> @@ -4341,39 +4341,100 @@ uma_dbg_free(uma_zone_t zone, uma_slab_t slab, void 
> *i
>  #endif /* INVARIANTS */
>  
>  #ifdef DDB
> +static int64_t
> +get_uma_stats(uma_keg_t kz, uma_zone_t z, uint64_t *allocs, uint64_t *used,
> +    uint64_t *sleeps, uint64_t *xdomain, long *cachefree)

xdomain and cachefree are reversed by callers of this function. Probably 
simpler to change the definition here than the two use instances below.

> +{
> +	uint64_t frees;
> +	int i;
> +
> +	if (kz->uk_flags & UMA_ZFLAG_INTERNAL) {
> +		*allocs = counter_u64_fetch(z->uz_allocs);
> +		frees = counter_u64_fetch(z->uz_frees);
> +		*sleeps = z->uz_sleeps;
> +		*cachefree = 0;
> +		*xdomain = 0;
> +	} else
> +		uma_zone_sumstat(z, cachefree, allocs, &frees, sleeps,
> +		    xdomain);
> +	if (!((z->uz_flags & UMA_ZONE_SECONDARY) &&
> +	    (LIST_FIRST(&kz->uk_zones) != z)))
> +		*cachefree += kz->uk_free;
> +	for (i = 0; i < vm_ndomains; i++)
> +		*cachefree += z->uz_domain[i].uzd_nitems;
> +	*used = *allocs - frees;
> +	return (((int64_t)*used + *cachefree) * kz->uk_size);
> +}
> +
>  DB_SHOW_COMMAND(uma, db_show_uma)
>  {
> +	const char *fmt_hdr, *fmt_entry;
>  	uma_keg_t kz;
>  	uma_zone_t z;
> -	uint64_t allocs, frees, sleeps, xdomain;
> +	uint64_t allocs, used, sleeps, xdomain;
>  	long cachefree;
> -	int i;
> +	/* variables for sorting */
> +	uma_keg_t cur_keg;
> +	uma_zone_t cur_zone, last_zone;
> +	int64_t cur_size, last_size, size;
> +	int ties;
>  
> -	db_printf("%18s %8s %8s %8s %12s %8s %8s %8s\n", "Zone", "Size", "Used"
> ,
> -	    "Free", "Requests", "Sleeps", "Bucket", "XFree");
> -	LIST_FOREACH(kz, &uma_kegs, uk_link) {
> -		LIST_FOREACH(z, &kz->uk_zones, uz_link) {
> -			if (kz->uk_flags & UMA_ZFLAG_INTERNAL) {
> -				allocs = counter_u64_fetch(z->uz_allocs);
> -				frees = counter_u64_fetch(z->uz_frees);
> -				sleeps = z->uz_sleeps;
> -				cachefree = 0;
> -			} else
> -				uma_zone_sumstat(z, &cachefree, &allocs,
> -				    &frees, &sleeps, &xdomain);
> -			if (!((z->uz_flags & UMA_ZONE_SECONDARY) &&
> -			    (LIST_FIRST(&kz->uk_zones) != z)))
> -				cachefree += kz->uk_free;
> -			for (i = 0; i < vm_ndomains; i++)
> -				cachefree += z->uz_domain[i].uzd_nitems;
> +	/* /i option produces machine-parseable CSV output */
> +	if (modif[0] == 'i') {
> +		fmt_hdr = "%s,%s,%s,%s,%s,%s,%s,%s,%s\n";
> +		fmt_entry = "\"%s\",%ju,%jd,%ld,%ju,%ju,%u,%jd,%ju\n";
> +	} else {
> +		fmt_hdr = "%18s %6s %7s %7s %11s %7s %7s %10s %8s\n";
> +		fmt_entry = "%18s %6ju %7jd %7ld %11ju %7ju %7u %10jd %8ju\n";
> +	}
>  
> -			db_printf("%18s %8ju %8jd %8ld %12ju %8ju %8u %8ju\n",
> -			    z->uz_name, (uintmax_t)kz->uk_size,
> -			    (intmax_t)(allocs - frees), cachefree,
> -			    (uintmax_t)allocs, sleeps, z->uz_count, xdomain);
> -			if (db_pager_quit)
> -				return;
> +	db_printf(fmt_hdr, "Zone", "Size", "Used", "Free", "Requests",
> +	    "Sleeps", "Bucket", "Total Mem", "XFree");
> +
> +	/* Sort the zones with largest size first. */
> +	last_zone = NULL;
> +	last_size = INT64_MAX;
> +	for (;;) {
> +		cur_zone = NULL;
> +		cur_size = -1;
> +		ties = 0;
> +		LIST_FOREACH(kz, &uma_kegs, uk_link) {
> +			LIST_FOREACH(z, &kz->uk_zones, uz_link) {
> +				/*
> +				 * In the case of size ties, print out zones
> +				 * in the order they are encountered.  That is,
> +				 * when we encounter the most recently output
> +				 * zone, we have already printed all preceding
> +				 * ties, and we must print all following ties.
> +				 */
> +				if (z == last_zone) {
> +					ties = 1;
> +					continue;
> +				}
> +				size = get_uma_stats(kz, z, &allocs, &used,
> +				    &sleeps, &cachefree, &xdomain);

cachefree and xdomain are reversed from the function header above.

> +				if (size > cur_size && size < last_size + ties)
> +				{
> +					cur_size = size;
> +					cur_zone = z;
> +					cur_keg = kz;
> +				}
> +			}
>  		}
> +		if (cur_zone == NULL)
> +			break;
> +
> +		size = get_uma_stats(cur_keg, cur_zone, &allocs, &used,
> +		    &sleeps, &cachefree, &xdomain);

cachefree and xdomain are reversed from the function header above.

> +		db_printf(fmt_entry, cur_zone->uz_name,
> +		    (uintmax_t)cur_keg->uk_size, (intmax_t)used, cachefree,
> +		    (uintmax_t)allocs, (uintmax_t)sleeps,
> +		    (unsigned)cur_zone->uz_count, (intmax_t)size, xdomain);
> +
> +		if (db_pager_quit)
> +			return;
> +		last_zone = cur_zone;
> +		last_size = cur_size;
>  	}
>  }
>  
>



-- 
Cheers,
Cy Schubert <Cy.Schubert@cschubert.com>
FreeBSD UNIX:  <cy@FreeBSD.org>   Web:  http://www.FreeBSD.org

	The need of the many outweighs the greed of the few.





Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201910110420.x9B4KinB006128>