blob: 1e96d74f4580b04b767507f8d3688ceb72430a17 [file] [log] [blame]
#!/usr/bin/env bash
# -*- mode: sh; sh-basic-offset: 2; indent-tabs-mode: nil; -*-
# Copyright 2021 The Verible Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
###
# Simple smoke test by running all Verible tools over various code-bases
# that can be fetched by git and making sure it does not crash, letting
# out the magic smoke.
#
# We explicitly do _not_ look if the tools fail with some other exit
# code, as we don't care if e.g. there are lint issues in the projects
# at hand, just that Verible is not crashing on input.
#
# Once we are able to parse full of system verilog, we should check
# that verible-verilog-syntax always return 0.
# For now, even if there are syntax errors reported: as long as it doesn't
# crash Verible, we're good.
###
# Suppress '... aborted' messages bash would print when a tool crashes.
# Comment out to see syntax errors in bash while working on script.
exec 2>/dev/null
set -u # Be strict: only allow using a variable after it is assigned
BAZEL_BUILD_OPTIONS="-c opt"
TMPDIR="${TMPDIR:-/tmp}"
readonly BASE_TEST_DIR=${TMPDIR}/test/verible-smoke-test
# Some terminal codes to highlight
readonly TERM_RED=$'\033[1;31m'
readonly TERM_BOLD=$'\033[1m'
readonly TERM_RESET=$'\033[0m'
readonly BINARY_BASE_DIR=bazel-bin/verilog/tools
# In case the binaries are run with ASAN:
# By default, failing asan binaries exit with exit code 1.
# Let's change it to something that we can distinguish from 'normal operation'
export ASAN_OPTIONS="exitcode=140"
readonly VERIBLE_TOOLS_TO_RUN="syntax/verible-verilog-syntax \
lint/verible-verilog-lint \
formatter/verible-verilog-format \
project/verible-verilog-project \
kythe/verible-verilog-kythe-extractor"
# A few projects that can be fetched from git and represent a good
# cross-section of different styles.
# Always in search for more Open Source github Verilog and SystemVerilog
# projects to add here.
#
# We run all Verible tools on these.
#
# Not included: https://github.com/google/skywater-pdk (the recursive
# checkout is gigantic.)
#
# There are some known issues which are all recorded in the associative
# array below, mapping them to Verible issue tracker numbers.
readonly TEST_GIT_PROJECTS="https://github.com/lowRISC/ibex \
https://github.com/lowRISC/opentitan \
https://github.com/chipsalliance/Cores-VeeR-EH2 \
https://github.com/openhwgroup/cva6 \
https://github.com/SymbiFlow/uvm \
https://github.com/taichi-ishitani/tnoc \
https://github.com/ijor/fx68k \
https://github.com/jamieiles/80x86 \
https://github.com/SymbiFlow/XilinxUnisimLibrary \
https://github.com/black-parrot/black-parrot
https://github.com/steveicarus/ivtest \
https://github.com/krevanth/ZAP \
https://github.com/trivialmips/nontrivial-mips \
https://github.com/pulp-platform/axi \
https://github.com/rsd-devel/rsd \
https://github.com/syntacore/scr1 \
https://github.com/bespoke-silicon-group/basejump_stl"
##
# Some of the files in the projects will have issues.
# We record each of them with the associated bug number in this set so that
# we can locally waive the issue and continue with more files,
# but also have it documented which are affected.
#
# Any new issues that arise should be recorded in the verible bug-tracker to
# be fixed.
# Goal: The following list shall be empty :)
declare -A KnownIssue
#--- Opentitan
# Original issue - 1006 has been resolved
# There is also bug 1008 which only shows up if compiled with asan
#--- ivtest
KnownIssue[formatter:$BASE_TEST_DIR/ivtest/ivltests/pr2202846c.v]=1015
#--- nontrivial-mips
KnownIssue[formatter:$BASE_TEST_DIR/nontrivial-mips/src/cpu/decode/decoder.sv]=984
KnownIssue[formatter:$BASE_TEST_DIR/nontrivial-mips/src/cpu/exec/multi_cycle_exec.sv]=984
#--- rsd
KnownIssue[formatter:$BASE_TEST_DIR/rsd/Processor/Src/Privileged/CSR_Unit.sv]=984
#--- scr1
KnownIssue[formatter:$BASE_TEST_DIR/scr1/src/core/scr1_tapc.sv]=1015
KnownIssue[formatter:$BASE_TEST_DIR/scr1/src/tb/scr1_memory_tb_ahb.sv]=1015
#--- Too many to mention manually, so here we do the 'waive all' approach
declare -A KnownProjectToolIssue
##
# Some tools still not fully process all files and return a non-zero exit
# count.
# This is to record the expected non-zero exits, so that we can report if
# things improve or regress.
# TODO: get these numbers to zero. If zero, the corresponding line can be
# removed.
declare -A ExpectedFailCount
ExpectedFailCount[syntax:ibex]=14
ExpectedFailCount[lint:ibex]=14
ExpectedFailCount[project:ibex]=192
ExpectedFailCount[syntax:opentitan]=35
ExpectedFailCount[lint:opentitan]=35
ExpectedFailCount[project:opentitan]=727
ExpectedFailCount[syntax:Cores-VeeR-EH2]=2
ExpectedFailCount[lint:Cores-VeeR-EH2]=2
ExpectedFailCount[project:Cores-VeeR-EH2]=42
ExpectedFailCount[syntax:cva6]=4
ExpectedFailCount[lint:cva6]=4
ExpectedFailCount[project:cva6]=70
ExpectedFailCount[syntax:uvm]=1
ExpectedFailCount[lint:uvm]=1
ExpectedFailCount[project:uvm]=43
ExpectedFailCount[syntax:tnoc]=3
ExpectedFailCount[lint:tnoc]=3
ExpectedFailCount[project:tnoc]=24
ExpectedFailCount[project:80x86]=2
ExpectedFailCount[syntax:XilinxUnisimLibrary]=9
ExpectedFailCount[lint:XilinxUnisimLibrary]=9
ExpectedFailCount[project:XilinxUnisimLibrary]=27
ExpectedFailCount[syntax:black-parrot]=165
ExpectedFailCount[lint:black-parrot]=165
ExpectedFailCount[project:black-parrot]=178
ExpectedFailCount[syntax:ivtest]=188
ExpectedFailCount[lint:ivtest]=188
ExpectedFailCount[formatter:ivtest]=2
ExpectedFailCount[project:ivtest]=217
ExpectedFailCount[project:ZAP]=29
ExpectedFailCount[syntax:nontrivial-mips]=2
ExpectedFailCount[lint:nontrivial-mips]=2
ExpectedFailCount[formatter:nontrivial-mips]=2
ExpectedFailCount[project:nontrivial-mips]=81
ExpectedFailCount[project:axi]=69
ExpectedFailCount[syntax:rsd]=1
ExpectedFailCount[lint:rsd]=1
ExpectedFailCount[formatter:rsd]=1
ExpectedFailCount[project:rsd]=43
ExpectedFailCount[formatter:scr1]=2
ExpectedFailCount[project:scr1]=45
ExpectedFailCount[syntax:basejump_stl]=462
ExpectedFailCount[lint:basejump_stl]=462
ExpectedFailCount[project:basejump_stl]=569
# Ideally, we expect all tools to process all files with a zero exit code.
# However, that is not always the case, so we document the current
# state, so that we can see regressions.
# Currently, either way will not change the output.
function verify_expected_non_zero_exit_count() {
local TOOL_SHORT_NAME=$1
local PROJECT_NAME=$2
local FILE_COUNT=$3
local OBSERVED_NONZERO_COUNT=$4
echo " -> Non zero exit code ${OBSERVED_NONZERO_COUNT}/${NUM_FILES}"
expected_count_key="${TOOL_SHORT_NAME}:${PROJECT_NAME}"
if [[ -v ExpectedFailCount[${expected_count_key}] ]]; then
expected_count=${ExpectedFailCount[${expected_count_key}]}
# We allow some 5% deviation from expected values before we complain loudly
local GRACE_VALUE=$(( ${expected_count} / 20 ))
if [ ${GRACE_VALUE} -lt 2 ]; then
GRACE_VALUE=2
fi
if [ ${OBSERVED_NONZERO_COUNT} -gt ${expected_count} ] ; then
local ALLOWED_UP_TO=$((${expected_count} + ${GRACE_VALUE}))
if [ ${OBSERVED_NONZERO_COUNT} -gt ${ALLOWED_UP_TO} ]; then
echo "::error:: 😱 ${expected_count_key} Expected failures ${expected_count}, got ${OBSERVED_NONZERO_COUNT}"
return 1
else
echo "::warning:: 😱 ${expected_count_key} Expected failures ${expected_count}, got ${OBSERVED_NONZERO_COUNT} (will be an error once ${GRACE_VALUE} more)"
return 0 # Still within grace range.
fi
elif [ ${OBSERVED_NONZERO_COUNT} -lt ${expected_count} ] ; then
echo "::notice:: 🎉 Yay, reduced non-zero exit count ${expected_count} -> ${OBSERVED_NONZERO_COUNT}"
echo "Set ExpectedFailCount[${TOOL_SHORT_NAME}:${PROJECT_NAME}]=${OBSERVED_NONZERO_COUNT}"
fi
else
if [ ${OBSERVED_NONZERO_COUNT} -gt 0 ] ; then
echo "::error:: ********************* Not mentioned in ExpectedFailCount *******"
echo "Add ExpectedFailCount[${TOOL_SHORT_NAME}:${PROJECT_NAME}]=${OBSERVED_NONZERO_COUNT}"
return 1
fi
fi
return 0
}
# Run smoke test on provided files for project.
# Returns 0 if all tools finished without crashing.
#
# First parameter : project name
# Second parameter: name of file containing a list of {System}Verilog files
function run_smoke_test() {
local PROJECT_FILE_LIST=${TMPDIR}/filelist.$$.list
local TOOL_OUT=${TMPDIR}/tool.$$.out
local PROJECT_NAME=$1
local FILELIST=$2
local GIT_URL=$3
local NUM_FILES=$(wc -l < ${FILELIST})
local result=0
echo "::group::== Running verible on ${TERM_BOLD}${PROJECT_NAME}${TERM_RESET} with ${NUM_FILES} files =="
for tool in $VERIBLE_TOOLS_TO_RUN ; do
printf "%-20s %-32s\n" ${PROJECT_NAME} ${tool}
local short_tool_name=$(dirname ${tool})
local non_zero_exit_code=0
while read single_file; do
# TODO(hzeller) the project tool and kythe extractor are meant to run
# on a bunch of files not individual files. Create a complete file-list
if [[ $tool == *-project ]]; then
EXTRA_PARAM="symbol-table-defs --file_list_root=/ --file_list_path"
# a <(echo $single_file) does not work, so use actual file.
echo ${single_file} > ${PROJECT_FILE_LIST}
file_param="${PROJECT_FILE_LIST}"
elif [[ $tool == *-extractor ]]; then
EXTRA_PARAM="--file_list_root=/ --file_list_path"
# a <(echo $single_file) does not work, so use actual file.
echo ${single_file} > ${PROJECT_FILE_LIST}
file_param="${PROJECT_FILE_LIST}"
elif [[ $tool == *-lint ]]; then
EXTRA_PARAM="--lint_fatal=false"
file_param=${single_file}
else
EXTRA_PARAM=""
file_param=${single_file}
fi
${BINARY_BASE_DIR}/${tool} ${EXTRA_PARAM} ${file_param} > ${TOOL_OUT} 2>&1
local EXIT_CODE=$?
# Even though we don't fail globally, let's at least count how many times
# our tools exit with non-zero. Long term, we'd like to have them succeed
# on all files
if [ $EXIT_CODE -ne 0 ]; then
non_zero_exit_code=$[non_zero_exit_code + 1]
fi
# A regular error exit code we accept as normal operation of the tool if
# it encountered a syntax error. Here, we are only interested in not
# receiving a signal such as an abort or segmentation fault.
# So we check for 126, 127 (command not found or executable)
# and >= 128 (128 + signal-number)
# https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
if [ $EXIT_CODE -ge 126 ]; then
if [[ $tool == *-project ]]; then
file_param="<(echo $single_file)" # make easy to reproduce
fi
echo "${TERM_RED}${BINARY_BASE_DIR}/${tool} ${EXTRA_PARAM} ${file_param} ${TERM_RESET}"
waive_file_key="${short_tool_name}:${single_file}"
waive_project_key="${short_tool_name}:${PROJECT_NAME}"
if [[ -v KnownIssue[${waive_file_key}] ]]; then
bug_number=${KnownIssue[${waive_file_key}]}
echo " --> Known issue: 🐞 https://github.com/chipsalliance/verible/issues/$bug_number"
unset KnownIssue[${waive_file_key}]
elif [[ -v KnownProjectToolIssue[${waive_project_key}] ]]; then
echo " --> Known issue 🐞🐞 possibly one of ${KnownProjectToolIssue[${waive_project_key}]}"
else
# This is an so far unknown issue
echo "::error:: 😱 ${single_file}: crash exit code $EXIT_CODE for $tool"
echo "Input File URL: ${GIT_URL}/blob/master/$(echo $single_file | cut -d/ -f6-)"
head -15 ${TOOL_OUT} # Might be useful in this case
result=$((${result} + 1))
fi
fi
done < ${FILELIST}
# Let's see if number of non-zero exit codes match what we expect
verify_expected_non_zero_exit_count ${short_tool_name} ${PROJECT_NAME} ${NUM_FILES} ${non_zero_exit_code}
result=$((${result} + $?))
done # for tool
rm -f ${PROJECT_FILE_LIST} ${TOOL_OUT}
return ${result}
}
mkdir -p $BASE_TEST_DIR
trap 'rm -rf -- "$BASE_TEST_DIR"' EXIT
status_sum=0
#-- Prepare stuff in parallel
# - build the binaries
# - git fetch the projects.
# Build all the installable binaries that we're going to use below.
# TODO: Consider running with
# * address sanitizer (best result with libc++). CAVE: slow
# * running with -D_GLIBCXX_DEBUG (also see #1056)
bazel build ${BAZEL_BUILD_OPTIONS} :install-binaries &
# While compiling, run potentially slow network ops
for git_project in ${TEST_GIT_PROJECTS} ; do
PROJECT_NAME=$(basename $git_project)
PROJECT_DIR=${BASE_TEST_DIR}/${PROJECT_NAME}
git clone ${git_project} ${PROJECT_DIR} 2>/dev/null &
done
echo "Waiting... for compilation and project download finished"
wait
for git_project in ${TEST_GIT_PROJECTS} ; do
PROJECT_NAME=$(basename $git_project)
PROJECT_DIR=${BASE_TEST_DIR}/${PROJECT_NAME}
# Already cloned above
# Just collect everything that looks like Verilog/SystemVerilog
FILELIST=${PROJECT_DIR}/verible.filelist
find ${PROJECT_DIR} -name "*.sv" -o -name "*.svh" -o -name "*.v" | sort > ${FILELIST}
run_smoke_test ${PROJECT_NAME} ${FILELIST} ${git_project}
status_sum=$((${status_sum} + $?))
echo
done
echo "::endgroup::"
echo "There were a total of ${status_sum} new, undocumented issues."
# Let's see if there are any issues that are fixed in the meantime.
if [ ${#KnownIssue[@]} -ne 0 ]; then
echo "::warning ::There are ${#KnownIssue[@]} tool/file combinations, that no longer fail"
declare -A DistinctIssues
for key in "${!KnownIssue[@]}"; do
echo " 🎉 ${key}"
DistinctIssues[${KnownIssue[${key}]}]=1
done
echo
echo "::notice ::These were referencing the following issues. Maybe they are fixed now ?"
for issue_id in "${!DistinctIssues[@]}"; do
echo " 🐞 https://github.com/chipsalliance/verible/issues/$issue_id"
done
echo
fi
exit ${status_sum}