Skip to content

Instantly share code, notes, and snippets.

@firewave
Created February 9, 2022 22:06
Show Gist options
  • Save firewave/abecbb6dac0cfeed664ebed7983bab36 to your computer and use it in GitHub Desktop.
Save firewave/abecbb6dac0cfeed664ebed7983bab36 to your computer and use it in GitHub Desktop.
Cppcheck Hang Bisect
#!/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
#!/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
@firewave
Copy link
Author

@pfultz2 FYI the script used for the performance regression bisect - not ready for a PR yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment