aboutsummaryrefslogtreecommitdiff
path: root/kernel/mm/memcheck.py
diff options
context:
space:
mode:
authornthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
committernthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
commitc63f340d90800895f007de64b7d2d14624263331 (patch)
tree2c0849fa597dd6da831c8707b6f2603403778d7b /kernel/mm/memcheck.py
Created student weenix repository
Diffstat (limited to 'kernel/mm/memcheck.py')
-rw-r--r--kernel/mm/memcheck.py158
1 files changed, 158 insertions, 0 deletions
diff --git a/kernel/mm/memcheck.py b/kernel/mm/memcheck.py
new file mode 100644
index 0000000..49f40fd
--- /dev/null
+++ b/kernel/mm/memcheck.py
@@ -0,0 +1,158 @@
+import gdb
+
+import string
+
+import weenix
+import weenix.kmem
+import weenix.stack
+
+
+class SlabAllocation:
+ def __init__(self, addr, stack, allocator, initialization):
+ self.addr = addr
+ self.stack = stack
+ self.allocator = allocator
+ self.initialization = initialization
+
+
+class PageAllocation:
+ def __init__(self, addr, stack, npages, slabdata, initialization):
+ self.addr = addr
+ self.stack = stack
+ self.npages = npages
+ self.slabdata = slabdata
+ self.initialization = initialization
+
+
+class Memcheck:
+ def __init__(self):
+ self._slab_alloc_count = 0
+ self._slab_free_count = 0
+ self._slab_invalid_free = 0
+ self._slab_allocated = {}
+ self._page_alloc_count = 0
+ self._page_free_count = 0
+ self._page_invalid_free = 0
+ self._page_allocated = {}
+ self._initialized = False
+ weenix.Hook("slab_obj_alloc", self._slab_alloc_callback)
+ weenix.Hook("slab_obj_free", self._slab_free_callback)
+ weenix.Hook("page_alloc", self._page_alloc_callback)
+ weenix.Hook("page_free", self._page_free_callback)
+ weenix.Hook("initialized", self._initialized_callback)
+ weenix.Hook("shutdown", self._shutdown_callback)
+
+ def _slab_alloc_callback(self, args):
+ addr = args["addr"]
+ if string.atol(addr, 16) == 0:
+ return False
+ stack = weenix.stack.Stack(gdb.newest_frame().older())
+ allocator = weenix.kmem.SlabAllocator(
+ gdb.Value(string.atol(args["allocator"].split(" ")[0], 16)).cast(
+ gdb.lookup_type("void").pointer()
+ )
+ )
+ self._slab_allocated[addr] = SlabAllocation(
+ addr, stack, allocator, not self._initialized
+ )
+ if self._initialized:
+ self._slab_alloc_count += 1
+ return False
+
+ def _slab_free_callback(self, args):
+ if not args["addr"] in self._slab_allocated:
+ self._slab_invalid_free += 1
+ print("Invalid free of address " + args["addr"] + ":")
+ print(weenix.stack.Stack(gdb.newest_frame().older()))
+ else:
+ if not self._slab_allocated[args["addr"]].initialization:
+ self._slab_free_count += 1
+ del self._slab_allocated[args["addr"]]
+ return False
+
+ def _page_alloc_callback(self, args):
+ addr = args["addr"]
+ if string.atol(addr, 16) == 0:
+ return False
+ stack = weenix.stack.Stack(gdb.newest_frame().older())
+ slabdata = stack.contains("_slab_allocator_grow")
+ self._page_allocated[addr] = PageAllocation(
+ addr, stack, string.atoi(args["npages"]), slabdata, not self._initialized
+ )
+ if self._initialized and not slabdata:
+ self._page_alloc_count += 1
+ return False
+
+ def _page_free_callback(self, args):
+ if not args["addr"] in self._page_allocated:
+ self._page_invalid_free += 1
+ print("Invalid free of address " + args["addr"] + ":")
+ print(weenix.stack.Stack(gdb.newest_frame().older()))
+ elif self._page_allocated[args["addr"]].npages != string.atoi(args["npages"]):
+ self._page_invalid_free += 1
+ print(
+ "Address "
+ + args["addr"]
+ + " allocated for "
+ + str(self._page_allocated[args["addr"]].npages)
+ + " pages:"
+ )
+ print(self._page_allocated[args["addr"]].stack)
+ print("but freed with " + args["npages"] + " pages:")
+ print(weenix.stack.Stack(gdb.newest_frame().older()))
+ del self._page_allocated[args["addr"]]
+ else:
+ if (
+ not self._page_allocated[args["addr"]].initialization
+ and not self._page_allocated[args["addr"]].slabdata
+ ):
+ self._page_free_count += 1
+ del self._page_allocated[args["addr"]]
+ return False
+
+ def _initialized_callback(self, args):
+ self._initialized = True
+ return False
+
+ def _shutdown_callback(self, args):
+ size = 0
+ for alloc in self._slab_allocated.itervalues():
+ if not alloc.initialization:
+ size += alloc.allocator.size()
+ print(
+ 'Leaked {0} bytes from "{1}" at {2}:'.format(
+ alloc.allocator.size(), alloc.allocator.name(), alloc.addr
+ )
+ )
+ print(alloc.stack)
+ npages = 0
+ for page in self._page_allocated.itervalues():
+ if not page.initialization and not page.slabdata:
+ npages += page.npages
+ print("Leaked {0} pages at {1}:".format(page.npages, page.addr))
+ print(page.stack)
+ print(
+ "{0} slab allocs, {1} frees ({2} bytes leaked)".format(
+ self._slab_alloc_count, self._slab_free_count, size
+ )
+ )
+ print(
+ "{0} page allocs, {1} frees ({2} pages leaked)".format(
+ self._page_alloc_count, self._page_free_count, npages
+ )
+ )
+ print("{0} invalid slab frees".format(self._slab_invalid_free))
+ print("{0} invalid page frees".format(self._page_invalid_free))
+ return False
+
+
+class MemcheckFlag(weenix.Flag):
+ def __init__(self):
+ weenix.Flag.__init__(self, "memcheck", gdb.COMMAND_DATA)
+
+ def callback(self, value):
+ if value:
+ Memcheck()
+
+
+MemcheckFlag()