![]() |
PMMM - The Poor Man's Memory Monitor | |
---|---|---|
was written by Burton M. Strauss III (BStrauss@acm.org) |
PMMM is based in large part on a concept and code from Luca Deri's leaks.c, part of NTop.
This is version 0.1.2, but not expected to change much
/* * Copyright (C) 2002 Burton M. Strauss III* portions Copyright (C) 1998-2001 Luca Deri * Portions by Stefano Suin * * http://www.ntop.org/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Variables and functions exposed here may be used by any program which uses the functions of PMMM, the Poor Man's Memory Monitor. Information private to PMMM is defined in pmmm.c */ /* To use PMMM, all you need to do is include pmmm.h - the defines here point your references to PMMM's version of various allocation functions PMMM is auto-initializing. With no parameters, it functions with reasonable default options. You may CHOOSE to set run-time parameters using environment variables or the function pmmm_setParameters You may CHOOSE to access data exported out of PMMM for reporting You may CHOOSE to use the freeAll call at the end of your program You may CHOOSE to track memory allocated through other means (e.g. various library calls, using the blockAdd function. None of those are required! */
These are internal defines, used by automake and PMMM:
#define PMMM_VERSION "0.1.2" #define PMMM_VERSION_DATE "31Jan2002" /* ********************************************************************************************* PMMM Memory Allocation Functions ********************************************************************************************* Macro defines: These replace the call to the c library function with PMMM's, including the file (program) and line number of the call. ********************************************************************************************* */ #define free(a) pmmm_free((void*)&(a), __FILE__, __LINE__) #define malloc(sz) pmmm_malloc(sz, __FILE__, __LINE__) #define malloc_mayfail(sz) pmmm_malloc_mayfail(sz, __FILE__, __LINE__) #define calloc(c,sz) pmmm_calloc(c, sz, __FILE__, __LINE__) #define calloc_mayfail(c,sz) pmmm_calloc_mayfail(c, sz, __FILE__, __LINE__) #define realloc(p,sz) pmmm_realloc(p, sz, __FILE__, __LINE__) #define realloc_mayfail(p,sz) pmmm_realloc_mayfail(p, sz, __FILE__, __LINE__) #undef strdup #define strdup(p) pmmm_strdup(p, __FILE__, __LINE__)
So, for example, your code:
char *a = malloc(3200)
is translated into
char *a = pmmm_malloc(3200, "yourprogram.c", 121)
Where 121 is the line # in the program. This calls PMMM's malloc routine.
/* ********************************************************************************************* Basic Function definitions ********************************************************************************************* */ /* Allocation routines */
Although these are exposed to you, you really should use the macros, above - because that way there are no code changes required to use PMMM.
extern void* pmmm_malloc_mayfail(unsigned int sz, char* file, int line); extern void* pmmm_malloc(unsigned int sz, char* file, int line); extern void* pmmm_calloc_mayfail(unsigned int c, unsigned int sz, char* file, int line); extern void* pmmm_calloc(unsigned int c, unsigned int sz, char* file, int line); extern void* pmmm_realloc_mayfail(void* ptr, unsigned int sz, char* file, int line); extern void* pmmm_realloc(void* ptr, unsigned int sz, char* file, int line); extern void pmmm_free(void **ptr, char* file, int line); extern char* pmmm_strdup(char *ptr, char* file, int line); /* ********************************************************************************************* Extended Function macros These allow you to track non-xalloc memory, if you want to ********************************************************************************************* */
If you use routines, like gdbm, that allocate memory directly, you can use these
routines to track that usage. However, this may not always be a good idea. Here is how I do
it in Ntop:
#ifdef HAVE_PMMM_H
pmmm_otherAllocation(data_data.dsize, data_data.dptr, "gf", {
data_data = gdbm_fetch(myGlobals.gdbm_file, key_data);
} );
#else
data_data = gdbm_fetch(myGlobals.gdbm_file, key_data);
#endif
The single line in the else clause, "data_data = gdbm_fetch(myGlobals.gdbm_file, key_data);", is the original code. (The macro, pmmm_otherAllocation, is defined at the bottom of pmmm.h.)
#define pmmm_freeAll() pmmm_FreeAll(__FILE__, __LINE__) #define pmmm_blockAdd(a, b, c) pmmm_BlockAdd_protected(a, b, c, __FILE__, __LINE__) #define pmmm_blockDelete(a, b) pmmm_BlockDelete_protected(a, b, __FILE__, __LINE__) #define pmmm_blockFree(a) pmmm_BlockFree_protected(a, __FILE__, __LINE__) /* ********************************************************************************************* Extended Function definitions ********************************************************************************************* */
These two functions allow the developer to interact with PMMM. setParameters is used to set PMMM's parameters - and MUST be called before the automatic initialization that happens on the first call. The parameters are:
Parameter | Units | Description |
---|---|---|
debugFlag | int | Controls the level of messages PMMM writes to stdout. 0 = none 1 = ERRORS 2 = WARNINGS and NOTES 3 = INFO - lots of internal blatherings 4 = Special case messages, used to limit noise during debugging - if you don't want all the level 3 messages! |
memoryBlocks | int | 5 to 10000 (set via #define) - this is the number of blocks reserved for tracking allocations. Each block tracks a single allocation, regardless of size. The default is 500. Each block takes up 44 bytes. |
minimumAllocToTrace | int | This is the minimum size allocation to trace. The default value is 63. |
minimumStringToTrace | int | This is the minimum size string (strdup) to trace. The default value is 63. |
statisticsBuckets | int | This is the number of buckets to allocate for statistics. Actually two more than the given number are allocated, to account for large and untracked allocations. The default is 7. Each bucket takes 36 bytes. |
statisticsBase | int | Default 6. |
statisticsMultipler | int | Default . |
statisticsGrowth | char | L (Linear) or E (Exponential). Default is E. |
The preceeding four statistics parameters
are combined to determine the upper limit for each statistics bucket. The linear method uses
this formula: bucketnumber * multiplier + base, for a (base=1024, multipler=512 ) progression of (1024, 1536, 2048, ...) The exponential method uses this formula: bucketnumber ^ (2 * multiplier + base), for a (base=6, multiplier=2) progression of (128, 512, 2048, ...) Note that the upper limit of ONE of the buckets will be adjusted to match the minimum size allocation being traced. | ||
trapWildFreesFlag | int | This flag, if set to 1, turns on some testing in pmmm_free that looks for "wild" frees (that is a free of space other than what was allocated). These often cause segmentation faults. However, the testing is dependant on PMMM having full knowledge of all allocations (i.e. a BIG memory blocks setting. |
singleThreadFlag | int | *FUTURE* 0 = 1 = |
extern void pmmm_setParameters(int debugFlag, int memoryBlocks, int minimumAllocToTrace, int minimumStringToTrace, int statisticsBuckets, int statisticsBase, int statisticsMultipler, char statisticsGrowth, int trapWildFreesFlag, int singleThreadFlag); extern void pmmm_FreeAll(char* file, int line);
Although the following routines are exposed to you, you really should use the macros, above - again because that way there are no code changes required to use PMMM.
extern int pmmm_BlockAdd_protected(int sz, void* thePtr, char* prefix, char* file, int line); extern int pmmm_BlockDelete_protected(void* thePtr, char* prefix, char* file, int line); extern void pmmm_BlockFree_protected(int blockIndex, char* prefix, char* file, int line); /* ********************************************************************************************* Inquiry functions ********************************************************************************************* */
These routines provide extra information to you, although they are not required.
pmmm_getVersion returns a string with the PMMM version number and date in it. This is marginally useful for reporting, if you want to make a fuss about using PMMM.
extern char * pmmm_getVersion(void);
pmmm_printfMemoryBlocks returns a dump of the memory blocks table as a *VERY* long string, suitable for printf("%s\n"); use.
extern char * pmmm_printfMemoryBlocks(void);
pmmm_printfStatistics returns a formatted version of the statistics table as a long string, suitable for printf("%s\n"); use.
extern char * pmmm_printfStatistics(void);
pmmm_printhtmlMemoryBlocks returns a dump (summary if the parameter is 1) of the Memory Blocks table as a *VERY* long string. The HTML is self-contained as a <table>stuff</table> block. For a slightly dated example, check the history page.
extern char * pmmm_printhtmlMemoryBlocks(int);
pmmm_printhtmlStatistics returns a dump (summary if the parameter is 1) of the Statistics table as a long string. Again, the HTML is self-contained as a <table>stuff</table> block. For a slightly dated example, check the history page.
extern char * pmmm_printhtmlStatistics(int); /* ********************************************************************************************* Termination function ********************************************************************************************* */
Optional - cleansup things like the memory blocks and statistics tables. If you want to restart PMMM with a different set of parameters, you must call pmmm_terminate and then immediately call pmmm_setParameters.
extern void pmmm_terminate(void); /* ********************************************************************************************* Macro - this allows tracking of storage allocated other ways (for example a library routine) ********************************************************************************************* */ #define pmmm_otherAllocation(size, pointer, tag, ...) { \ /* generated code: 1. perform the function */ \ { \ __VA_ARGS__; \ } \ /* generated code: 2. if the pointer isn't null, record the allocation */ \ if (pointer != NULL) { \ pmmm_blockAdd(size, pointer, tag); \ } \ } /* ********************************************************************************************* ********************************************************************************************* ********************************************************************************************* ********************************************************************************************* */