donate_cpu_lib.py: improved timeout reporting and process handling (#3153)

This commit is contained in:
Oliver Stöneberg 2021-02-27 03:19:08 +01:00 committed by GitHub
parent bc8eb164a3
commit be62fab737
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 59 additions and 35 deletions

View File

@ -38,6 +38,7 @@ jobs:
python -m pip install pytest python -m pip install pytest
python -m pip install pygments python -m pip install pygments
python -m pip install requests python -m pip install requests
python -m pip install psutil
- name: run Shellcheck - name: run Shellcheck
if: matrix.python-version == '3.8' if: matrix.python-version == '3.8'

View File

@ -10,12 +10,13 @@ import re
import signal import signal
import tarfile import tarfile
import shlex import shlex
import psutil
# 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.3.8" CLIENT_VERSION = "1.3.9"
# Timeout for analysis with Cppcheck in seconds # Timeout for analysis with Cppcheck in seconds
CPPCHECK_TIMEOUT = 30 * 60 CPPCHECK_TIMEOUT = 30 * 60
@ -255,14 +256,25 @@ def has_include(path, includes):
def run_command(cmd): def run_command(cmd):
print(cmd) print(cmd)
startTime = time.time() startTime = time.time()
comm = None
p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid) p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid)
try: try:
comm = p.communicate(timeout=CPPCHECK_TIMEOUT) comm = p.communicate(timeout=CPPCHECK_TIMEOUT)
return_code = p.returncode return_code = p.returncode
p = None
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
os.killpg(os.getpgid(p.pid), signal.SIGTERM) # Send the signal to all the process groups
comm = p.communicate()
return_code = RETURN_CODE_TIMEOUT return_code = RETURN_CODE_TIMEOUT
# terminate all the child processes so we get messages about which files were hanging
child_procs = psutil.Process(p.pid).children(recursive=True)
if len(child_procs) > 0:
for child in child_procs:
child.terminate()
comm = p.communicate()
p = None
finally:
if p:
os.killpg(os.getpgid(p.pid), signal.SIGTERM) # Send the signal to all the process groups
comm = p.communicate()
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')
@ -290,6 +302,39 @@ def scan_package(work_path, cppcheck_path, jobs, libraries):
cppcheck_cmd = cppcheck_path + '/cppcheck' + ' ' + options cppcheck_cmd = cppcheck_path + '/cppcheck' + ' ' + options
cmd = 'nice ' + cppcheck_cmd cmd = 'nice ' + cppcheck_cmd
returncode, stdout, stderr, elapsed_time = run_command(cmd) returncode, stdout, stderr, elapsed_time = run_command(cmd)
# collect messages
information_messages_list = []
issue_messages_list = []
internal_error_messages_list = []
count = 0
for line in stderr.split('\n'):
if ': information: ' in line:
information_messages_list.append(line + '\n')
elif line:
issue_messages_list.append(line + '\n')
if re.match(r'.*:[0-9]+:.*\]$', line):
count += 1
if ': error: Internal error: ' in line:
internal_error_messages_list.append(line + '\n')
print('Number of issues: ' + str(count))
# Collect timing information
stdout_lines = stdout.split('\n')
timing_info_list = []
overall_time_found = False
max_timing_lines = 6
current_timing_lines = 0
for reverse_line in reversed(stdout_lines):
if reverse_line.startswith('Overall time:'):
overall_time_found = True
if overall_time_found:
if not reverse_line or current_timing_lines >= max_timing_lines:
break
timing_info_list.insert(0, ' ' + reverse_line + '\n')
current_timing_lines += 1
timing_str = ''.join(timing_info_list)
# detect errors
sig_num = -1 sig_num = -1
sig_msg = 'Internal error: Child process crashed with signal ' sig_msg = 'Internal error: Child process crashed with signal '
sig_pos = stderr.find(sig_msg) sig_pos = stderr.find(sig_msg)
@ -297,9 +342,11 @@ def scan_package(work_path, cppcheck_path, jobs, libraries):
sig_start_pos = sig_pos + len(sig_msg) sig_start_pos = sig_pos + len(sig_msg)
sig_num = int(stderr[sig_start_pos:stderr.find(' ', sig_start_pos)]) sig_num = int(stderr[sig_start_pos:stderr.find(' ', sig_start_pos)])
print('cppcheck finished with ' + str(returncode) + ('' if sig_num == -1 else ' (signal ' + str(sig_num) + ')')) print('cppcheck finished with ' + str(returncode) + ('' if sig_num == -1 else ' (signal ' + str(sig_num) + ')'))
if returncode == RETURN_CODE_TIMEOUT: if returncode == RETURN_CODE_TIMEOUT:
print('Timeout!') print('Timeout!')
return returncode, stdout, '', elapsed_time, options, '' return returncode, ''.join(internal_error_messages_list), '', elapsed_time, options, ''
# generate stack trace for SIGSEGV, SIGABRT, SIGILL, SIGFPE, SIGBUS # generate stack trace for SIGSEGV, SIGABRT, SIGILL, SIGFPE, SIGBUS
if returncode in (-11, -6, -4, -8, -7) or sig_num in (11, 6, 4, 8, 7): if returncode in (-11, -6, -4, -8, -7) or sig_num in (11, 6, 4, 8, 7):
print('Crash!') print('Crash!')
@ -319,48 +366,24 @@ def scan_package(work_path, cppcheck_path, jobs, libraries):
if not stacktrace: if not stacktrace:
stacktrace = stdout stacktrace = stdout
return returncode, stacktrace, '', returncode, options, '' return returncode, stacktrace, '', returncode, options, ''
if returncode != 0: if returncode != 0:
print('Error!') print('Error!')
if returncode > 0: if returncode > 0:
returncode = -100-returncode returncode = -100-returncode
return returncode, stdout, '', returncode, options, '' return returncode, stdout, '', returncode, options, ''
err_s = 'Internal error: Child process crashed with signal '
err_pos = stderr.find(err_s) if sig_pos != -1:
if err_pos != -1:
print('Error!') print('Error!')
pos2 = stderr.find(' [cppcheckError]', err_pos) pos2 = stderr.find(' [cppcheckError]', sig_pos)
signr = int(stderr[err_pos+len(err_s):pos2]) signr = int(stderr[sig_pos+len(sig_msg):pos2])
return -signr, '', '', -signr, options, '' return -signr, '', '', -signr, options, ''
thr_pos = stderr.find('#### ThreadExecutor') thr_pos = stderr.find('#### ThreadExecutor')
if thr_pos != -1: if thr_pos != -1:
print('Thread!') print('Thread!')
return -222, stderr[thr_pos:], '', -222, options, '' return -222, stderr[thr_pos:], '', -222, options, ''
information_messages_list = []
issue_messages_list = []
count = 0
for line in stderr.split('\n'):
if ': information: ' in line:
information_messages_list.append(line + '\n')
elif line:
issue_messages_list.append(line + '\n')
if re.match(r'.*:[0-9]+:.*\]$', line):
count += 1
print('Number of issues: ' + str(count))
# Collect timing information
stdout_lines = stdout.split('\n')
timing_info_list = []
overall_time_found = False
max_timing_lines = 6
current_timing_lines = 0
for reverse_line in reversed(stdout_lines):
if reverse_line.startswith('Overall time:'):
overall_time_found = True
if overall_time_found:
if not reverse_line or current_timing_lines >= max_timing_lines:
break
timing_info_list.insert(0, ' ' + reverse_line + '\n')
current_timing_lines += 1
timing_str = ''.join(timing_info_list)
return count, ''.join(issue_messages_list), ''.join(information_messages_list), elapsed_time, options, timing_str return count, ''.join(issue_messages_list), ''.join(information_messages_list), elapsed_time, options, timing_str