valgrind_check_error.py 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. #!/usr/bin/env python
  2. # coding: UTF-8
  3. # This Source Code Form is subject to the terms of the Mozilla Public
  4. # License, v. 2.0. If a copy of the MPL was not distributed with this
  5. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. # This script checks the valgrind output for errors.
  7. # The track-fds does not cause an error if there are too many FDs open,
  8. # therefore we parse the output and fail if there are open FDs
  9. import sys
  10. import subprocess
  11. import os.path
  12. import re
  13. logfile = sys.argv[1]
  14. valgrind_command = ' '.join('"' + item + '"' for item in sys.argv[2:])
  15. # Execute a command and output its stdout text
  16. def execute(command):
  17. process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  18. # Poll process for new output until finished
  19. while True:
  20. nextline = process.stdout.readline().decode('utf-8')
  21. if nextline == '' and process.poll() is not None:
  22. break
  23. sys.stdout.write(nextline)
  24. sys.stdout.flush()
  25. return process.returncode
  26. ret_code = execute(valgrind_command)
  27. if not os.path.isfile(logfile):
  28. print("### PYTHON ERROR: Valgrind logfile does not exist: " + logfile)
  29. exit(1)
  30. log_content = ""
  31. with open(logfile, 'r') as content_file:
  32. log_content = content_file.read()
  33. if len(log_content) == 0:
  34. print("### PYTHON ERROR: Valgrind logfile is empty: " + logfile)
  35. exit(1)
  36. # Try to parse the output. Look for the following line:
  37. # ==17054== FILE DESCRIPTORS: 5 open at exit.
  38. descriptors_re = re.compile(r"^==(\d+)==\s+FILE DESCRIPTORS: (\d+) open at exit.$", re.MULTILINE)
  39. m = descriptors_re.match(log_content)
  40. if not m:
  41. print("### PYTHON ERROR: File descriptors header not found: " + logfile)
  42. print(log_content)
  43. exit(1)
  44. log_content = descriptors_re.sub('', log_content)
  45. valgrind_number = m.group(1)
  46. open_count = int(m.group(2))
  47. # Remove the open file descriptors which are inherited from parent. they look like this:
  48. #==21343== Open file descriptor 3: /home/user/open62541/build/bin/tests/discovery.log
  49. #==21343== <inherited from parent>
  50. #==21343==
  51. #==21343== Open file descriptor 2:
  52. #==21343== <inherited from parent>
  53. #==21343==
  54. #==21343== Open file descriptor 1:
  55. #==21343== <inherited from parent>
  56. #==21343==
  57. #==21343== Open file descriptor 0: /dev/pts/1
  58. #==21343== <inherited from parent>
  59. replace_re = re.compile(r"^=="+str(valgrind_number)+r"==\s+Open file descriptor \d+:\s*[^\s]*$\n^=="+str(valgrind_number)+r"==\s+<inherited from parent>$\n(^=="+str(valgrind_number)+r"==\s+$\n)*", re.MULTILINE)
  60. log_content = replace_re.sub('', log_content)
  61. # Valgrind detected a memleak if ret_code != 0
  62. if ret_code != 0:
  63. print(log_content)
  64. exit(ret_code)
  65. # No issues by valgrind
  66. if log_content.isspace():
  67. exit(0)
  68. # There is something fishy in the valgrind output, so error-exit
  69. print(log_content)
  70. exit(1)