7. Интерфейс отладки

Lua не имеет никаких встроенных средств отладки. Вместо этого, это предлагает специальный интерфейс, посредством функций и обработчиков прерываний, который позволяет создание различных видов отладчиков, профилировщиков и других инструментальных средств, которые нуждаются во внутренней информации из интерпретатора. Этот интерфейс объявлен в файле заголовков luadebug.h.

7.1 Информация стека и функций

Основная функция, чтобы получить информацию относительно стека интерпретатора:

int lua_getstack (lua_State *L, int level, lua_Debug *ar);
Это заполняет части структуры lua_Debug с идентификацией записи активации функции, выполняющейся в заданном уровне. Уровень 0 текущая функция управления, в то время как уровень n+1 функция, которая вызвала уровнем n. Обычно lua_getstack вернет 1, когда вызвана с уровнем больше, чем глубина стека, она возвращает 0.

Структура lua_Debug используется, чтобы нести различные части информации относительно активной функции:

typedef struct lua_Debug {
  const char *event;     /* "call", "return" */
  int currentline;       /* (l) */
  const char *name;      /* (n) */
  const char *namewhat;
          /* (n) поля, глобальные и локальные переменные, методы тэгов */
  int nups;              /* (u) количество upvalues */
  int linedefined;       /* (S) */
  const char *what;      /* (S) "Lua" функция, "C" функция, Lua "main" */
  const char *source;    /* (S) */
  char short_src[LUA_IDSIZE]; /* (S) */
  /* private part */
  ...
} lua_Debug;
lua_getstack заполняет только одну из частей этой структуры для будущего использования. Чтобы заполнить другие поля lua_Debug полезной информацией, надо вызвать:
int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
Эта функция возвращает 0 на ошибке (например, недопустимая опция в what). Каждый символ в строке what указывает некоторые поля ar, которые будет заполнены, как обозначено символом в круглых скобках в определении lua_Debug: S заполняет поле исходником (source), linedefined и what, l заполняет поле текущей строкой (currentline) и так далее. Кроме того, f помещает в стек функцию, которая работает в данном уровне.

Чтобы добираться до информации относительно функции, которая не активна (то есть она не в стеке), Вы помещаете функцию в стек и начинаете строку what с символа >. Например, чтобы знать, в которой строке функция f была определена, Вы можете писать:

  lua_Debug ar;
  lua_getglobal(L, "f");
  lua_getinfo(L, ">S", &ar);
  printf("%d\n", ar.linedefined);
Поля lua_Debug имеют следующее значение:
source
Если функция была определена в строке, source как раз и будет этой строкой, а если функция была определена в файле, source начинается с @, а дальше имя файла.
short_src
Пригодная к печати версия source, чтобы использоваться в сообщениях об ошибке.
linedefined
Код строки, где было начато определение функции.
what
Строка "Lua", если это функция Lua, "C", если это функция C или "main", если это основная часть chunk.
currentline
Текущая строка, где данная функция выполняется. Когда никакая информация о строке недоступна, currentline установлен в -1.
name
Приемлемое имя для данной функции. Так как функции в Lua значения первого класса, они не имеют фиксированных имен. Именем функции может быть значение многих глобальных переменных, в то время как другие функции могут быть сохранены только в поле таблицы. Функция lua_getinfo проверяет, является ли данная функция методом тэга или значением глобальной переменной. Если данная функция представляет собой метод тэга, name указывает на имя события. Если данная функция является значением глобальной переменной, то name указывает на имя переменной. Если данная функция не является ни методом тэга, ни глобальной переменной, то name установлен в NULL.
namewhat
Объясняет предыдущее поле. Если функция глобальная переменная, namewhat равен "global". Если функция метод тэга, namewhat равен "tag-method", иначе namewhat равен "" (пустой строке).
nups
Число upvalues в функции.

7.2 Управление локальными переменными

Для манипулирования локальными переменными luadebug.h использует индексы: первый параметр или локальная переменная имеет индекс 1 и так далее до последней активной локальной переменной.

Следующие функции позволяют манипулировать локальными переменными данной активной записи.

const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
Параметр ar должен быть имеющей силу записью активации, заполненной предыдущим обращением к lua_getstack или данный как параметр обработчика прерываний. Функция lua_getlocal получает индекс локальной переменной (n), помещает значение в стек и возвращает имя. Для lua_setlocal Вы помещаете новое значение в стек, а функция назначает это значение переменной и возвращает имя. Обе функции возвращают NULL при сбое. Это случается, если заданный индекс больше, чем число активных локальных переменных.

Как пример, следующая функция вносит в список имена всех локальных переменных функции в данном уровне стека:

int listvars (lua_State *L, int level) {
  lua_Debug ar;
  int i = 1;
  const char *name;

  if (lua_getstack(L, level, &ar) == 0) return 0;
     /* failure: no such level in the stack */
  while ((name = lua_getlocal(L, &ar, i++)) != NULL) {
    printf("%s\n", name);
    lua_pop(L, 1);  /* remove variable value */
  }
  return 1;
}

