2012-03-13 53 views
2

我用泥调试我Common Lisp的功能未绑定变量。在函数内部,我将它人为地发出错误信号(试图“调试” -perhaps我应该踩着),像这样:评估表达式仅产生

(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") 

现在我打Ë到调用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结合的变量与ê。 :<

回答

3

尝试使用符号的完整名称(即package::symbol-name)而不是仅使用符号名称(即symbol-name)。在目前的情况下,可能是net.telent.date::last-space

另外,还要确保你与调试支持设置为最高(例如,试着将一个(declaim (optimize debug))之前,你的功能和重新编译文件 - C-C C-K)编制。

检查你要调试的代码有足够的调试信息:在SLDB窗口,把光标放在相关线路的回溯,按 - 这应该扩大为所有的帧,并显示值当地人。按t再次折叠本地帧信息。如果没有足够的调试信息,则不会看到局部变量,但有些组成了名称(如SBCL下的SB-DEBUG:ARG-0)。里面的SLDB窗口,如果按把光标放在第一个值输入,它将(如果该值是表示截断了一长串有用)展开值到检查器窗口。

此外,泥调试支持似乎与实施而有所不同。上述建议适用于Linux下的SBCL,YMMV在不同的实现下。

+0

在试图评估调试器中的东西时使用'package :: symbol-name'的另一种方法是使用'in-package'在REPL中选择相关的包。 – 2012-03-13 08:53:58

+0

谢谢! '(声明(优化调试))'是我错过的咒语。之后,所有当地人都列在“t”列表中(可通过“e”访问)。我也刚刚了解到'package :: not-necessary-exported-symbol'语法 - 谢谢! – Ashe 2012-03-13 10:43:11

+3

您也可以使用'C-u'' C-c'' C-k'来编译最大的调试设置。 – Daimrod 2012-03-13 10:43:58

2

变量似乎被优化掉。从SBCL Manual

变量的值可能由于这些原因不可用:

  1. 调试优化质量的值可以省略调试,以确定变量是否可用需要 信息。 除非变量是一个参数,它的价值将只提供 当调试至少
  2. 编译器做了一辈子的分析和 确定该值已经不再需要,即使其范围 尚未退出。当调试 优化质量为
  3. 时,生命周期分析被禁止变量的名称是一个未中断的 符号(gensym)。为了节省空间,当调试优化 质量为
  4. 因为输入了调试器,因此帧的位置未知(请参见第5.3.5节[未知位置和中断],第31页),因此编译器仅转储有关未中断变量的调试 信息由于中断或意外的硬件错误导致的 。在这些 条件下,参数值将可用,但可能是 不正确。这是上面提到的例外。
  5. 变量(或引用它的代码 )已经过优化。没有读取的变量 总是被优化掉。 编译器删除变量的程度取决于编译速度优化质量的值,但大多数源代码级优化是在所有编译策略下完成的。
  6. 该变量从未设置且其定义看起来像(LET ((var1 var2)) ...)在这种情况下,var1被替换为var2
  7. 该变量从不设置,并且只被引用一次。在这种情况下,基准取代有可变初始值

你可以看到所有可用的本地变量与

如果您在之后添加了对变量的引用,那么您的错误条件可能会在相应的调试帧中得到它的值。

+1

感谢您的参考!在这种情况下,它确实是导致我的问题的第一个问题,解决方案是“(声明(优化调试))”等。 – Ashe 2012-03-13 10:48:03

3

还有另一个选项可用于调试Lisp函数:如果可用,请使用解释器。大多数实现可以在解释和编译代码之间切换。你可以运行编译的所有代码,但是你想要调试的函数可以运行解释。在极端情况下,您甚至可以闯入解析函数的步进器(如果可用)。

请注意,根据您的要求定义默认优化设置是有意义的。

  • 不设置安全很低全球。只在代码段中做到这一点很有用。

  • 不设置速度全球范围内非常高。只在代码段中做到这一点很有用。

  • 保持调试信息,使用2.根据实现方式的设置高调试设置可能会关闭尾调用优化(TCO) - 这可能是不可取的执行和理想的调试。

我会为提出不同的设置:

  • 发展:安全+调试友好
  • 部署:的数字代码安全
  • 优化,本地速度

通过默认情况下,您应该使用对交互式使用有用的设置:

  • 速度1-2。速度有点重要
  • 安全2-3。安全非常重要。在运行时检查所有操作
  • debug 2-3。调试信息被保留,操作应该可以被调试器中断。
  • 空间1.代码的大小并不重要。
  • 编译速度1.编译过程的速度并不那么重要。

范围从0到3.没有数字默认为3. 3更高。

根据实施情况,可能会有额外的优化设置以及配置编译器的一些变量。

在速度临界区域,您可以设置高于安全和调试的速度。但请注意,在某些情况下,这会更改代码执行的语义(错误检测,溢出等),并且缺少运行时检查可能会导致代码破坏Lisp堆。

在某些交付情况下,调试设置非常低也可能有用。但是,如果你的Lisp编译器设置为高速优化和低调试,那么通过查看堆栈的回溯来调试代码可能会变得更加困难。