#!/bin/bash
set -u

# This script generates symbol lists from all tested/testable artefacts (libraries).
# These lists are stored in subdirectories  ./tests or ./tests.slow.
#
# Format of generated symbol lists (SYMLIST_FORMAT):
#
# each line contains one symbol definition as semicolon-separated list
# containing the following fields:
#
#   Index  Content     Description
#   -----  -------     -----------
#     1    scope       'ref' = reference to symbol; 'def' = definition of symbol
#     2    type        'sub' = subroutine; 'var' = variable; 'unk' = unknown
#     3    symbol      demangled linkage name of symbol
#     4    location    [optional] location of symbol definition in the form 'file:line[:column]'
#
# Notes:
# - location is present in DEBUG mode and for all defined symbols.
# - symbol references always have unknown type.
# - only external symbols are listed.

TEMPDIR=$(mktemp -d)
trap 'rm -rf "$TEMPDIR"' EXIT

filt_od_out() {
    ./filter_objdump_syms.pl
}

filt_od_err() {
    # exitcode of this method is ignored.

    # occurs with SANITIZE=all
    grep -v 'Invalid location list entry type 8' \
         | sed 's/^/ERR: /'
}

annotate_symlist() {
    local SYMLIST="$1"
    ./annotate_dwarf_locations.pl ${SYMLIST}
}

TEEDWARFTO=
TEESYMSTO=

EXTRACT_LOCATIONS=1
if [ ${DARWIN} -eq 1 ]; then
    echo "Note: symbol locations not supported under OSX" >&2
    EXTRACT_LOCATIONS=0
fi

# to test DARWIN behavior under LINUX uncommenct the next line:
# EXTRACT_LOCATIONS=0

extract_symbols() {
    local artefact="$1"

    local syms_cmd="objdump --syms ${artefact} | c++filt"
    local TEMPSYMS=${TEMPDIR}/syms.out

    local RESULT=0
    local result= # result contains exitcodes of: writer[0] reader[1]
    set -o pipefail

    if [ -n "${TEESYMSTO}" ]; then
        eval "$syms_cmd" 2> >(filt_od_err >&2) | tee "${TEESYMSTO}" | filt_od_out > ${TEMPSYMS}
        result=( ${PIPESTATUS[0]} ${PIPESTATUS[2]} )
    else
        eval "$syms_cmd" 2> >(filt_od_err >&2) | filt_od_out > ${TEMPSYMS}
        result=( ${PIPESTATUS[0]} ${PIPESTATUS[1]} )
    fi

    if [ ${result[1]} -ne 0 ]; then
        RESULT=${result[1]}
        echo "Error: reader filt_od_out() failed with exit code ${RESULT}" >&2
    elif [ ${result[0]} -ne 0 ]; then
        RESULT=${result[0]}
        echo "Error: writer syms_cmd failed with exit code ${RESULT}: $syms_cmd" >&2
    fi
    if [ ${RESULT} -eq 0 ]; then
        if [ ${EXTRACT_LOCATIONS} -eq 0 ]; then
            echo "Note: skipping symbol location extraction" >&2
            cat ${TEMPSYMS}
        else
            local dwarf_cmd="objdump --dwarf ${artefact} | c++filt"
            if [ -n "${TEEDWARFTO}" ]; then
                eval "$dwarf_cmd" 2> >(filt_od_err >&2) | tee "${TEEDWARFTO}" | annotate_symlist ${TEMPSYMS}
                result=( ${PIPESTATUS[0]} ${PIPESTATUS[2]} )
            else
                eval "$dwarf_cmd" 2> >(filt_od_err >&2) | annotate_symlist ${TEMPSYMS}
                result=( ${PIPESTATUS[0]} ${PIPESTATUS[1]} )
            fi
            if [ ${result[1]} -ne 0 ]; then
                RESULT=${result[1]}
                echo "Error: reader annotate_symlist() failed with exit code ${RESULT}" >&2
            elif [ ${result[0]} -ne 0 ]; then
                RESULT=${result[0]}
                echo "Error: writer dwarf_cmd failed with exit code ${RESULT}: $dwarf_cmd" >&2
            fi
        fi
    fi
    wait
    set +o pipefail
    exit ${RESULT}
}

# ----------------------------------------

FULLLIB=${1:-}
SYMLIST=${2:-}

if [ -z ${SYMLIST} ]; then
    echo "Usage: $0 <fulllib> <symlist>";
    exit 1
fi

# For debugging you may tee the output from one or both objdump calls to files,
# by un-commenting the following lines:
# TEEDWARFTO=${SYMLIST/.sym/.dwarf_sym}
# TEESYMSTO=${SYMLIST/.sym/.syms_sym}

echo "[extracting with objdump ..]"
extract_symbols "$FULLLIB" > "$SYMLIST"
RESULT=$?

echo "[done]"
exit $RESULT
