valgrind_check_error.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  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. import os
  14. logfile = sys.argv[1]
  15. valgrind_command = ' '.join('"' + item + '"' for item in sys.argv[2:])
  16. # Execute a command and output its stdout text
  17. def execute(command):
  18. my_env = os.environ.copy()
  19. my_env["TEMP"] = my_env["TRAVIS_BUILD_DIR"] + "/tmp"
  20. my_env["TMPDIR"] = my_env["TRAVIS_BUILD_DIR"] + "/tmp"
  21. print("Temp Dir: " + my_env["TMPDIR"])
  22. f= open(my_env["TMPDIR"] + "/test.txt","w+")
  23. f.write("This is line %d\r\n")
  24. f.close()
  25. process = subprocess.Popen(command, env=my_env, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  26. # Poll process for new output until finished
  27. while True:
  28. nextline = process.stdout.readline().decode('utf-8')
  29. if nextline == '' and process.poll() is not None:
  30. break
  31. sys.stdout.write(nextline)
  32. sys.stdout.flush()
  33. return process.returncode
  34. ret_code = execute(valgrind_command)
  35. if not os.path.isfile(logfile):
  36. print("### PYTHON ERROR: Valgrind logfile does not exist: " + logfile)
  37. exit(1)
  38. log_content = ""
  39. with open(logfile, 'r') as content_file:
  40. log_content = content_file.read()
  41. if len(log_content) == 0:
  42. print("### PYTHON ERROR: Valgrind logfile is empty: " + logfile)
  43. exit(1)
  44. # Try to parse the output. Look for the following line:
  45. # ==17054== FILE DESCRIPTORS: 5 open at exit.
  46. descriptors_re = re.compile(r"^==(\d+)==\s+FILE DESCRIPTORS: (\d+) open at exit.$", re.MULTILINE)
  47. m = descriptors_re.match(log_content)
  48. if not m:
  49. print("### PYTHON ERROR: File descriptors header not found: " + logfile)
  50. print(log_content)
  51. exit(1)
  52. log_content = descriptors_re.sub('', log_content)
  53. valgrind_number = m.group(1)
  54. open_count = int(m.group(2))
  55. # Remove the open file descriptors which are inherited from parent. they look like this:
  56. #==21343== Open file descriptor 3: /home/user/open62541/build/bin/tests/discovery.log
  57. #==21343== <inherited from parent>
  58. #==21343==
  59. #==21343== Open file descriptor 2:
  60. #==21343== <inherited from parent>
  61. #==21343==
  62. #==21343== Open file descriptor 1:
  63. #==21343== <inherited from parent>
  64. #==21343==
  65. #==21343== Open file descriptor 0: /dev/pts/1
  66. #==21343== <inherited from parent>
  67. 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)
  68. log_content = replace_re.sub('', log_content)
  69. # Valgrind detected a memleak if ret_code != 0
  70. if ret_code != 0:
  71. print(log_content)
  72. exit(ret_code)
  73. # No issues by valgrind
  74. if log_content.isspace():
  75. exit(0)
  76. # There is something fishy in the valgrind output, so error-exit
  77. print(log_content)
  78. exit(1)