[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

17. Отладка

В GNU Emacs имеется два отладчика --- debug и edebug. Первый встроен внутрь Emacs и поэтому всегда с вами; второй --- это расширение к Emacs, которое стало частью стандартного дистрибутива с 19 версии.

Оба отладчика подробно описаны в section `Debugging Lisp Programs' in The GNU Emacs Lisp Reference Manual. В этой главе я коротко покажу их применение.

17.1 debug  Использование встроенного отладчика.
17.2 debug-on-entry  Запуск отладчика при вызове функции.
17.3 debug-on-quit и (debug)  Запуск отладчика при аварийном выходе C-g.
17.4 Отладчик на уровне исходного кода edebug  Использование Edebug, более сложного отладчика.
17.5 Упражнения по отладке  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

17.1 debug

Предположим, что вы написали определение функции которая возвращает сумму чисел от 1 до заданного числа. (Это функция triangle описанная ранее).

Однако, в определение функции вкралась ошибка. Вы набрали `1=' вместо `1-'. Вот неправильное определение функции:

 
(defun triangle-bugged (number)
  "Возвращает сумму чисел от 1 до  NUMBER включительно."
  (let ((total 0))
    (while (> number 0)
      (setq total (+ total number))
      (setq number (1= number)))      ; Здесь ошибка.
    total))

Если вы читаете это в Info, то вы можете вычислить это определение обычным образом. После этого в эхо-области должно появится triangle-bugged.

Сейчас вычислите функцию triangle-bugged с аргументом 4:

 
(triangle-bugged 4)

 
Вы получите сообщение об ошибке, которое гласит:

Symbol's function definition is void: 1=

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

Вы может включить отладку, установив значением переменной debug-on-error в значение t:

 
(setq debug-on-error t)

Это заставит Emacs запустить отладчик когда он в следующий раз встретит ошибку.

Вы может выключить отладку установив значением переменной debug-on-error в значение nil:

 
(setq debug-on-error nil)

Установите debug-on-error в t и вычислите следующее:

 
(triangle-bugged 4)

В этот раз, Emacs создаст буфер, который называется `*Backtrace*' и выглядит следующим образом:

 
---------- Buffer: *Backtrace* ----------
Signalling: (void-function 1=)
  (1= number))
  (setq number (1= number)))
  (while (> number 0) (setq total (+ total number))
        (setq number (1= number))))
  (let ((total 0)) (while (> number 0) (setq total ...)
        (setq number ...)) total))
  triangle-bugged(4)
  eval((triangle-bugged 4))
  eval-last-sexp(nil)
* call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------

(Я немного переформатировал пример --- отладчик не урезает длинные строки).

Вы должны читать буфер `*Backtrace*' с самого низа; в нем перечисляется что делал Emacs, и что привело к ошибке. В нашем случае Emacs вначале выполнил интерактивный вызов функции связанной с C-x C-e (eval-last-sexp), что привело к вычислению выражения triangle-bugged. Каждая последующая строка расскажет вам о том, что интерпретатор Лиспа вычислял после этого.

Третья строка сверху это

 
(setq number (1= number))

Emacs попробовал вычислить это выражение; для того, чтобы сделать это, надо было вычислить внутреннее выражение, которое показано во второй строке сверху:

 
(1= number)

Здесь и произошла ошибка, поскольку верхняя строка сообщает:

 
Signalling: (void-function 1=)

Теперь вы можете исправить опечатку, заново вычислить определение функции и после этого, снова запустить тестовую программу.

Если вы читаете это в Info, то вы можете сейчас выключить debug-on-error установив переменную в nil:

 
(setq debug-on-error nil)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

17.2 debug-on-entry

Второй способ запустить debug для функции --- это вызвать отладчик когда вы запускаете эту функцию. Вы может сделать это с помощью функции debug-on-entry.

Наберите:

 
M-x debug-on-entry RET triangle-bugged RET

Теперь, вычислите следующее выражение:

 
(triangle-bugged 5)

Emacs создаст буфер `*Backtrace*' и скажет вам, что он находится в начале вычисления функции triangle-bugged:

 
---------- Buffer: *Backtrace* ----------
Entering:
* triangle-bugged(5)
  eval((triangle-bugged 5))
  eval-last-sexp(nil)
* call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------

В буфере `*Backtrace*', нажмите d. Emacs вычислит первое выражение функции triangle-bugged; и буфер будет выглядеть следующим образом:

 
---------- Buffer: *Backtrace* ----------
Beginning evaluation of function call form:
* (let ((total 0)) (while (> number 0) (setq total ...)
        (setq number ...)) total))
  triangle-bugged(5)
* eval((triangle-bugged 5))
  eval-last-sexp(nil)
* call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------

Затем, нажмите d снова, и так, не спеша, восемь раз. Каждый раз когда вы нажимает d, Emacs будет вычислять следующее выражение из определения функции. Постепенно буфер будет выглядеть следующим образом:

 
---------- Buffer: *Backtrace* ----------
Beginning evaluation of function call form:
* (setq number (1= number)))
* (while (> number 0) (setq total (+ total number)) 
        (setq number (1= number))))
* (let ((total 0)) (while (> number 0) 
        (setq total ...) (setq number ...)) total))
  triangle-bugged(5)
* eval((triangle-bugged 5))
  eval-last-sexp(nil)
* call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------

