#!/usr/bin/env python
#
# leak.py
#
# A toy memory leak detector for ltrace
#
# This is a proof of concept work for detecting memory leaks from the output of
# ltrace. The idea is to simply trace malloc and free calls in an execution and
# report non-freed allocations in the end. Other allocation methods are not
# added to keep it simple.
#
# This can only detect the existence of leaks without showing the location of
# the allocations in the source code. Also, since this is a dynamic analysis
# method, absence of leaks does not necessarily mean the program is leak free
# for all execution paths. Therefore, this is not very useful in practice.
#
# To run the script, you need to redirect ltrace output from stderr to stdout,
# and then redirect regular output to null device, and pipe stdout to this
# script. An example for `ls` command is shown below:
#
# ltrace ls 2>&1 >/dev/null | ./leak.py
#
import re
import sys
lines = list(filter(lambda l: "malloc" in l or "free" in l, sys.stdin))
print("These are all the malloc and free calls in the execution")
for line in lines:
print(line, end="")
mems = {}
for line in lines:
toks = line.split()
# malloc(size) = addr
if "malloc" in toks[0]:
size = re.search(r"malloc\((.*)\)", toks[0]).group(1)
addr = toks[2]
mems[addr] = size
# free(addr) = <void>
elif "free" in toks[0]:
addr = re.search(r"free\((.*)\)", toks[0]).group(1)
mems.pop(addr, None)
print()
print("Execution ended with the following non-freed allocations")
for k, v in mems.items():
print("leaked", v, "bytes of memory at", k)