Начиная с версии 7.10, GHC может генерировать отладочную информацию, которая помогает отладочным инструментам понимать код, сгенерированный GHC. Эта отладочная информация используется большинством утилит отладки UNIX.
-
-g -
-g⟨n⟩ -
- Since:
-
7.10, числовые уровни с 8.0
- Implies:
-
-fexpose-internal-symbols, когда ⟨n⟩ >= 2.
Генерирует отладочную информацию в объектный код. В настоящее время поддерживается только отладочная информация DWARF для архитектур x86-64 и i386. В настоящее время принимаются уровни отладки от 0 до 3:
-
-g0: отладочная информация не генерируется -
-g1: генерируются записи для разворачивания стека для функций верхнего уровня (достаточно для основных трассировок стека) -
-g2: генерируются записи для разворачивания стека для функций верхнего уровня и внутренних блоков (позволяя более точную трассировку стека, чем с-g1). -
-g3: генерирует специфичную для GHC информацию DWARF для использования более сложными отладочными инструментами, знакомыми с Haskell (см. Сущности отладочной информации для получения подробной информации)
Если ⟨n⟩ опущено, предполагается уровень 2.
Обратите внимание, что для надежного разворачивания стека все библиотеки, включая внешние библиотеки и библиотеки, поставляемые с GHC, такие как base, должны быть скомпилированы с информацией о разворачивании стека. Бинарные дистрибутивы GHC, настроенные таким образом, предоставляются для определенного числа платформ; другим платформам рекомендуется использовать трансформер +debug_info от Hadrian. Также обратите внимание, что встроенная поддержка разворачивания стека, предоставляемая модулем GHC.ExecutionStack библиотеки base, требует, чтобы система времени выполнения была скомпилирована с включенной поддержкой libdw (используя флаг --enable-dwarf-unwind для configure во время компиляции компилятора) и платформы, которая libdw поддерживает.
9.1. Учебник
Рассмотрим простой пример,
1 -- fib.hs 2 fib :: Int -> Int 3 fib 0 = 0 4 fib 1 = 1 5 fib n = fib (n-1) + fib (n-2) 6 7 main :: IO () 8 main = print $ fib 50
Сначала посмотрим, как выполняется эта программа. Мы начнем с указания GHC, что нам нужна отладочная информация,
$ ghc -g -rtsopts fib.hs
Здесь мы использовали опцию -g для того, чтобы сообщить GHC, что он должен добавить отладочную информацию в сгенерированный двоичный файл. Существует три уровня вывода отладки: -g0 (отладочная информация не генерируется, по умолчанию), -g1 (достаточно для основных трассировок стека), -g2 (или просто -g для краткости; генерируется вся информация, известная GHC). Обратите внимание, что эта отладочная информация не влияет на оптимизации, выполняемые GHC.
Подсказка
В Mac OS X отладочная информация хранится отдельно от исполняемого файла. После компиляции исполняемого файла вам нужно использовать утилиту dsymutil для извлечения отладочной информации и размещения ее в архиве отладки,
$ dsymutil fib
Это должно создать файл fib.dSYM.
Теперь давайте посмотрим на поток управления. Для этого мы можем просто запустить нашу программу в gdb (или эквивалентном отладчике), как любой другой исполняемый файл,
$ gdb --args ./Fib +RTS -V0 Reading symbols from Fib...done. (gdb) run Starting program: /opt/exp/ghc/ghc-dwarf/Fib [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". ^C Program received signal SIGINT, Interrupt. 0x000000000064fc7c in cfy4_info () at libraries/integer-gmp/src/GHC/Integer/Type.hs:424 424 minusInteger x y = inline plusInteger x (inline negateInteger y) (gdb)
Здесь мы использовали опцию системы времени выполнения -V0 для отключения периодического таймера RTS, который может помешать нашей сессии отладки. При входе в программу gdb показывает нам местоположение в исходном коде, соответствующее текущей точке выполнения.
Кроме того, мы можем попросить gdb рассказать нам о потоке выполнения, который привел нас к этой точке в программе,
(gdb) bt #0 0x000000000064fc7c in cfy4_info () at libraries/integer-gmp/src/GHC/Integer/Type.hs:424 #1 0x00000000006eb0c0 in ?? () #2 0x000000000064301c in cbuV_info () at libraries/integer-gmp/src/GHC/Integer/Type.hs:323 #3 0x000000000064311b in integerzmgmp_GHCziIntegerziType_eqInteger_info () at libraries/integer-gmp/src/GHC/Integer/Type.hs:312 #4 0x0000000000406eca in roz_info () at Fib.hs:2 #5 0x00000000006eb0c0 in ?? () #6 0x000000000064f075 in cfru_info () at libraries/integer-gmp/src/GHC/Integer/Type.hs:412 #7 0x00000000006eb0c0 in ?? () #8 0x000000000064f075 in cfru_info () at libraries/integer-gmp/src/GHC/Integer/Type.hs:412 #9 0x00000000006eb0c0 in ?? () #10 0x000000000064eefe in integerzmgmp_GHCziIntegerziType_plusInteger_info () at libraries/integer-gmp/src/GHC/Integer/Type.hs:393 ... #64 0x0000000000643ac8 in integerzmgmp_GHCziIntegerziType_ltIntegerzh_info () at libraries/integer-gmp/src/GHC/Integer/Type.hs:343 #65 0x00000000004effcc in base_GHCziShow_zdwintegerToString_info () at libraries/base/GHC/Show.hs:443 #66 0x00000000004f0795 in base_GHCziShow_zdfShowIntegerzuzdcshow_info () at libraries/base/GHC/Show.hs:145 #67 0x000000000048892b in cdGW_info () at libraries/base/GHC/IO/Handle/Text.hs:595 #68 0x0000000000419cb2 in base_GHCziBase_thenIO1_info () at libraries/base/GHC/Base.hs:1072
Подсказка
Здесь мы видим, что первая часть трассировки стека содержит много неопределенных кадров стека по адресу 0x006eb0c0. Если мы спросим у gdb об этом местоположении, мы обнаружим, что эти кадры на самом деле являются замыканиями обновлений STG,
(gdb) print/a 0x006eb0c0 $1 = 0x6eb0c0 <stg_upd_frame_info>
Причина, по которой gdb не отображает это имя символа в выводе трассировки стека, заключается в неточности интерпретации отладочной информации, которая предполагает инвариант, сохраняющийся в C-программах, но не в Haskell.
Примечание
Из-за агрессивной оптимизации, которую выполняет GHC для компилируемых им программ, довольно сложно точно определить, какой момент в исходном коде соответствует данному машинные инструкции. Фактически, GHC внутренне связывает каждую инструкцию со множеством мест в исходном коде. При генерации стандартной отладочной информации, используемой gdb и другими языковыми отладчиками, GHC вынужден эвристически выбрать одно местоположение из этого множества.
По этой причине следует проявлять осторожность при интерпретации местоположений в исходном коде, предоставленных GDB. Хотя эти местоположения обычно будут в некотором смысле «правильными», они не всегда полезны. Именно поэтому инструменты профилирования, ориентированные на Haskell, должны дополнять стандартную информацию о местоположении в исходном коде аннотациями, специфичными для GHC (генерируемыми с помощью -g2), при назначении затрат.
Действительно, мы даже можем установить точки останова,
(gdb) break fib.hs:4 Breakpoint 1 at 0x406c60: fib.hs:4. (5 locations) (gdb) run Starting program: /opt/exp/ghc/ghc-dwarf/Fib Breakpoint 1, c1RV_info () at Fib.hs:4 4 fib n = fib (n-1) + fib (n-2) (gdb) bt #0 c1RV_info () at Fib.hs:4 #1 0x00000000006eb0c0 in ?? () #2 0x0000000000643ac8 in integerzmgmp_GHCziIntegerziType_ltIntegerzh_info () at libraries/integer-gmp/src/GHC/Integer/Type.hs:343 #3 0x00000000004effcc in base_GHCziShow_zdwintegerToString_info () at libraries/base/GHC/Show.hs:443 #4 0x00000000004f0795 in base_GHCziShow_zdfShowIntegerzuzdcshow_info () at libraries/base/GHC/Show.hs:145 #5 0x000000000048892b in cdGW_info () at libraries/base/GHC/IO/Handle/Text.hs:595 #6 0x0000000000419cb2 in base_GHCziBase_thenIO1_info () at libraries/base/GHC/Base.hs:1072 #7 0x00000000006ebcb0 in ?? () at rts/Exception.cmm:332 #8 0x00000000006e7320 in ?? () (gdb)
Из-за особенностей кучи GHC и сильной оптимизации, которую он выполняет, довольно сложно исследовать значения связей во время выполнения. Таким образом, опыт отладки программы Haskell с поддержкой DWARF всё ещё немного уступает типичным императивным отладчикам.
9.2. Запрос трассировки стека из кода Haskell
Система времени выполнения GHC имеет встроенную поддержку сбора информации о трассировке стека из выполняемой программы Haskell. В настоящее время это требует наличия библиотеки libdw из пакета elfutils. Конечно, трассировка стека будет мало полезна, если отладочная информация не доступна в исполняемом файле и его зависимых библиотеках.
Функциональность трассировки стека для использования программами Haskell экспонирована в модуле GHC.ExecutionStack. См. документацию Haddock в этом модуле для получения подробностей о его использовании.
9.3. Запрос трассировки стека с SIGQUIT
На платформах, совместимых с POSIX, система времени выполнения GHC (при компиляции с поддержкой libdw) будет генерировать трассировку стека при получении сигнала stderr сигнала SIGQUIT (на многих системах этот сигнал можно отправить, используя Ctrl-\). Например (используя тот же fib.hs что и выше),
$ ./fib & killall -SIGQUIT fib
Caught SIGQUIT; Backtrace:
0x7f3176b15dd8 dwfl_thread_getframes (/usr/lib/x86_64-linux-gnu/libdw-0.163.so)
0x7f3176b1582f (null) (/usr/lib/x86_64-linux-gnu/libdw-0.163.so)
0x7f3176b15b57 dwfl_getthreads (/usr/lib/x86_64-linux-gnu/libdw-0.163.so)
0x7f3176b16150 dwfl_getthread_frames (/usr/lib/x86_64-linux-gnu/libdw-0.163.so)
0x6dc857 libdwGetBacktrace (rts/Libdw.c:248.0)
0x6e6126 backtrace_handler (rts/posix/Signals.c:541.0)
0x7f317677017f (null) (/lib/x86_64-linux-gnu/libc-2.19.so)
0x642e1c integerzmgmp_GHCziIntegerziType_eqIntegerzh_info (libraries/integer-gmp/src/GHC/Integer/Type.hs:320.1)
0x643023 integerzmgmp_GHCziIntegerziType_eqInteger_info (libraries/integer-gmp/src/GHC/Integer/Type.hs:312.1)
0x406eca roz_info (/opt/exp/ghc/ghc-dwarf//Fib.hs:2.1)
0x6eafc0 stg_upd_frame_info (rts/Updates.cmm:31.1)
0x64ee06 integerzmgmp_GHCziIntegerziType_plusInteger_info (libraries/integer-gmp/src/GHC/Integer/Type.hs:393.1)
0x6eafc0 stg_upd_frame_info (rts/Updates.cmm:31.1)
...
0x6439d0 integerzmgmp_GHCziIntegerziType_ltIntegerzh_info (libraries/integer-gmp/src/GHC/Integer/Type.hs:343.1)
0x4efed4 base_GHCziShow_zdwintegerToString_info (libraries/base/GHC/Show.hs:442.1)
0x4f069d base_GHCziShow_zdfShowIntegerzuzdcshow_info (libraries/base/GHC/Show.hs:145.5)
0x488833 base_GHCziIOziHandleziText_zdwa8_info (libraries/base/GHC/IO/Handle/Text.hs:582.1)
0x6ebbb0 stg_catch_frame_info (rts/Exception.cmm:370.1)
0x6e7220 stg_stop_thread_info (rts/StgStartup.cmm:42.1)