Fix memory leak detection and reporting which accidentally was dropped a month ago

Signed-off-by: Martin Winter <mwinter@opensourcerouting.org>
This commit is contained in:
Martin Winter 2017-04-26 19:54:25 -07:00 committed by Donald Sharp
parent e9125d9286
commit 50c40bdebb
7 changed files with 193 additions and 10 deletions

View File

@ -81,8 +81,8 @@ And create frr User and frrvty group as follows:
py.test -s -v --tb=no py.test -s -v --tb=no
All test_* scripts in subdirectories are detected and executed (unless disabled in All test_* scripts in subdirectories are detected and executed (unless
`pytest.ini` file) disabled in `pytest.ini` file)
`--tb=no` disables the python traceback which might be irrelevant unless the `--tb=no` disables the python traceback which might be irrelevant unless the
test script itself is debugged test script itself is debugged
@ -101,6 +101,38 @@ For the simulated topology, see the description in the python file
If you need to clear the mininet setup between tests (if it isn't cleanly If you need to clear the mininet setup between tests (if it isn't cleanly
shutdown), then use the `mn -c` command to clean up the environment shutdown), then use the `mn -c` command to clean up the environment
#### (Optional) StdErr log from daemos after exit
To enable the reporting of any messages seen on StdErr after the
daemons exit, the following env variable can be set.
export TOPOTESTS_CHECK_STDERR=Yes
(The value doesn't matter at this time. The check is if the env variable
exists or not)
There is no pass/fail on this reporting. The Output will be reported to
the console
export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
This will enable the check and output to console and the writing of
the information to files with the given prefix (followed by testname),
ie `/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case of a
memory leak.
#### (Optional) Collect Memory Leak Information
FreeRangeRouting processes have the capabilities to report remaining memory
allocations upon exit. To enable the reporting of the memory, define an
enviroment variable `TOPOTESTS_CHECK_MEMLEAK` with the file prefix, ie
export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
This will enable the check and output to console and the writing of
the information to files with the given prefix (followed by testname),
ie `/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case of a
memory leak.
## License ## License
All the configs and scripts are licensed under a ISC-style license. See All the configs and scripts are licensed under a ISC-style license. See

View File

@ -810,11 +810,13 @@ def test_shutdown_check_stderr():
print("******************************************\n") print("******************************************\n")
if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: if os.environ.get('TOPOTESTS_CHECK_STDERR') is None:
print("SKIPPED (Disabled) - TOPOTESTS_CHECK_STDERR undefined\n") print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n")
pytest.skip('Skipping test for Stderr output and memory leaks') pytest.skip('Skipping test for Stderr output')
thisDir = os.path.dirname(os.path.realpath(__file__)) thisDir = os.path.dirname(os.path.realpath(__file__))
print("thisDir=" + thisDir)
net['r1'].stopRouter() net['r1'].stopRouter()
log = net['r1'].getStdErr('ripd') log = net['r1'].getStdErr('ripd')
@ -836,6 +838,25 @@ def test_shutdown_check_stderr():
print("\nZebra StdErr Log:\n" + log) print("\nZebra StdErr Log:\n" + log)
def test_shutdown_check_memleak():
global fatal_error
global net
# Skip if previous fatal error condition is raised
if (fatal_error != ""):
pytest.skip(fatal_error)
if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None:
print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n")
pytest.skip('Skipping test for memory leaks')
thisDir = os.path.dirname(os.path.realpath(__file__))
for i in range(1, 2):
net['r%s' % i].stopRouter()
net['r%s' % i].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__))
if __name__ == '__main__': if __name__ == '__main__':
setLogLevel('info') setLogLevel('info')

View File

@ -310,6 +310,7 @@ def test_bgp_routingTable():
# For debugging after starting FRR/Quagga daemons, uncomment the next line # For debugging after starting FRR/Quagga daemons, uncomment the next line
# CLI(net) # CLI(net)
def test_shutdown_check_stderr(): def test_shutdown_check_stderr():
global fatal_error global fatal_error
global net global net
@ -319,7 +320,8 @@ def test_shutdown_check_stderr():
pytest.skip(fatal_error) pytest.skip(fatal_error)
if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: if os.environ.get('TOPOTESTS_CHECK_STDERR') is None:
pytest.skip('Skipping test for Stderr output and memory leaks') print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n")
pytest.skip('Skipping test for Stderr output')
thisDir = os.path.dirname(os.path.realpath(__file__)) thisDir = os.path.dirname(os.path.realpath(__file__))
@ -334,6 +336,24 @@ def test_shutdown_check_stderr():
print("\nZebra StdErr Log:\n" + log) print("\nZebra StdErr Log:\n" + log)
def test_shutdown_check_memleak():
global fatal_error
global net
# Skip if previous fatal error condition is raised
if (fatal_error != ""):
pytest.skip(fatal_error)
if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None:
print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n")
pytest.skip('Skipping test for memory leaks')
thisDir = os.path.dirname(os.path.realpath(__file__))
net['r1'].stopRouter()
net['r1'].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__))
if __name__ == '__main__': if __name__ == '__main__':
setLogLevel('info') setLogLevel('info')

View File

