Функции пропуска трассировки стека - PullRequest
3 голосов
/ 25 марта 2020

Я смотрю на следы стека, сгенерированные в 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, где оно был фактически сгенерирован.

Как получить полную трассировку стека, включая функцию, которая фактически сгенерировала исключение?

1 Ответ

7 голосов
/ 25 марта 2020

Вы стали жертвой оптимизаций, которые затрудняют отладку в любом наборе инструментов.

Обратите внимание, что для fsc оптимизации включены по умолчанию . Это:

Inlining

foo() даже не вызывается - это простой метод c stati, который можно легко встроить. Полученный IL эквивалентен:

let main argv =
    PrintfModule.PrintFormatToStringThenFail(new PrintfFormat<_>("foo"));
    0

Мы можем отключить это с помощью --optimize-

fsc -g --optimize- Program.fs

Но этого все еще недостаточно. Из-за ...

Оптимизация хвостового вызова

Оптимизация хвостового вызова позволяет избежать выделения нового стекового фрейма для функции. foo - это простая функция, которая шортит main. А поскольку нет стекового фрейма, вы не видите его в трассировке стека.

Мы можем отключить это с помощью --tailcalls-.

Для полной отладки, go с что 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
...