pleriche / FastMM5

FastMM is a fast replacement memory manager for Embarcadero Delphi applications that scales well across multiple threads and CPU cores, is not prone to memory fragmentation, and supports shared memory without the use of external .DLL files.
290 stars 75 forks source link

Breakpoint on allocation order number #55

Closed janrysavy closed 1 week ago

janrysavy commented 1 week ago

Hi, I’m wondering if FastMM5 could support the equivalent of the _CrtSetBreakAlloc function, which stops in the debugger at a specific allocation. In the C++ world, this method is sometimes used to track down the location of an allocation. See https://learn.microsoft.com/en-us/cpp/c-runtime-library/find-memory-leaks-using-the-crt-library?view=msvc-170 for reference.

pleriche commented 1 week ago

Hi Jan,

FastMM has an AllocationNumber field in the debug block header. Every new block gets assigned the next number. (Numbers will thus be unique, until it wraps around to 0 after 2^32 - 1.) What I could do is to add a facility to have FastMM call DebugBreak if a certain AllocationNumber is reached in a GetMem call. This will only be in debug mode, of course.

Will this work for you?

janrysavy commented 1 week ago

That would be great, thank you.

I have a second question related to this topic: would it be possible to log a list of allocated memory blocks, including their address and allocation order?

I am dealing with a case where I see that an object is overwritten with the value $80 (it has already been deallocated), and we are trying to release it again. If I could determine the allocation order based on its address and set a breakpoint according to the first post, it would make troubleshooting easier.

pleriche commented 1 week ago

I have a second question related to this topic: would it be possible to log a list of allocated memory blocks, including their address and allocation order?

Have you looked at FastMM_WalkBlocks? It passes a structure to the callback, and the field you should be looking at is ABlockInfo.DebugInformation.AllocationNumber.

I have to note though, that unless your application is extremely deterministic then allocation numbers will vary between runs and attempting to debug using the allocation number will be futile.

I am dealing with a case where I see that an object is overwritten with the value $80 (it has already been deallocated), and we are trying to release it again. If I could determine the allocation order based on its address and set a breakpoint according to the first post, it would make troubleshooting easier.

Does the FastMM double free crash report not provide enough information? In debug mode it will give you the stack trace of when the block was allocated, when it was freed the first time, as well as the current stack trace. I find that is usually enough information to identify the problem.

janrysavy commented 1 week ago

Have you looked at FastMM_WalkBlocks? It passes a structure to the callback, and the field you should be looking at is ABlockInfo.DebugInformation.AllocationNumber.

Great, it is exactly what I'm looking for, thank you!

I have to note though, that unless your application is extremely deterministic then allocation numbers will vary between runs and attempting to debug using the allocation number will be futile.

In our case it is single-thread application and it is deterministic (for now).

Does the FastMM double free crash report not provide enough information? In debug mode it will give you the stack trace of when the block was allocated, when it was freed the first time, as well as the current stack trace. I find that is usually enough information to identify the problem.

Double free reporting works for me, unfortunately this is different case. Simplified version:

type
  IIface = interface
    procedure DoSometing;
  end;

  TSimple = class(TInterfacedObject, IIface)
  public
    procedure DoSometing;
  end;

var
  Iface, AnotherIface: IIface;
...
  Iface := TSimple.Create;
  Iface.DoSometing;
  Pointer(AnotherIface) := Pointer(Iface); // disable ref-counting
  Iface := nil;
  AnotherIface := nil; // exception, interface is already deallocated and filled with $80

In this case you will receive following exception:

Project Test.exe raised exception class $C0000005 with message 'access violation at 0x5ab88bd8: read of address 0x80808088'.

If you can implement the function you suggested for breaking on a specified allocation, that would be great.

pleriche commented 1 week ago

If you can implement the function you suggested for breaking on a specified allocation, that would be great.

I've just pushed a commit that adds FastMM_DebugBreakAllocationNumber. If the block with matching allocation number is allocated then a break point will be triggered in the debugger.

janrysavy commented 1 week ago

Thank you