Стек вызовов в ocamldebug - это реальный стек вызовов, поэтому функции, которые сделали хвостовой вызов, не отображаются в нем. Это смущает. Как получить обратную трассировку, которая включает хвостовые вызовы?
Как я могу получить трассировку стека с хвостовыми вызовами, включенными в Ocaml?
Ответ 1
Самый простой способ - изменить вашу функцию, чтобы она не была рекурсивной. Это то, что я использую, когда хочу, чтобы хорошие обратные отображения отображались, когда исключение прерывает программу (в этом случае нет необходимости в ocamldebug, достаточно запустить программу под OCAMLRUNPARAM="b"
; документация).
Моя личная техника заключается в изменении хвостового вызова на
let result = <tail call> in result
Ocaml в основном компилирует код так, как он написан, и в этом случае это здорово: компилятор не делает этого, и вы получаете красивую обратную линию. Конечно, вы можете легко удалить эту деоптимизацию после обнаружения ошибки.
(Это отлично работает, когда у вас всего несколько хвостов, если их много, вы можете обернуть тело всей функции в let result = <body> in result
, но я нахожу его немного менее удобным и понятным.)
Если вам нужна функция, которая по-прежнему должна быть по-разному (например, у вас есть ограничение размера стека OS-набора, которое вы можете исчерпать), вы можете восстановить стек вызовов для этой функции в структуру данных, поворачивая
let rec f arg1 arg2 .. argN =
...
f arg1' arg2' .. argN'
в
let rec f stack arg1 arg2 .. argN =
let stack' = (arg1,arg2,..,argN)::stack in
...
f stack' arg1' arg2' .. argN'
Затем вы можете в ocamldebug изучить значение переменной stack
, чтобы получить трассировку стека по функциям.
Ответ 2
Чтобы увидеть, где находится реальный хвостовой вызов, я могу несколько раз набирать "start", чтобы отменить выполнение и вытащить стек, пока я не доберусь до интересующего пункта назначения вызова, а затем вернусь назад. Трудолюбивый и должен выполняться по принципу "за звонком", но он работает.