Memory Context Info dump

Started by Rajeev rastogiover 10 years ago3 messages
#1Rajeev rastogi
rajeev.rastogi@huawei.com
2 attachment(s)

In our customer environment as well as during development, we have observed that many time we need to get details of memory used by each contexts in order to analyze the memory consumption/leak.
So I would like to propose one contrib function interface, which will dump the whole memory context into a file.

One of the major infrastructure i.e. the function MemoryContextStats (which dump all memory context details on the console) is already there in PG but it is being automatically called for scenarios
such as "system is out of memory".

So Now:

1. Create one contrib function dump_memctxt_info, which is when executed, it will call the existing function MemoryContextStats.

2. The file to dump will be created in the following format:

context_<process id>_<timestamp>.dump

3. The file handler will be passed to MemoryContextStats.

4. If the parameter to MemoryContextStats is NULL (All existing calls will pass NULL), then it will behave as earlier i.e. dump on console.

5. The output format of the context will be same as earlier.

6. One sample file is attached.

Use-case:
In order to check the memory leak, this contrib function can be called before and after execution of query. Then comparison of these two dump will be helpful to further analyze.

Attached is the patch. Please provide your opinion. If it is OK, I will add it to next commitFest.

Thanks and Regards,
Kumar Rajeev Rastogi

Attachments:

memory_ctxt_dumpv1.patchapplication/octet-stream; name=memory_ctxt_dumpv1.patchDownload
*** a/contrib/Makefile
--- b/contrib/Makefile
***************
*** 47,53 **** SUBDIRS = \
  		tsm_system_time \
  		tsearch2	\
  		unaccent	\
! 		vacuumlo
  
  ifeq ($(with_openssl),yes)
  SUBDIRS += sslinfo
--- 47,54 ----
  		tsm_system_time \
  		tsearch2	\
  		unaccent	\
! 		vacuumlo	\
! 		memdump
  
  ifeq ($(with_openssl),yes)
  SUBDIRS += sslinfo
*** /dev/null
--- b/contrib/memdump/Makefile
***************
*** 0 ****
--- 1,21 ----
+ # contrib/memdump/Makefile
+ 
+ MODULES = memdump
+ 
+ EXTENSION = memdump
+ DATA = memdump--1.0.sql memdump--unpackaged--1.0.sql
+ 
+ REGRESS = memdump
+ 
+ LDFLAGS_SL += $(filter -lm, $(LIBS))
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/memdump
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
*** /dev/null
--- b/contrib/memdump/memdump--1.0.sql
***************
*** 0 ****
--- 1,9 ----
+ /* contrib/memdump/memdump--1.0.sql */
+ 
+ -- complain if script is sourced in psql, rather than via CREATE EXTENSION
+ \echo Use "CREATE EXTENSION memdump" to load this file. \quit
+ 
+ CREATE FUNCTION dump_memctxt_info() 
+ RETURNS void
+ AS 'MODULE_PATHNAME','dump_memctxt_info'
+ LANGUAGE C VOLATILE STRICT; 
*** /dev/null
--- b/contrib/memdump/memdump--unpackaged--1.0.sql
***************
*** 0 ****
--- 1,7 ----
+ /* contrib/memdump/memdump--unpackaged--1.0.sql */
+ 
+ -- complain if script is sourced in psql, rather than via CREATE EXTENSION
+ \echo Use "CREATE EXTENSION memdump" to load this file. \quit
+ 
+ ALTER EXTENSION memdump ADD function dump_memctxt_info(void);
+ 
*** /dev/null
--- b/contrib/memdump/memdump.c
***************
*** 0 ****
--- 1,58 ----
+ /*-------------------------------------------------------------------------
+  *
+  * misc.c
+  *
+  *
+  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  contrib/memdump/memdump.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include "postgres.h"
+ 
+ #include "utils/memutils.h"
+ #include "storage/fd.h"
+ #include "pgtime.h"
+ #include <time.h>
+ #include "utils/builtins.h"
+ #include "pgtime.h"
+ 
+ PG_MODULE_MAGIC;
+ 
+ extern Datum dump_memctxt_info(PG_FUNCTION_ARGS);
+ PG_FUNCTION_INFO_V1(dump_memctxt_info);
+ 
+ Datum
+ dump_memctxt_info(PG_FUNCTION_ARGS)
+ {
+ 	FILE *file = NULL;
+ 	char filename[MAXPGPATH];
+ 	int		   len;
+ 	pg_time_t tnow = (pg_time_t) time(NULL);
+ 	struct pg_tm *tz;
+ 
+ 	memset(filename, 0, MAXPGPATH);
+ 
+ 	/*
+ 	 * For the file to dump memory context details in the format of
+ 	 * "context_<process id>_<timestamp>.dump".
+ 	 */
+ 	tz = pg_localtime(&tnow, pg_get_timezone());
+ 
+ 	snprintf(filename, MAXPGPATH, "context_%d", (int)getpid());
+ 	len = strlen(filename);
+ 
+ 	pg_strftime(filename + len, MAXPGPATH - len, "_%Y-%m-%d_%H%M%S.dump", tz);
+ 
+ 	file = AllocateFile(filename,"w");
+ 
+ 	/* Do the actual work*/
+ 	MemoryContextStats(TopMemoryContext, file);
+ 
+ 	return 0;
+ }
*** /dev/null
--- b/contrib/memdump/memdump.control
***************
*** 0 ****
--- 1,5 ----
+ # memdump extension
+ comment = 'functions that dump all memory context'
+ default_version = '1.0'
+ module_pathname = '$libdir/memdump'
+ relocatable = true
*** a/src/backend/utils/misc/timeout.c
--- b/src/backend/utils/misc/timeout.c
***************
*** 21,27 ****
  #include "utils/timeout.h"
  #include "utils/timestamp.h"
  
