blob: c679a2a96236dfcce212ee7d28d04e164e061c28 [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"
# Write log files to this directory
readonly SMOKE_LOGGING_DIR="${SMOKE_LOGGING_DIR:-$BASE_TEST_DIR/error-logs}"
# Make the directory for the logs
if [ ! -d "${SMOKE_LOGGING_DIR}" ]; then
mkdir -p "${SMOKE_LOGGING_DIR}"
fi
# 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/verible/verilog/tools
readonly ISSUE_PREFIX="https://github.com/chipsalliance/verible/issues"
# 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 \
preprocessor/verible-verilog-preprocessor \
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.
# TODO(hzeller): there should be a configuration file that contains two
# columns: URL + hash, so that we can fetch a particular known version not
# a moving target.
readonly TEST_GIT_PROJECTS="https://github.com/lowRISC/ibex \
https://github.com/lowRISC/opentitan \
https://github.com/chipsalliance/sv-tests \
https://github.com/chipsalliance/Cores-VeeR-EH2 \
https://github.com/chipsalliance/caliptra-rtl \
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/trivialmips/nontrivial-mips \
https://github.com/pulp-platform/axi \
https://github.com/rsd-devel/rsd \
https://github.com/syntacore/scr1 \
https://github.com/olofk/serv \
https://github.com/bespoke-silicon-group/basejump_stl \
https://github.com/gtaylormb/opl3_fpga"
##
# 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 :)
# Format: <tool-name>:<basename of file-name in project>
declare -A KnownIssue
# At the moment, the list is empty
#--- Too many to mention manually, so here we do the 'waive all' approach
# Format: <tool-name>:<project>
declare -A KnownProjectToolIssue
KnownProjectToolIssue[project:caliptra-rtl]=1946
##
# 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]=17
ExpectedFailCount[lint:ibex]=17
ExpectedFailCount[project:ibex]=222
ExpectedFailCount[preprocessor:ibex]=394
ExpectedFailCount[syntax:opentitan]=101
ExpectedFailCount[lint:opentitan]=101
ExpectedFailCount[project:opentitan]=1133
ExpectedFailCount[formatter:opentitan]=0
ExpectedFailCount[preprocessor:opentitan]=3061
ExpectedFailCount[syntax:sv-tests]=77
ExpectedFailCount[lint:sv-tests]=76
ExpectedFailCount[project:sv-tests]=187
ExpectedFailCount[preprocessor:sv-tests]=139
ExpectedFailCount[syntax:caliptra-rtl]=39
ExpectedFailCount[lint:caliptra-rtl]=38
ExpectedFailCount[project:caliptra-rtl]=419
ExpectedFailCount[preprocessor:caliptra-rtl]=879
ExpectedFailCount[syntax:Cores-VeeR-EH2]=2
ExpectedFailCount[lint:Cores-VeeR-EH2]=2
ExpectedFailCount[project:Cores-VeeR-EH2]=42
ExpectedFailCount[preprocessor:Cores-VeeR-EH2]=43
ExpectedFailCount[syntax:cva6]=7
ExpectedFailCount[lint:cva6]=7
ExpectedFailCount[project:cva6]=91
ExpectedFailCount[preprocessor:cva6]=141
ExpectedFailCount[syntax:uvm]=1
ExpectedFailCount[lint:uvm]=1
ExpectedFailCount[project:uvm]=43
ExpectedFailCount[preprocessor:uvm]=115
ExpectedFailCount[syntax:tnoc]=3
ExpectedFailCount[lint:tnoc]=3
ExpectedFailCount[project:tnoc]=24
ExpectedFailCount[preprocessor:tnoc]=57
ExpectedFailCount[project:80x86]=2
ExpectedFailCount[preprocessor:80x86]=7
ExpectedFailCount[syntax:XilinxUnisimLibrary]=4
ExpectedFailCount[lint:XilinxUnisimLibrary]=4
ExpectedFailCount[project:XilinxUnisimLibrary]=22
ExpectedFailCount[preprocessor:XilinxUnisimLibrary]=96
ExpectedFailCount[syntax:black-parrot]=154
ExpectedFailCount[lint:black-parrot]=154
ExpectedFailCount[project:black-parrot]=169
ExpectedFailCount[preprocessor:black-parrot]=170
ExpectedFailCount[syntax:ivtest]=166
ExpectedFailCount[lint:ivtest]=166
ExpectedFailCount[project:ivtest]=198
ExpectedFailCount[preprocessor:ivtest]=26
ExpectedFailCount[syntax:nontrivial-mips]=2
ExpectedFailCount[lint:nontrivial-mips]=2
ExpectedFailCount[project:nontrivial-mips]=81
ExpectedFailCount[preprocessor:nontrivial-mips]=78
ExpectedFailCount[project:axi]=80
ExpectedFailCount[preprocessor:axi]=77
ExpectedFailCount[syntax:rsd]=5
ExpectedFailCount[lint:rsd]=5
ExpectedFailCount[project:rsd]=52
ExpectedFailCount[preprocessor:rsd]=49
ExpectedFailCount[project:scr1]=45
ExpectedFailCount[preprocessor:scr1]=46
ExpectedFailCount[project:serv]=1
ExpectedFailCount[preprocessor:serv]=1
ExpectedFailCount[syntax:basejump_stl]=490
ExpectedFailCount[lint:basejump_stl]=490
ExpectedFailCount[project:basejump_stl]=609
ExpectedFailCount[formatter:basejump_stl]=1
ExpectedFailCount[preprocessor:basejump_stl]=648
ExpectedFailCount[syntax:opl3_fpga]=3
ExpectedFailCount[lint:opl3_fpga]=3
ExpectedFailCount[project:opl3_fpga]=5
ExpectedFailCount[preprocessor:opl3_fpga]=4
# 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 PROJECT_NAME=$1
local FILELIST=$2
local GIT_URL=$3
local NUM_FILES=$(wc -l < ${FILELIST})
local result=0
local PROJECT_LOG_BASE="${SMOKE_LOGGING_DIR}/${PROJECT_NAME}"
mkdir -p ${PROJECT_LOG_BASE}
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
local PROJECT_TOOL_LOG_BASE="${PROJECT_LOG_BASE}/${short_tool_name}"
mkdir -p "${PROJECT_TOOL_LOG_BASE}"
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}
elif [[ $tool == *-preprocessor ]]; then
EXTRA_PARAM="preprocess"
file_param=${single_file}
else
EXTRA_PARAM=""
file_param=${single_file}
fi
local FNAME=$(basename ${file_param}) # make hash ?
local PROJECT_FILE_TOOL_OUT="${PROJECT_TOOL_LOG_BASE}/${FNAME}"
${BINARY_BASE_DIR}/${tool} ${EXTRA_PARAM} ${file_param} > ${PROJECT_FILE_TOOL_OUT} 2>&1
local EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
rm -f "${PROJECT_FILE_TOOL_OUT}" # not interested keeping logfiles of successful runs
else
# 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
non_zero_exit_code=$[non_zero_exit_code + 1]
# The error-log-analyzer.py requires the files in a particular format
local ANALYZE_DIR="${SMOKE_LOGGING_DIR}/${PROJECT_NAME}-nonzeros"
mkdir -p "${ANALYZE_DIR}"
cp "${PROJECT_FILE_TOOL_OUT}" "${ANALYZE_DIR}/${EXIT_CODE}-${FNAME}_${short_tool_name}"
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}:$(basename ${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: 🐞 ${ISSUE_PREFIX}/${bug_number}"
unset KnownIssue[${waive_file_key}]
elif [[ -v KnownProjectToolIssue[${waive_project_key}] ]]; then
echo " --> Known issue 🐞🐞 possibly one of ${ISSUE_PREFIX}/${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 ${PROJECT_FILE_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}
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 "base test dir ${BASE_TEST_DIR}; writing logs to ${SMOKE_LOGGING_DIR}"
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
if [ ! -d "${PROJECT_DIR}" ]; then
echo "Didn't see ${PROJECT_DIR}. Network connection flaky ? Skipping."
continue
fi
# 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 " 🐞 ${ISSUE_PREFIX}/${issue_id}"
done
echo
fi
exit ${status_sum}