Добавлен , опубликован
Иногда бывает полезно продебажить код и узнать что же происходит/фаталит.
В луа есть встроенные средства - модуль debug.
В один момент, когда я делал карту на UjAPI, у меня очень подгорело, и я потратил 6 часов чтобы вкурить как это всё работает.
В итоге получилось вот это:
раскрыть
--------------------------------------------------------------------
-- 256head debugging
--------------------------------------------------------------------
--- https://wiki.facepunch.com/gmod/Structures/DebugInfo

local isDebug = true

local function InitDebugHooks()
    function OnNewFunctionCall()
        local debugInfo
        local funcInfo
        local paraminfo
        local localargs
        local string_format = string.format

        stackDepth = 2

        debugInfo = debug.getinfo(stackDepth)

        if debugInfo == nil then return end

        funcInfo = debug.getinfo(debugInfo.func)

        if(funcInfo.what == "C")then        --func from C - lua cant access that
            paraminfo = "C func, ?"
        else
            paraminfo = funcInfo.nparams
        end

        printc(string_format("[%s] call: %s [%s args] | src: %s:%s ", stackDepth, debugInfo.name, paraminfo, debugInfo.source, debugInfo.linedefined))

        localargs = ""

        local arg_n = 0
        --if we know that its not C - amount of params is greater than 0
        if(funcInfo.nparams ~= 0)then
            for i = 1, funcInfo.nparams do
                local name, value = debug.getlocal(stackDepth, i)
                localargs = localargs .. string_format("\n %s: [%s] = %s", i, name, value)
            end
            arg_n = funcInfo.nparams
            printc(string_format("%s args: %s", arg_n, localargs))
        else
            -- if we dosent know amount of args, we print first N (10 is enough)
            -- first N is args of function, rest are up-values
            for i = 1, 10 do
                local name, value = debug.getlocal(stackDepth, i)
                if not name then break end
                arg_n = i
                localargs = localargs .. string_format("\n %s: [%s] = %s", i, name, value)
            end
            printc(string_format("first %s local variables: %s", arg_n, localargs))
        end

        printc("")
    end


    local TriggerAddAction_hook = TriggerAddAction

    function TriggerAddAction(trigger, action)

        local function executionHook()
            debug.sethook(OnNewFunctionCall, "c", 0)
            action()
        end

        TriggerAddAction_hook(trigger, executionHook)
    end


    local TimerStart_hook = TimerStart

    function TimerStart(whichTimer, timeout, periodic, handlerFunc)

        local function executionHook()
            debug.sethook(OnNewFunctionCall, "c", 0)
            handlerFunc()
        end

        TimerStart_hook(whichTimer, timeout, periodic, executionHook)
    end

    -- hook on new function call
    debug.sethook(OnNewFunctionCall, "c", 0)
end

if(isDebug)then InitDebugHooks() end
Основные актёры:
debug.sethook(thread, hook, mask, count)
debug.getinfo(thread, f, what)
debug.getlocal(thread, f, var)
Что этот код собственно делает:
Одной переменной мы управляем дебагом - включать его или нет.
Включается он до того места, где вы хотите подебажить.

debug.sethook

У функции debug.sethook много разных вариаций использования.
В кратце - он выполняет прокинутую функцию каждый раз после определённого ивента, который посылается 2 аргументом.
debug.sethook(OnNewFunctionCall, "c", 0)
Эта строка вешает хук после выполнения любой функции.
Есть ещё другие варианты, но как показало тестирование, в моём случае они были не особо полезны. Почитать о них вы можете тут.

debug.getinfo

Вариативная функция, возвращает разные данные в зависимости от аргументов.
Если отправить number - сканирует стек на number глубине, возвращая DebugInfo.
Если отправить функцию - возвращает известную информацию о функции.
Мы сканируем стек только на втором уровне - последняя выполненная функция (даже не спрашивайте почему)
(можно сканировать фул стек вызовов, но толку от этого не много)
Функция возвращает нам конструкцию типа DebugInfo про которую можно почитать например вот здесь
О подключённых функциях из С куда меньше данных
Нужные нам поля:
.func - Указатель на текущую функцию
.name - Имя текущей функции
.source - Файл в которой объявлена функция; неизвестен, если функция из другого языка
.linedefined - Строка, на которой идёт объявление функции; неизвестно если функция из другого языка
После чего мы вызываем debug.getinfo но уже на эту функцию.
Мы снова получаем конструкцию debugInfo, но уже с данными функции, которые известны lua. Там есть много чего полезного, но нас интересуют только некоторые поля.
.what - Откуда эта функция - "C" или "Lua"
.nparams - Количество аргументов у функции, если это функция из С, то будет 0
Дальше мы используем лунную магию функцию под названием debug.getlocal

debug.getlocal

debug.getlocal(thread, var_n)
С помощью неё можно достать локальные переменные по нужной глубине стека.
В первый аргумент отправляется глубина стека, во второй - номер переменной.
Она возвращает нам название переменной и значение. Если имени нет - значит переменные закончились.
Если чуть раньше мы смогли узнать количество аргументов в функции - мы получаем только их. Если же количество аргументов неизвестно - мы сканируем стек, получая первые 10 переменных (или всё что там есть). В таком случае переменные сперва идут в порядке объявления функции - сначала N аргументов самой функции, потом up-values (что есть closure/замыкание) и локальные переменные объявленные внутри функции, также могут быть и временные переменные без названия.
Функция printc собственно есть вывод в консоль.
Всё это дело выводится в консоль вот в таком виде:
Так как это всё ещё варкрафт, то нам нужны хуки на другие потоки, создаваемые варкрафтом.
У меня используются только таймеры и триггеры, поэтому хуки расставлены только на них.
Внимание - Скорость выполнения кода при включёном дебаге падает примерно в 40 раз, так что проводить бенчмарки с включённым дебагом такого типа крайне не рекомендую
Ещё внимание - Если вы хотите подрубить это на рефордж - у вас не получится по 2 причинам - у вас нет консоли (но вы можете переделать этот код) и насколько я знаю там выключен модуль debug (да я зажал сколько то килограммов риса за рефордж)
`
ОЖИДАНИЕ РЕКЛАМЫ...
38
Перенёс из программирования. Копание в недрах игры из 2002 это скорее садомазо, чем программирование)
Чтобы оставить комментарий, пожалуйста, войдите на сайт.