@ -724,7 +724,8 @@ def test_shutdown_check_stderr():
pytest.skip(fatal_error) pytest.skip(fatal_error)
if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: if os.environ.get('TOPOTESTS_CHECK_STDERR') is None:
pytest.skip('Skipping test for Stderr output and memory leaks') print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n")
pytest.skip('Skipping test for Stderr output')
thisDir = os.path.dirname(os.path.realpath(__file__)) thisDir = os.path.dirname(os.path.realpath(__file__))
@ -741,6 +742,24 @@ def test_shutdown_check_stderr():
print("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log)) print("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log))
def test_shutdown_check_memleak():
global fatal_error
global net
# Skip if previous fatal error condition is raised
if (fatal_error != ""):
pytest.skip(fatal_error)
if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None:
print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n")
pytest.skip('Skipping test for memory leaks')
thisDir = os.path.dirname(os.path.realpath(__file__))
for i in range(1, 5):
net['r%s' % i].stopRouter()
net['r%s' % i].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__))
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -23,6 +23,7 @@
# #
import os import os
import errno
import re import re
import sys import sys
import glob import glob
@ -51,6 +52,27 @@ def int2dpid(dpid):
'please either specify a dpid or use a ' 'please either specify a dpid or use a '
'canonical switch name such as s23.') 'canonical switch name such as s23.')
def pid_exists(pid):
"Check whether pid exists in the current process table."
if pid <= 0:
return False
try:
os.kill(pid, 0)
except OSError as err:
if err.errno == errno.ESRCH:
# ESRCH == No such process
return False
elif err.errno == errno.EPERM:
# EPERM clearly means there's a process to deny access to
return True
else:
# According to "man 2 kill" possible error values are
# (EINVAL, EPERM, ESRCH)
raise
else:
return True
def addRouter(topo, name): def addRouter(topo, name):
"Adding a FRRouter (or Quagga) to Topology" "Adding a FRRouter (or Quagga) to Topology"
@ -119,7 +141,9 @@ class Router(Node):
rundaemons = self.cmd('ls -1 /var/run/%s/*.pid' % self.routertype) rundaemons = self.cmd('ls -1 /var/run/%s/*.pid' % self.routertype)
if rundaemons is not None: if rundaemons is not None:
for d in StringIO.StringIO(rundaemons): for d in StringIO.StringIO(rundaemons):
self.cmd('kill -7 `cat %s`' % d.rstrip()) daemonpid = self.cmd('cat %s' % d.rstrip()).rstrip()
if pid_exists(int(daemonpid)):
self.cmd('kill -7 %s' % daemonpid)
self.waitOutput() self.waitOutput()
def removeIPs(self): def removeIPs(self):
for interface in self.intfNames(): for interface in self.intfNames():
@ -261,6 +285,34 @@ class Router(Node):
"Return the type of Router (frr or quagga)" "Return the type of Router (frr or quagga)"
return self.routertype return self.routertype
def report_memory_leaks(self, filename_prefix, testscript):
"Report Memory Leaks to file prefixed with given string"
leakfound = False
filename = filename_prefix + re.sub(r"\.py", "", testscript) + ".txt"
for daemon in self.daemons:
if (self.daemons[daemon] == 1):
log = self.getStdErr(daemon)
if "memstats" in log:
# Found memory leak
print("\nRouter %s %s StdErr Log:\n%s" % (self.name, daemon, log))
if not leakfound:
leakfound = True
# Check if file already exists
fileexists = os.path.isfile(filename)
leakfile = open(filename, "a")
if not fileexists:
# New file - add header
leakfile.write("# Memory Leak Detection for topotest %s\n\n" % testscript)
leakfile.write("## Router %s\n" % self.name)
leakfile.write("### Process %s\n" % daemon)
log = re.sub("core_handler: ", "", log)
log = re.sub(r"(showing active allocations in memory group [a-zA-Z0-9]+)", r"\n#### \1\n", log)
log = re.sub("memstats: ", " ", log)
leakfile.write(log)
leakfile.write("\n")
if leakfound:
leakfile.close()
class LegacySwitch(OVSSwitch): class LegacySwitch(OVSSwitch):

View File

@ -380,7 +380,8 @@ def test_shutdown_check_stderr():
pytest.skip(fatal_error) pytest.skip(fatal_error)
if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: if os.environ.get('TOPOTESTS_CHECK_STDERR') is None:
pytest.skip('Skipping test for Stderr output and memory leaks') print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n")
pytest.skip('Skipping test for Stderr output')
thisDir = os.path.dirname(os.path.realpath(__file__)) thisDir = os.path.dirname(os.path.realpath(__file__))
@ -395,6 +396,25 @@ def test_shutdown_check_stderr():
print("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log)) print("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log))
def test_shutdown_check_memleak():
global fatal_error
global net
# Skip if previous fatal error condition is raised
if (fatal_error != ""):
pytest.skip(fatal_error)
if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None:
print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n")
pytest.skip('Skipping test for memory leaks')
thisDir = os.path.dirname(os.path.realpath(__file__))
for i in range(1, 5):
net['r%s' % i].stopRouter()
net['r%s' % i].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__))
if __name__ == '__main__': if __name__ == '__main__':
setLogLevel('info') setLogLevel('info')

View File

@ -338,7 +338,8 @@ def test_shutdown_check_stderr():
pytest.skip(fatal_error) pytest.skip(fatal_error)
if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: if os.environ.get('TOPOTESTS_CHECK_STDERR') is None:
pytest.skip('Skipping test for Stderr output and memory leaks') print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n")
pytest.skip('Skipping test for Stderr output')
thisDir = os.path.dirname(os.path.realpath(__file__)) thisDir = os.path.dirname(os.path.realpath(__file__))
@ -353,6 +354,24 @@ def test_shutdown_check_stderr():
print("\nZebra StdErr Log:\n" + log) print("\nZebra StdErr Log:\n" + log)
def test_shutdown_check_memleak():
global fatal_error
global net
# Skip if previous fatal error condition is raised
if (fatal_error != ""):
pytest.skip(fatal_error)
if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None:
print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n")
pytest.skip('Skipping test for memory leaks')
thisDir = os.path.dirname(os.path.realpath(__file__))
net['r1'].stopRouter()
net['r1'].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__))
if __name__ == '__main__': if __name__ == '__main__':
setLogLevel('info') setLogLevel('info')