t/valgrind/analyze.sh

Summary

Maintainability
Test Coverage
#!/bin/sh

# Get TEST_OUTPUT_DIRECTORY from GIT-BUILD-OPTIONS if it's there...
. "$(dirname "$0")/../../GIT-BUILD-OPTIONS"
# ... otherwise set it to the default value.
: ${TEST_OUTPUT_DIRECTORY=$(dirname "$0")/..}

output=
count=0
total_count=0
missing_message=
new_line='
'

# start outputting the current valgrind error in $out_prefix.++$count,
# and the test case which failed in the corresponding .message file
start_output () {
    test -z "$output" || return

    # progress
    total_count=$(($total_count+1))
    test -t 2 && printf "\rFound %d errors" $total_count >&2

    count=$(($count+1))
    output=$out_prefix.$count
    : > $output

    echo "*** $1 ***" > $output.message
}

finish_output () {
    test ! -z "$output" || return
    output=

    # if a test case has more than one valgrind error, we need to
    # copy the last .message file to the previous errors
    test -z "$missing_message" || {
        while test $missing_message -lt $count
        do
            cp $out_prefix.$count.message \
                $out_prefix.$missing_message.message
            missing_message=$(($missing_message+1))
        done
        missing_message=
    }
}

# group the valgrind errors by backtrace
output_all () {
    last_line=
    j=0
    i=1
    while test $i -le $count
    do
        # output <number> <backtrace-in-one-line>
        echo "$i $(tr '\n' ' ' < $out_prefix.$i)"
        i=$(($i+1))
    done |
    sort -t ' ' -k 2 | # order by <backtrace-in-one-line>
    while read number line
    do
        # find duplicates, do not output backtrace twice
        if test "$line" != "$last_line"
        then
            last_line=$line
            j=$(($j+1))
            printf "\nValgrind error $j:\n\n"
            cat $out_prefix.$number
            printf "\nfound in:\n"
        fi
        # print the test case where this came from
        printf "\n"
        cat $out_prefix.$number.message
    done
}

handle_one () {
    OLDIFS=$IFS
    IFS="$new_line"
    while read line
    do
        case "$line" in
        # backtrace, possibly a new one
        ==[0-9]*)

            # Does the current valgrind error have a message yet?
            case "$output" in
            *.message)
                test -z "$missing_message" &&
                missing_message=$count
                output=
            esac

            start_output $(basename $1)
            echo "$line" |
            sed 's/==[0-9]*==/==valgrind==/' >> $output
            ;;
        # end of backtrace
        '}')
            test -z "$output" || {
                echo "$line" >> $output
                test $output = ${output%.message} &&
                output=$output.message
            }
            ;;
        # end of test case
        '')
            finish_output
            ;;
        # normal line; if $output is set, print the line
        *)
            test -z "$output" || echo "$line" >> $output
            ;;
        esac
    done < $1
    IFS=$OLDIFS

    # just to be safe
    finish_output
}

for test_script in "$TEST_OUTPUT_DIRECTORY"/test-results/*.out
do
    handle_one $test_script
done

output_all