From 9a2be6163af144139e5830af03ea8ffc95f490d0 Mon Sep 17 00:00:00 2001 From: Emiliano Testa Date: Mon, 5 Dec 2022 19:59:58 +0000 Subject: [PATCH 1/4] Introduced a script to capture leaks from malloc / free --- leak_detector/README.md | 15 ++++++ leak_detector/leak_detector.py | 83 ++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 leak_detector/README.md create mode 100644 leak_detector/leak_detector.py diff --git a/leak_detector/README.md b/leak_detector/README.md new file mode 100644 index 0000000..3b9398f --- /dev/null +++ b/leak_detector/README.md @@ -0,0 +1,15 @@ +# Leak detector + +Matches all calls to `malloc` and `free` and shows any unmatched `malloc` call +with a Bbcount to jump to. + +## Usage +``` +ugo start +mleak +``` + +Before using the script it must be loaded in to the debugger: +``` +source PATHTOADDONS/leak_detector/leak_detector.py +``` diff --git a/leak_detector/leak_detector.py b/leak_detector/leak_detector.py new file mode 100644 index 0000000..3a63113 --- /dev/null +++ b/leak_detector/leak_detector.py @@ -0,0 +1,83 @@ +""" +Find memory leaks when malloc and free are used. +Starting from the current position it matches all calls to malloc +with calls to free with the same pointer and +keeps track of the calls that have no corresponding call to free. +It is recommended to go to the start of time and then use the command. +It prints a full list of unmatched calls at the end. + Usage: mleak + +Contibutors: Emiliano Testa +Copyright (C) 2022 Undo Ltd +""" + +import gdb +import copy + +from undodb.debugger_extensions import ( + debugger_utils, + udb, +) + + +ALLOC_FN="malloc" +FREE_FN="free" +all_allocs = [] + + +class MemAlloc(): + def __init__(self, addr, size, bbcount): + self.addr = addr + self.size = size + self.bbcount = bbcount + +def handle_alloc_fn(): + frame = gdb.selected_frame() + size = frame.read_register("rdi") + bbcount = udb.time.get().bbcount + gdb.execute("finish") + frame = gdb.selected_frame() + addr = frame.read_register("rax") + global all_allocs + all_allocs.append(MemAlloc(addr, size, bbcount)) + +def handle_free_fn(): + frame = gdb.selected_frame() + addr = frame.read_register("rdi") + bbcount = udb.time.get().bbcount + global all_allocs + for alloc in copy.copy(all_allocs): + if alloc.addr == addr: + all_allocs.remove(alloc) + + +def handle_bpEvent(event): + if hasattr(event, 'breakpoints'): + for bp in event.breakpoints: + if bp.location == ALLOC_FN: + handle_alloc_fn() + elif bp.location == FREE_FN: + handle_free_fn() + + +class LeakDetect(gdb.Command): + def __init__(self): + super().__init__("mleaks", gdb.COMMAND_USER) + + @staticmethod + def invoke(arg, from_tty): + bp_malloc = gdb.Breakpoint(ALLOC_FN) + bp_free = gdb.Breakpoint(FREE_FN) + gdb.events.stop.connect(handle_bpEvent) + end_of_time = udb.get_event_log_extent().max_bbcount + gdb.execute("continue") + while udb.time.get().bbcount < end_of_time: + gdb.execute("continue") + print("Calls to allocator fn that don't have a corresponding free") + global all_allocs + for alloc in all_allocs: + print(f'{hex(alloc.addr)} - {hex(alloc.size)} - {alloc.bbcount}') + + +LeakDetect() + From 519bff3e6b8fc3555d248653e7af35e24afeb271 Mon Sep 17 00:00:00 2001 From: Emiliano Testa Date: Mon, 5 Dec 2022 20:06:02 +0000 Subject: [PATCH 2/4] fixup! Introduced a script to capture leaks from malloc / free --- leak_detector/leak_detector.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/leak_detector/leak_detector.py b/leak_detector/leak_detector.py index 3a63113..c1ed117 100644 --- a/leak_detector/leak_detector.py +++ b/leak_detector/leak_detector.py @@ -11,11 +11,10 @@ Copyright (C) 2022 Undo Ltd """ -import gdb import copy +import gdb from undodb.debugger_extensions import ( - debugger_utils, udb, ) @@ -44,14 +43,13 @@ def handle_alloc_fn(): def handle_free_fn(): frame = gdb.selected_frame() addr = frame.read_register("rdi") - bbcount = udb.time.get().bbcount global all_allocs for alloc in copy.copy(all_allocs): if alloc.addr == addr: all_allocs.remove(alloc) -def handle_bpEvent(event): +def handle_bp_event(event): if hasattr(event, 'breakpoints'): for bp in event.breakpoints: if bp.location == ALLOC_FN: @@ -66,9 +64,9 @@ def __init__(self): @staticmethod def invoke(arg, from_tty): - bp_malloc = gdb.Breakpoint(ALLOC_FN) - bp_free = gdb.Breakpoint(FREE_FN) - gdb.events.stop.connect(handle_bpEvent) + gdb.Breakpoint(ALLOC_FN) + gdb.Breakpoint(FREE_FN) + gdb.events.stop.connect(handle_bp_event) end_of_time = udb.get_event_log_extent().max_bbcount gdb.execute("continue") while udb.time.get().bbcount < end_of_time: @@ -80,4 +78,3 @@ def invoke(arg, from_tty): LeakDetect() - From c35af91672b8ea8ed21891b917be7fffbb8fca70 Mon Sep 17 00:00:00 2001 From: Emiliano Testa Date: Mon, 5 Dec 2022 20:17:03 +0000 Subject: [PATCH 3/4] fixup! Introduced a script to capture leaks from malloc / free --- leak_detector/leak_detector.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/leak_detector/leak_detector.py b/leak_detector/leak_detector.py index c1ed117..c9ae058 100644 --- a/leak_detector/leak_detector.py +++ b/leak_detector/leak_detector.py @@ -19,17 +19,18 @@ ) -ALLOC_FN="malloc" -FREE_FN="free" +ALLOC_FN = "malloc" +FREE_FN = "free" all_allocs = [] -class MemAlloc(): +class MemAlloc: def __init__(self, addr, size, bbcount): self.addr = addr self.size = size self.bbcount = bbcount + def handle_alloc_fn(): frame = gdb.selected_frame() size = frame.read_register("rdi") @@ -37,20 +38,21 @@ def handle_alloc_fn(): gdb.execute("finish") frame = gdb.selected_frame() addr = frame.read_register("rax") - global all_allocs + # global all_allocs all_allocs.append(MemAlloc(addr, size, bbcount)) + def handle_free_fn(): frame = gdb.selected_frame() addr = frame.read_register("rdi") - global all_allocs + # global all_allocs for alloc in copy.copy(all_allocs): if alloc.addr == addr: all_allocs.remove(alloc) def handle_bp_event(event): - if hasattr(event, 'breakpoints'): + if hasattr(event, "breakpoints"): for bp in event.breakpoints: if bp.location == ALLOC_FN: handle_alloc_fn() @@ -72,9 +74,9 @@ def invoke(arg, from_tty): while udb.time.get().bbcount < end_of_time: gdb.execute("continue") print("Calls to allocator fn that don't have a corresponding free") - global all_allocs + # global all_allocs for alloc in all_allocs: - print(f'{hex(alloc.addr)} - {hex(alloc.size)} - {alloc.bbcount}') + print(f"{hex(alloc.addr)} - {hex(alloc.size)} - {alloc.bbcount}") LeakDetect() From e73561c4847944a66281a52e81b0eafe2e9502fa Mon Sep 17 00:00:00 2001 From: Emiliano Testa Date: Mon, 5 Dec 2022 20:18:13 +0000 Subject: [PATCH 4/4] fixup! Introduced a script to capture leaks from malloc / free --- leak_detector/leak_detector.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/leak_detector/leak_detector.py b/leak_detector/leak_detector.py index c9ae058..822033d 100644 --- a/leak_detector/leak_detector.py +++ b/leak_detector/leak_detector.py @@ -38,14 +38,12 @@ def handle_alloc_fn(): gdb.execute("finish") frame = gdb.selected_frame() addr = frame.read_register("rax") - # global all_allocs all_allocs.append(MemAlloc(addr, size, bbcount)) def handle_free_fn(): frame = gdb.selected_frame() addr = frame.read_register("rdi") - # global all_allocs for alloc in copy.copy(all_allocs): if alloc.addr == addr: all_allocs.remove(alloc) @@ -74,7 +72,6 @@ def invoke(arg, from_tty): while udb.time.get().bbcount < end_of_time: gdb.execute("continue") print("Calls to allocator fn that don't have a corresponding free") - # global all_allocs for alloc in all_allocs: print(f"{hex(alloc.addr)} - {hex(alloc.size)} - {alloc.bbcount}")