Donate CPU/test-my-pr: Add timeout for the Cppcheck execution (#2500)

This adds a timeout of 60 minutes for the Cppcheck analysis.
Timed out results do not count as crash but they are uploaded and
marked with "TO!" in the list of the latest results. No "diff" is
generated for timed out results so they do not add wrong entries to
the "Diff report".
In test-my-pr.py the timed out results are listed separately just like
the crashes.
donate-cpu-server.py: Add timeout report
This commit is contained in:
Sebastian 2020-01-24 12:33:51 +01:00 committed by GitHub
parent 11993ed999
commit 2d95c35974
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 95 additions and 6 deletions

View File

@ -22,7 +22,7 @@ import operator
# Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/ # Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/
# Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic # Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic
# changes) # changes)
SERVER_VERSION = "1.3.1" SERVER_VERSION = "1.3.2"
OLD_VERSION = '1.90' OLD_VERSION = '1.90'
@ -67,6 +67,7 @@ def overviewReport() -> str:
html = '<html><head><title>daca@home</title></head><body>\n' html = '<html><head><title>daca@home</title></head><body>\n'
html += '<h1>daca@home</h1>\n' html += '<h1>daca@home</h1>\n'
html += '<a href="crash.html">Crash report</a><br>\n' html += '<a href="crash.html">Crash report</a><br>\n'
html += '<a href="timeout.html">Timeout report</a><br>\n'
html += '<a href="stale.html">Stale report</a><br>\n' html += '<a href="stale.html">Stale report</a><br>\n'
html += '<a href="diff.html">Diff report</a><br>\n' html += '<a href="diff.html">Diff report</a><br>\n'
html += '<a href="head.html">HEAD report</a><br>\n' html += '<a href="head.html">HEAD report</a><br>\n'
@ -82,7 +83,7 @@ def overviewReport() -> str:
def fmt(a: str, b: str, c: str = None, d: str = None, e: str = None, link: bool = True) -> str: def fmt(a: str, b: str, c: str = None, d: str = None, e: str = None, link: bool = True) -> str:
column_width = [40, 10, 5, 6, 6, 8] column_width = [40, 10, 5, 7, 7, 8]
ret = a ret = a
while len(ret) < column_width[0]: while len(ret) < column_width[0]:
ret += ' ' ret += ' '
@ -218,6 +219,47 @@ def crashReport(results_path: str) -> str:
return html return html
def timeoutReport(results_path: str) -> str:
html = '<html><head><title>Timeout report</title></head><body>\n'
html += '<h1>Timeout report</h1>\n'
html += '<pre>\n'
html += '<b>' + fmt('Package', 'Date Time', OLD_VERSION, 'Head', link=False) + '</b>\n'
current_year = datetime.date.today().year
for filename in sorted(glob.glob(os.path.expanduser(results_path + '/*'))):
if not os.path.isfile(filename):
continue
datestr = ''
with open(filename, 'rt') as file_:
for line in file_:
line = line.strip()
if line.startswith('cppcheck: '):
if OLD_VERSION not in line:
# Package results seem to be too old, skip
break
else:
# Current package, parse on
continue
if line.startswith(str(current_year) + '-') or line.startswith(str(current_year - 1) + '-'):
datestr = line
if line.startswith('count:'):
if line.find('TO!') < 0:
break
package = filename[filename.rfind('/')+1:]
counts = line.strip().split(' ')
c2 = ''
if counts[2] == 'TO!':
c2 = 'Timeout'
c1 = ''
if counts[1] == 'TO!':
c1 = 'Timeout'
html += fmt(package, datestr, c2, c1) + '\n'
break
html += '</pre>\n'
html += '</body></html>\n'
return html
def staleReport(results_path: str) -> str: def staleReport(results_path: str) -> str:
html = '<html><head><title>Stale report</title></head><body>\n' html = '<html><head><title>Stale report</title></head><body>\n'
html += '<h1>Stale report</h1>\n' html += '<h1>Stale report</h1>\n'
@ -773,6 +815,9 @@ class HttpClientThread(Thread):
elif url == 'crash.html': elif url == 'crash.html':
html = crashReport(self.resultPath) html = crashReport(self.resultPath)
httpGetResponse(self.connection, html, 'text/html') httpGetResponse(self.connection, html, 'text/html')
elif url == 'timeout.html':
html = timeoutReport(self.resultPath)
httpGetResponse(self.connection, html, 'text/html')
elif url == 'stale.html': elif url == 'stale.html':
html = staleReport(self.resultPath) html = staleReport(self.resultPath)
httpGetResponse(self.connection, html, 'text/html') httpGetResponse(self.connection, html, 'text/html')

View File

@ -172,6 +172,7 @@ while True:
print("No files to process") print("No files to process")
continue continue
crash = False crash = False
timeout = False
count = '' count = ''
elapsed_time = '' elapsed_time = ''
results_to_diff = [] results_to_diff = []
@ -193,6 +194,10 @@ while True:
if c == -101 and 'error: could not find or open any of the paths given.' in errout: if c == -101 and 'error: could not find or open any of the paths given.' in errout:
# No sourcefile found (for example only headers present) # No sourcefile found (for example only headers present)
count += ' 0' count += ' 0'
elif c == RETURN_CODE_TIMEOUT:
# Timeout
count += ' TO!'
timeout = True
else: else:
crash = True crash = True
count += ' Crash!' count += ' Crash!'
@ -220,7 +225,7 @@ while True:
info_output += 'info messages:\n' + head_info_msg info_output += 'info messages:\n' + head_info_msg
if 'head' in cppcheck_versions: if 'head' in cppcheck_versions:
output += 'head results:\n' + results_to_diff[cppcheck_versions.index('head')] output += 'head results:\n' + results_to_diff[cppcheck_versions.index('head')]
if not crash: if not crash and not timeout:
output += 'diff:\n' + diff_results(work_path, cppcheck_versions[0], results_to_diff[0], cppcheck_versions[1], results_to_diff[1]) + '\n' output += 'diff:\n' + diff_results(work_path, cppcheck_versions[0], results_to_diff[0], cppcheck_versions[1], results_to_diff[1]) + '\n'
if package_url: if package_url:
print('=========================================================') print('=========================================================')

View File

@ -13,7 +13,13 @@ import tarfile
# Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/ # Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/
# Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic # Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic
# changes) # changes)
CLIENT_VERSION = "1.1.42" CLIENT_VERSION = "1.2.0"
# Timeout for analysis with Cppcheck in seconds
CPPCHECK_TIMEOUT = 60 * 60
# Return code that is used to mark a timed out analysis
RETURN_CODE_TIMEOUT = -999
def check_requirements(): def check_requirements():
@ -249,12 +255,18 @@ def run_command(cmd):
print(cmd) print(cmd)
startTime = time.time() startTime = time.time()
p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
comm = p.communicate() try:
comm = p.communicate(timeout=CPPCHECK_TIMEOUT)
return_code = p.returncode
except subprocess.TimeoutExpired:
p.kill()
comm = p.communicate()
return_code = RETURN_CODE_TIMEOUT
stop_time = time.time() stop_time = time.time()
stdout = comm[0].decode(encoding='utf-8', errors='ignore') stdout = comm[0].decode(encoding='utf-8', errors='ignore')
stderr = comm[1].decode(encoding='utf-8', errors='ignore') stderr = comm[1].decode(encoding='utf-8', errors='ignore')
elapsed_time = stop_time - startTime elapsed_time = stop_time - startTime
return p.returncode, stdout, stderr, elapsed_time return return_code, stdout, stderr, elapsed_time
def scan_package(work_path, cppcheck_path, jobs, libraries): def scan_package(work_path, cppcheck_path, jobs, libraries):
@ -300,6 +312,9 @@ def scan_package(work_path, cppcheck_path, jobs, libraries):
else: else:
stacktrace = stdout[last_check_pos:] stacktrace = stdout[last_check_pos:]
return returncode, stacktrace, '', returncode, options, '' return returncode, stacktrace, '', returncode, options, ''
if returncode == RETURN_CODE_TIMEOUT:
print('Timeout!')
return returncode, stdout, '', elapsed_time, options, ''
if returncode != 0: if returncode != 0:
print('Error!') print('Error!')
if returncode > 0: if returncode > 0:

View File

@ -69,6 +69,7 @@ if __name__ == "__main__":
packages_processed = 0 packages_processed = 0
crashes = [] crashes = []
timeouts = []
while packages_processed < args.p and len(packages_idxs) > 0: while packages_processed < args.p and len(packages_idxs) > 0:
package = lib.get_package(lib.server_address, packages_idxs.pop()) package = lib.get_package(lib.server_address, packages_idxs.pop())
@ -90,12 +91,18 @@ if __name__ == "__main__":
master_crashed = False master_crashed = False
your_crashed = False your_crashed = False
master_timeout = False
your_timeout = False
libraries = lib.get_libraries() libraries = lib.get_libraries()
c, errout, info, time, cppcheck_options, timing_info = lib.scan_package(work_path, master_dir, jobs, libraries) c, errout, info, time, cppcheck_options, timing_info = lib.scan_package(work_path, master_dir, jobs, libraries)
if c < 0: if c < 0:
if c == -101 and 'error: could not find or open any of the paths given.' in errout: if c == -101 and 'error: could not find or open any of the paths given.' in errout:
# No sourcefile found (for example only headers present) # No sourcefile found (for example only headers present)
print('Error: 101') print('Error: 101')
elif c == lib.RETURN_CODE_TIMEOUT:
print('Master timed out!')
master_timeout = True
else: else:
print('Master crashed!') print('Master crashed!')
master_crashed = True master_crashed = True
@ -106,6 +113,9 @@ if __name__ == "__main__":
if c == -101 and 'error: could not find or open any of the paths given.' in errout: if c == -101 and 'error: could not find or open any of the paths given.' in errout:
# No sourcefile found (for example only headers present) # No sourcefile found (for example only headers present)
print('Error: 101') print('Error: 101')
elif c == lib.RETURN_CODE_TIMEOUT:
print('Your code timed out!')
your_timeout = True
else: else:
print('Your code crashed!') print('Your code crashed!')
your_crashed = True your_crashed = True
@ -121,6 +131,16 @@ if __name__ == "__main__":
who = 'Your' who = 'Your'
crashes.append(package + ' ' + who) crashes.append(package + ' ' + who)
if master_timeout or your_timeout:
who = None
if master_timeout and your_timeout:
who = 'Both'
elif master_timeout:
who = 'Master'
else:
who = 'Your'
timeouts.append(package + ' ' + who)
with open(result_file, 'a') as myfile: with open(result_file, 'a') as myfile:
myfile.write(package + '\n') myfile.write(package + '\n')
diff = lib.diff_results(work_path, 'master', results_to_diff[0], 'your', results_to_diff[1]) diff = lib.diff_results(work_path, 'master', results_to_diff[0], 'your', results_to_diff[1])
@ -134,4 +154,8 @@ if __name__ == "__main__":
myfile.write('\n\ncrashes\n') myfile.write('\n\ncrashes\n')
myfile.write('\n'.join(crashes)) myfile.write('\n'.join(crashes))
with open(result_file, 'a') as myfile:
myfile.write('\n\ntimeouts\n')
myfile.write('\n'.join(timeouts))
print('Result saved to: ' + result_file) print('Result saved to: ' + result_file)