c - Issue with Skipped malloc Breakpoints in GDB When Using finish and continue - Stack Overflow

admin2025-04-19  2

Requirement

Track the total memory allocated (malloc) and freed (free). Capture backtraces of all malloc and free calls (for now, logging can be ignored). Current Approach For malloc:

I need every malloc call to complete execution. After completion, I retrieve $rax and use malloc_usable_size($rax) to determine the actual allocated memory. For free:

This is simpler since malloc_usable_size($rdi) gives the size being freed. Manually continuing execution from the GDB prompt works fine. However, my debugging case involves thousands of malloc and free calls, so I want this to run automatically without user intervention.

The Issue

When I add continue inside hookpost-myfinish, I observe skipped malloc breakpoints. As a result, malloc_count, free_count, total_malloced, and total_freed become incorrect. I suspect this happens when consecutive malloc calls occur: finish from malloc returns execution to main(), and another continue causes the next malloc entry breakpoint to be skipped. Reproduction Steps Using gdb_commands.txt → Works correctly (manual continuation required). Using gdb_commands_continue.txt → Causes alternate malloc calls to be skipped, leading to incorrect tracking. This seems to be due to how continue interacts with finish, but I’m unsure of the best way to ensure every malloc and free call is properly accounted for without skipping breakpoints.

Could someone help me understand why continue is skipping alternate malloc calls and suggest a reliable way to handle this?

code.c

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>  // Required for malloc_usable_size

int main() {
    printf("Starting memory allocation test...\n");

    // Allocate memory blocks of different sizes
    void *ptr1 = malloc(32);
    printf("ptr1 allocated at address: %p\n", ptr1);
 
    free(ptr1);
    printf("ptr1 freed");
    void *ptr2 = malloc(64);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);

    void *ptr3 = malloc(128);
    printf("ptr3 allocated at address: %p\n", ptr3);

    free(ptr2);
    free(ptr3);

    void *ptr4 = malloc(128);
    printf("ptr4 allocated at address: %p\n", ptr4);

    void *ptr5 = malloc(128);
    printf("ptr5 allocated at address: %p\n", ptr5);

    void *ptr6 = malloc(256);
    printf("ptr6 allocated at address: %p\n", ptr6);
    void *ptr7 = malloc(256);
    void *ptr8 = malloc(256);
    void *ptr9 = malloc(256);
    void *ptr10 = malloc(256);
    void *ptr11 = malloc(256);
    void *ptr12 = malloc(256);
    void *ptr13 = malloc(256);
    void *ptr14 = malloc(256);
    void *ptr15 = malloc(256);
    void *ptr16 = malloc(256);

    free(ptr5);
    free(ptr4);
    free(ptr6);
    free(ptr7);
    free(ptr8);
    free(ptr9);
    free(ptr10);
    free(ptr11);
    free(ptr12);
    free(ptr13);
    free(ptr14);
    free(ptr15);
    free(ptr16);
    


    printf("Memory allocation test completed.\n");
    return 0;
}

gdb_commands.txt

Manually pressing continue works

set unwindonsignal off
set $mc = 0 
set $fc = 0
set $mallocsize = 0
set $in_malloc = 0
set $total_malloced = 0
set $total_freed = 0
b memory_test.c:8
b malloc
b free
disable 2 3

commands 1
    silent
    enable 2 3
    continue
end

commands 2
  set $mc = $mc + 1
  set $mallocsize = $rdi
  printf "Asked to malloc %d bytes\n", $mallocsize
  set $in_malloc = 1
  myfinish
end

commands 3
  set $fc = $fc + 1
  if ($rdi)
    set $total_freed = $total_freed + (size_t)malloc_usable_size($rdi)
    printf "Freed %d bytes \n ", (size_t)malloc_usable_size($rdi)
  end
  continue
end


define myfinish
    finish
end


define hookpost-myfinish
    set $total_malloced = $total_malloced + (size_t)malloc_usable_size($rax)
    printf "Actually malloced %d bytes and total malloced till now is %d \n", (size_t)malloc_usable_size($rax), $total_malloced
    set $in_malloc = 0
