From 2d95c35974bc7048b9f226886dfa21e37ee48238 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 24 Jan 2020 12:33:51 +0100 Subject: [PATCH] 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 --- tools/donate-cpu-server.py | 49 ++++++++++++++++++++++++++++++++++++-- tools/donate-cpu.py | 7 +++++- tools/donate_cpu_lib.py | 21 +++++++++++++--- tools/test-my-pr.py | 24 +++++++++++++++++++ 4 files changed, 95 insertions(+), 6 deletions(-) diff --git a/tools/donate-cpu-server.py b/tools/donate-cpu-server.py index fca62838a..ac4bca607 100755 --- a/tools/donate-cpu-server.py +++ b/tools/donate-cpu-server.py @@ -22,7 +22,7 @@ import operator # 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 # changes) -SERVER_VERSION = "1.3.1" +SERVER_VERSION = "1.3.2" OLD_VERSION = '1.90' @@ -67,6 +67,7 @@ def overviewReport() -> str: html = 'daca@home\n' html += '

daca@home

\n' html += 'Crash report
\n' + html += 'Timeout report
\n' html += 'Stale report
\n' html += 'Diff report
\n' html += 'HEAD report
\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: - column_width = [40, 10, 5, 6, 6, 8] + column_width = [40, 10, 5, 7, 7, 8] ret = a while len(ret) < column_width[0]: ret += ' ' @@ -218,6 +219,47 @@ def crashReport(results_path: str) -> str: return html +def timeoutReport(results_path: str) -> str: + html = 'Timeout report\n' + html += '

Timeout report

\n' + html += '
\n'
+    html += '' + fmt('Package', 'Date       Time', OLD_VERSION, 'Head', link=False) + '\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 += '
\n' + html += '\n' + return html + + def staleReport(results_path: str) -> str: html = 'Stale report\n' html += '

Stale report

\n' @@ -773,6 +815,9 @@ class HttpClientThread(Thread): elif url == 'crash.html': html = crashReport(self.resultPath) httpGetResponse(self.connection, html, 'text/html') + elif url == 'timeout.html': + html = timeoutReport(self.resultPath) + httpGetResponse(self.connection, html, 'text/html') elif url == 'stale.html': html = staleReport(self.resultPath) httpGetResponse(self.connection, html, 'text/html') diff --git a/tools/donate-cpu.py b/tools/donate-cpu.py index 6e0cf68df..498060a2e 100755 --- a/tools/donate-cpu.py +++ b/tools/donate-cpu.py @@ -172,6 +172,7 @@ while True: print("No files to process") continue crash = False + timeout = False count = '' elapsed_time = '' 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: # No sourcefile found (for example only headers present) count += ' 0' + elif c == RETURN_CODE_TIMEOUT: + # Timeout + count += ' TO!' + timeout = True else: crash = True count += ' Crash!' @@ -220,7 +225,7 @@ while True: info_output += 'info messages:\n' + head_info_msg if 'head' in cppcheck_versions: 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' if package_url: print('=========================================================') diff --git a/tools/donate_cpu_lib.py b/tools/donate_cpu_lib.py index a24f4073d..e14131392 100644 --- a/tools/donate_cpu_lib.py +++ b/tools/donate_cpu_lib.py @@ -13,7 +13,13 @@ import tarfile # 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 # 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(): @@ -249,12 +255,18 @@ def run_command(cmd): print(cmd) startTime = time.time() 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() stdout = comm[0].decode(encoding='utf-8', errors='ignore') stderr = comm[1].decode(encoding='utf-8', errors='ignore') 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): @@ -300,6 +312,9 @@ def scan_package(work_path, cppcheck_path, jobs, libraries): else: stacktrace = stdout[last_check_pos:] return returncode, stacktrace, '', returncode, options, '' + if returncode == RETURN_CODE_TIMEOUT: + print('Timeout!') + return returncode, stdout, '', elapsed_time, options, '' if returncode != 0: print('Error!') if returncode > 0: diff --git a/tools/test-my-pr.py b/tools/test-my-pr.py index 3c762c315..98f4cd9d0 100755 --- a/tools/test-my-pr.py +++ b/tools/test-my-pr.py @@ -69,6 +69,7 @@ if __name__ == "__main__": packages_processed = 0 crashes = [] + timeouts = [] while packages_processed < args.p and len(packages_idxs) > 0: package = lib.get_package(lib.server_address, packages_idxs.pop()) @@ -90,12 +91,18 @@ if __name__ == "__main__": master_crashed = False your_crashed = False + master_timeout = False + your_timeout = False + libraries = lib.get_libraries() c, errout, info, time, cppcheck_options, timing_info = lib.scan_package(work_path, master_dir, jobs, libraries) if c < 0: 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) print('Error: 101') + elif c == lib.RETURN_CODE_TIMEOUT: + print('Master timed out!') + master_timeout = True else: print('Master crashed!') 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: # No sourcefile found (for example only headers present) print('Error: 101') + elif c == lib.RETURN_CODE_TIMEOUT: + print('Your code timed out!') + your_timeout = True else: print('Your code crashed!') your_crashed = True @@ -121,6 +131,16 @@ if __name__ == "__main__": who = 'Your' 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: myfile.write(package + '\n') 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'.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)