打印堆栈跟踪
问题描述:
我有一个很短的测试文件:打印堆栈跟踪
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
上市?我该如何编写一个函数来打印它所调用位置的堆栈轨迹?
答
这里是做我所建议的代码。如果可能的话,我推荐使用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
我意识到,因为未捕获的异常,你真的没有过的方式,子进程退出任何控制。这是这个代码太棘手的原因之一。请不要责怪我,如果它不适合你,但我希望它确实有用。
我使用OCaml 3.12.0测试了Mac OS X 10.6.8上的代码。
最好的问候,
答
为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
这回答我的问题的一半,所以谢谢你。我是否应该将另一半的沉默表示为不可能做我想做的事,即打印完整的堆栈跟踪并继续? –
我没有看到一个好的方法来实现它,而不会破坏OCaml运行时。也许在ocamldebug下运行?或者因为您似乎在某种Unix中运行,如果这只是为了快速排除故障,您可以fork()您的进程并在子进程中引发未捕获的异常(这将打印完整的堆栈跟踪),然后退出孩子并继续进入父母程序。我在C年前做过这样的事情,它对我很有帮助。但它很丑,很可能会搞砸了。如果你尝试这样做(很可能是一个坏主意),请在子节点中调用_Exit()以避免刷新缓冲区。问候, –
如果编译为本地代码,您可以尝试使用打印堆栈的标准功能,例如libunwind或从glibc回溯。请注意,结果可能不是很漂亮,但通常足以查明问题。 – ygrek