Выражения оценки во фрейме дают только UNBOUND-VARIABLE

Я использую SLIME для отладки моей функции Common Lisp. Внутри функции я сделал так, чтобы она искусственно сигнализировала об ошибке (пытаясь «отладить» — возможно, мне следует пошагово) вот так:

(define-condition unknown-zone (error)
  ((text :initarg :text :reader text)))

(defun parse-mime-date (date)
  (let ((last-space (position #\Space date :from-end t)))
    (let ((date-time (net.telent.date:parse-time (subseq date 0 last-space)))
          (zone (subseq date (1+ last-space))))
      (unless (or (char= (elt zone 0) #\+)
                  (char= (elt zone 0) #\-))
        (error 'unknown-zone :text (format nil "Unknown timezone: ~a" zone)))
      (let ((hours (parse-integer (subseq zone 0 3)))
            (minutes (parse-integer
                      (concatenate 'string
                                   (list (elt zone 0))
                                   (subseq zone 3)))))
        (error 'unknown-zone :text "LOL")
        (let ((adjusted-date-time (- date-time (* 60 (+ minutes (* 60 hours))))))
          (format t "date-time: ~a; zone: ~a~%" date-time zone)
          (format t "adjusted: ~a" (net.telent.date:universal-time-to-http-date adjusted-date-time)))))))

Я пытаюсь обойти то, что, по-видимому, является недостатком в net.telent.date:parse-time (кажется, что это портит обработку часового пояса, хотя я еще не на 100%).

Ошибка "LOL" unknown-zone, конечно же, является искусственной точкой останова.

Когда он попадает в эту часть функции, SLDB честно открывается с обратной трассировкой:

Bad type argument:
  NS-MAIL2ZD::UNKNOWN-ZONE
   [Condition of type SIMPLE-TYPE-ERROR]

Restarts:
 0: [RETRY] Retry SLIME REPL evaluation request.
 1: [*ABORT] Return to SLIME's top level.
 2: [REMOVE-FD-HANDLER] Remove #<SB-IMPL::HANDLER INPUT on descriptor 7: #<CLOSURE (LABELS SWANK-BACKEND::RUN :IN SWANK-BACKEND:ADD-FD-HANDLER) {10030AD9FB}>>
 3: [ABORT] Exit debugger, returning to top level.

Backtrace:
  0: (MAKE-CONDITION NS-MAIL2ZD::UNKNOWN-ZONE :TEXT "LOL")
  1: (ERROR NS-MAIL2ZD::UNKNOWN-ZONE :TEXT "LOL")
  2: (NS-MAIL2ZD:PARSE-MIME-DATE "Wed, 14 Mar 2012 06:59:36 +1100")
  3: (SB-INT:SIMPLE-EVAL-IN-LEXENV (NS-MAIL2ZD:PARSE-MIME-DATE *LOL*) #<NULL-LEXENV>)
  4: (EVAL (NS-MAIL2ZD:PARSE-MIME-DATE *LOL*))
 --more--

Затем я перехожу к фрейму:

Backtrace:
  0: (MAKE-CONDITION NS-MAIL2ZD::UNKNOWN-ZONE :TEXT "LOL")
  1: (ERROR NS-MAIL2ZD::UNKNOWN-ZONE :TEXT "LOL")
      Locals:
        SB-KERNEL::ARGUMENTS = (:TEXT "LOL")
        SB-KERNEL::DATUM = NS-MAIL2ZD::UNKNOWN-ZONE
  2: (NS-MAIL2ZD:PARSE-MIME-DATE "Wed, 14 Mar 2012 06:59:36 +1100")

Теперь я нажимаю e, чтобы вызвать sldb-eval-in-frame, и набираю last-space, так как это должно быть доступно там, где было сообщено об ошибке.

Кажется, это не так, как это должно (?) Работать:

The variable LAST-SPACE is unbound.
   [Condition of type UNBOUND-VARIABLE]

Restarts:
 0: [ABORT] Return to sldb level 1.
 1: [RETRY] Retry SLIME REPL evaluation request.
 2: [*ABORT] Return to SLIME's top level.
 3: [REMOVE-FD-HANDLER] Remove #<SB-IMPL::HANDLER INPUT on descriptor 7: #<CLOSURE (LABELS SWANK-BACKEND::RUN :IN SWANK-BACKEND:ADD-FD-HANDLER) {10030AD9FB}>>
 4: [ABORT] Exit debugger, returning to top level.

Backtrace:
  0: ((LAMBDA (#:G1144)) #<unavailable argument>)
 --more--

Есть ли способ сделать то, что я хочу? Я слишком усложняю вопросы?

Спасибо!


Дополнение: я пытался использовать (break) (это кажется более каноничным), но я все еще не вижу переменных, привязанных к let, с e. :‹


person Asherah    schedule 13.03.2012    source источник


Ответы (3)


Попробуйте использовать полное имя символа (например, package::symbol-name), а не только имя символа (например, symbol-name). В данном случае, возможно, net.telent.date::last-space.

Также убедитесь, что вы скомпилировали с максимальной поддержкой отладки (например, попробуйте поставить (declaim (optimize debug)) перед вашей функцией и перекомпилировать файл - C-c C-k).

Проверка того, что код, который вы пытаетесь отладить, имеет достаточно отладочной информации: в окне sldb поместите курсор на соответствующую строку в трассировке и нажмите t — это должно развернуть фрейм и показать значения для все местные жители. Повторное нажатие t сворачивает информацию о локальном фрейме. Если отладочной информации недостаточно, вы увидите не локальные переменные, а некоторые выдуманные имена (например, SB-DEBUG:ARG-0 в SBCL). Внутри окна sldb, если вы нажмете Enter, когда курсор находится на значении, оно развернет это значение в окно инспектора (полезно, если значение представляет собой длинный список, который отображается усеченным).

Кроме того, поддержка отладки SLIME, похоже, зависит от реализации. Совет выше работает на SBCL под Linux, YMMV под разными реализациями.

person Miron Brezuleanu    schedule 13.03.2012
comment
Альтернативой использованию package::symbol-name при попытке оценить вещи в отладчике является выбор соответствующего пакета в REPL с помощью in-package. - person Miron Brezuleanu; 13.03.2012
comment
Благодарю вас! (declaim (optimize debug)) было заклинанием, которого мне не хватало. После этого все местные жители в этой точке перечислены в списке t (и доступны через e). Я также только что узнал о синтаксисе package::not-necessarily-exported-symbol — спасибо! - person Asherah; 13.03.2012
comment
Вы также можете использовать C-u C-c C-k для компиляции с максимальными настройками отладки. - person Daimrod; 13.03.2012
comment
@Daimrod Спасибо, это круто! - person Asherah; 13.03.2012
comment
@Daimrod Не знал об этом, надеюсь, запомню! - person Miron Brezuleanu; 13.03.2012
comment
@ArlenCuss: вы также можете использовать положительный аргумент для компиляции с настройками максимальной скорости (например, M-1 C-c C-k). - person Daimrod; 13.03.2012

Есть еще один вариант отладки функции Лиспа: использовать интерпретатор, если он доступен. Большинство реализаций могут переключаться между интерпретируемым и скомпилированным кодом. Вы можете запустить весь скомпилированный код, но функция, которую вы хотите отлаживать, может выполняться интерпретируемой. В крайнем случае вы можете даже взломать степпер для интерпретируемой функции (если он доступен).

Обратите внимание, что имеет смысл определить настройку оптимизации по умолчанию на основе ваших требований.

  • не устанавливайте безопасность очень низкой во всем мире. Делайте это только в тех разделах кода, где это полезно.

  • не устанавливайте слишком высокую скорость во всем мире. Делайте это только в тех разделах кода, где это полезно.

  • сохранить отладочную информацию, используйте параметр 2. В зависимости от реализации высокий параметр отладки может отключить оптимизацию хвостового вызова (TCO), что может быть нежелательным для выполнения и желательным для отладки.

Я бы предложил разные настройки для:

  • разработка: безопасная + удобная для отладки
  • развертывание: безопасно
  • оптимизация числового кода, локально для скорости

По умолчанию вы должны использовать параметр, полезный для интерактивного использования:

  • скорость 1-2. скорость несколько важна
  • безопасность 2-3. безопасность очень важна. все операции проверяются во время выполнения
  • отладка 2-3. отладочная информация сохраняется, и операции должны прерываться отладчиком.
  • пробел 1. Размер кода не важен.
  • компиляция-скорость 1. Скорость процесса компиляции не так важна.

Диапазон значений от 0 до 3. По умолчанию нет числа 3. 3 больше.

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

В критичных по скорости участках можно установить скорость выше безопасности и отладки. Но имейте в виду, что в некоторых случаях это изменяет семантику выполнения кода (обнаружение ошибок, переполнение и т. д.) и что отсутствие проверок во время выполнения может привести к тому, что ваш код испортит кучу Lisp.

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

person Rainer Joswig    schedule 13.03.2012

Переменная, кажется, оптимизирована. Из Руководства по SBCL:

Значение переменной может быть недоступно по следующим причинам:

  1. В значении качества оптимизации отладки может быть пропущена отладочная информация, необходимая для определения доступности переменной. Если переменная не является аргументом, ее значение будет доступно только тогда, когда отладка не ниже
  2. Компилятор провел анализ жизненного цикла и определил, что значение больше не нужно, даже если его область действия не вышла. Анализ жизненного цикла запрещается, когда качество оптимизации отладки
  3. Имя переменной представляет собой неинтернированный символ (gensym). Для экономии места компилятор выводит отладочную информацию о неинтернированных переменных только тогда, когда качество отладочной оптимизации
  4. Расположение фрейма неизвестно (см. Раздел 5.3.5 [Неизвестные местоположения и прерывания], с. 31), поскольку отладчик был запущен из-за прерывания или неожиданной аппаратной ошибки. В этих условиях значения аргументов будут доступны, но могут быть неверными. Это исключение, упомянутое выше.
  5. Переменная (или код, ссылающийся на нее) была оптимизирована. Переменные без чтения всегда оптимизируются. Степень, в которой компилятор удаляет переменные, будет зависеть от значения качества оптимизации скорости компиляции, но большинство оптимизаций на уровне исходного кода выполняются во всех политиках компиляции.
  6. Переменная никогда не устанавливается, и ее определение выглядит как (LET ((var1 var2)) ...). В этом случае var1 заменяется на var2.
  7. Переменная никогда не устанавливается и на нее ссылаются ровно один раз. В этом случае ссылка заменяется начальным значением переменной

Вы можете увидеть все доступные локальные переменные с помощью t.

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

person Vsevolod Dyomkin    schedule 13.03.2012
comment
Спасибо за ссылку! В этом случае это действительно был первый из тех пунктов, которые вызвали мою проблему, решение было (declaim (optimize debug)) или тому подобное. - person Asherah; 13.03.2012