From 2500964b586e478e95836edc565cb7ce77500752 Mon Sep 17 00:00:00 2001 From: Maksim Derbasov Date: Wed, 23 Oct 2019 10:12:15 +0300 Subject: [PATCH] [tools] test-my-PR functionality (#2288) * test my PR functionallity * Improvement for test-my-pr script * test-my-pr align master with branch --- tools/donate-cpu.py | 448 +------------------------------------ tools/donate_cpu_lib.py | 478 ++++++++++++++++++++++++++++++++++++++++ tools/test-my-pr.py | 134 +++++++++++ 3 files changed, 613 insertions(+), 447 deletions(-) mode change 100644 => 100755 tools/donate-cpu.py create mode 100644 tools/donate_cpu_lib.py create mode 100755 tools/test-my-pr.py diff --git a/tools/donate-cpu.py b/tools/donate-cpu.py old mode 100644 new mode 100755 index c40c02012..457608975 --- a/tools/donate-cpu.py +++ b/tools/donate-cpu.py @@ -35,454 +35,8 @@ import time import re import tarfile import platform +from donate_cpu_lib import * - -# 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.38" - - -def check_requirements(): - result = True - for app in ['g++', 'git', 'make', 'wget', 'gdb']: - try: - subprocess.call([app, '--version']) - except OSError: - print(app + ' is required') - result = False - return result - - -def get_cppcheck(cppcheck_path, work_path): - print('Get Cppcheck..') - for i in range(5): - if os.path.exists(cppcheck_path): - try: - os.chdir(cppcheck_path) - subprocess.check_call(['git', 'checkout', '-f']) - subprocess.check_call(['git', 'pull']) - except: - print('Failed to update Cppcheck sources! Retrying..') - time.sleep(10) - continue - else: - try: - subprocess.check_call(['git', 'clone', 'https://github.com/danmar/cppcheck.git', cppcheck_path]) - except: - print('Failed to clone, will try again in 10 minutes..') - time.sleep(600) - continue - time.sleep(2) - return True - if os.path.exists(cppcheck_path): - print('Failed to update Cppcheck sources, trying a fresh clone..') - try: - os.chdir(work_path) - shutil.rmtree(cppcheck_path) - get_cppcheck(cppcheck_path, work_path) - except: - print('Failed to remove Cppcheck folder, please manually remove ' + work_path) - return False - return False - - -def get_cppcheck_info(cppcheck_path): - try: - os.chdir(cppcheck_path) - return subprocess.check_output(['git', 'show', "--pretty=%h (%ci)", 'HEAD', '--no-patch', '--no-notes']).decode('utf-8').strip() - except: - return '' - - -def compile_version(work_path, jobs, version): - if os.path.isfile(work_path + '/' + version + '/cppcheck'): - return True - os.chdir(work_path + '/cppcheck') - subprocess.call(['git', 'checkout', version]) - subprocess.call(['make', 'clean']) - subprocess.call(['make', jobs, 'MATCHCOMPILER=yes', 'CXXFLAGS=-O2 -g']) - if os.path.isfile(work_path + '/cppcheck/cppcheck'): - os.mkdir(work_path + '/' + version) - destPath = work_path + '/' + version + '/' - subprocess.call(['cp', '-R', work_path + '/cppcheck/cfg', destPath]) - subprocess.call(['cp', 'cppcheck', destPath]) - subprocess.call(['git', 'checkout', 'master']) - try: - subprocess.call([work_path + '/' + version + '/cppcheck', '--version']) - except OSError: - return False - return True - - -def compile(cppcheck_path, jobs): - print('Compiling Cppcheck..') - try: - os.chdir(cppcheck_path) - subprocess.call(['make', jobs, 'MATCHCOMPILER=yes', 'CXXFLAGS=-O2 -g']) - subprocess.call([cppcheck_path + '/cppcheck', '--version']) - except OSError: - return False - return True - - -def get_cppcheck_versions(server_address): - print('Connecting to server to get Cppcheck versions..') - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - sock.connect(server_address) - sock.send(b'GetCppcheckVersions\n') - versions = sock.recv(256) - except socket.error as err: - print('Failed to get cppcheck versions: ' + str(err)) - return None - sock.close() - return versions.decode('utf-8').split() - - -def get_package(server_address): - print('Connecting to server to get assigned work..') - package = None - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - sock.connect(server_address) - sock.send(b'get\n') - package = sock.recv(256) - except socket.error: - package = '' - sock.close() - return package.decode('utf-8') - - -def handle_remove_readonly(func, path, exc): - import stat - if not os.access(path, os.W_OK): - # Is the error an access error ? - os.chmod(path, stat.S_IWUSR) - func(path) - - -def remove_tree(folderName): - if not os.path.exists(folderName): - return - count = 5 - while count > 0: - count -= 1 - try: - shutil.rmtree(folderName, onerror=handle_remove_readonly) - break - except OSError as err: - time.sleep(30) - if count == 0: - print('Failed to cleanup {}: {}'.format(folderName, err)) - sys.exit(1) - - -def wget(url, destfile, bandwidth_limit): - if os.path.exists(destfile): - if os.path.isfile(destfile): - os.remove(destfile) - else: - print('Error: ' + destfile + ' exists but it is not a file! Please check the path and delete it manually.') - sys.exit(1) - wget_call = ['wget', '--tries=10', '--timeout=300', '-O', destfile, url] - if bandwidth_limit and isinstance(bandwidth_limit, str): - wget_call.append('--limit-rate=' + bandwidth_limit) - exitcode = subprocess.call(wget_call) - if exitcode != 0: - print('wget failed with ' + str(exitcode)) - os.remove(destfile) - return False - if not os.path.isfile(destfile): - return False - return True - - -def download_package(work_path, package, bandwidth_limit): - print('Download package ' + package) - destfile = work_path + '/temp.tgz' - if not wget(package, destfile, bandwidth_limit): - return None - return destfile - - -def unpack_package(work_path, tgz): - print('Unpacking..') - temp_path = work_path + '/temp' - remove_tree(temp_path) - os.mkdir(temp_path) - os.chdir(temp_path) - found = False - if tarfile.is_tarfile(tgz): - tf = tarfile.open(tgz) - for member in tf: - if member.name.startswith(('/', '..')): - # Skip dangerous file names - continue - elif member.name.lower().endswith(('.c', '.cpp', '.cxx', '.cc', '.c++', '.h', '.hpp', - '.h++', '.hxx', '.hh', '.tpp', '.txx', '.qml')): - try: - tf.extract(member.name) - found = True - except OSError: - pass - except AttributeError: - pass - tf.close() - os.chdir(work_path) - return found - - -def has_include(path, includes): - re_includes = [re.escape(inc) for inc in includes] - re_expr = '^[ \t]*#[ \t]*include[ \t]*(' + '|'.join(re_includes) + ')' - for root, _, files in os.walk(path): - for name in files: - filename = os.path.join(root, name) - try: - if sys.version_info.major < 3: - f = open(filename, 'rt') - else: - f = open(filename, 'rt', errors='ignore') - filedata = f.read() - try: - # Python2 needs to decode the data first - filedata = filedata.decode(encoding='utf-8', errors='ignore') - except AttributeError: - # Python3 directly reads the data into a string object that has no decode() - pass - f.close() - if re.search(re_expr, filedata, re.MULTILINE): - return True - except IOError: - pass - return False - - -def run_command(cmd): - print(cmd) - startTime = time.time() - p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) - comm = p.communicate() - 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 - - -def scan_package(work_path, cppcheck_path, jobs, libraries): - print('Analyze..') - os.chdir(work_path) - libs = '' - for library in libraries: - if os.path.exists(os.path.join(cppcheck_path, 'cfg', library + '.cfg')): - libs += ' --library=' + library - - # Reference for GNU C: https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html - options = jobs + libs + ' -D__GNUC__ --showtime=top5 --check-library --inconclusive --enable=style,information --platform=unix64 --template=daca2 -rp=temp temp' - cppcheck_cmd = cppcheck_path + '/cppcheck' + ' ' + options - cmd = 'nice ' + cppcheck_cmd - returncode, stdout, stderr, elapsed_time = run_command(cmd) - sig_num = -1 - sig_msg = 'Internal error: Child process crashed with signal ' - sig_pos = stderr.find(sig_msg) - if sig_pos != -1: - sig_start_pos = sig_pos + len(sig_msg) - 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) + ')')) - # 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): - print('Crash!') - stacktrace = '' - if cppcheck_path == 'cppcheck': - # re-run within gdb to get a stacktrace - cmd = 'gdb --batch --eval-command=run --eval-command=bt --return-child-result --args ' + cppcheck_cmd + " -j1" - dummy, stdout, stderr, elapsed_time = run_command(cmd) - gdb_pos = stdout.find(" received signal") - if not gdb_pos == -1: - last_check_pos = stdout.rfind('Checking ', 0, gdb_pos) - if last_check_pos == -1: - stacktrace = stdout[gdb_pos:] - else: - stacktrace = stdout[last_check_pos:] - return returncode, stacktrace, '', returncode, options, '' - if returncode != 0: - print('Error!') - if returncode > 0: - returncode = -100-returncode - return returncode, stdout, '', returncode, options, '' - if stderr.find('Internal error: Child process crashed with signal ') > 0: - print('Error!') - s = 'Internal error: Child process crashed with signal ' - pos1 = stderr.find(s) - pos2 = stderr.find(' [cppcheckError]', pos1) - signr = int(stderr[pos1+len(s):pos2]) - return -signr, '', '', -signr, options, '' - if stderr.find('#### ThreadExecutor') > 0: - print('Thread!') - return -222, '', '', -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 - - -def split_results(results): - ret = [] - w = None - for line in results.split('\n'): - if line.endswith(']') and re.search(r': (error|warning|style|performance|portability|information|debug):', line): - if w is not None: - ret.append(w.strip()) - w = '' - if w is not None: - w += ' ' * 5 + line + '\n' - if w is not None: - ret.append(w.strip()) - return ret - - -def diff_results(work_path, ver1, results1, ver2, results2): - print('Diff results..') - ret = '' - r1 = sorted(split_results(results1)) - r2 = sorted(split_results(results2)) - i1 = 0 - i2 = 0 - while i1 < len(r1) and i2 < len(r2): - if r1[i1] == r2[i2]: - i1 += 1 - i2 += 1 - elif r1[i1] < r2[i2]: - ret += ver1 + ' ' + r1[i1] + '\n' - i1 += 1 - else: - ret += ver2 + ' ' + r2[i2] + '\n' - i2 += 1 - while i1 < len(r1): - ret += ver1 + ' ' + r1[i1] + '\n' - i1 += 1 - while i2 < len(r2): - ret += ver2 + ' ' + r2[i2] + '\n' - i2 += 1 - - return ret - - -def send_all(connection, data): - bytes = data.encode('ascii', 'ignore') - while bytes: - num = connection.send(bytes) - if num < len(bytes): - bytes = bytes[num:] - else: - bytes = None - - -def upload_results(package, results, server_address): - print('Uploading results.. ' + str(len(results)) + ' bytes') - max_retries = 4 - for retry in range(max_retries): - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.connect(server_address) - cmd = 'write\n' - send_all(sock, cmd + package + '\n' + results + '\nDONE') - sock.close() - print('Results have been successfully uploaded.') - return True - except socket.error as err: - print('Upload error: ' + str(err)) - if retry < (max_retries - 1): - print('Retrying upload in 30 seconds') - time.sleep(30) - print('Upload permanently failed!') - return False - - -def upload_info(package, info_output, server_address): - print('Uploading information output.. ' + str(len(info_output)) + ' bytes') - max_retries = 3 - for retry in range(max_retries): - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.connect(server_address) - send_all(sock, 'write_info\n' + package + '\n' + info_output + '\nDONE') - sock.close() - print('Information output has been successfully uploaded.') - return True - except socket.error as err: - print('Upload error: ' + str(err)) - if retry < (max_retries - 1): - print('Retrying upload in 30 seconds') - time.sleep(30) - print('Upload permanently failed!') - return False - - -def get_libraries(): - libraries = ['posix', 'gnu'] - library_includes = {'boost': [''], <= enable after release of version 1.90 - 'cppunit': [''], - 'gtk': ['', '', ''], - # 'kde': ['', '', ''], - 'libcurl': [''], - 'lua': ['', '"lua.h"'], - 'microsoft_sal': [''], - 'motif': ['', '"prtypes.h"'], - 'opengl': ['', '', ''], - 'openmp': [''], - 'python': ['', '"Python.h"'], - 'qt': ['', '', '', '', '', '', '', '', '', '', '', ''], - 'sqlite3': ['', '"sqlite3.h"'], - 'tinyxml2': [''], - } - for library, includes in library_includes.items(): - if has_include('temp', includes): - libraries.append(library) - return libraries - - -jobs = '-j1' -stop_time = None -work_path = os.path.expanduser('~/cppcheck-donate-cpu-workfolder') -package_url = None -server_address = ('cppcheck1.osuosl.org', 8000) -bandwidth_limit = None -max_packages = None -do_upload = True for arg in sys.argv[1:]: # --stop-time=12:00 => run until ~12:00 and then stop if arg.startswith('--stop-time='): diff --git a/tools/donate_cpu_lib.py b/tools/donate_cpu_lib.py new file mode 100644 index 000000000..c1cec857b --- /dev/null +++ b/tools/donate_cpu_lib.py @@ -0,0 +1,478 @@ +# Donate CPU client library + +import shutil +import os +import subprocess +import sys +import socket +import time +import re +import tarfile +import platform + + +# 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.39" + + +def check_requirements(): + result = True + for app in ['g++', 'git', 'make', 'wget', 'gdb']: + try: + subprocess.call([app, '--version']) + except OSError: + print(app + ' is required') + result = False + return result + + +def get_cppcheck(cppcheck_path, work_path): + print('Get Cppcheck..') + for i in range(5): + if os.path.exists(cppcheck_path): + try: + os.chdir(cppcheck_path) + subprocess.check_call(['git', 'checkout', '-f', 'master']) + subprocess.check_call(['git', 'pull']) + except: + print('Failed to update Cppcheck sources! Retrying..') + time.sleep(10) + continue + else: + try: + subprocess.check_call(['git', 'clone', 'https://github.com/danmar/cppcheck.git', cppcheck_path]) + except: + print('Failed to clone, will try again in 10 minutes..') + time.sleep(600) + continue + time.sleep(2) + return True + if os.path.exists(cppcheck_path): + print('Failed to update Cppcheck sources, trying a fresh clone..') + try: + os.chdir(work_path) + shutil.rmtree(cppcheck_path) + get_cppcheck(cppcheck_path, work_path) + except: + print('Failed to remove Cppcheck folder, please manually remove ' + work_path) + return False + return False + + +def get_cppcheck_info(cppcheck_path): + try: + os.chdir(cppcheck_path) + return subprocess.check_output(['git', 'show', "--pretty=%h (%ci)", 'HEAD', '--no-patch', '--no-notes']).decode('utf-8').strip() + except: + return '' + + +def compile_version(work_path, jobs, version): + if os.path.isfile(work_path + '/' + version + '/cppcheck'): + return True + os.chdir(work_path + '/cppcheck') + subprocess.call(['git', 'checkout', version]) + subprocess.call(['make', 'clean']) + subprocess.call(['make', jobs, 'MATCHCOMPILER=yes', 'CXXFLAGS=-O2 -g']) + if os.path.isfile(work_path + '/cppcheck/cppcheck'): + os.mkdir(work_path + '/' + version) + destPath = work_path + '/' + version + '/' + subprocess.call(['cp', '-R', work_path + '/cppcheck/cfg', destPath]) + subprocess.call(['cp', 'cppcheck', destPath]) + subprocess.call(['git', 'checkout', 'master']) + try: + subprocess.call([work_path + '/' + version + '/cppcheck', '--version']) + except OSError: + return False + return True + + +def compile(cppcheck_path, jobs): + print('Compiling Cppcheck..') + try: + os.chdir(cppcheck_path) + subprocess.call(['make', jobs, 'MATCHCOMPILER=yes', 'CXXFLAGS=-O2 -g']) + subprocess.call([cppcheck_path + '/cppcheck', '--version']) + except OSError: + return False + return True + + +def get_cppcheck_versions(server_address): + print('Connecting to server to get Cppcheck versions..') + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.connect(server_address) + sock.send(b'GetCppcheckVersions\n') + versions = sock.recv(256) + except socket.error as err: + print('Failed to get cppcheck versions: ' + str(err)) + return None + sock.close() + return versions.decode('utf-8').split() + + +def get_packages_count(server_address): + print('Connecting to server to get count of packages..') + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.connect(server_address) + sock.send(b'getPackagesCount\n') + packages = int(sock.recv(64)) + except socket.error as err: + print('Failed to get count of packages: ' + str(err)) + return None + sock.close() + return packages + + +def get_package(server_address, package_index = None): + print('Connecting to server to get assigned work..') + package = None + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.connect(server_address) + if package_index is None: + sock.send(b'get\n') + else: + request = 'getPackageIdx:' + str(package_index) + '\n' + sock.send(request.encode()) + package = sock.recv(256) + except socket.error: + package = '' + sock.close() + return package.decode('utf-8') + + +def handle_remove_readonly(func, path, exc): + import stat + if not os.access(path, os.W_OK): + # Is the error an access error ? + os.chmod(path, stat.S_IWUSR) + func(path) + + +def remove_tree(folderName): + if not os.path.exists(folderName): + return + count = 5 + while count > 0: + count -= 1 + try: + shutil.rmtree(folderName, onerror=handle_remove_readonly) + break + except OSError as err: + time.sleep(30) + if count == 0: + print('Failed to cleanup {}: {}'.format(folderName, err)) + sys.exit(1) + + +def wget(url, destfile, bandwidth_limit): + if os.path.exists(destfile): + if os.path.isfile(destfile): + os.remove(destfile) + else: + print('Error: ' + destfile + ' exists but it is not a file! Please check the path and delete it manually.') + sys.exit(1) + wget_call = ['wget', '--tries=10', '--timeout=300', '-O', destfile, url] + if bandwidth_limit and isinstance(bandwidth_limit, str): + wget_call.append('--limit-rate=' + bandwidth_limit) + exitcode = subprocess.call(wget_call) + if exitcode != 0: + print('wget failed with ' + str(exitcode)) + os.remove(destfile) + return False + if not os.path.isfile(destfile): + return False + return True + + +def download_package(work_path, package, bandwidth_limit): + print('Download package ' + package) + destfile = work_path + '/temp.tgz' + if not wget(package, destfile, bandwidth_limit): + return None + return destfile + + +def unpack_package(work_path, tgz): + print('Unpacking..') + temp_path = work_path + '/temp' + remove_tree(temp_path) + os.mkdir(temp_path) + os.chdir(temp_path) + found = False + if tarfile.is_tarfile(tgz): + tf = tarfile.open(tgz) + for member in tf: + if member.name.startswith(('/', '..')): + # Skip dangerous file names + continue + elif member.name.lower().endswith(('.c', '.cpp', '.cxx', '.cc', '.c++', '.h', '.hpp', + '.h++', '.hxx', '.hh', '.tpp', '.txx', '.qml')): + try: + tf.extract(member.name) + found = True + except OSError: + pass + except AttributeError: + pass + tf.close() + os.chdir(work_path) + return found + + +def has_include(path, includes): + re_includes = [re.escape(inc) for inc in includes] + re_expr = '^[ \t]*#[ \t]*include[ \t]*(' + '|'.join(re_includes) + ')' + for root, _, files in os.walk(path): + for name in files: + filename = os.path.join(root, name) + try: + if sys.version_info.major < 3: + f = open(filename, 'rt') + else: + f = open(filename, 'rt', errors='ignore') + filedata = f.read() + try: + # Python2 needs to decode the data first + filedata = filedata.decode(encoding='utf-8', errors='ignore') + except AttributeError: + # Python3 directly reads the data into a string object that has no decode() + pass + f.close() + if re.search(re_expr, filedata, re.MULTILINE): + return True + except IOError: + pass + return False + + +def run_command(cmd): + print(cmd) + startTime = time.time() + p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + comm = p.communicate() + 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 + + +def scan_package(work_path, cppcheck_path, jobs, libraries): + print('Analyze..') + os.chdir(work_path) + libs = '' + for library in libraries: + if os.path.exists(os.path.join(cppcheck_path, 'cfg', library + '.cfg')): + libs += ' --library=' + library + + # Reference for GNU C: https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html + options = jobs + libs + ' -D__GNUC__ --showtime=top5 --check-library --inconclusive --enable=style,information --platform=unix64 --template=daca2 -rp=temp temp' + cppcheck_cmd = cppcheck_path + '/cppcheck' + ' ' + options + cmd = 'nice ' + cppcheck_cmd + returncode, stdout, stderr, elapsed_time = run_command(cmd) + sig_num = -1 + sig_msg = 'Internal error: Child process crashed with signal ' + sig_pos = stderr.find(sig_msg) + if sig_pos != -1: + sig_start_pos = sig_pos + len(sig_msg) + 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) + ')')) + # 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): + print('Crash!') + stacktrace = '' + if cppcheck_path == 'cppcheck': + # re-run within gdb to get a stacktrace + cmd = 'gdb --batch --eval-command=run --eval-command=bt --return-child-result --args ' + cppcheck_cmd + " -j1" + dummy, stdout, stderr, elapsed_time = run_command(cmd) + gdb_pos = stdout.find(" received signal") + if not gdb_pos == -1: + last_check_pos = stdout.rfind('Checking ', 0, gdb_pos) + if last_check_pos == -1: + stacktrace = stdout[gdb_pos:] + else: + stacktrace = stdout[last_check_pos:] + return returncode, stacktrace, '', returncode, options, '' + if returncode != 0: + print('Error!') + if returncode > 0: + returncode = -100-returncode + return returncode, stdout, '', returncode, options, '' + if stderr.find('Internal error: Child process crashed with signal ') > 0: + print('Error!') + s = 'Internal error: Child process crashed with signal ' + pos1 = stderr.find(s) + pos2 = stderr.find(' [cppcheckError]', pos1) + signr = int(stderr[pos1+len(s):pos2]) + return -signr, '', '', -signr, options, '' + if stderr.find('#### ThreadExecutor') > 0: + print('Thread!') + return -222, '', '', -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 + + +def split_results(results): + ret = [] + w = None + for line in results.split('\n'): + if line.endswith(']') and re.search(r': (error|warning|style|performance|portability|information|debug):', line): + if w is not None: + ret.append(w.strip()) + w = '' + if w is not None: + w += ' ' * 5 + line + '\n' + if w is not None: + ret.append(w.strip()) + return ret + + +def diff_results(work_path, ver1, results1, ver2, results2): + print('Diff results..') + ret = '' + r1 = sorted(split_results(results1)) + r2 = sorted(split_results(results2)) + i1 = 0 + i2 = 0 + while i1 < len(r1) and i2 < len(r2): + if r1[i1] == r2[i2]: + i1 += 1 + i2 += 1 + elif r1[i1] < r2[i2]: + ret += ver1 + ' ' + r1[i1] + '\n' + i1 += 1 + else: + ret += ver2 + ' ' + r2[i2] + '\n' + i2 += 1 + while i1 < len(r1): + ret += ver1 + ' ' + r1[i1] + '\n' + i1 += 1 + while i2 < len(r2): + ret += ver2 + ' ' + r2[i2] + '\n' + i2 += 1 + + return ret + + +def send_all(connection, data): + bytes = data.encode('ascii', 'ignore') + while bytes: + num = connection.send(bytes) + if num < len(bytes): + bytes = bytes[num:] + else: + bytes = None + + +def upload_results(package, results, server_address): + print('Uploading results.. ' + str(len(results)) + ' bytes') + max_retries = 4 + for retry in range(max_retries): + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(server_address) + cmd = 'write\n' + send_all(sock, cmd + package + '\n' + results + '\nDONE') + sock.close() + print('Results have been successfully uploaded.') + return True + except socket.error as err: + print('Upload error: ' + str(err)) + if retry < (max_retries - 1): + print('Retrying upload in 30 seconds') + time.sleep(30) + print('Upload permanently failed!') + return False + + +def upload_info(package, info_output, server_address): + print('Uploading information output.. ' + str(len(info_output)) + ' bytes') + max_retries = 3 + for retry in range(max_retries): + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(server_address) + send_all(sock, 'write_info\n' + package + '\n' + info_output + '\nDONE') + sock.close() + print('Information output has been successfully uploaded.') + return True + except socket.error as err: + print('Upload error: ' + str(err)) + if retry < (max_retries - 1): + print('Retrying upload in 30 seconds') + time.sleep(30) + print('Upload permanently failed!') + return False + + +def get_libraries(): + libraries = ['posix', 'gnu'] + library_includes = {'boost': [''], <= enable after release of version 1.90 + 'cppunit': [''], + 'gtk': ['', '', ''], + # 'kde': ['', '', ''], + 'libcurl': [''], + 'lua': ['', '"lua.h"'], + 'microsoft_sal': [''], + 'motif': ['', '"prtypes.h"'], + 'opengl': ['', '', ''], + 'openmp': [''], + 'python': ['', '"Python.h"'], + 'qt': ['', '', '', '', '', '', '', '', '', '', '', ''], + 'sqlite3': ['', '"sqlite3.h"'], + 'tinyxml2': [''], + } + for library, includes in library_includes.items(): + if has_include('temp', includes): + libraries.append(library) + return libraries + + +my_script_name = os.path.splitext(sys.argv[0])[0] +jobs = '-j1' +stop_time = None +work_path = os.path.expanduser('~/cppcheck-' + my_script_name + '-workfolder') +package_url = None +server_address = ('cppcheck1.osuosl.org', 8000) +bandwidth_limit = None +max_packages = None +do_upload = True diff --git a/tools/test-my-pr.py b/tools/test-my-pr.py new file mode 100755 index 000000000..cc26e2720 --- /dev/null +++ b/tools/test-my-pr.py @@ -0,0 +1,134 @@ + +# Run this script from your branch with proposed Cppcheck patch to verify your +# patch against current master. It will compare output of testing bunch of +# opensource packages + +import donate_cpu_lib as lib +import argparse +import os +import sys +import random +import time +import subprocess + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Run this script from your branch with proposed Cppcheck patch to verify your patch against current master. It will compare output of testing bunch of opensource packages') + parser.add_argument('-j', default=1, type=int, help='Concurency execution threads') + parser.add_argument('-p', default=1000, type=int, help='Count of packages to check') + parser.add_argument('-o', default='my_check_diff.log', help='Filename of result inside a working path dir') + parser.add_argument('--work-path', '--work-path=', default=lib.work_path, type=str, help='Working directory for reference repo') + args = parser.parse_args() + + print(args) + + work_path = args.work_path + if not os.path.exists(work_path): + os.makedirs(work_path) + master_dir = os.path.join(work_path, 'cppcheck') + + jobs = '-j' + str(args.j) + result_file = os.path.join(work_path, args.o) + your_repo_dir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))) + + if os.path.exists(result_file): + os.remove(result_file) + + if not lib.get_cppcheck(master_dir, work_path): + print('Failed to clone master of Cppcheck, retry later') + sys.exit(1) + + try: + os.chdir(your_repo_dir) + commit_id = (subprocess.check_output(['git', 'merge-base', 'master', 'HEAD'])).strip().decode('ascii') + with open(result_file, 'a') as myfile: + myfile.write('Common ancestor: ' + commit_id + '\n\n') + + os.chdir(master_dir) + subprocess.check_call(['git', 'checkout', '-f', commit_id]) + except: + print('Failed to switch to common ancestor of your branch and master') + sys.exit(1) + + if not lib.compile(master_dir, jobs): + print('Failed to compile master of Cppcheck') + sys.exit(1) + + print('Testing your PR from directory: ' + your_repo_dir) + if not lib.compile(your_repo_dir, jobs): + print('Failed to compile your version of Cppcheck') + sys.exit(1) + + packages_count = lib.get_packages_count(lib.server_address) + if not packages_count: + print("network or server might be temporarily down..") + sys.exit(1) + + packages_idxs = list(range(packages_count)) + random.shuffle(packages_idxs) + + packages_processed = 0 + crashes = [] + + while packages_processed < args.p and len(packages_idxs) > 0: + package = lib.get_package(lib.server_address, packages_idxs.pop()) + if len(package) == 0: + print("network or server might be temporarily down..") + sys.exit(1) + + tgz = lib.download_package(work_path, package, None) + if tgz is None: + print("No package downloaded") + continue + + if not lib.unpack_package(work_path, tgz): + print("No files to process") + continue + + results_to_diff = [] + + master_crashed = False + your_crashed = 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') + else: + print('Master crashed!') + master_crashed = True + results_to_diff.append(errout) + + c, errout, info, time, cppcheck_options, timing_info = lib.scan_package(work_path, your_repo_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') + else: + print('Your code crashed!') + your_crashed = True + results_to_diff.append(errout) + + if master_crashed or your_crashed: + who = None + if master_crashed and your_crashed: + who = 'Both' + elif master_crashed: + who = 'Master' + else: + who = 'Your' + crashes.append(package + ' ' + who) + + with open(result_file, 'a') as myfile: + myfile.write(package + '\n') + myfile.write('diff:\n' + lib.diff_results(work_path, 'master', results_to_diff[0], 'your', results_to_diff[1]) + '\n') + + packages_processed += 1 + print(str(packages_processed) + ' of ' + str(args.p) + ' packages processed\n') + + with open(result_file, 'a') as myfile: + myfile.write('\n\ncrashes\n') + myfile.write('\n'.join(crashes)) + + print('Result saved to: ' + result_file)