#!/bin/sh
#**************************************************************************
#*                                                                        *
#*                                 OCaml                                  *
#*                                                                        *
#*      Damien Doligez, Xavier Leroy, projet Gallium, INRIA Paris         *
#*                                                                        *
#*   Copyright 2018 Institut National de Recherche en Informatique et     *
#*     en Automatique.                                                    *
#*                                                                        *
#*   All rights reserved.  This file is distributed under the terms of    *
#*   the GNU Lesser General Public License version 2.1, with the          *
#*   special exception on linking described in the file LICENSE.          *
#*                                                                        *
#**************************************************************************

# This script is run on Inria's continuous-integration servers to recompile
# from scratch, adding more run-time checks ("sanitizers") to the C code,
# and run the test suite.

# In this context, it is necessary to skip a few tests whose behaviour
# is modified by the instrumentation:

export OCAMLTEST_SKIP_TESTS="tests/afl-instrumentation/afltest.ml \
tests/runtime-errors/stackoverflow.ml"

jobs=-j8
make=make

#########################################################################

# Print each command before its execution
set -x

# stop on error
set -e

# Tell gcc to use only ASCII in its diagnostic outputs.
export LC_ALL=C

# How to run the test suite
if test -n "$jobs" && test -x /usr/bin/parallel; then
  export PARALLEL="$jobs $PARALLEL"
  run_testsuite="$make -C testsuite parallel"
else
  run_testsuite="$make -C testsuite all"
fi

# A tool that makes error backtraces nicer
# Need to pick the one that matches clang-9 and is named "llvm-symbolizer"
# (/usr/bin/llvm-symbolizer-9 doesn't work, that would be too easy)
export ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-9/bin/llvm-symbolizer
export TSAN_SYMBOLIZER_PATH="$ASAN_SYMBOLIZER_PATH"

#########################################################################

echo "======== clang 9, address sanitizer, UB sanitizer =========="

git clean -q -f -d -x

# Use clang 9

# These are the undefined behaviors we want to check
# Others occur on purpose e.g. signed arithmetic overflow
ubsan="\
bool,\
builtin,\
bounds,\
enum,\
nonnull-attribute,\
nullability,\
object-size,\
pointer-overflow,\
returns-nonnull-attribute,\
shift-exponent,\
unreachable"

# Select address sanitizer and UB sanitizer, with trap-on-error behavior
sanitizers="-fsanitize=address -fsanitize-trap=$ubsan"

# Don't optimize too much to get better backtraces of errors

./configure \
  CC=clang-9 \
  CFLAGS="-O1 -fno-omit-frame-pointer $sanitizers" \
  --disable-stdlib-manpages --enable-dependency-generation

# Build the system.  We want to check for memory leaks, hence
# 1- force ocamlrun to free memory before exiting
# 2- add an exception for ocamlyacc, which doesn't free memory

OCAMLRUNPARAM="c=1" \
LSAN_OPTIONS="suppressions=$(pwd)/tools/ci/inria/sanitizers/lsan-suppr.txt" \
make $jobs

# Run the testsuite.
# We deactivate leak detection for two reasons:
# - The suppressed leak detections related to ocamlyacc mess up the
# output of the tests and are reported as failures by ocamltest.
# - The Ocaml runtime does not free the memory when a fatal error
# occurs.

# We already use sigaltstack for stack overflow detection. Our use
# interacts with ASAN's. Hence, we tell ASAN not to use it.

ASAN_OPTIONS="detect_leaks=0,use_sigaltstack=0" $run_testsuite

#########################################################################

echo "======== clang 9, thread sanitizer =========="

git clean -q -f -d -x

# Select thread sanitizer
# Don't optimize too much to get better backtraces of errors

./configure \
  CC=clang-9 \
  CFLAGS="-O1 -fno-omit-frame-pointer -fsanitize=thread" \
  --disable-stdlib-manpages --enable-dependency-generation

# Build the system
make $jobs

# ThreadSanitizer has problems with processes that exit via
# pthread_exit in the last thread.
# It also reports errors for the error case of unlocking an
# error-checking mutex.
# Exclude the corresponding test
export OCAMLTEST_SKIP_TESTS="$OCAMLTEST_SKIP_TESTS \
tests/lib-threads/pr9971.ml \
tests/statmemprof/thread_exit_in_callback.ml \
tests/lib-threads/mutex_errors.ml"

# Run the testsuite.
# ThreadSanitizer complains about fork() in threaded programs,
# we ask it to just continue in this case.
TSAN_OPTIONS="die_after_fork=0" $run_testsuite

#########################################################################

# This is a failed attempt at using the memory sanitizer
# (to detect reads from uninitialized memory).
# Some alarms are reported that look like false positive
# and are impossible to debug.

# echo "======== clang 6.0, memory sanitizer =========="

# git clean -q -f -d -x

# # Use clang 6.0
# # Memory sanitizer doesn't like the static data generated by ocamlopt,
# # hence build bytecode only
# # Select memory sanitizer
# # Don't optimize at all to get better backtraces of errors

# ./configure \
#   CC=clang-9 \
#   CFLAGS="-O0 -g -fno-omit-frame-pointer -fsanitize=memory" \
#   --disable-native-compiler
# # A tool that makes error backtraces nicer
# # Need to pick the one that matches clang-6.0
# export MSAN_SYMBOLIZER_PATH=/usr/lib/llvm-6.0/bin/llvm-symbolizer

# # Build the system (bytecode only) and test
# make $jobs
# $run_testsuite