7.3 Обработчики прерываний

Lua-интерпретатор предлагает два обработчика прерываний для целей отладки: call и line. Оба обработчика имеют тот же самый тип:

typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
Вы можете устанавливать их со следующими функциями:
lua_Hook lua_setcallhook (lua_State *L, lua_Hook func);
lua_Hook lua_setlinehook (lua_State *L, lua_Hook func);
Обработчик прерываний заблокирован, когда значение NULL, что и является начальным значением обоих обработчиков прерываний. Функции lua_setcallhook и lua_setlinehook устанавливают соответствующие обработчики прерываний и возвращают их предыдущие значения.

Обработчик прерываний call вызван всякий раз, когда интерпретатор вызывает или оставляет функцию. Поле события event записи ar имеет строки "call" или "return". Этот ar может затем использоваться в обращениях для lua_getinfo, lua_getlocal и lua_setlocal, чтобы получить большее количество информации относительно функции и управлять локальными переменными.

Обработчик прерываний line вызван каждый раз, когда интерпретатор изменяет строку кода, которую выполняет. Поле event в ar имеет строку "line", а поле currentline хранит код строки. Вы можете использовать этот ar в других обращениях к отладочному API.

В то время как Lua управляет обработчиком прерываний, это отключает другие обращения к обработчикам прерываний. Следовательно, если обработчик прерываний вызывает Lua, чтобы выполнить функцию или chunk, это выполнение идет без обращений к обработчикам прерываний.

7.4 Рефлексивный интерфейс отладки

Библиотека ldblib обеспечивает функциональные возможности интерфейса отладки программам Lua. Если Вы хотите использовать эту библиотеку, Ваша ведущая прикладная программа должна открыть ее вызовом lua_dblibopen.

Вы должны проявить большую осторожность при использовании этой библиотеки. Функции, обеспеченные здесь, должны использоваться исключительно для отладки и подобных задач (например, профилирования). Пожалуйста, сопротивляйтесь искушению использовать их как обычный инструмент программирования. Они медленны и нарушают некоторые аспекты языка (например, секретность локальных переменных). Как общее правило, если Ваша программа не нуждается в этой библиотеке, не открывайте ее вообще.

getinfo (function, [what])

Эта функция возвращает таблицу с информацией относительно функции. Вы можете давать функцию непосредственно, или Вы можете давать число как значение function, что означает функциональное управление в уровне стека function. Уровень 0 считается текущей функцией (непосредственно getinfo), уровень 1: функция, которая вызвала getinfo и так далее. Если function представляет собой число большее, чем число активных функций, то getinfo сразу вернет nil.

Возвращенная таблица содержит все поля, возвращенные lua_getinfo со строкой what описывающий, что нужно получить. Значение по умолчанию для what: нужно получить всю доступную информацию.

Например, выражение getinfo(1,"n").name вернет имя текущей функции, если приемлемое имя может быть найдено, и getinfo(print) возвращает таблицу со всей доступной информацией относительно функции print.

getlocal (level, local)

Эта функция возвращает имя и значение локальной переменной с индексом, local на уровне level стека. Первый параметр или локальная переменная имеет индекс 1 и так далее до последней активной локальной переменной. Функция вернет nil, если не имеется никакой локальной переменной с данным индексом, и поднимает ошибку когда вызвана с level вне диапазона. Вы можете вызывать getinfo, чтобы проверить, имеет ли этот уровень силу.

setlocal (level, local, value)

Эта функция назначает значение value локальной переменной с индексом local функции на уровне level стека. Функция вернет nil, если не имеется никакой локальной переменной с данным индексом, и поднимает ошибку когда вызвана с уровнем level вне диапазона.

setcallhook (hook)

Устанавливает функциональный обработчик прерываний hook как обработчик прерываний call. Этот обработчик прерываний будет вызван каждый раз при начале и завершении интерпретации функции. Единственный параметр на обработчик прерываний call: имя события ("call" или "return"). Вы можете вызывать getinfo с уровнем 2, чтобы получить большее количество информации относительно функции (уровень 0 соответствует функции getinfo, а уровень 1 задает функцию обработчика прерываний. Когда вызвана без параметров, эта функция выключает обработчики прерываний call. setcallhook вернет старый обработчик прерываний.

setlinehook (hook)

Устанавливает функциональный обработчик прерываний hook как обработчик прерываний line. Этот обработчик прерываний будет вызван каждый раз, когда интерпретатор изменяет обрабатываемую строку кода. Единственный параметр на обработчике прерываний line: код строки, которую интерпретатор собирается выполнять. Когда вызвана без параметров, эта функция выключает обработчики прерываний line. Вызов setlinehook возвращает старый обработчик прерываний.