end

gdb_commands_continue.txt

malloc calls gets skipped with continue.

set unwindonsignal off
set $mc = 0 
set $fc = 0
set $mallocsize = 0
set $in_malloc = 0
set $total_malloced = 0
set $total_freed = 0
b memory_test.c:8
b malloc
b free
disable 2 3

commands 1
    silent
    enable 2 3
    continue
end

commands 2
  set $mc = $mc + 1
  set $mallocsize = $rdi
  printf "Asked to malloc %d bytes\n", $mallocsize
  set $in_malloc = 1
  myfinish
end

commands 3
  set $fc = $fc + 1
  if ($rdi)
    set $total_freed = $total_freed + (size_t)malloc_usable_size($rdi)
    printf "Freed %d bytes \n ", (size_t)malloc_usable_size($rdi)
  end
  continue
end


define myfinish
    finish
end


define hookpost-myfinish
    set $total_malloced = $total_malloced + (size_t)malloc_usable_size($rax)
    printf "Actually malloced %d bytes and total malloced till now is %d \n", (size_t)malloc_usable_size($rax), $total_malloced
    set $in_malloc = 0
    continue
end

Would appreciate any insights or alternative approaches!

Requirement

Track the total memory allocated (malloc) and freed (free). Capture backtraces of all malloc and free calls (for now, logging can be ignored). Current Approach For malloc:

I need every malloc call to complete execution. After completion, I retrieve $rax and use malloc_usable_size($rax) to determine the actual allocated memory. For free:

This is simpler since malloc_usable_size($rdi) gives the size being freed. Manually continuing execution from the GDB prompt works fine. However, my debugging case involves thousands of malloc and free calls, so I want this to run automatically without user intervention.

The Issue

When I add continue inside hookpost-myfinish, I observe skipped malloc breakpoints. As a result, malloc_count, free_count, total_malloced, and total_freed become incorrect. I suspect this happens when consecutive malloc calls occur: finish from malloc returns execution to main(), and another continue causes the next malloc entry breakpoint to be skipped. Reproduction Steps Using gdb_commands.txt → Works correctly (manual continuation required). Using gdb_commands_continue.txt → Causes alternate malloc calls to be skipped, leading to incorrect tracking. This seems to be due to how continue interacts with finish, but I’m unsure of the best way to ensure every malloc and free call is properly accounted for without skipping breakpoints.

Could someone help me understand why continue is skipping alternate malloc calls and suggest a reliable way to handle this?

code.c

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>  // Required for malloc_usable_size

int main() {
    printf("Starting memory allocation test...\n");

    // Allocate memory blocks of different sizes
    void *ptr1 = malloc(32);
    printf("ptr1 allocated at address: %p\n", ptr1);
 
    free(ptr1);
    printf("ptr1 freed");
    void *ptr2 = malloc(64);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);
    printf("ptr2 allocated at address: %p\n", ptr2);

    void *ptr3 = malloc(128);
    printf("ptr3 allocated at address: %p\n", ptr3);

    free(ptr2);
    free(ptr3);

    void *ptr4 = malloc(128);
    printf("ptr4 allocated at address: %p\n", ptr4);

    void *ptr5 = malloc(128);
    printf("ptr5 allocated at address: %p\n", ptr5);

    void *ptr6 = malloc(256);
    printf("ptr6 allocated at address: %p\n", ptr6);
    void *ptr7 = malloc(256);
    void *ptr8 = malloc(256);
    void *ptr9 = malloc(256);
    void *ptr10 = malloc(256);
    void *ptr11 = malloc(256);
    void *ptr12 = malloc(256);
    void *ptr13 = malloc(256);
    void *ptr14 = malloc(256);
    void *ptr15 = malloc(256);
    void *ptr16 = malloc(256);

    free(ptr5);
    free(ptr4);
    free(ptr6);
    free(ptr7);
    free(ptr8);
    free(ptr9);
    free(ptr10);
    free(ptr11);
    free(ptr12);
    free(ptr13);
    free(ptr14);
    free(ptr15);
    free(ptr16);
    


    printf("Memory allocation test completed.\n");
    return 0;
}

