How to catch output of

21 Sep 2011

Hi,

I ran across two interesting heap related funtions that give insight in heap allocations (it would allows to detect memory leaks).

Question though is: how do I get the output into some string instead of to an output stream (so I can do some math with the results)?

heapvalid() and heapstats()

See http://mbed.org/forum/mbed/topic/2698/ for sample output.

wvd_vegt (i hope the links are correct).

24 Sep 2011

No one any ideas?

24 Sep 2011

This is a sample that I created for capturing the __heapstats() output into a string buffer.

#include <mbed.h>
#include <stdarg.h>

int CaptureLine(void* pBuffer, char const* pFormatString, ...)
{
    char*   pStringEnd = (char*)pBuffer + strlen((char*)pBuffer);
    va_list valist;
    
    va_start(valist, pFormatString);
    
    return vsprintf(pStringEnd, pFormatString, valist);
}

int main() 
{
    char OutputBuffer[256];
    
    printf("\r\nBefore malloc.\r\n");
    OutputBuffer[0] = '\0';
    __heapstats(CaptureLine, OutputBuffer);
    printf("%s", OutputBuffer);
    
    void* pvTest = malloc(1024);
    
    printf("After malloc.\r\n");
    OutputBuffer[0] = '\0';
    __heapstats(CaptureLine, OutputBuffer);
    printf("%s", OutputBuffer);

    free(pvTest);    

    printf("After free.\r\n");
    OutputBuffer[0] = '\0';
    __heapstats(CaptureLine, OutputBuffer);
    printf("%s", OutputBuffer);
    
    return 0;
}

Note: You will need to make sure that OutputBuffer is large enough to hold the whole string or it will result in stack corruption.

You could also parse the string contents right in CaptureLine. You could even just validate that the format string matched an expected value and then pull the values straight out of the function parameters using va_arg so that you could grab the values before they get converted to strings.

Hope that helps.

25 Sep 2011

Hi Adam,

Quite an elegant solution you' ve cooked up. Thanks a lot!!

For the buffer size i may use the windows method and have the function return the number of bytes needed if the buffer is null so you can size it correctly and make another call. But i would like to see that buffer not being part of the stackdump.

Your code is a good starter for some further coding and decoding,

Again thanks. wvd_vegt

25 Sep 2011

Hi Adam,

Thanks for this.

I wanted to put a wrapper around printf but was not sure how to handle the variable arguments. Your example showed me how.

The result was:

#include <stdarg.h>

// Print at specifed location on display
// This routine is just for convenience.
// It does the same job as locate() then printf();

int DogLCD::atPrintf(int col, int row, const char* format, ...) {
    char buffer[40];
    int rVal;
    va_list args;

    va_start(args, format);
    vsprintf(buffer, format, args);
    va_end(args);
    locate(col, row);
    rVal = printf("%s", buffer);
    return rVal;
}

Thanks again,
Paul

25 Sep 2011

Paul, glad I was able to help in some little way.

This is another sample which is able to parse the results of the __heapvalid() call without needing to store any strings or parsing the resulting strings. It pulls the block address and size from the stack like a *printf() function would. It then places the highest addresses used by allocated and free blocks into the pHeapInfo structure.

#include <mbed.h>
#include <stdarg.h>

struct SHeapInfo
{
    const char* HighestAllocBlock;
    const char* HighestFreeBlock;
};

int CaptureLine(void* pvHeapInfo, char const* pFormatString, ...)
{
    static const char*  pAllocFormatString = "alloc block %p size %3lx";
    static const char*  pFreeFormatString = "free block  %p size %3lx next=%p";
    static const char*  pCompleteFormatString = "------- heap validation complete";
    SHeapInfo*          pHeapInfo = (SHeapInfo*)pvHeapInfo;
    va_list             valist;
    
    va_start(valist, pFormatString);
    
    if (pFormatString == strstr(pFormatString, pAllocFormatString))
    {
        const char* pBlock = va_arg(valist, const char*);
        unsigned long BlockSize = va_arg(valist, unsigned long);
        const char* pBlockLastByte = pBlock + BlockSize - 1;
        
        if (pBlockLastByte > pHeapInfo->HighestAllocBlock)
        {
            pHeapInfo->HighestAllocBlock = pBlockLastByte;
        }
    }
    else if (pFormatString == strstr(pFormatString, pFreeFormatString))
    {
        const char* pBlock = va_arg(valist, const char*);
        unsigned long BlockSize = va_arg(valist, unsigned long);
        const char* pBlockLastByte = pBlock + BlockSize - 1;
        
        if (pBlockLastByte > pHeapInfo->HighestFreeBlock)
        {
            pHeapInfo->HighestFreeBlock = pBlockLastByte;
        }
    }
    else if (pFormatString == strstr(pFormatString, pCompleteFormatString))
    {
        // Ignoring end of dump string.
    }
    else
    {
        // Unrecognized format string.
        printf("Unrecognized format of %s", pFormatString);
    }

    return 1;
}

void DisplayAndClearHeapInfo(SHeapInfo* pHeapInfo)
{
    printf("Highest allocated block address: %p\r\n", pHeapInfo->HighestAllocBlock);
    printf("Highest free block address: %p\r\n", pHeapInfo->HighestFreeBlock);
    
    memset(pHeapInfo, 0, sizeof(*pHeapInfo));
}

int main() 
{
    SHeapInfo HeapInfo = { 0, 0 };
    
    printf("\r\nBefore malloc.\r\n");
    __heapvalid(CaptureLine, &HeapInfo, 1);
    DisplayAndClearHeapInfo(&HeapInfo);

    void* pvTest = malloc(1024);
    
    printf("After malloc.\r\n");
    __heapvalid(CaptureLine, &HeapInfo, 1);
    DisplayAndClearHeapInfo(&HeapInfo);

    free(pvTest);    

    printf("After free.\r\n");
    __heapvalid(CaptureLine, &HeapInfo, 1);
    DisplayAndClearHeapInfo(&HeapInfo);
    
    return 0;
}