forked from vmware/concord-bft
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrun-cppcheck.sh
executable file
·249 lines (215 loc) · 9.74 KB
/
run-cppcheck.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#!/usr/bin/env bash
# This script runs Cppcheck.
# Prerequisites:
# 1) Cppcheck version CPPCHECK_VERSION is insalled.
# 2) Protocol Buffer, Concocrd Messages and CMF make targets exist under ${CONCORD_BFT_BUILD_DIR}.
# They can be generated by calling 'cmake gen-cmake'.
# 3) Gcc/Clang compiler installed.
#
# Running the script without any parameters from the project root folder is similar to running cppcheck via CMake,
# (by defining -DCPPCHECK when generating project files). The main difference is that the script doesn't build
# any files, it only analyzes them. CMake option triggers cppcheck for each file after a successful build.
# For more differences, please see:
# https://confluence.eng.vmware.com/pages/viewpage.action?spaceKey=BLOC&title=Improving+code+quality+using+Static+Code+Analysis+Tools%2C+Sanitizers+and+Linters
#
# For help on optional input, see usage()
set -eo pipefail
start_time=$(date +%s)
echo "=== Running: $0 $@"
DEFAULT_TARGET_PATH="."
TARGET_PATH=${DEFAULT_TARGET_PATH}
CONCORD_BFT_BUILD_DIR=build/
NUM_JOBS=$(grep -c ^processor /proc/cpuinfo)
CPPCHECK_VERSION=2.8
FULL_REPORTS_FILE_NAME=full_report.txt
LOAD_PROJECT_OPT=
EXTRA_OPTS=
QUIET_OPT="--quiet"
PROCESS_RESULTS_ONLY=false
# trap errors tp print_error
function print_error {
read -r line file <<<$(caller)
echo "An error occurred in line $line of file $file:" >&2
sed "${line}q;d" "$file" >&2
}
trap print_error ERR
usage() {
printf "\nusage:\n"
printf "%s\n" " -h | --help, print this message"
printf "%s%s\n" " -b | --project-build-folder <path>: relative/absolute file path to concord-bft build folder.Default: " \
"${DEFAULT_TARGET_PATH}"
printf "%s\n" " -t | --target-path <path>: relative or absolute path a folder or a file to be anslayzed." \
"Can have wildcards. Default: ./build"
printf "%s\n" " -d | --detect-unused-func: enable detection of unused functions in the code." \
" When enabled, number of jobs is reduced to 1. Default: disabled"
printf "%s\n" " -e | --extra-options: a string of additional valid Cppcheck aand possible arguments to be passed to Cppcheck." \
" Must be double quoted. These options might overide other options, as they are placed last."
printf "%s\n" " -s | --show-progress: show progress reports. Default: disabled."
printf "%s%s\n" " -p | --process-results: only process results, do not run cppcheck." \
" This assumes that reports file exit. Default: false."
}
print_debug_info() {
# Leave for debug
echo "==REPORTS_PATH=${REPORTS_PATH}"
echo "==CPPCHECK_CFG_PATH=${CPPCHECK_CFG_PATH}"
echo "==TARGET_PATH=${TARGET_PATH}"
echo "==CONCORD_BFT_BUILD_DIR=${CONCORD_BFT_BUILD_DIR}"
echo "==CPPCHECK_BUILD_PATH=${CPPCHECK_BUILD_PATH}"
echo "==DEFAULT_TARGET_PATH=${DEFAULT_TARGET_PATH}"
echo "==NUM_JOBS=${NUM_JOBS}"
echo "==CPPCHECK_VERSION=${CPPCHECK_VERSION}"
echo "==FULL_REPORTS_FILE_NAME=${FULL_REPORTS_FILE_NAME}"
echo "==LOAD_PROJECT_OPT=${LOAD_PROJECT_OPT}"
echo "==EXTRA_OPTS=${EXTRA_OPTS}"
echo "==QUIET_OPT=${QUIET_OPT}"
echo "==SUPPRESS_ON_PARTIAL_SCAN_OPT=${SUPPRESS_ON_PARTIAL_SCAN_OPT}"
echo "==PROCESS_RESULTS_ONLY=${PROCESS_RESULTS_ONLY}"
}
parser() {
while [ "$1" ]; do
case $1 in
-h | --help)
usage
exit 0
;;
-b | --project-build-folder)
if [[ $# -lt 2 ]]; then echo "Error: bad input for option -b | --project-build-folder!" >&2; exit; fi
CONCORD_BFT_BUILD_DIR=$(realpath $2)
if [[ ! -d "${CONCORD_BFT_BUILD_DIR}" ]]; then echo "Error: ${CONCORD_BFT_BUILD_DIR} is not a valid folder path!"; exit; fi;
shift 2
;;
-t | --target-path)
if [[ $# -lt 2 ]]; then echo "Error: bad input for option -t | --target-path!" >&2; exit; fi
TARGET_PATH=$2 # do not validate input, as this might hold wildecards. Use as an input to Cppcheck as-is.
shift 2
;;
-d | --detect-unused-func)
NUM_JOBS=1
shift 1
;;
-e | --extra-options)
if [[ $# -lt 2 ]]; then echo "Error: bad input for option -e | --extra-options!" >&2; exit; fi
EXTRA_OPTS=$2
shift 2
;;
-s | --show-progress)
unset QUIET_OPT
shift 1
;;
-p | --process-results)
PROCESS_RESULTS_ONLY=true
shift 1
;;
*)
echo "Error: unknown input $1!" >&2
exit 1
;;
esac
done
}
parser "$@"
CPPCHECK_BUILD_PATH=$(realpath "${CONCORD_BFT_BUILD_DIR}/cppcheck/")
CPPCHECK_CFG_PATH=$(realpath ".cppcheck")
if ! ${PROCESS_RESULTS_ONLY}; then
# Clean reports folder
rm -rf "${CPPCHECK_BUILD_PATH}/reports/}"
mkdir -p "${CPPCHECK_BUILD_PATH}/reports/}"
REPORTS_PATH=$(realpath "${CPPCHECK_BUILD_PATH}/reports/")
if [[ ! -d "${CPPCHECK_CFG_PATH}" ]]; then
echo "Error: Cppcheck configuration path ${CPPCHECK_CFG_PATH} does not exist!"
exit 1
fi
if [[ "$(cppcheck --version)" != *"${CPPCHECK_VERSION}"* ]]; then
echo "Error: expecting cppcheck version ${CPPCHECK_VERSION} to be installed!"
exit 1
fi
# Cppcheck analases all files in compile commands file. We should import from project only if TARGET_PATH is not supplied, or when it
# is not equal to DEFAULT_TARGET_PATH (repository root folder).
if [[ $(realpath "${TARGET_PATH}") == $(realpath "DEFAULT_TARGET_PATH") ]]; then
if [[ ! -f "${CONCORD_BFT_BUILD_DIR}/compile_commands.json" ]]; then
(cd "${CONCORD_BFT_BUILD_DIR}"; cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..)
fi
LOAD_PROJECT_OPT="--project=${CONCORD_BFT_BUILD_DIR}/compile_commands.json"
else
# Supress unusedFunction when only part of the code is scanned
SUPPRESS_ON_PARTIAL_SCAN_OPT="--suppress=unusedFunction:*:*"
fi
# Compile commands file includes generated source code. We must make sure that Protocol Buffers, Concord message targets and CMF targets
# are compiled before invoking Cppcheck
(cd "${CONCORD_BFT_BUILD_DIR}" && \
echo 'Building Protocol Buffers/CMF/Concord message targets to support CMake project import...' && \
make clientservice-proto concord-kvbc-proto replica-state-snapshot-proto thin-replica-proto &> /dev/null && \
make categorized_kvbc_msgs ccron_msgs event_group_msgs pruning_msgs db_checkpoint_msg &> /dev/null && \
make cmf_messages execution_data_cmf keys_and_signatures_cmf skvbc_messages_cmf &> /dev/null)
#print_debug_info # uncomment to debug
# We use Bash's backtick command substitution in order to put inline comments
set -x
cppcheck "${LOAD_PROJECT_OPT}" \
--template='cppcheck1' \
--inconclusive \ `#Allow that Cppcheck reports even though the analysis is inconclusive.` \
--enable=all \
"${SUPPRESS_ON_PARTIAL_SCAN_OPT}" \ `#Possibly suppress code when having a partial code scan.` \
--platform=unix64 \
--std=c++17 \
-j ${NUM_JOBS} \
--error-exitcode=0 \ `#Return exit code 0 for errors found. Actual exit code is detemined later.` \
--max-configs=1 \ `#By default, enable a single configuration for a faster scan.` \
--cppcheck-build-dir"=${CPPCHECK_BUILD_PATH}" \
`#We might need those includes in partial code scan, and for relative paths` \
--includes-file="${CPPCHECK_CFG_PATH}/includes.txt" \
--output-file="${REPORTS_PATH}/${FULL_REPORTS_FILE_NAME}" \
--suppressions-list="${CPPCHECK_CFG_PATH}/suppressions.txt" \
--exitcode-suppressions="${CPPCHECK_CFG_PATH}/exitcode-suppressions.txt" \
-i${CONCORD_BFT_BUILD_DIR}/ -iCMakeFiles \ `#ignore Cmake source code and build folder` \
`#Load boost, openSSL and googletest libraries for better analaysis` \
--library=boost.cfg --library=openssl.cfg --library=googletest \
`#Load thread safety addon , to locate thread safety issues like static local objects used by multiple threads.` \
--addon=threadsafety.py \
"${QUIET_OPT}" \
${EXTRA_OPTS} \ `#Do not double quote, as we want word splitting.` \
"${TARGET_PATH}"
set +x
echo "Cppcheck execution is over.."
fi # if ! ${PROCESS_RESULTS_ONLY};
#print_debug_info # uncomment to debug
REPORTS_PATH=$(realpath "${CPPCHECK_BUILD_PATH}/reports/")
if [[ ! -f "${REPORTS_PATH}/${FULL_REPORTS_FILE_NAME}" ]]; then
echo "${REPORTS_PATH}/${FULL_REPORTS_FILE_NAME} does not exist... "
exit 1;
fi
# Process results: grep ${FULL_REPORTS_FILE_NAME} by categories, print report...
echo "Processing output and creating report files..."
cd "${REPORTS_PATH}"
declare -a categories=("(error)" "(.*inconclusive.*)" "(warning)" "(style)" "(performance)" "(portability)" "(information)" \
"(unusedFunction)" "(missingInclude)")
printf "\n========== Cppcheck - #Results by Category ==========\n"
found=false
for category in "${categories[@]}" ;do
if grep -q "${category}" ${FULL_REPORTS_FILE_NAME}; then
output_filename="$(echo ${category} | sed 's/[()*.]//g').txt"
grep "${category}" "${FULL_REPORTS_FILE_NAME}" > "${output_filename}"
found=true
wc -l "${output_filename}"
fi
done;
if $found; then
printf "..............................\n"
wc -l "${FULL_REPORTS_FILE_NAME}"
else
printf "\t\tNo issues found!"
fi
printf "\n=====================================================\n"
# We consider success if there are no errors/warnings
if (grep -q "warning" "${FULL_REPORTS_FILE_NAME}") || (grep -q "error" "${FULL_REPORTS_FILE_NAME}"); then
EXIT_CODE=1
echo "Cppcheck failed!"
else
EXIT_CODE=0
echo "Cppcheck finished successfully (no errors/warnings were found, but there might be lower level issues)";
fi
echo "Reports are under ${REPORTS_PATH}."
printf "%s includes the full report, while other entries by category.\n\n" "${FULL_REPORTS_FILE_NAME}"
end_time=$(date +%s)
total_runtime_sec=$((end_time - start_time))
echo "Total runtime: ${total_runtime_sec} seconds"
exit ${EXIT_CODE}