Теперь, если вы нажмете d еще два раза, то Emacs достигнет ошибки и две верхнии строки буфера `*Backtrace*' будут выглядеть следующим образом:

 
---------- Buffer: *Backtrace* ----------
Signalling: (void-function 1=)
* (1= number))
...
---------- Buffer: *Backtrace* ----------

Последовательно нажимая d, вы смогли пройтись по всей функции.

Вы можете выйти из буфера `*Backtrace*' нажав q; это отменяет трассировку, но не отменяет debug-on-entry.

Чтобы отменить эффект debug-on-entry, запустите функцию cancel-debug-on-entry и задайте ей имя функции, например:

 
M-x cancel-debug-on-entry RET triangle-debugged RET

(Если вы читаете это в Info, то отмените debug-on-entry прямо сейчас).


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

17.3 debug-on-quit и (debug)

Кроме установки debug-on-error или запуска debug-on-entry, есть два других способа активизировать отладчик debug.

Вы можете запустить debug в любое время, когда вы нажимаете C-g (keyboard-quit), просто установив переменную debug-on-quit в значение t. Это может быть полезным для отладки бесконечных циклов.

Или вы можете вставить строку, которая говорит запустит (debug) прямо в вашей программе, там где вы хотите чтобы был запущен отладчик. Например, вот так:

 
(defun triangle-bugged (number)
  "Возвращает сумму чисел от 1 до  NUMBER включительно."
  (let ((total 0))
    (while (> number 0)
      (setq total (+ total number))
      (debug)                         ; Запуск отладчика.
      (setq number (1= number)))      ; Ошибка здесь.
    total))

Более подробно функция debug описана в section `The Lisp Debugger' in The GNU Emacs Lisp Reference Manual.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

17.4 Отладчик на уровне исходного кода edebug

Edebug обычно показывает исходный код программы, которую вы отлаживаете, со стрелочкой на левой стороне, которая отмечает текущую исполняемую строку.

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

Более подробно edebug описан в section `Edebug' in The GNU Emacs Lisp Reference Manual.

Вот ошибочное определение функции рекурсивный-треугольник (просто для напоминания). Этот пример представлен без выравнивания влево defun, как обьяснено ниже.

 
(defun triangle-recursively-bugged (number)
  "Возвращает сумму чисел от 1 до  NUMBER включительно.
Использует рекурсию."
  (if (= number 1)                   
      1                              
    (+ number                        
       (triangle-recursively-bugged   
        (1= number)))))               ; Ошибка здесь.

Обычно вы устанавливаете это определение расположив курсор после закрывающей последней скобки функции и нажав C-x C-e (eval-last-sexp), или расположив курсор в теле функции и нажав C-M-x (eval-defun). (По умолчанию команда eval-defun работает только в режимах Emacs Lisp и Lisp Interactive). Однако, чтобы подготовить это определение функции для Edebug вы должны вначале инструментировать код с помощью другой команды. В версии 19 Emacs, вы может сделать это расположив курсор в теле определения и нажав следующую комбинацию клавиш:

 
M-x edebug-defun RET

Это заставит Emacs загрузить Edebug автоматически, если он еще не загружен, и правильно инструментировать функцию. (После загрузки Edebug вы сможете использовать стандартные клавиши, такие как C-u C-M-x (eval-defun с префикс-аргументом) для запуска edebug-defun).

В версии 18 Emacs, вы должны загрузить Edebug сами; вы можете сделать это записав соответствующую команду load в свой файл `.emacs'.

Если вы читаете это в Info, то вы можете инструментировать функцию triangle-recursively-bugged, которая показана выше. edebug-defun не сможет правильно определить границы функции если строка с defun выровнена влево; так что пример представлен без обычных пробелов влево для defun.

После того, как вы проинструментировали функцию, расположите курсор за следующим выражением и нажмите C-x C-e (eval-last-sexp):

 
(triangle-recursively-bugged 3)

Вы прыгнете сразу назад к исходному коду triangle-recursively-bugged и курсор будет расположет в начале строки функции содержащей функцию if. Также слева вы увидете стрелку, которая выглядит следующим образом --- `=>'. Эта стрелка отмечает текущую позицию выполнения функции.

 
=>-!-(if (= number 1)

В примере положение точки отображено как `-!-' (В книге оно показано, как пять звездочек).

Если сейчас вы нажмете клавишу SPC, то точка переместится к следующему выражению, которое будет исполнятся; строка будет выглядеть следующим образом:

 
=>(if -!-(= number 1)

Когда вы продолжите нажимать клавишу SPC, точка будет перемещаться от выражение к выражению. Одновременно с этим, когда выражение возвращает значение, это значение будет отображатся в эхо-области. Например, после того, как вы переместили точку мимо number вы увидите следующее:

 
Result: 3 = C-c

Это значит, что значение number 3 --- то есть это ASCII CTL-C (третья буква английского алфавита).

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

 
=>        -!-(1= number)))))               ; Ошибка здесь.

Если вы нажмете SPC еще раз, то появится сообщение об ошибке, которое гласит:

 
Symbol's function definition is void: 1=

Это и есть ошибка.

Нажмите `q' чтобы покинуть Edebug.

Чтобы удалить инструментарий из определения функции, надо просто заново вычислите определение с помощью команды, которая не инструментируют ее. Например, вы можете расположить курсор за последней скобкой функции и нажать C-x C-e.

Edebug может делать намного больше чем просто позволить вам построчно выполнять функцию. Вы можете задать условия таким образом, чтобы он останавливался только на ошибках; вы можете задать отображение различных выражений; можете узнать сколько раз была вызвана та или иная функция и много чего другого.

Edebug полностью описан в section `Edebug' in The GNU Emacs Lisp Reference Manual.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

17.5 Упражнения по отладке


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated on March, 10 2004 using texi2html