我正在查看F#中生成的堆栈跟踪,它们正在跳过函数。一个测试用例:
let foo() =
failwithf "foo"
[<EntryPoint>]
let main argv =
foo()
0
已编译调试信息,并运行:
(torch) C:\t>fsc -g test.fs
Microsoft (R) F# Compiler version 10.8.0.0 for F# 4.7
Copyright (c) Microsoft Corporation. All Rights Reserved.
(torch) C:\t>test
Unhandled Exception: System.Exception: foo
at Microsoft.FSharp.Core.PrintfModule.PrintFormatToStringThenFail@1637.Invoke(String message)
at Test.main(String[] argv) in C:\t\test.fs:line 7
它说该异常是从main
生成的,而没有提及实际在哪里生成的foo
。
如何获得完整的堆栈跟踪信息,包括实际生成异常的函数?
您一直是优化的受害者,这使得调试在任何工具链中都非常困难。对于fsc
,优化为turned on by default。
Inlining
foo()
甚至没有被调用-这是一个简单的静态方法,可以轻松地内联。产生的IL等效于:
let main argv =
PrintfModule.PrintFormatToStringThenFail(new PrintfFormat<_>("foo"));
0
我们可以通过--optimize-
关闭此功能>
fsc -g --optimize- Program.fs
但是这还不够。由于...
通话优化
通过尾部调用优化,可以避免为函数分配新的堆栈框架。 foo
是使main
短路的简单功能。而且由于没有堆栈框架,因此您不会在堆栈跟踪中看到它。
我们可以通过--tailcalls-
将其关闭。
有关完整的调试体验,请遵循VS对DEBUG
所做的基本工作:
fsc --debug:full --define:DEBUG --define:TRACE --optimize- --tailcalls- Program.fs
现在,如果我们运行目标,我们将按预期获得堆栈跟踪:
Unhandled Exception: System.Exception: foo
at Microsoft.FSharp.Core.PrintfModule.PrintFormatToStringThenFail@1639.Invoke(String message)
at Program.foo[a]() in Program.fs:line 4
at Program.main(String[] argv) in Program.fs:line 13