#!/usr/bin/awk -f
#
# sydtrace.awk:
# Feed the output of sydtrace.bt to this script.
#
# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>
# SPDX-License-Identifier: GPL-3.0

BEGIN {
    # Lookup command (override by setting CARGO_BIN_EXE_syd-sys in the environment)
    lookup_cmd = "syd-sys"
    if (ENVIRON["CARGO_BIN_EXE_syd-sys"] != "") lookup_cmd = ENVIRON["CARGO_BIN_EXE_syd-sys"]
}

{
    # Keep kstack/ustack and stack lines untouched.
    if ($0 ~ /^kstack:/ || $0 ~ /^ustack:/ || $0 ~ /^[ \t]*»/) {
        print
        next
    }

    # Extract sys=NUMBER and comm=TOKEN (if present) using RSTART/RLENGTH
    sysnum = ""
    commtok = ""

    if (match($0, /sys=[-]?[0-9]+/)) {
        # sys= starts at RSTART, length RLENGTH; value begins after "sys="
        sysnum = substr($0, RSTART + 4, RLENGTH - 4)
    }
    if (match($0, /comm=[^ ]+/)) {
        # comm= starts at RSTART, length RLENGTH; value begins after "comm="
        commtok = substr($0, RSTART + 5, RLENGTH - 5)
    }

    # If either missing, print unchanged.
    if (sysnum == "" || commtok == "") {
        print
        next
    }

    # If sys is negative, do not query and leave line as-is.
    if (substr(sysnum, 1, 1) == "-") {
        print
        next
    }

    # Defensive numeric check: require all digits
    if (sysnum !~ /^[0-9]+$/) {
        print
        next
    }

    # Lookup (cached). `seen` flags whether we've cached this sysnum.
    if (seen[sysnum]) {
        name = cache[sysnum]
    } else {
        name = ""
        cmd = lookup_cmd " " sysnum
        if ((cmd | getline out) == 1) {
            # parse first whitespace-separated token as the syscall name
            n = split(out, parts)
            if (n >= 1) name = parts[1]
        }
        close(cmd)
        cache[sysnum] = name
        seen[sysnum] = 1
    }

    # If lookup failed (empty name), leave original line unchanged.
    if (name == "") {
        print
        next
    }

    line = $0
    if (name == "ioctl") {
        line = replace_ioctl_request(line)
    }

    # Insert name=<name> immediately after the comm=... token.
    if (match(line, /comm=[^ ]+/)) {
        prefix = substr(line, 1, RSTART - 1)
        token = substr(line, RSTART, RLENGTH)
        rest = substr(line, RSTART + RLENGTH)
        print prefix token " name=" name rest
    } else {
        # defensive fallback
        print
    }
}

function trim(s) {
    sub(/^[ \t\r\n]+/, "", s)
    sub(/[ \t\r\n]+$/, "", s)
    return s
}

# If line has args=[...], and there is a second element, try to map it via syd-sys -i
function replace_ioctl_request(line,   args_start, args_len, before, inside, after, n, i, req, names, arr, rebuilt) {
    if (!match(line, /args=\[[^]]*\]/)) return line

    args_start = RSTART
    args_len = RLENGTH

    before = substr(line, 1, args_start - 1)
    inside = substr(line, args_start + 6, args_len - 7)
    after = substr(line, args_start + args_len)

    n = split(inside, arr, /,[ \t]*/)
    if (n < 2) return line

    for (i = 1; i <= n; i++) arr[i] = trim(arr[i])

    req = arr[2]
    names = ioctl_names(req)
    # no mapping -> keep as-is
    if (names == "") return line
    arr[2] = names

    # Rebuild args with a consistent ", " separator
    rebuilt = arr[1]
    for (i = 2; i <= n; i++) rebuilt = rebuilt ", " arr[i]

    return before "args=[" rebuilt "]" after
}

# Cache for ioctl request -> "NAME|NAME2|..."
function ioctl_names(req, cmd, out, parts, n, names) {
    if (ioc_seen[req]) return ioc_cache[req]

    names = ""
    cmd = lookup_cmd " -i " req
    while ((cmd | getline out) > 0) {
        n = split(out, parts)
        if (n >= 1 && parts[1] != "") {
            if (names != "") names = names "|" parts[1]
            else names = parts[1]
        }
    }
    close(cmd)

    ioc_cache[req] = names
    ioc_seen[req] = 1
    return names
}
