Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

topotato: add valgrind support and check for memory leaks #148

Open
wants to merge 1 commit into
base: topotato-base
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions tools/valgrind.supp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
{
<zlog_keep_working_at_exit>
Memcheck:Leak
match-leak-kinds: reachable
fun:calloc
fun:qcalloc
fun:zlog_target_clone
}
{
<libyang1_1.0.184>
Memcheck:Leak
match-leak-kinds: reachable
fun:calloc
fun:_dlerror_run
fun:dlopen@@GLIBC_2.2.5
obj:/usr/lib/x86_64-linux-gnu/libyang.so.1.9.2
fun:ly_load_plugins
}
{
<zprivs_init leak in a function we do not control>
Memcheck:Leak
fun:calloc
fun:cap_init
fun:zprivs_caps_init
}
{
<sqlite3 leak in a function we do not control>
Memcheck:Leak
fun:malloc
...
fun:sqlite3_step
}
{
<libyang2 prefix_data stuff>
Memcheck:Leak
fun:calloc
fun:ly_store_prefix_data
...
fun:yang_module_load
}
{
<libyang2 lys_compile_type_union>
Memcheck:Leak
fun:realloc
fun:lys_compile_type_union
...
fun:yang_module_load
}
{
<libyang2 pcre2_compile>
Memcheck:Leak
fun:malloc
fun:pcre2_compile_8
...
fun:yang_module_load
}
{
<libyang2 lys_compile_type_patterns malloc>
Memcheck:Leak
fun:malloc
fun:lys_compile_type_patterns
...
fun:yang_module_load
}
{
<libyang2 lys_compile_type_patterns calloc>
Memcheck:Leak
fun:calloc
fun:lys_compile_type_patterns
...
fun:yang_module_load
}
{
<libyang2 lys_compile_type>
Memcheck:Leak
fun:calloc
fun:lys_compile_type
...
fun:yang_module_load
}
{
<libyang2 lys_compile_type_range>
Memcheck:Leak
...
fun:lys_compile_type_range
...
fun:yang_module_load
}
27 changes: 26 additions & 1 deletion topotato/frr/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,6 @@ class FRRRouterNS(TopotatoNetwork.RouterNS, CallableNS):
rundir: Optional[str]
rtrcfg: Dict[str, str]
livelogs: Dict[str, LiveLog]

def __init__(
self, instance: TopotatoNetwork, name: str, configs: _FRRConfigProtocol
):
Expand Down Expand Up @@ -614,6 +613,32 @@ def start_daemon(self, daemon: str):

execpath = os.path.join(frrpath, binmap[daemon])
cmdline = []

# Add valgrind

this_dir = os.path.dirname(
os.path.abspath(os.path.realpath(__file__))
)

supp_file = os.path.abspath(
os.path.join(this_dir, "../../tools/valgrind.supp")
)

cmdline.extend(
[
"valgrind",
# "--leak-check=full",
# "--show-leak-kinds=all",
"--track-origins=yes",
"--trace-children=yes",
"--num-callers=50",
"--gen-suppressions=all",
"--expensive-definedness-checks=yes",
"--error-exitcode=1",
"--suppressions=%s" % supp_file,
"--log-file=%s" % self.tempfile(daemon + ".valgrind.log"),
]
)

cmdline.extend(
[
Expand Down
37 changes: 37 additions & 0 deletions topotato/topolinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,10 +518,47 @@ def coverage_wait(self) -> Optional[str]:
self._lcov.wait()
self._lcov = None
return self._covdatafile

def _check_memory_leaks(self):

class DaemonMemoryLeak(Exception):
...

def has_memory_leak(valgrind_output):
lines = valgrind_output.split('\n')
bytes = 0
for line in lines:
if 'definitely lost' in line:
# Extract bytes for definitely lost memory
parts = line.split(':')
bytes = int(parts[-1].split()[0].replace(',', ''))
elif 'indirectly lost' in line:
# Extract bytes for indirectly lost memory
parts = line.split(':')
bytes = int(parts[-1].split()[0].replace(',', ''))
elif 'possibly lost' in line:
# Extract bytes for possibly lost memory
parts = line.split(':')
bytes = int(parts[-1].split()[0].replace(',', ''))

return bytes > 0

for rns in self.routers.values():
def check_valgrind(daemons):
for daemon in daemons:
valgrind = rns.tempfile(daemon + ".valgrind.log")
with open(valgrind, "r") as f:
valgrind_output = f.read()
if (has_memory_leak(valgrind_output)):
raise DaemonMemoryLeak(f"Valgrind found memory leaks on {rns.name} on {daemon}: \n" + valgrind_output)

rns.end_prep()
check_valgrind(rns.logfiles.keys())

def stop(self):
for rns in self.routers.values():
rns.end_prep()
self._check_memory_leaks()
for rns in self.routers.values():
rns.end()
self.switch_ns.end()
Expand Down
5 changes: 4 additions & 1 deletion vm/ubuntu/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ sudo apt-get install \
python3-pytest python3-scapy python3-exabgp \
install-info build-essential libsnmp-dev perl linux-modules-extra-`uname -r` \
libcap-dev python2 libelf-dev libunwind-dev cmake libpcre2-dev \
protobuf-c-compiler libprotobuf-c-dev libzmq5 libzmq3-dev -y
protobuf-c-compiler libprotobuf-c-dev libzmq5 libzmq3-dev libc6-i386 libc6-dbg -y

# install valgrind after installing the dependencies
sudo apt install valgrind -y

cd /tmp
git clone https://github.com/CESNET/libyang.git
Expand Down