Created
February 9, 2022 22:06
-
-
Save firewave/abecbb6dac0cfeed664ebed7983bab36 to your computer and use it in GitHub Desktop.
Cppcheck Hang Bisect
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# TODO: set -e | |
set -x | |
# TODO: check parameters | |
hash_good=$1 | |
hash_bad=$2 | |
options=$3 | |
script_dir="$(dirname "$(realpath "$0")")" | |
# TODO: make configurable | |
bisect_dir=~/.bisect | |
mkdir -p "$bisect_dir" || exit 1 | |
cd "$bisect_dir" || exit 1 | |
if [ ! -d 'cppcheck' ]; then | |
git clone https://github.com/danmar/cppcheck.git || exit 1 | |
fi | |
bisect_repo_dir="$bisect_dir/cppcheck" | |
cd $bisect_repo_dir || exit 1 | |
git fetch || exit 1 | |
# clean up in case we previously exited prematurely | |
git restore . || exit 1 | |
git clean -df || exit 1 | |
#git checkout head || exit 1 | |
git bisect reset || exit 1 | |
# TODO: filter addons, cfg and platforms based on the options | |
# limit to paths which actually affect the analysis | |
git bisect start -- Makefile 'addons/*.py' 'cfg/*.cfg' 'cli/*.cpp' 'cli/*.h' 'externals/**/*.cpp' 'externals/**/*.h' 'lib/*.cpp' 'lib/*.h' platforms tools/matchcompiler.py || exit 1 | |
git bisect bad "$hash_bad" || exit 1 | |
git checkout "$hash_good" || exit 1 | |
# TODO: exitcode overflow on 255 | |
# get expected time from good commit | |
python3 "$script_dir/bisect_hang.py" "$bisect_dir" "$options" | |
elapsed_time=$? | |
git bisect good || exit 1 | |
git bisect run python3 "$script_dir/bisect_hang.py" "$bisect_dir" "$options" $elapsed_time || exit 1 | |
git bisect log || exit 1 | |
git bisect reset || exit 1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import shutil | |
import subprocess | |
import time | |
import sys | |
import os | |
# TODO: detect missing file | |
def run(cppcheck_path, options, elapsed_time=None): | |
timeout = None | |
if elapsed_time: | |
timeout = elapsed_time * 2 | |
cmd = options.split() | |
cmd.insert(0, cppcheck_path) | |
print('running {}'.format(cppcheck_path)) | |
p = subprocess.Popen(cmd) | |
try: | |
p.communicate(timeout=timeout) | |
if p.returncode != 0: | |
print('error') | |
return None | |
print('done') | |
except subprocess.TimeoutExpired: | |
print('timeout') | |
p.kill() | |
p.communicate() | |
return False | |
return True | |
def build(bisect_path): | |
commit_hash = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).decode('ascii').strip() | |
install_path = os.path.join(bisect_path, commit_hash) | |
cppcheck_path = os.path.join(install_path, 'cppcheck') | |
if os.path.exists(install_path): | |
print('binary for {} already exists'.format(commit_hash)) | |
return cppcheck_path | |
bisect_repo_dir = os.path.join(bisect_path, 'cppcheck') | |
if os.path.exists(os.path.join(bisect_repo_dir, 'cppcheck')): | |
os.remove(os.path.join(bisect_repo_dir, 'cppcheck')) | |
# TODO: make jobs configurable | |
# TODO: use "make install"? | |
print('building {}'.format(commit_hash)) | |
subprocess.check_call(['make', '-C', bisect_repo_dir, '-j7', 'MATCHCOMPILER=yes', 'CXXFLAGS=-O2 -g -w']) | |
print('installing {}'.format(commit_hash)) | |
os.mkdir(install_path) | |
shutil.copytree(os.path.join(bisect_repo_dir, 'cfg'), os.path.join(install_path, 'cfg')) | |
shutil.copytree(os.path.join(bisect_repo_dir, 'platforms'), os.path.join(install_path, 'platforms')) | |
shutil.copy(os.path.join(bisect_repo_dir, 'cppcheck'), cppcheck_path) | |
return cppcheck_path | |
# TODO: check arguments | |
bisect_path = sys.argv[1] | |
options = sys.argv[2] | |
if '--error-exitcode=0' not in options: | |
options += ' --error-exitcode=0' | |
if len(sys.argv) == 4: | |
elapsed_time = float(sys.argv[3]) | |
else: | |
elapsed_time = None | |
try: | |
cppcheck_path = build(bisect_path) | |
except Exception as e: | |
# TODO: how to persist this so we don't keep compiling these | |
print(e) | |
sys.exit(125) # tells bisect to skip this commit since it cannot be tested | |
if not elapsed_time: | |
t = time.perf_counter() | |
# TODO: handle error result | |
run(cppcheck_path, options) | |
elapsed_time = time.perf_counter() - t | |
print('elapsed_time: {}'.format(elapsed_time)) | |
# TODO: write to stdout and redirect all all printing to stderr | |
sys.exit(round(elapsed_time + .5)) # return the time | |
run_res = run(cppcheck_path, options, elapsed_time) | |
if run_res is None: | |
sys.exit(125) # error occured - tell bisect to skip this commit since it cannot be tested | |
if not run_res: | |
sys.exit(1) # timeout occured - bad | |
sys.exit(0) # no timeout - good |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@pfultz2 FYI the script used for the performance regression bisect - not ready for a PR yet.