-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Golang coverage report test #3142
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Copyright 2020 Google LLC | ||
// | ||
// 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. | ||
|
||
package mypackagebeingfuzzed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we would need a Google copyright here:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
"runtime/pprof" | ||
"testing" | ||
) | ||
|
||
func TestFuzzCorpus(t *testing.T) { | ||
dir := os.Getenv("FUZZ_CORPUS_DIR") | ||
if dir == "" { | ||
t.Logf("No fuzzing corpus directory set") | ||
return | ||
} | ||
infos, err := ioutil.ReadDir(dir) | ||
if err != nil { | ||
t.Logf("Not fuzzing corpus directory %s", err) | ||
return | ||
} | ||
filename := "" | ||
defer func() { | ||
if r := recover(); r != nil { | ||
t.Error("Fuzz panicked in "+filename, r) | ||
} | ||
}() | ||
profname := os.Getenv("FUZZ_PROFILE_NAME") | ||
if profname != "" { | ||
f, err := os.Create(profname + ".cpu.prof") | ||
if err != nil { | ||
t.Logf("error creating profile file %s\n", err) | ||
} else { | ||
_ = pprof.StartCPUProfile(f) | ||
} | ||
} | ||
for i := range infos { | ||
filename = dir + infos[i].Name() | ||
data, err := ioutil.ReadFile(filename) | ||
if err != nil { | ||
t.Error("Failed to read corpus file", err) | ||
} | ||
FuzzFunction(data) | ||
} | ||
if profname != "" { | ||
pprof.StopCPUProfile() | ||
f, err := os.Create(profname + ".heap.prof") | ||
if err != nil { | ||
t.Logf("error creating heap profile file %s\n", err) | ||
} | ||
if err = pprof.WriteHeapProfile(f); err != nil { | ||
t.Logf("error writing heap profile file %s\n", err) | ||
} | ||
f.Close() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ COPY --from=base-clang /usr/local/bin/llvm-cov /usr/local/bin/ | |
COPY --from=base-clang /usr/local/bin/llvm-profdata /usr/local/bin/ | ||
COPY --from=base-clang /usr/local/bin/llvm-symbolizer /usr/local/bin/ | ||
|
||
RUN apt-get update | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not think so, this must have been a debug leftover There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for verifying that it's needed! |
||
RUN apt-get install -y \ | ||
binutils \ | ||
file \ | ||
|
@@ -64,3 +65,24 @@ ENV MSAN_OPTIONS="print_stats=1:strip_path_prefix=/workspace/:symbolize=1:dedup_ | |
ENV UBSAN_OPTIONS="print_stacktrace=1:print_summary=1:silence_unsigned_overflow=1:strip_path_prefix=/workspace/:symbolize=1:dedup_token_length=3" | ||
ENV FUZZER_ARGS="-rss_limit_mb=2560 -timeout=25" | ||
ENV AFL_FUZZER_ARGS="-m none" | ||
|
||
# Download and install the latest stable Go. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copied from base-builder Maybe we can reuse the binaries already present in /out/root/go... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so we need golang to be installed in the base-runner image, as it's being used for generating a coverage report? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @oliverchang as we also install Go in the base-runner, would you be ok with us doing that once in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leaving it here is probably fine. We plan to diverge images here and migrate runners to be based on ClusterFuzz images instead, so we'd have to add it back here anyway. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ack, thanks Oliver! |
||
ADD https://storage.googleapis.com/golang/getgo/installer_linux $SRC/ | ||
RUN chmod +x $SRC/installer_linux && \ | ||
SHELL="bash" $SRC/installer_linux && \ | ||
rm $SRC/installer_linux | ||
|
||
# Set up Golang environment variables (copied from /root/.bash_profile). | ||
ENV GOPATH /root/go | ||
|
||
# /root/.go/bin is for the standard Go binaries (i.e. go, gofmt, etc). | ||
# $GOPATH/bin is for the binaries from the dependencies installed via "go get". | ||
ENV PATH $PATH:/root/.go/bin:$GOPATH/bin | ||
|
||
# gocovmerge merges coverage profiles. | ||
RUN go get -u github.com/wadey/gocovmerge | ||
# pprof-merge merges performance profiles. | ||
RUN go get -u github.com/rakyll/pprof-merge | ||
# gocovsum produces a json summary. | ||
RUN go get -u github.com/catenacyber/gocovsum | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -111,21 +111,37 @@ function run_fuzz_target { | |
fi | ||
} | ||
|
||
export SYSGOPATH=$GOPATH | ||
export GOPATH=$OUT/$GOPATH | ||
# Run each fuzz target, generate raw coverage dumps. | ||
for fuzz_target in $FUZZ_TARGETS; do | ||
# Continue if not a fuzz target. | ||
if [[ $FUZZING_ENGINE != "none" ]]; then | ||
grep "LLVMFuzzerTestOneInput" $fuzz_target > /dev/null 2>&1 || continue | ||
fi | ||
|
||
echo "Running $fuzz_target" | ||
run_fuzz_target $fuzz_target & | ||
|
||
if [[ -z $objects ]]; then | ||
# The first object needs to be passed without -object= flag. | ||
objects="$fuzz_target" | ||
# Test if fuzz target is a golang one. | ||
if [[ $FUZZING_LANGUAGE == "go" ]]; then | ||
# Continue if not a fuzz target. | ||
if [[ $FUZZING_ENGINE != "none" ]]; then | ||
grep "go test -run" $fuzz_target > /dev/null 2>&1 || continue | ||
fi | ||
cd $GOPATH/src | ||
echo "Running go target $fuzz_target" | ||
export FUZZ_CORPUS_DIR="/corpus/${fuzz_target}/" | ||
export FUZZ_PROFILE_NAME="$DUMPS_DIR/$fuzz_target.perf" | ||
bash $OUT/$fuzz_target $DUMPS_DIR/$fuzz_target.profdata & | ||
cd $OUT | ||
else | ||
objects="$objects -object=$fuzz_target" | ||
# Continue if not a fuzz target. | ||
if [[ $FUZZING_ENGINE != "none" ]]; then | ||
grep "LLVMFuzzerTestOneInput" $fuzz_target > /dev/null 2>&1 || continue | ||
fi | ||
|
||
echo "Running $fuzz_target" | ||
run_fuzz_target $fuzz_target & | ||
|
||
if [[ -z $objects ]]; then | ||
# The first object needs to be passed without -object= flag. | ||
objects="$fuzz_target" | ||
else | ||
objects="$objects -object=$fuzz_target" | ||
fi | ||
fi | ||
|
||
# Do not spawn more processes than the number of CPUs available. | ||
|
@@ -139,32 +155,47 @@ done | |
# Wait for background processes to finish. | ||
wait | ||
|
||
# From this point on the script does not tolerate any errors. | ||
set -e | ||
if [[ $FUZZING_LANGUAGE == "go" ]]; then | ||
$SYSGOPATH/bin/gocovmerge $DUMPS_DIR/*.profdata > fuzz.cov | ||
go tool cover -html=fuzz.cov -o $REPORT_ROOT_DIR/index.html | ||
$SYSGOPATH/bin/gocovsum fuzz.cov > $SUMMARY_FILE | ||
cp $REPORT_ROOT_DIR/index.html $REPORT_PLATFORM_DIR/index.html | ||
$SYSGOPATH/bin/pprof-merge $DUMPS_DIR/*.perf.cpu.prof | ||
mv merged.data $REPORT_ROOT_DIR/cpu.prof | ||
$SYSGOPATH/bin/pprof-merge $DUMPS_DIR/*.perf.heap.prof | ||
mv merged.data $REPORT_ROOT_DIR/heap.prof | ||
#TODO some proxy for go tool pprof -http=127.0.0.1:8001 $DUMPS_DIR/cpu.prof | ||
echo "Finished generating code coverage report for Go fuzz targets." | ||
else | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add one indentation level for all the commands from here down to line 190 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, I had not done this at first for easier review ;-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. smart! |
||
# Merge all dumps from the individual targets. | ||
rm -f $PROFILE_FILE | ||
llvm-profdata merge -sparse $DUMPS_DIR/*.profdata -o $PROFILE_FILE | ||
# From this point on the script does not tolerate any errors. | ||
set -e | ||
|
||
# TODO(mmoroz): add script from Chromium for rendering directory view reports. | ||
# The first path in $objects does not have -object= prefix (llvm-cov format). | ||
shared_libraries=$(coverage_helper shared_libs -build-dir=$OUT -object=$objects) | ||
objects="$objects $shared_libraries" | ||
# Merge all dumps from the individual targets. | ||
rm -f $PROFILE_FILE | ||
llvm-profdata merge -sparse $DUMPS_DIR/*.profdata -o $PROFILE_FILE | ||
|
||
# It's important to use $LLVM_COV_COMMON_ARGS as the last argument due to | ||
# positional arguments (SOURCES) that can be passed via $COVERAGE_EXTRA_ARGS. | ||
LLVM_COV_ARGS="-instr-profile=$PROFILE_FILE $objects $LLVM_COV_COMMON_ARGS" | ||
# TODO(mmoroz): add script from Chromium for rendering directory view reports. | ||
# The first path in $objects does not have -object= prefix (llvm-cov format). | ||
shared_libraries=$(coverage_helper shared_libs -build-dir=$OUT -object=$objects) | ||
objects="$objects $shared_libraries" | ||
|
||
# Generate HTML report. | ||
llvm-cov show -format=html -output-dir=$REPORT_ROOT_DIR \ | ||
-Xdemangler c++filt -Xdemangler -n $LLVM_COV_ARGS | ||
# It's important to use $LLVM_COV_COMMON_ARGS as the last argument due to | ||
# positional arguments (SOURCES) that can be passed via $COVERAGE_EXTRA_ARGS. | ||
LLVM_COV_ARGS="-instr-profile=$PROFILE_FILE $objects $LLVM_COV_COMMON_ARGS" | ||
|
||
# Export coverage summary in JSON format. | ||
llvm-cov export -summary-only $LLVM_COV_ARGS > $SUMMARY_FILE | ||
# Generate HTML report. | ||
llvm-cov show -format=html -output-dir=$REPORT_ROOT_DIR \ | ||
-Xdemangler c++filt -Xdemangler -n $LLVM_COV_ARGS | ||
|
||
# Post process HTML report. | ||
coverage_helper -v post_process -src-root-dir=/ -summary-file=$SUMMARY_FILE \ | ||
-output-dir=$REPORT_ROOT_DIR $PATH_EQUIVALENCE_ARGS | ||
# Export coverage summary in JSON format. | ||
llvm-cov export -summary-only $LLVM_COV_ARGS > $SUMMARY_FILE | ||
|
||
# Post process HTML report. | ||
coverage_helper -v post_process -src-root-dir=/ -summary-file=$SUMMARY_FILE \ | ||
-output-dir=$REPORT_ROOT_DIR $PATH_EQUIVALENCE_ARGS | ||
|
||
fi | ||
|
||
if [[ -n $HTTP_PORT ]]; then | ||
# Serve the report locally. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,22 @@ function compile_fuzzer { | |
function=$2 | ||
fuzzer=$3 | ||
|
||
if [[ $SANITIZER = *coverage* ]]; then | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. something like this needs to be done for each Go project, right? Sadly, we decided not to put There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this needs to be done for each project. Do you want this generalization be part of this PR ? (or rather in another one) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's do it in another PR |
||
cd $GOPATH/src/$path | ||
fuzzed_package=`pwd | rev | cut -d'/' -f 1 | rev` | ||
cp $GOPATH/ossfuzz_coverage_runner.go ./"${function,,}"_test.go | ||
sed -i -e 's/FuzzFunction/'$function'/' ./"${function,,}"_test.go | ||
sed -i -e 's/mypackagebeingfuzzed/'$fuzzed_package'/' ./"${function,,}"_test.go | ||
sed -i -e 's/TestFuzzCorpus/Test'$function'Corpus/' ./"${function,,}"_test.go | ||
|
||
echo "#/bin/sh" > $OUT/$fuzzer | ||
echo "cd $path" >> $OUT/$fuzzer | ||
echo "go test -run Test${function}Corpus -v -tags fuzz -coverprofile \$1 " >> $OUT/$fuzzer | ||
chmod +x $OUT/$fuzzer | ||
|
||
cd - | ||
return 0 | ||
fi | ||
# Compile and instrument all Go files relevant to this fuzz target. | ||
go-fuzz -tags fuzz -func $function -o $fuzzer.a $path | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need the whole
$GOPATH
to be copied over to$OUT
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it can be just $GOPATH/src and $GOPATH/pkg but I am not sure...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, let's leave it this way