调试命令行程序

Debugging a command-line program

如果我有一个用作命令行工具的程序,我有哪些调试选项?

为了便于举例,假设程序如下所示。

列表do_stuff.pl

main :-
    current_prolog_flag(argv, Argv),
    do_stuff(Argv),
    halt.
main :-
    halt(1).

使用 SWI-Prolog,我可以编译它:

swipl --goal=main -o do_stuff -c do_stuff.pl

我可以 运行 只需调用

$ ./do_stuff foo bar baz

就目前而言,如果 do_stuff/1 失败,这将以 1 退出。我如何才能看到失败的第一个(最早、最深的)目标?或者更好的是,整个回溯?我假设我应该可以使用 debugleash,例如:

main :-
    current_prolog_flag(argv, Argv),
    debug, leash(+fail),
    do_stuff(Argv),
    halt.

...但我尝试过任何方法都没有效果。

我唯一半途而废的想法是为每个我期望确定性成功但没有成功的谓词抛出错误。这当然是可行的,但似乎有点过分?

动机

用作命令行工具的程序(通常)意味着 运行 一次,获取其参数,读取其输入,写入输出。在这种情况下,失败意味着什么?我的解释是,意外失败是程序错误。

单元测试可能有帮助(单独测试谓词);但是,根据定义,这对于由于程序员缺乏对问题、范围或工具的理解而导致的错误没有帮助。只有 运行 将程序与实际输入结合起来才能捕获此 class 错误。

所以,鉴于上面的例子,如果某个用例导致 do_stuff/1 失败,并且程序以非零代码退出,程序员在确定哪个谓词失败时有什么选择?

给出了一种解决方案。但是(如果我理解正确的话)这确实需要程序员系统地检查执行流程,直到找到有问题的谓词调用。

这正是我希望避免的。

与更多面向命令的语言相比,失败在 Prolog 中是一件非常不寻常的事情。它从第一天起就引起了人们的兴趣。事实上,即使在 Prolog 0(Prolog I 之前的版本)中,在跟踪选项 ECRIRE 旁边还有一个特殊选项 IMPASSES,它只显示失败。

稍后,Mirelle Ducassé 的特别工作试图自动找出如何解释失败。

失败的奇怪之处在于它们并不一定表示出了问题。但有时,他们是。

我想说,有两个不同的方向可以理解失败。第一个更程序化,第二个更声明。

注释

在许多程序中,我使用 表示我希望目标总是成功。感谢运算符声明,这只是一个额外的字符:

   ...,
   @goal_aux_togoalaux_spec(OQuery, FVect0, Query, Spec),
   ...

如果目标失败,则会发出错误消息。记录嵌套异常也很重要。如果时间紧迫,则必须删除这些 @。但是,我只是在 120kLOP 中计算了 ~400。

请注意,@ 也适用于具有多个答案的目标。像 @member(1,[X,Y]).

此技术适用于 事实上 模式程序。想想 的准备(就是上面的例子)。在那里,你处于主要思考的情况:这是一个程序,什么是合适的切片?在这种情况下,答案:"No there is no slice" 将不是答案。你真的希望它总是成功。如果你没有这样的模式程序,你通常可以通过强制坚定来改造现有的未模式程序:

p(X, Y) :-
   wellformed(X),
   @p_old(X, Yc),
   Yc = Y.

该技术在纯关系、声明性代码中迅速失去吸引力。拍一张 of the 。在那里,几乎不可能添加 @ - 除了第一个目标。在这种情况下,需要一种更具声明性的方法,如下所示。

概括

对于更复杂的问题,@ 效果不佳。相反,需要程序 modification/slicing。需要通过添加前缀 * 来概括程序。请参阅 以获取手动使用此技术的 SO 上此类调试会话的集合。 这种技术的要点是你在确定最大泛化时不必理解程序的真正含义。您只需要关注失败的目标。

理想情况下,这种概括会自动产生。然而,有很多障碍。一方面,它们只适用于纯单调代码(事实上,这是一个很好的动机,为什么人们应该坚持这样的代码)。因此,首先必须对现有代码进行分析和分类。如果系统不符合并随机更改其行为(如您提到的系统),这将更加困难。