打印堆栈跟踪

3

我有一个非常短的测试文件:

let print_backtrace () = try raise Not_found with
    Not_found -> Printexc.print_backtrace stdout;;

let f () = print_backtrace (); Printf.printf "this is to make f non-tail-recursive\n";;

f ();

我进行编译和运行:

我编译并运行:

% ocamlc -g test.ml      
% OCAMLRUNPARAM=b ./a.out
Raised at file "test.ml", line 1, characters 35-44
this is to make f non-tail-recursive

为什么堆栈跟踪中没有列出f?我应该如何编写一个函数,以便打印调用它的位置的堆栈跟踪?

2个回答

6

Printexc.print_backtrace的文档说:

回溯列表显示了最近引发异常以及通过函数调用传播异常的程序位置。

事实上,它似乎正在做正确的事情。该异常尚未通过f返回。

如果我将对f的调用之外的对Printexc.print_backtrace的调用,我会看到完整的回溯信息。

$ cat test2.ml
let print_backtrace () = raise Not_found

let f () = let res = print_backtrace () in res ;;

try f () with Not_found -> Printexc.print_backtrace stdout
$ /usr/local/ocaml312/bin/ocamlc -g test2.ml
$ OCAMLRUNPARAM=b a.out 
Raised at file "test2.ml", line 1, characters 31-40
Called from file "test2.ml", line 3, characters 21-39
Called from file "test2.ml", line 5, characters 4-8

这回答了我问题的一半,所以谢谢你。对于另一半的沉默,我应该把它看作是无法实现我想要的打印完整堆栈跟踪并继续执行的迹象吗? - Daniel Wagner
3
我认为没有好的方法可以在不对OCaml运行时进行篡改的情况下完成它。也许可以在ocamldebug下运行?或者由于您似乎在某种Unix环境中运行,如果只是进行快速排除故障,则可以fork()您的进程并在子进程中引发一个未捕获的异常(这将打印完整的堆栈跟踪),然后在子进程中退出并在父进程中继续。我多年前在C语言中做过类似的事情,对我很有效。但这很丑陋,很可能会弄乱一些东西。如果您要尝试此操作(这可能是个坏主意),请在子进程中调用_Exit()以避免刷新缓冲区。问候。 - Jeffrey Scofield
如果编译为本机代码,您可以尝试使用标准函数来打印堆栈,例如libunwind或glibc中的backtrace。请注意,结果可能不太美观,但通常足以确定问题所在。 - ygrek
我刚刚尝试使用backtrace(3)和backtrace_symbols_fd(3)来运行上面的简单示例,但结果(在Mac OS X上)并不是很有用。 在更大的示例中可能会更有用,但在这种小情况下,在回溯中出现的唯一可识别函数名称是我编写的用于调用backtrace()的C包装器。 OCaml函数f没有出现。 另一方面,以这种方式执行似乎不太可能干扰程序的行为。 - Jeffrey Scofield

5

这里是实现我建议的代码。如果可能的话,我建议使用ocamldebug,因为这段代码非常棘手。但是对于这个简单的例子,在我的系统上它能够工作。

let print_backtrace () =
    match Unix.fork () with
    | 0 -> raise Not_found
    | pid -> let _ = Unix.waitpid [] pid in ()

let f () =
    begin
    print_backtrace ();
    Printf.printf "after the backtrace\n";
    end

;;

f ()

这是一个测试运行。

$ /usr/local/ocaml312/bin/ocamlc unix.cma -g test3.ml
$ OCAMLRUNPARAM=b a.out
Fatal error: exception Not_found
Raised at file "test3.ml", line 3, characters 17-26
Called from file "test3.ml", line 8, characters 4-22
Called from file "test3.ml", line 14, characters 0-4
after the backtrace

我意识到由于未捕获的异常,您无法真正控制子进程退出的方式。这就是为什么这段代码过于复杂的原因之一。如果它不能为您工作,请不要责怪我,但我希望它能够证明有用。

我在 Mac OS X 10.6.8 上使用 OCaml 3.12.0 测试了该代码。

最好的问候,


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接