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.

Go to the SourceForge Project Page

PMMM Notes:

  1. PMMM does not have visibility of every memory allocation.

    For those it sees, only 'large' allocations are tracked. The specific allocation for the (many) tiny mallocs (and thus their associated frees) are not tracked. This means that the InUse bytes counts will not equal the system reports of memory used. This is not necessarily a bug!

  2. Failed allocations are those where NTop safely continued.

    However, the information available from Ntop would be better if more memory was available. The size of the tracking table is set at compile time, as is the break point between 'large' and 'small' allocations.

    Look in ntop.h for the constants.

    Statistical reporting, the # of buckets and the minimum size (MB) allocation to report on in the log:

    #define PMMM_STATISTICS_MAXIMUM  7
    #define PMMM_REPORT_OVER        0.2

    The number of tracking blocks and the minimum size allocation to track:

    #define PMMM_MAXIMUM_MEMORY_BLOCKS 1000
    #define PMMM_MINIMUM_SIZE_TO_TRACE 17

    The granularity of the buckets is set in main.c:

    main.c:112:    pmmmStatistics[i].bucketMaximum = pow(2,(2*i+6));
    BucketByte limit
    02*0+6 = 6 or 64
    12*1+6 = 8 or 256
    22*2+6 = 10 or 1024
     etc.

    So why does the formula show 64 and the report above show 16? Because there is a magic adjustment in there so that the minimum allocation to track begins it's own bucket.

  3. For realloc, freed bytes are reported in the bucket based on the size of the new allocation.

    That's pretty trivial, but it does mean that as many small place holder allocations are reallocated to realistic sizes (much larger), the data won't seem to make sense. the minimum block size * the # of reallocations could be a lot more than the reported freed bytes. In the example above, it's not (9 reallocations + 139 frees of blocks between 17 and 256 bytes means we would expect to free between 2516 and 37888 bytes - 25955 is right in the range), but it could be outside. I did this rather than scan the bucket table ANOTHER time.

  4. The block table dump report is printed during normal and abnormal shutdowns, in ntop.c at the end of cleanup for signals Ntop caught and in main.c at the end of the clean shutdown (after endNtop is set).

  5. Want to see the PMMM in action? Developers want to check YOUR code for leaks?

    Start up Ntop with the PMMM and run for an hour or so of "typical" traffic. Then shut Ntop down and look in the log:

    Along with 100s of entries like these, that show properly freed memory:

    Jan 11 10:41:39 piglet ntop[13640]:           7.         0       (nil)                  f-ntop.c(994) 01/11/02 10:41:39 
    Jan 11 10:41:39 piglet ntop[13640]:           8.         0       (nil)                 f-ntop.c(1001) 01/11/02 10:41:39 
    Jan 11 10:41:39 piglet ntop[13640]:           9.         0       (nil)                  f-ntop.c(994) 01/11/02 10:41:39 
    Jan 11 10:41:39 piglet ntop[13640]:          10.         0       (nil)                 f-ntop.c(1001) 01/11/02 10:41:39 
    

    You will find these, that were still unfreed as Ntop was going down:

    Jan 11 10:41:39 piglet ntop[13640]:           5.        52   0x80558b0                 rn-ntop.c(374) 01/11/02 10:26:11 
    Jan 11 10:41:39 piglet ntop[13640]:           6.      1216   0x80558e8                    ntop.c(397) 01/11/02 10:26:11 
    
    Jan 11 10:41:39 piglet ntop[13640]:          11.        36   0x805cf58                c-plugin.c(253) 01/11/02 10:26:11 
    Jan 11 10:41:39 piglet ntop[13640]:          12.        24   0x805cf80                c-plugin.c(259) 01/11/02 10:26:11 
    

    So how do you read 'em? In order, the columns are:

    1. Bucket number
    2. Allocated bytes
    3. Pointer address (meaningless, mostly)
    4. Where allocated
    5. When allocated

    The meat is in the Where and When! Prefixed to where is one of five values.

    f- and ro- should ONLY have zero sizes. These are freed blocks, which are still in the table (because it's static). Since a 1st empty search is used to find a block to record an allocation, ro-s happen if there is an empty slot before the one being freed. They are reported because it might be of interest where the frees happened in the event of a crash.

    So, what does this say? Looks like memory allocated for the port mapper in ntop.c at 397:

    ipPortMapper = (PortMapper*)malloc(theSize);

    is not being freed during shutdown. Oops...

    Also, all the allocations happened early in the run, and the frees at the end. No obvious on-going memory leaks here. Look for a series of time-sequenced allocations at the end of the table that never get freed.

  6. You could trace every allocation. Just make the block table very big.

  7. Actual memory allocation information is only available under GCC.

    For other environments, you would just need to acquire and manipulate the data like the GCC mallinfo does (see Statistics for Memory Allocation with malloc at http://www.gnu.org/manual/glibc-2.2.3/html_node/libc_34.html#SEC35).

Don't like it? Write a better 'Poor Man's Memory Monitor'

Back to home.