- 
  /* Data about any one timeout reason */
  typedef struct timeout_params
  {
--- 21,26 ----
*** a/src/backend/utils/mmgr/aset.c
--- b/src/backend/utils/mmgr/aset.c
***************
*** 254,260 **** static void AllocSetDelete(MemoryContext context);
  static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer);
  static bool AllocSetIsEmpty(MemoryContext context);
  static void AllocSetStats(MemoryContext context, int level, bool print,
! 			  MemoryContextCounters *totals);
  
  #ifdef MEMORY_CONTEXT_CHECKING
  static void AllocSetCheck(MemoryContext context);
--- 254,260 ----
  static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer);
  static bool AllocSetIsEmpty(MemoryContext context);
  static void AllocSetStats(MemoryContext context, int level, bool print,
! 			  MemoryContextCounters *totals, FILE *file);
  
  #ifdef MEMORY_CONTEXT_CHECKING
  static void AllocSetCheck(MemoryContext context);
***************
*** 499,505 **** AllocSetContextCreate(MemoryContext parent,
  		block = (AllocBlock) malloc(blksize);
  		if (block == NULL)
  		{
! 			MemoryContextStats(TopMemoryContext);
  			ereport(ERROR,
  					(errcode(ERRCODE_OUT_OF_MEMORY),
  					 errmsg("out of memory"),
--- 499,505 ----
  		block = (AllocBlock) malloc(blksize);
  		if (block == NULL)
  		{
! 			MemoryContextStats(TopMemoryContext, NULL);
  			ereport(ERROR,
  					(errcode(ERRCODE_OUT_OF_MEMORY),
  					 errmsg("out of memory"),
***************
*** 1237,1243 **** AllocSetIsEmpty(MemoryContext context)
   */
  static void
  AllocSetStats(MemoryContext context, int level, bool print,
! 			  MemoryContextCounters *totals)
  {
  	AllocSet	set = (AllocSet) context;
  	Size		nblocks = 0;
--- 1237,1243 ----
   */
  static void
  AllocSetStats(MemoryContext context, int level, bool print,
! 			  MemoryContextCounters *totals, FILE *file)
  {
  	AllocSet	set = (AllocSet) context;
  	Size		nblocks = 0;
***************
*** 1270,1277 **** AllocSetStats(MemoryContext context, int level, bool print,
  		int			i;
  
  		for (i = 0; i < level; i++)
! 			fprintf(stderr, "  ");
! 		fprintf(stderr,
  			"%s: %zu total in %zd blocks; %zu free (%zd chunks); %zu used\n",
  				set->header.name, totalspace, nblocks, freespace, freechunks,
  				totalspace - freespace);
--- 1270,1277 ----
  		int			i;
  
  		for (i = 0; i < level; i++)
! 			fprintf(file, "  ");
! 		fprintf(file,
  			"%s: %zu total in %zd blocks; %zu free (%zd chunks); %zu used\n",
  				set->header.name, totalspace, nblocks, freespace, freechunks,
  				totalspace - freespace);
*** a/src/backend/utils/mmgr/mcxt.c
--- b/src/backend/utils/mmgr/mcxt.c
***************
*** 54,60 **** MemoryContext PortalContext = NULL;
  static void MemoryContextCallResetCallbacks(MemoryContext context);
  static void MemoryContextStatsInternal(MemoryContext context, int level,
  						   bool print, int max_children,
! 						   MemoryContextCounters *totals);
  
  /*
   * You should not do memory allocations within a critical section, because
--- 54,61 ----
  static void MemoryContextCallResetCallbacks(MemoryContext context);
  static void MemoryContextStatsInternal(MemoryContext context, int level,
  						   bool print, int max_children,
! 						   MemoryContextCounters *totals,
! 						   FILE *file);
  
  /*
   * You should not do memory allocations within a critical section, because
***************
*** 484,493 **** MemoryContextIsEmpty(MemoryContext context)
   * The statistics are sent to stderr.
   */
  void
! MemoryContextStats(MemoryContext context)
  {
  	/* A hard-wired limit on the number of children is usually good enough */
! 	MemoryContextStatsDetail(context, 100);
  }
  
  /*
--- 485,494 ----
   * The statistics are sent to stderr.
   */
  void
! MemoryContextStats(MemoryContext context, FILE *file)
  {
  	/* A hard-wired limit on the number of children is usually good enough */
! 	MemoryContextStatsDetail(context, 100, file);
  }
  
  /*
***************
*** 496,510 **** MemoryContextStats(MemoryContext context)
   * Entry point for use if you want to vary the number of child contexts shown.
   */
  void
! MemoryContextStatsDetail(MemoryContext context, int max_children)
  {
  	MemoryContextCounters grand_totals;
  
  	memset(&grand_totals, 0, sizeof(grand_totals));
  
! 	MemoryContextStatsInternal(context, 0, true, max_children, &grand_totals);
  
! 	fprintf(stderr,
  	"Grand total: %zu bytes in %zd blocks; %zu free (%zd chunks); %zu used\n",
  			grand_totals.totalspace, grand_totals.nblocks,
  			grand_totals.freespace, grand_totals.freechunks,
--- 497,515 ----
   * Entry point for use if you want to vary the number of child contexts shown.
   */
  void
! MemoryContextStatsDetail(MemoryContext context, int max_children, FILE *file)
  {
  	MemoryContextCounters grand_totals;
  
  	memset(&grand_totals, 0, sizeof(grand_totals));
  
! 	/* Default case will report to standard output*/
! 	if (!file)
! 		file = stderr;
  
! 	MemoryContextStatsInternal(context, 0, true, max_children, &grand_totals, file);
! 
! 	fprintf(file,
  	"Grand total: %zu bytes in %zd blocks; %zu free (%zd chunks); %zu used\n",
  			grand_totals.totalspace, grand_totals.nblocks,
  			grand_totals.freespace, grand_totals.freechunks,
***************
*** 521,527 **** MemoryContextStatsDetail(MemoryContext context, int max_children)
  static void
  MemoryContextStatsInternal(MemoryContext context, int level,
  						   bool print, int max_children,
! 						   MemoryContextCounters *totals)
  {
  	MemoryContextCounters local_totals;
  	MemoryContext child;
--- 526,533 ----
  static void
  MemoryContextStatsInternal(MemoryContext context, int level,
  						   bool print, int max_children,
! 						   MemoryContextCounters *totals,
! 						   FILE *file)
  {
  	MemoryContextCounters local_totals;
  	MemoryContext child;
***************
*** 530,536 **** MemoryContextStatsInternal(MemoryContext context, int level,
  	AssertArg(MemoryContextIsValid(context));
  
  	/* Examine the context itself */
! 	(*context->methods->stats) (context, level, print, totals);
  
  	/*
  	 * Examine children.  If there are more than max_children of them, we do
--- 536,542 ----
  	AssertArg(MemoryContextIsValid(context));
  
  	/* Examine the context itself */
! 	(*context->methods->stats) (context, level, print, totals, file);
  
  	/*
  	 * Examine children.  If there are more than max_children of them, we do
***************
*** 545,555 **** MemoryContextStatsInternal(MemoryContext context, int level,
  		if (ichild < max_children)
  			MemoryContextStatsInternal(child, level + 1,
  									   print, max_children,
! 									   totals);
  		else
  			MemoryContextStatsInternal(child, level + 1,
  									   false, max_children,
! 									   &local_totals);
  	}
  
  	/* Deal with excess children */
--- 551,563 ----
  		if (ichild < max_children)
  			MemoryContextStatsInternal(child, level + 1,
  									   print, max_children,
! 									   totals,
! 									   file);
  		else
  			MemoryContextStatsInternal(child, level + 1,
  									   false, max_children,
! 									   &local_totals,
! 									   file);
  	}
  
  	/* Deal with excess children */
***************
*** 560,567 **** MemoryContextStatsInternal(MemoryContext context, int level,
  			int			i;
  
  			for (i = 0; i <= level; i++)
! 				fprintf(stderr, "  ");
! 			fprintf(stderr,
  					"%d more child contexts containing %zu total in %zd blocks; %zu free (%zd chunks); %zu used\n",
  					ichild - max_children,
  					local_totals.totalspace,
--- 568,575 ----
  			int			i;
  
  			for (i = 0; i <= level; i++)
! 				fprintf(file, "  ");
! 			fprintf(file,
  					"%d more child contexts containing %zu total in %zd blocks; %zu free (%zd chunks); %zu used\n",
  					ichild - max_children,
  					local_totals.totalspace,
***************
*** 762,768 **** MemoryContextAlloc(MemoryContext context, Size size)
  	ret = (*context->methods->alloc) (context, size);
  	if (ret == NULL)
  	{
! 		MemoryContextStats(TopMemoryContext);
  		ereport(ERROR,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of memory"),
--- 770,776 ----
  	ret = (*context->methods->alloc) (context, size);
  	if (ret == NULL)
  	{
! 		MemoryContextStats(TopMemoryContext, NULL);
  		ereport(ERROR,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of memory"),
***************
*** 797,803 **** MemoryContextAllocZero(MemoryContext context, Size size)
  	ret = (*context->methods->alloc) (context, size);
  	if (ret == NULL)
  	{
! 		MemoryContextStats(TopMemoryContext);
  		ereport(ERROR,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of memory"),
--- 805,811 ----
  	ret = (*context->methods->alloc) (context, size);
  	if (ret == NULL)
  	{
! 		MemoryContextStats(TopMemoryContext, NULL);
  		ereport(ERROR,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of memory"),
***************
*** 834,840 **** MemoryContextAllocZeroAligned(MemoryContext context, Size size)
  	ret = (*context->methods->alloc) (context, size);
  	if (ret == NULL)
  	{
! 		MemoryContextStats(TopMemoryContext);
  		ereport(ERROR,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of memory"),
--- 842,848 ----
  	ret = (*context->methods->alloc) (context, size);
  	if (ret == NULL)
  	{
! 		MemoryContextStats(TopMemoryContext, NULL);
  		ereport(ERROR,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of memory"),
***************
*** 871,877 **** MemoryContextAllocExtended(MemoryContext context, Size size, int flags)
  	{
  		if ((flags & MCXT_ALLOC_NO_OOM) == 0)
  		{
! 			MemoryContextStats(TopMemoryContext);
  			ereport(ERROR,
  					(errcode(ERRCODE_OUT_OF_MEMORY),
  					 errmsg("out of memory"),
--- 879,885 ----
  	{
  		if ((flags & MCXT_ALLOC_NO_OOM) == 0)
  		{
! 			MemoryContextStats(TopMemoryContext, NULL);
  			ereport(ERROR,
  					(errcode(ERRCODE_OUT_OF_MEMORY),
  					 errmsg("out of memory"),
***************
*** 905,911 **** palloc(Size size)
  	ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size);
  	if (ret == NULL)
  	{
! 		MemoryContextStats(TopMemoryContext);
  		ereport(ERROR,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of memory"),
--- 913,919 ----
  	ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size);
  	if (ret == NULL)
  	{
! 		MemoryContextStats(TopMemoryContext, NULL);
  		ereport(ERROR,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of memory"),
***************
*** 934,940 **** palloc0(Size size)
  	ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size);
  	if (ret == NULL)
  	{
! 		MemoryContextStats(TopMemoryContext);
  		ereport(ERROR,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of memory"),
--- 942,948 ----
  	ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size);
  	if (ret == NULL)
  	{
! 		MemoryContextStats(TopMemoryContext, NULL);
  		ereport(ERROR,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of memory"),
***************
*** 968,974 **** palloc_extended(Size size, int flags)
  	{
  		if ((flags & MCXT_ALLOC_NO_OOM) == 0)
  		{
! 			MemoryContextStats(TopMemoryContext);
  			ereport(ERROR,
  					(errcode(ERRCODE_OUT_OF_MEMORY),
  					 errmsg("out of memory"),
--- 976,982 ----
  	{
  		if ((flags & MCXT_ALLOC_NO_OOM) == 0)
  		{
! 			MemoryContextStats(TopMemoryContext, NULL);
  			ereport(ERROR,
  					(errcode(ERRCODE_OUT_OF_MEMORY),
  					 errmsg("out of memory"),
***************
*** 1081,1087 **** MemoryContextAllocHuge(MemoryContext context, Size size)
  	ret = (*context->methods->alloc) (context, size);
  	if (ret == NULL)
  	{
! 		MemoryContextStats(TopMemoryContext);
  		ereport(ERROR,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of memory"),
--- 1089,1095 ----
  	ret = (*context->methods->alloc) (context, size);
  	if (ret == NULL)
  	{
! 		MemoryContextStats(TopMemoryContext, NULL);
  		ereport(ERROR,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of memory"),
*** a/src/include/nodes/memnodes.h
--- b/src/include/nodes/memnodes.h
***************
*** 63,69 **** typedef struct MemoryContextMethods
  	Size		(*get_chunk_space) (MemoryContext context, void *pointer);
  	bool		(*is_empty) (MemoryContext context);
  	void		(*stats) (MemoryContext context, int level, bool print,
! 									  MemoryContextCounters *totals);
  #ifdef MEMORY_CONTEXT_CHECKING
  	void		(*check) (MemoryContext context);
  #endif
--- 63,69 ----
  	Size		(*get_chunk_space) (MemoryContext context, void *pointer);
  	bool		(*is_empty) (MemoryContext context);
  	void		(*stats) (MemoryContext context, int level, bool print,
! 								  MemoryContextCounters *totals, FILE *file);
  #ifdef MEMORY_CONTEXT_CHECKING
  	void		(*check) (MemoryContext context);
  #endif
*** a/src/include/pgtime.h
--- b/src/include/pgtime.h
***************
*** 44,50 **** typedef struct pg_tzenum pg_tzenum;
  #define TZ_STRLEN_MAX 255
  
  /* these functions are in localtime.c */
- 
  extern struct pg_tm *pg_localtime(const pg_time_t *timep, const pg_tz *tz);
  extern struct pg_tm *pg_gmtime(const pg_time_t *timep);
  extern int pg_next_dst_boundary(const pg_time_t *timep,
--- 44,49 ----
***************
*** 74,79 **** extern pg_tz *session_timezone;
--- 73,80 ----
  extern pg_tz *log_timezone;
  
  extern void pg_timezone_initialize(void);
+ extern pg_tz *		pg_get_timezone(void);
+ 
  extern pg_tz *pg_tzset(const char *tzname);
  extern pg_tz *pg_tzset_offset(long gmtoffset);
  
*** a/src/include/utils/memutils.h
--- b/src/include/utils/memutils.h
***************
*** 103,110 **** extern Size GetMemoryChunkSpace(void *pointer);
  extern MemoryContext GetMemoryChunkContext(void *pointer);
  extern MemoryContext MemoryContextGetParent(MemoryContext context);
  extern bool MemoryContextIsEmpty(MemoryContext context);
! extern void MemoryContextStats(MemoryContext context);
! extern void MemoryContextStatsDetail(MemoryContext context, int max_children);
  extern void MemoryContextAllowInCriticalSection(MemoryContext context,
  									bool allow);
  
--- 103,110 ----
  extern MemoryContext GetMemoryChunkContext(void *pointer);
  extern MemoryContext MemoryContextGetParent(MemoryContext context);
  extern bool MemoryContextIsEmpty(MemoryContext context);
! extern void MemoryContextStats(MemoryContext context, FILE *file);
! extern void MemoryContextStatsDetail(MemoryContext context, int max_children, FILE *file);
  extern void MemoryContextAllowInCriticalSection(MemoryContext context,
  									bool allow);
  
*** a/src/timezone/localtime.c
--- b/src/timezone/localtime.c
***************
*** 1094,1100 **** pg_localtime(const pg_time_t *timep, const pg_tz *tz)
  	return localsub(timep, 0L, &tm, tz);
  }
  
- 
  /*
   * gmtsub is to gmtime as localsub is to localtime.
   */
--- 1094,1099 ----
*** a/src/timezone/pgtz.c
--- b/src/timezone/pgtz.c
***************
*** 355,360 **** pg_timezone_initialize(void)
--- 355,365 ----
  	log_timezone = session_timezone;
  }
  
+ pg_tz *
+ pg_get_timezone(void)
+ {
+ 	return log_timezone;
+ }
  
  /*
   * Functions to enumerate available timezones
context_13192_2015-09-08_181236.dumpapplication/octet-stream; name=context_13192_2015-09-08_181236.dumpDownload
#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Rajeev rastogi (#1)
Re: Memory Context Info dump

Rajeev rastogi <rajeev.rastogi@huawei.com> writes:

In our customer environment as well as during development, we have observed that many time we need to get details of memory used by each contexts in order to analyze the memory consumption/leak.
So I would like to propose one contrib function interface, which will dump the whole memory context into a file.

Under what circumstances would you invoke this? Not when you were already
out of memory.

Use-case:
In order to check the memory leak, this contrib function can be called before and after execution of query. Then comparison of these two dump will be helpful to further analyze.

That's pretty uncompelling, considering that intra-query memory leaks are
one of the major concerns. The evidence would be gone before you could
capture it.

1. Create one contrib function dump_memctxt_info, which is when executed, it will call the existing function MemoryContextStats.
2. The file to dump will be created in the following format:
context_<process id>_<timestamp>.dump

Dumping to a file seems like probably the second least friendly API you
could devise ... although I'm not sure how to do better offhand. The
existing code is made to not need to allocate any more memory, and
anything that could return data to SQL would have to do that.

I don't disagree that it would be useful to have better info available in
this area, but this patch seems like a quick hack rather than a useful
tool.

As an example of potentially-more-useful aids, I'm wondering about
tracking the high-water memory consumption of each memory context.
(This probably wouldn't be terribly expensive if it were done at the
granularity of malloc requests rather than individual pallocs.)
Then perhaps something to log a context's peak usage at context
destruction time, if it exceeds some threshold or other.

Our past discussions about tracking total usage in a context tree
would be relevant here too.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Greg Stark
stark@mit.edu
In reply to: Tom Lane (#2)
Re: Memory Context Info dump

On Tue, Sep 8, 2015 at 4:30 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

As an example of potentially-more-useful aids, I'm wondering about
tracking the high-water memory consumption of each memory context.
(This probably wouldn't be terribly expensive if it were done at the
granularity of malloc requests rather than individual pallocs.)
Then perhaps something to log a context's peak usage at context
destruction time, if it exceeds some threshold or other.

What I've been itching for in my testing is a way to save the memory
context stats, and then later print the difference between them. That
would let me see where memory was actually going to.

There are some cases where that could be tricky. If a context is
destroyed and a new one by the same name is created is it the same
context or a different one? If there are a bunch with the same name
and later there are a bunch but some are the same context and some are
new do we try to match them up, maybe sort by size?

But it would be nice to be able to see right away what context the
extra memory was allocated in between two points.

--
greg

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers