#!/bin/bash
set -u

function print_usage()
{
    cat << EOF
$(basename "$0") -- CUDA API Trace

    No arguments.

    Output: All time values given in nanoseconds
        Start : Timestamp when API call was made
        Duration : Length of API calls
        Name : API function name
        Result : return value of API call
        CorrID : Correlation used to map to other CUDA calls
        Pid : Process ID that made the call
        Tid : Thread ID that made the call
        T-Pri : Run priority of call thread
        Thread Name : Name of thread that called API function

    This report provides a trace record of CUDA API function calls and
    their execution times.
EOF
}

### BEGIN include inc_setup ###

EXIT_HELP=25
EXIT_DB=26
EXIT_NODATA=27

# Verify number of params
if [ $# -lt 1 ]
then
    print_usage ${BASH_SOURCE[0]}
    exit ${EXIT_HELP}
fi

# Set DB file
DATABASE="$1"

# Verify DB file exists
if [ ! -f "${DATABASE}" ]
then
    exit ${EXIT_DB}
fi

# Verify DB file contents
# The sqlite3 file format is defined at https://sqlite.org/fileformat.html
DB_FILE_HEADER=$(head -c 16 "$DATABASE" | tr '\0' '\n')
if [ "${DB_FILE_HEADER}" != "SQLite format 3" ]
then
    exit ${EXIT_DB}
fi

# Helper function for error messages
function echoerr() # accepts multiple args
{
    echo "$@" >&2
}

# Setup standard vars

# If we were run by nsys, the path to the preferred sqlite3 should have been
# passed as an env-var.  If not, hope the user has it in their path.
SQLITE3="${NSYS_STATS_SCRIPTS_SQLITE:-sqlite3}"
SQLITE3OPTS="-header -csv -readonly"

RUN_SQLITE="eval \"${SQLITE3}\" ${SQLITE3OPTS} \"${DATABASE}\""

### END include inc_setup ###

### BEGIN: include from inc_table_exists ###

TABLE_EXISTS_TABLES=( )

function table_exists()
{
    local TABLE_NAME=$1

    if [ "${#TABLE_EXISTS_TABLES[@]}" -eq 0 ]
    then
        TABLE_EXISTS_TABLES=( $("${SQLITE3}" ${SQLITE3OPTS} "${DATABASE}" \
                "SELECT name FROM sqlite_master WHERE type = 'table' OR type = 'view'") )
    fi

    for TABLE in "${TABLE_EXISTS_TABLES[@]}"
    do
        if [ "${TABLE}" = "${TABLE_NAME}" ]
        then
            echo "true"
            return 1
        fi
    done
    echo "false"
    return 0
}

### END: include from inc_table_exists ###


if ! $(table_exists "CUPTI_ACTIVITY_KIND_RUNTIME")
then
    # Assume ThreadNames and StringIds also exist
    echoerr "$DATABASE does not contain CUDA trace data."
    exit ${EXIT_NODATA}
fi

${RUN_SQLITE} << EOF

SELECT
    api.start AS "Start Time (ns)",
    api.end - api.start AS "Duration (ns)",
    CASE substr(nstr.value, -6, 2)
        WHEN '_v'THEN substr(nstr.value, 1, length(nstr.value)-6)
        ELSE nstr.value
    END AS "Name",
    api.returnValue AS "Result",
    api.correlationId AS "CorrID",
    -- (api.globalTid >> 40) & 0xFF AS "HWid",
    -- (api.globalTid >> 32) & 0xFF AS "VMid",
    (api.globalTid >> 24) & 0xFFFFFF AS "Pid",
    (api.globalTid      ) & 0xFFFFFF AS "Tid",
    tname.priority AS "T-Pri",
    tstr.value AS "Thread Name"
FROM
    CUPTI_ACTIVITY_KIND_RUNTIME AS api
LEFT OUTER JOIN
    StringIds AS nstr
    ON (nstr.id = api.nameId)
LEFT OUTER JOIN
    ThreadNames AS tname
    ON (tname.globalTid = api.globalTid)
LEFT OUTER JOIN
    StringIds AS tstr
    ON (tstr.id = tname.nameId)
ORDER BY 1
;

EOF
