如果没有交互式调试器的好处,我如何轻松找到错误源?

问题描述

当我抱怨 Common Lisp 实现没有 show line and column numbers in error backtraces 时,我被告知错误的行号和列号“在 Common Lisp 中不是必需的”,因为按v 在 SLIME 中。考虑到几乎所有其他编程语言实现(包括 Scheme 和 Racket)都能够显示错误文件名、行号和列号,我对这种倒退的想法感到不安。

我认为在这些不使用 SLIME 的情况下,错误的行号和列号是必不可少的:

  • 当 Lisp 程序以批处理模式运行时(例如 sbcl --script myprogram.lisp),交互式调试器被禁用。发生错误时,可能会打印回溯并退出程序。

  • 在长时间运行的应用程序(例如 Web 应用程序)中,通常通过将回溯记录到日志记录工具(例如 syslog)以供日后检查来“处理”错误,同时继续为请求提供服务,尽管发生了错误

假设在上述情况之一中发生错误。如果回溯不包含错误文件名、行号和列号,如何轻松识别错误源?考虑一个包含一千个文件、几十万行和一万个函数的 Lisp 项目。如果发生错误并且我只有回溯,如果没有交互式调试器(例如 SLIME 调试器)的好处,我如何确定错误的位置?

我觉得我一定遗漏了一些东西,因为即使在我的 1000 行小程序中,我也没有发现非交互式回溯有用。我想知道其他 Common Lisp 程序员是如何在他们拥有数千个文件的百万行代码库中读取非交互式回溯的。一般是怎么做的?

解决方法

简要

以交互方式运行程序时,您会以完全相同的方式找到源代码:

  1. 有一个编辑器,通过 tags / etags 的某种变体或通过查询 Lisp 系统(不是正在运行的守护进程,而是您加载系统的另一个环境),可以构建从符号名称到位置的映射;
  2. 使用此编辑器编辑回溯;
  3. 在回溯中找到有趣的一行,然后在编辑器中输入“编辑此符号的定义”命令;
  4. ...;
  5. 利润。

或者,更长

曾几何时,当世界还很年轻的时候,一个年轻而愚蠢的物理专业学生过去常常使用打印的回溯来调试他的 FORTRAN 程序。我的意思是打印回溯:当机器崩溃时,连接到它的链式打印机会吐出许多纸,上面有程序名称和行号。并且,在重新启动机器以便下一个用户可以轮到他们崩溃之后,他将获取此打印输出并获取他的程序的打印输出并比较行号:查看代码并尝试找出出错的地方。他就这样在深夜度过了好几个小时。

后来,这个学生(虽然现在是数学系学生)发现了一件奇妙的事情:一种叫做 Lisp 的编程语言,你可以用它编写程序来解决他所在领域感兴趣的复杂代数问题。尽管从理论上讲,如果你拥有数学系买不起的那种计算机,Lisp 是一种交互式语言,但如果你拥有的只是那种计算机数学系负担得起。所以事情和以前一样继续:他会对他的程序进行一些更改,设置它要尝试求解的方程,然后在深夜没有其他用户不便时,开始运行。早上可能会有奇迹般的解决方案,但更有可能只有程序的尸体,在它被一些严重的错误致命伤后,以精心制作的回溯的形式出现(还没有想到错误处理)的)。不过,这一次,回溯将在运行的日志文件中。

学生又发现了一个文本编辑环境:一些远方的人使用了某种文本编辑环境,他们可以使用更大更好的计算机,这些计算机声称可以很好地支持 Lisp 编程:当然比他当时使用的编辑器更好.他设法从某人的磁带上获得了这个环境的副本(传说它是 17.64 版),并且他设法让它在数学系的机器上运行。他教了它足够多的关于他正在使用的 Lisp 方言,它确实很有帮助,但如果经常让其他用户感到恼火,因为它需要相当多的机器容量来支持它。而且情况好一些。

这个文本编辑系统附带了一个相当棒的工具:一个叫做“标签”的程序,它可以为它理解的语言创建一个数据库,在定义名称和它们在文件系统中的位置之间进行映射。他还修改了这个标签程序来理解他使用的 Lisp 方言。非常奇妙的是,系统还可以处理定义移动的情况,它几乎一直都有,这使得行号和列号之类的东西变得如此脆弱和无用(当时可能已经发明了源代码控制,但学生知道什么都没有)。当然,这是自动系统的原始祖先之一,可以找到任何信誉良好的编辑器,甚至一些信誉不佳的编辑器现在都有的符号定义。

现在,当他早上进来寻找前一天晚上运行的新回溯时,他会在编辑系统中编辑这个回溯,并在其中找到有趣的行,在那里他会输入非常精彩的' meta-dot' 或者,正如他所知(没有带元键的键盘),'escape dot' 命令。然后磁盘灯会亮一点,然后他会查看他感兴趣的定义。

这就是回溯被征服了。从那天起,它再也不敢在礼貌的陪伴下再次抬起头来,而是潜伏在系统的黑暗角落里,除了现在记得它的少数人之外,没有人注意到。至于那个学生,嗯,现在根本没人记得他了。

,

查看它的一种方法是从错误诊断中询问“您真正想要什么?”——即“源代码中的位置”会给出什么你呢?

它给你

  • 一种查看现有代码的方法(您可以导航到它)
  • 一种修改现有代码的方法(您可以进行更改,然后重试之前发生的任何事情)

使用 Common Lisp(在这方面,Smalltalk 是类似的),您已经拥有了“形象”——这表明了两种出路。

  1. 使用 REPL“进入图像”,您可以跳转到堆栈中的函数并“直接”进行所需的更改
  2. 标准支持 (ed ...),它允许您更改表单(函数、顶级变量)而不必担心它位于何处——因此您无需真正需要行号和列号即可检查和修改源代码。

在实践中,我认为 (2) 不如 (1) 常用,但它的存在应该可以消除“缺少源坐标”的恐惧,因为这是运行时已经为您处理的事情。

编辑 #1

编辑 #2

  • trivial-backtrace(可通过 Quicklisp 获得,源代码 here)可被视为描述性堆栈跟踪的便携式解决方案。