This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

DSP/BIOS heap analysis

Hello,

We are attempting to do some crash analyse on our product and want to analyze the heap allocations, basically separating all the nodes allocated vs free and then print those with some additional information. With DSP/BIOS the free list is tracked but we would like to also be able to pick out the allocated nodes. I know that every node has a MEM_Header structure:

typedef struct MEM_Header {

  struct MEM_Header *next; /* form a free memory link list */
  MEM_sizep size; /* size of the free memory */
} MEM_Header;

I was hoping that it would be as easy as starting at the beginning of the heap memory, reading in this structure, store it, increment the pointer by the size which would point to the next structure, store it, repeat, until you get to the end of the heap space. One detail that I have found from examining the provided malloc function is that the size stored in this structure is the size requested + sizeof (MEM_Header) but in addition to this the actual size of the node is rounded up to be aligned with the sizeof MEM_Header (8). So say I was to read a MEM_Header structure and it has a size of 0xC the actual node size would be 0x10. 0x10 size includes the size of the header. Just to test out this heap walking method after powerup we went ahead and allocated as many nodes of size 2 as possible until the heap was full and then took a system dump. I am only able to traverse for a few nodes until I hit one node that has a size that would increment the pointer back to the position of the free list. If I continue to look through the memory to find another location that seems to be a MEM_Header I also am able to manually parse through a few blocks until I hit yet another node that has a size that increments back to point at the free node list. One may think that these are free nodes but this is a heap that has been fully allocated. The free node list has a next of 0 and size of 8. I have a memory dump that I can provide in a fairly nice format for example:

C11FFFF0 00 00 00 00 00000000 ....
C11FFFF4 08 00 00 00 00000008 ....
C11FFFF8 F0 FF 1F C1 C11FFFF0 ....
C11FFFFC F8 FF 1F 00 001FFFF8 ....

Our heap starts at 0xC1000000 and is 0x200000 long (verified from reading this info from the memory segment structure) and the free node start is at address 0xC11FFFF8. So as you can see from the memory above the free node list next pointer is 0 with size 8 which is just the structure size. Any hints on this? I feel like I am overlooking something since at numerous points through the heap there are size values that point directly back to free node structure example:

C1000130 00 00 00 00 00000000 ....
C1000134 C8 FE 1F 00 001FFEC8 ....

(0xC1000130 + 0x1FFEC8 = 0xC11FFFF8)

and that seems to be too much of a coincidence to be a mistake. Thanks for your time and I can provide this dump if needed as well as any other details or structure information in our system. Thanks again.

  • Hi Nathan,

    Your approach for traversing a list of allocated blocks will not work. The MEM module does add any space at the beginning of the requested buffer when an allocation occurs. The values in that block of memory are considered un-initialized. The first two fields might have some internal significance when the memory was free, but you cannot count on it. You'll have to implement you own linked list if you want this capability.

    I understand that traversing the allocated blocks is great for debugging a crash and we added the capability into SYS/BIOS. There is a HeapTrack module that sits on top of any type of memory heap. In ROV (or via runtime APIs) you can display memory allocated to specific tasks, when it was allocated, did you write past the end of the allocated buffer, etc. This was accomplished by adding some memory to the end of the allocated buffer. This allowed alignment requests to still be honored easily.

    Todd

  • Hi Todd,

    Thanks for getting back to me. I saw that the MEM module does not add any additional space but looking at malloc in the provided malloc.c which we have:

    void *malloc(size_t size)
    {
    MEM_Header *packet;

    if (size == 0) {
    return (NULL);
    }

    #ifdef _55L_
    if((size + sizeof(MEM_Header)) < size) {
    return (NULL);
    }
    #endif

    if ((packet = MEM_allocJ(MEM->MALLOCSEG, (size_t)(size+sizeof (MEM_Header)),
    (size_t)0)) == MEM_ILLEGAL) {
    return (NULL);
    }

    packet->size = size + sizeof (MEM_Header);

    return (packet + 1);
    }

    Isn't that adding extra header size at the beginning and keeping the size value coherent?

  • Sorry. I missed that you are using malloc.

    Yes, the fields reserved by malloc() should be correct. Off-hand, I see two problems that could occur with your traversal.

    1. If there is a free block anywhere in the heap the traversal fails. Can you re-confirm that all the memory is allocated? There is a MEM_Header at the beginning of a free block. The size denotes the size of the free memory. The first field is a next pointer (pointer to next free block).

    2. If a MEM_alloc, not malloc, occurred on the heap. Here the size field in the buffer will probably not be useful.

    Which version of DSP/BIOS are you using. If you confirmed the above 2 cases are not part of the problem, can you attach the sample code that causes the problem. Also what board/device are you using?

    Todd

  • We are using BIOS 5.41.04.18

    Regarding point 1. Would it matter whether or not its free if I am just following the sizes? The size listed there from looking at mem_free.c seems to be just the size of the node itself which should be fine.

    For point 2 it looks like you're absolutely right on that and I guess there would be no way to recover the traversal reliably. Maybe that is what's happening. I see in a few places throughout the DSP/BIOS source that MEM_allocJ is called directly so maybe that's what is messing all this up.

  • Depending on the size requested, the traversal could fail for a third reason.

    If you are requesting a size that is not a multiple of a sizeof(MEM_Header), the traversal will fail. Internally in MEM_alloc, the requested size is rounded up to be a multiple of sizeof(MEM_Header). The size that is being stored in malloc does not reflect this increase.

    Todd

  • Yes I took the size increase into account by performing the same technique that MEM_alloc performs to round up the size to a MEM_Header aligned value. I think that since direct calls to MEM_alloc will make this traversal process fail I will just have to limit our traversal to free nodes only. Thanks for your help!