Tuesday, June 21, 2011

Memory

One of the odd things about doing support for a large number of clients at the same time is that you get strange bursts of similar requests. This month, I had two requests for an "intention" parameter for memory allocations, after receiving zero requests over the previous five years. So hey, let's talk about a couple of changes to the memory subsystem in Granny that went into the 2.8.50.0 release!

First, the API as it existed until this month. Granny provides a method for talking over all allocations made by the library: GrannySetAllocator. You pass this function two function pointers, one for the allocation:

  GRANNY_CALLBACK(void *)
  granny_allocate_callback(char const * File,
                           granny_int32x Line,
                           granny_uintaddrx Alignment,
                           granny_uintaddrx Size);

and one for the deallocation:

  GRANNY_CALLBACK(void)
  granny_deallocate_callback(char const * File,
                             granny_int32x Line,
                             void * Memory);

You get to see exactly where in Granny each allocation is coming from so when a memory leak occurs, you can walk it back to the source. "OK, the leaked allocation comes from granny_file.cpp, I bet I forgot a GrannyFreeFile somewhere."

What you don't get is information on what each allocation is for or an indication of it's expected lifetime. For instance, allocations from granny_file.cpp might be temporary buffers used for decompression, or more permanent allocations representing file sections. So now granny_allocate_callback looks like:

  GRANNY_CALLBACK(void *) 
  granny_allocate_callback(char const * File,
                           granny_int32x Line,
                           granny_uintaddrx Alignment,
                           granny_uintaddrx Size, 
                           granny_int32x AllocationIntent);


Where AllocationIntent will be one of the values from the enum granny_allocation_intent. In our granny_file.cpp example, the section data will be allocated with an intent of GrannyAllocationFileData, while temporary buffers send GrannyAllocationTemporary, 'natch. So if you're paying extra close attention to memory fragmentation, you can route the temporary allocs to a heap that won't put holes in your main memory layout, while the section data can be sent to a heap for allocs that will be around for a while. Handy, and pretty simple on your side and mine!

While I was in there, I made a couple of other changes. Granny now has an analogue to the _CrtSetBreakAlloc API. In debug mode, the library allows you to set a breakpoint that will trigger when a specific allocation occurs: GrannySetBreakAllocation. The debug memory logging (check your docs for information on the GrannyBeginAllocationCheck API) now prints an allocation number with each leaked block of memory. If your game or application is deterministic, just plug this into the break alloc API in the next run, and presto! You've got your leak in the debugger.

Granny is getting some interesting Telemetry integration that will add even more debugging features, but we'll get into those in an upcoming post.

No comments:

Post a Comment