Use a special test launcher that links the sanitize runtime library directly

This commit is contained in:
Kovid Goyal 2017-05-15 10:18:59 +05:30
parent ba7a6e8106
commit 788f09e855
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
7 changed files with 97 additions and 35 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ build
README.html README.html
linux-package linux-package
logo/*.iconset logo/*.iconset
test-launcher

View File

@ -6,7 +6,7 @@ matrix:
group: beta group: beta
sudo: false sudo: false
env: env:
- CC=gcc ASANLIB=libasan.so.0 ASAN_ARG=--asan - CC=gcc SANITIZE_ARG=--sanitize
language: python language: python
python: "3.5" python: "3.5"
addons: addons:
@ -24,7 +24,25 @@ matrix:
group: beta group: beta
sudo: false sudo: false
env: env:
- CC=clang RUN_FLAKE=1 BUILD_PKG=1 - CC=clang SANITIZE_ARG=--sanitize
language: python
python: "3.5"
addons:
apt:
packages:
- libfontconfig1-dev
- libglew-dev
- libxi-dev
- libxrandr-dev
- libxinerama-dev
- libxcursor-dev
- os: linux
dist: trusty
group: beta
sudo: false
env:
- RUN_FLAKE=1 BUILD_PKG=1
language: python language: python
python: "3.5" python: "3.5"
addons: addons:
@ -45,6 +63,13 @@ matrix:
language: generic language: generic
env: USE_BREW=1 BUILD_PKG=1 env: USE_BREW=1 BUILD_PKG=1
env:
global:
- PYTHON=python3
- PKG_CONFIG_PATH=$HOME/glfw/lib/pkgconfig:$PKG_CONFIG_PATH
- LD_LIBRARY_PATH=$HOME/glfw/lib:$LD_LIBRARY_PATH
- ASAN_OPTIONS=leak_check_at_exit=0
install: | install: |
set -e set -e
if [[ "$RUN_FLAKE" == "1" ]]; then pip install flake8; fi if [[ "$RUN_FLAKE" == "1" ]]; then pip install flake8; fi
@ -59,7 +84,7 @@ install: |
fi fi
else else
wget -O glfw-3.2.1.zip https://github.com/glfw/glfw/archive/3.2.1.zip wget -O glfw-3.2.1.zip https://github.com/glfw/glfw/archive/3.2.1.zip
unzip glfw-3.2.1.zip unzip -q glfw-3.2.1.zip
cd glfw-3.2.1 cd glfw-3.2.1
cmake -DBUILD_SHARED_LIBS=ON -DGLFW_BUILD_EXAMPLES=OFF -DGLFW_BUILD_TESTS=OFF -DGLFW_BUILD_DOCS=OFF -DCMAKE_INSTALL_PREFIX=$HOME/glfw cmake -DBUILD_SHARED_LIBS=ON -DGLFW_BUILD_EXAMPLES=OFF -DGLFW_BUILD_TESTS=OFF -DGLFW_BUILD_DOCS=OFF -DCMAKE_INSTALL_PREFIX=$HOME/glfw
make make
@ -67,17 +92,18 @@ install: |
cd .. cd ..
fi fi
pkg-config --cflags glfw3 pkg-config --cflags glfw3
if [[ "$TRAVIS_OS_NAME" != 'osx' ]]; then
PLIB=$(ldd `which python` | grep libpython | cut -d ' ' -f 3)
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`dirname $PLIB`
fi
set +e set +e
env:
global:
- PKG_CONFIG_PATH=$HOME/glfw/lib/pkgconfig
- LD_LIBRARY_PATH=$HOME/glfw/lib
- ASAN_OPTIONS=leak_check_at_exit=0
- PYTHON=python3
before_script: before_script:
- $PYTHON setup.py build --debug $ASAN_ARG; - echo $LD_LIBRARY_PATH
- $PYTHON setup.py build --debug $SANITIZE_ARG;
- if [[ "$TRAVIS_OS_NAME" != 'osx' ]]; then ldd ./test-launcher `which $PYTHON`; fi
script: script:
- LD_PRELOAD=$ASANLIB $PYTHON setup.py test - ./test-launcher
- if [[ "$RUN_FLAKE" == "1" ]]; then flake8 --count .; fi - if [[ "$RUN_FLAKE" == "1" ]]; then flake8 --count .; fi
- if [[ "$BUILD_PKG" == "1" ]]; then $PYTHON setup.py linux-package; fi - if [[ "$BUILD_PKG" == "1" ]]; then $PYTHON setup.py linux-package; fi

View File

@ -140,7 +140,7 @@ static PyStructSequence_Field gm_fields[] = {
}; };
static PyStructSequence_Desc gm_desc = {"GlpyhMetrics", NULL, gm_fields, 8}; static PyStructSequence_Desc gm_desc = {"GlpyhMetrics", NULL, gm_fields, 8};
static PyTypeObject GlpyhMetricsType = {0}; static PyTypeObject GlpyhMetricsType = {{{0}}};
static PyObject* static PyObject*
glyph_metrics(Face *self) { glyph_metrics(Face *self) {
@ -167,7 +167,7 @@ static PyStructSequence_Field bm_fields[] = {
{NULL} {NULL}
}; };
static PyStructSequence_Desc bm_desc = {"Bitmap", NULL, bm_fields, 7}; static PyStructSequence_Desc bm_desc = {"Bitmap", NULL, bm_fields, 7};
static PyTypeObject BitmapType = {0}; static PyTypeObject BitmapType = {{{0}}};
static PyObject* static PyObject*
bitmap(Face *self) { bitmap(Face *self) {

View File

@ -95,7 +95,7 @@ index_type historybuf_push(HistoryBuf *self) {
bool bool
historybuf_resize(HistoryBuf *self, index_type lines) { historybuf_resize(HistoryBuf *self, index_type lines) {
HistoryBuf t = {0}; HistoryBuf t = {{0}};
t.xnum=self->xnum; t.xnum=self->xnum;
t.ynum=lines; t.ynum=lines;
if (t.ynum > 0 && t.ynum != self->ynum) { if (t.ynum > 0 && t.ynum != self->ynum) {

View File

@ -423,7 +423,7 @@ copy_old(LineBuf *self, PyObject *y) {
if (!PyObject_TypeCheck(y, &LineBuf_Type)) { PyErr_SetString(PyExc_TypeError, "Not a LineBuf object"); return NULL; } if (!PyObject_TypeCheck(y, &LineBuf_Type)) { PyErr_SetString(PyExc_TypeError, "Not a LineBuf object"); return NULL; }
LineBuf *other = (LineBuf*)y; LineBuf *other = (LineBuf*)y;
if (other->xnum != self->xnum) { PyErr_SetString(PyExc_ValueError, "LineBuf has a different number of columns"); return NULL; } if (other->xnum != self->xnum) { PyErr_SetString(PyExc_ValueError, "LineBuf has a different number of columns"); return NULL; }
Line sl = {0}, ol = {0}; Line sl = {{0}}, ol = {{0}};
sl.xnum = self->xnum; ol.xnum = other->xnum; sl.xnum = self->xnum; ol.xnum = other->xnum;
for (index_type i = 0; i < MIN(self->ynum, other->ynum); i++) { for (index_type i = 0; i < MIN(self->ynum, other->ynum); i++) {

View File

@ -53,7 +53,7 @@ def cc_version():
ver = tuple(map(int, ver)) ver = tuple(map(int, ver))
except Exception: except Exception:
ver = (0, 0) ver = (0, 0)
return ver return cc, ver
def get_python_flags(cflags): def get_python_flags(cflags):
@ -88,36 +88,43 @@ def get_python_flags(cflags):
return libs return libs
def init_env(debug=False, asan=False, native_optimizations=True): def get_sanitize_args(cc, ccver):
sanitize_args = set()
sanitize_args.add('-fno-omit-frame-pointer')
sanitize_args.add('-fsanitize=address')
if (cc == 'gcc' and ccver >= (5, 0)) or cc == 'clang':
sanitize_args.add('-fsanitize=undefined')
# if cc == 'gcc' or (cc == 'clang' and ccver >= (4, 2)):
# sanitize_args.add('-fno-sanitize-recover=all')
return sanitize_args
def init_env(debug=False, sanitize=False, native_optimizations=True):
global cflags, ldflags, cc, ldpaths global cflags, ldflags, cc, ldpaths
native_optimizations = native_optimizations and not asan and not debug native_optimizations = native_optimizations and not sanitize and not debug
ccver = cc_version() cc, ccver = cc_version()
print('CC:', cc, ccver)
stack_protector = '-fstack-protector' stack_protector = '-fstack-protector'
if ccver >= (4, 9): if ccver >= (4, 9) and cc == 'gcc':
stack_protector += '-strong' stack_protector += '-strong'
missing_braces = '' missing_braces = ''
if ccver < (5, 2): if ccver < (5, 2) and cc == 'gcc':
missing_braces = '-Wno-missing-braces' missing_braces = '-Wno-missing-braces'
cc = os.environ.get('CC', 'gcc') optimize = '-ggdb' if debug or sanitize else '-O3'
optimize = '-O3' sanitize_args = get_sanitize_args(cc, ccver) if sanitize else set()
if debug or asan:
optimize = '-ggdb'
if asan:
optimize += ' -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer'
cflags = os.environ.get( cflags = os.environ.get(
'OVERRIDE_CFLAGS', ( 'OVERRIDE_CFLAGS', (
'-Wextra -Wno-missing-field-initializers -Wall -std=c99 -D_XOPEN_SOURCE=700' '-Wextra -Wno-missing-field-initializers -Wall -std=c99 -D_XOPEN_SOURCE=700'
' -pedantic-errors -Werror {} -DNDEBUG -fwrapv {} {} -pipe {}' ' -pedantic-errors -Werror {} {} -DNDEBUG -fwrapv {} {} -pipe {}'
).format( ).format(
optimize, stack_protector, missing_braces, '-march=native' optimize, ' '.join(sanitize_args), stack_protector, missing_braces, '-march=native'
if native_optimizations else '' if native_optimizations else ''
) )
) )
cflags = shlex.split(cflags cflags = shlex.split(cflags
) + shlex.split(sysconfig.get_config_var('CCSHARED')) ) + shlex.split(sysconfig.get_config_var('CCSHARED'))
ldflags = os.environ.get( ldflags = os.environ.get(
'OVERRIDE_LDFLAGS', '-Wall ' + 'OVERRIDE_LDFLAGS', '-Wall ' + ' '.join(sanitize_args) + ('' if debug else ' -O3')
('-fsanitize=address -fsanitize=undefined' if asan else ('' if debug else '-O3'))
) )
ldflags = shlex.split(ldflags) ldflags = shlex.split(ldflags)
cflags += shlex.split(os.environ.get('CFLAGS', '')) cflags += shlex.split(os.environ.get('CFLAGS', ''))
@ -230,11 +237,13 @@ def option_parser():
help='Build extension modules with debugging symbols' help='Build extension modules with debugging symbols'
) )
p.add_argument( p.add_argument(
'--asan', '--sanitize',
default=False, default=False,
action='store_true', action='store_true',
help='Turn on address sanitization to detect memory access errors. Note that if you do turn it on,' help='Turn on sanitization to detect memory access errors and undefined behavior. Note that if you do turn it on,'
' you have to run kitty with the environment variable LD_PRELOAD=/usr/lib/libasan.so' ' a special executable will be built for running the test suite. If you want to run normal kitty'
' with sanitization, use LD_PRELOAD=libasan.so (for gcc) and'
' LD_PRELOAD=/usr/lib/clang/4.0.0/lib/linux/libclang_rt.asan-x86_64.so (for clang, changing path as appropriate).'
) )
p.add_argument( p.add_argument(
'--prefix', '--prefix',
@ -268,7 +277,7 @@ def find_c_files():
def build(args, native_optimizations=True): def build(args, native_optimizations=True):
init_env(args.debug, args.asan, native_optimizations) init_env(args.debug, args.sanitize, native_optimizations)
compile_c_extension( compile_c_extension(
'kitty/fast_data_types', args.incremental, *find_c_files() 'kitty/fast_data_types', args.incremental, *find_c_files()
) )
@ -281,6 +290,18 @@ def safe_makedirs(path):
pass pass
def build_test_launcher(args):
cc, ccver = cc_version()
cflags = '-g -Wall -Werror -fpie'.split()
pylib = get_python_flags(cflags)
sanitize_lib = (['-lasan'] if cc == 'gcc' else []) if args.sanitize else []
cflags.extend(get_sanitize_args(cc, ccver) if args.sanitize else [])
cmd = [cc] + cflags + [
'test-launcher.c', '-o', 'test-launcher',
] + sanitize_lib + pylib
run_tool(cmd)
def package(args, for_bundle=False): # {{{ def package(args, for_bundle=False): # {{{
ddir = args.prefix ddir = args.prefix
libdir = os.path.join(ddir, 'lib', 'kitty') libdir = os.path.join(ddir, 'lib', 'kitty')
@ -360,6 +381,7 @@ def main():
os.chdir(os.path.dirname(os.path.abspath(__file__))) os.chdir(os.path.dirname(os.path.abspath(__file__)))
if args.action == 'build': if args.action == 'build':
build(args) build(args)
build_test_launcher(args)
elif args.action == 'test': elif args.action == 'test':
os.execlp( os.execlp(
sys.executable, sys.executable, os.path.join(base, 'test.py') sys.executable, sys.executable, os.path.join(base, 'test.py')

13
test-launcher.c Normal file
View File

@ -0,0 +1,13 @@
/*
* linux-launcher.c
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include <Python.h>
int main(int argc, char *argv[]) {
wchar_t *wargv[2] = {L"kitty-test", L"test.py"};
return Py_Main(2, wargv);
}