gdb_commands.txt

Manually pressing continue works

set unwindonsignal off
set $mc = 0 
set $fc = 0
set $mallocsize = 0
set $in_malloc = 0
set $total_malloced = 0
set $total_freed = 0
b memory_test.c:8
b malloc
b free
disable 2 3

commands 1
    silent
    enable 2 3
    continue
end

commands 2
  set $mc = $mc + 1
  set $mallocsize = $rdi
  printf "Asked to malloc %d bytes\n", $mallocsize
  set $in_malloc = 1
  myfinish
end

commands 3
  set $fc = $fc + 1
  if ($rdi)
    set $total_freed = $total_freed + (size_t)malloc_usable_size($rdi)
    printf "Freed %d bytes \n ", (size_t)malloc_usable_size($rdi)
  end
  continue
end


define myfinish
    finish
end


define hookpost-myfinish
    set $total_malloced = $total_malloced + (size_t)malloc_usable_size($rax)
    printf "Actually malloced %d bytes and total malloced till now is %d \n", (size_t)malloc_usable_size($rax), $total_malloced
    set $in_malloc = 0
end

gdb_commands_continue.txt

malloc calls gets skipped with continue.

set unwindonsignal off
set $mc = 0 
set $fc = 0
set $mallocsize = 0
set $in_malloc = 0
set $total_malloced = 0
set $total_freed = 0
b memory_test.c:8
b malloc
b free
disable 2 3

commands 1
    silent
    enable 2 3
    continue
end

commands 2
  set $mc = $mc + 1
  set $mallocsize = $rdi
  printf "Asked to malloc %d bytes\n", $mallocsize
  set $in_malloc = 1
  myfinish
end

commands 3
  set $fc = $fc + 1
  if ($rdi)
    set $total_freed = $total_freed + (size_t)malloc_usable_size($rdi)
    printf "Freed %d bytes \n ", (size_t)malloc_usable_size($rdi)
  end
  continue
end


define myfinish
    finish
end


define hookpost-myfinish
    set $total_malloced = $total_malloced + (size_t)malloc_usable_size($rax)
    printf "Actually malloced %d bytes and total malloced till now is %d \n", (size_t)malloc_usable_size($rax), $total_malloced
    set $in_malloc = 0
    continue
end

Would appreciate any insights or alternative approaches!

Share Improve this question asked Mar 6 at 0:01 Puspaul HalderPuspaul Halder 311 bronze badge 5
  • Why do you even need the finish at all? You could just do set $total_malloced = $total_malloced + $mallocsize inside the commands 2 block. – ssbssa Commented Mar 6 at 6:58
  • I had been doing the same, but later found out that when we ask for malloc(N), the compiler does not give exact N bytes, but appends few bytes and allocates N+x amount of bytes. $mallocsize variable contains the value which is asked by user, in this case x. $mallocsize = N (size_t)malloc_usable_size($rax) = N+x total_freed = N+x Hence if you keep adding the $mallocsize it won't give the actual memory allocated, eventually leading to mismatch with total memory freed . – Puspaul Halder Commented Mar 6 at 7:33
  • Did you check whether MemorySanitizer provides you the information you are looking for? It comes with the clang compiler family. There is also tcmalloc, which can detect memory leaks or provide statistics on heap allocations. Instead of using malloc_usable_size in free, it would be better to keep track of allocations and their size. Your current approach will also give you wrong results for double-free. If you really want to implement your project in gdb, I suggest to look into the python API. This way you can collect your data in maps (dicts). – Joachim Commented Mar 7 at 7:31
  • i think there are issues with continuing execution after using finish in a command list for a breakpoint. See previous discussion: stackoverflow/questions/10501121/…. – storsan Commented Mar 16 at 5:33
  • The gdb documentation states: Any other commands in the command list, after a command that resumes execution, are ignored. This is because any time you resume execution (even with a simple next or step), you may encounter another breakpoint—which could have its own command list, leading to ambiguities about which list to execute. Refer sourceware./gdb/current/onlinedocs/gdb.html/… – storsan Commented Mar 16 at 5:34
Add a comment  | 

1 Answer 1

Reset to default 0

It appears that in GDB the finish command's default behavior is to stop execution after returning from the breakpointed function, so GDB stops and shows the command prompt.

To circumvent this behavior you could try using the GDB Python API. The API gives the GDB user more flexibility and control as compared to command lists. i changed your commands file for the malloc() breakpoint and finish processing while the free() breakpoint commands list was left as is:

set unwindonsignal off
#set trace-commands on
set pagination off
set $fc = 0
set $total_freed = 0
b gdb-testing.c:8
b free
enable 1 2

commands 1
    silent
    continue
end

commands 2
  set $fc = $fc + 1
  if ($rdi)
    set $total_freed = $total_freed + (size_t)malloc_usable_size($rdi)
    printf "Freed %d bytes \n ", (size_t)malloc_usable_size($rdi)
    printf "Total freed %d bytes \n", $total_freed
  end
  continue
end

python
import gdb
class MallocBreakpoint(gdb.Breakpoint):
    def __init__(self):
        super(MallocBreakpoint,self).__init__("malloc")
        self.silent=True
    def stop(self):
        frame = gdb.selected_frame()
        reqsz=gdb.parse_and_eval(f"$rdi")
        print (frame.name())
        print(f"Asked to malloc {reqsz} bytes")
        MallocFinishBreakpoint(frame)
        return False # True will stop at breakpoint

class MallocFinishBreakpoint(gdb.FinishBreakpoint):
    tot_malloced = 0
    def __init__(self,frame):
        super(MallocFinishBreakpoint,self).__init__(frame,internal=True)
        self.silent=True
    def stop(self):
        buffer_address = self.return_value
        print(f"malloc() returned buffer address: {hex(buffer_address)}")
        result = gdb.execute("call malloc_usable_size({})".format(buffer_address), to_string=True)
        # Extract the result (it’s returned as a string like "$4 = 24")
        size = int(result.split("=")[1].strip())
        MallocFinishBreakpoint.tot_malloced = MallocFinishBreakpoint.tot_malloced + size
        # Print the result
        print("Actual malloced: {} bytes".format(size))
        print("and total malloced till now is: {}". format(MallocFinishBreakpoint.tot_malloced))
        return False # True will stop at breakpoint

MallocBreakpoint()

end

If there are any glibc internal calls to malloc() you will have to account for that as well in the output.

i ran your code without the printf's and was able to see the allocated and freed bytes were matching:

Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
__GI___libc_malloc
Asked to malloc 32 bytes
malloc() returned buffer address: 0x5555555592a0
Actual malloced: 40 bytes
and total malloced till now is: 40


Breakpoint 2, __GI___libc_free (mem=0x5555555592a0) at ./malloc/malloc.c:3352
3352    ./malloc/malloc.c: No such file or directory.
Freed 40 bytes
 Total freed 40 bytes

...
...
__GI___libc_malloc
Asked to malloc 256 bytes
malloc() returned buffer address: 0x555555559ee0
Actual malloced: 264 bytes
and total malloced till now is: 3424

...
...

Breakpoint 2, __GI___libc_free (mem=0x555555559ee0) at ./malloc/malloc.c:3352
3352    in ./malloc/malloc.c
Freed 264 bytes
 Total freed 3424 bytes
[Inferior 1 (process 136) exited normally]

Relevant GDB documentation: https://sourceware./gdb/current/onlinedocs/gdb.html/Breakpoints-In-Python.html#Breakpoints-In-Python https://sourceware./gdb/current/onlinedocs/gdb.html/Finish-Breakpoints-in-Python.html#Finish-Breakpoints-in-Python

There is probably more than one way to do the breakpoint/finish processing using the Python API. One answer that you might want to look at: https://stackoverflow/a/42607055/11001972

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745001865a279276.html

最新回复(0)