Налагодження виконуваних файлів
-g генерує таблиці рядків вихідного коду Erlang. Вони працюють на кожному
рівні оптимізації та з --emit, позиційним компонуванням -o і цілями
проєкту.
Таблиці рядків
- Кожна згенерована функція та анонімний fun отримує налагоджувальну область
видимості (debug scope), названу за її іменем в Erlang (
leaf,-main/1-fun-0-), у файлі, що її оголошує, тож функція з включеного.hrlналежить тому файлу. Вона не має імені компонування (linkage name): інакше GDB показував би нативний символ (clausev1_...). - Кожен вираз прив'язано до його фізичного рядка у вихідному коді. Код, у який розгортається макрос, прив'язано до місця виклику макроса; включений код — до його рядка у включеному файлі.
- Змінні й типи не описуються, мова
DW_LANG_lo_user. Одиниця компіляції все одно має типFullDebug: LLVM не включає до DWARF області видимості функцій одиниціLineTablesOnly, якщо нічого не було вбудовано (inlined), і тоді налагоджувачі називають фрейми за символом. - Формат: CodeView для цілей MSVC, DWARF 5 для решти. Без
-gоб'єктні файли не містять таблиць рядків (інспекція IR та--emit llvm-irвсе одно зберігають розташування, потрібні для їхніх коментарів із вихідним кодом).
Компонування
-g передає -g драйверу Clang. Цілі MSVC також записують PDB, на який
виконуваний файл посилається лише за іменем файлу (/PDBALTPATH:%_PDB%); його
публікують поруч із виконуваним файлом як <name>.pdb, після виконуваного файлу,
в тій самій відкладеній публікації. Виконувані файли ELF зберігають DWARF
усередині. Виконуваним файлам Mach-O потрібен був би dsymutil над підготовленими
об'єктними файлами до видалення проміжної області; він не запускається, тож
виконувані файли macOS втрачають таблиці рядків під час компонування.
Стек викликів Erlang
Згенерований код переходить між функціями хвостовими передачами через стек
процесу (модель виконання), тому нативний backtrace
показує поточну функцію Erlang, а потім планувальник (ProcessStack::run,
Executor::work). Фрейми Erlang зупиненого процесу друкує допоміжна функція
runtime clause::runtime::debug_erlang_stack(), починаючи з найглибшого, у
вигляді module:function/arity; вона читає процес, що виконується в потоці, і
скомпільовані імена фреймів, ніколи не звертаючись до таблиць атомів runtime:
(lldb) breakpoint set --file debug.erl --line 8
(lldb) run
* thread #1, stop reason = breakpoint 1.1
frame #0: debug.exe`leaf at debug.erl:8
(lldb) expr -- clause::runtime::debug_erlang_stack()
debug:leaf/1
debug:middle/1
debug:main/1
У GDB виклик записується як call clause::runtime::debug_erlang_stack() після
set language c++ (фрейми коду Erlang не мають мови, в якій GDB розбирає імена
C++). Він називає фрейми twice () at /path/debug.hrl:5. Виконувані файли -Os
можуть відкинути цю допоміжну функцію разом з іншим кодом, на який немає посилань.
Тести
linking_debug_info: метадані IR (файли й рядки підпрограм, макрос, розташований у включеному файлі) і секції таблиць рядків в об'єктних файлах для x86-64 і x86 Windows, x86-64, AArch64 і ARMv7 Linux та arm64 macOS на O0/O2, і їх відсутність без-g.linking_debugger: компонує фікстуру з-gна O0/O2 (і з PDB поруч у Windows), потім керує LLDB (у Windows — з встановлення LLVM) або GDB: точки зупину вdebug.hrlіdebug.erlзупиняються вtwiceіleaf, а допоміжна функція друкує їхні стеки Erlang. Налагоджувач, що не запускається (LLDB з релізу LLVM без його бібліотеки Python), пропускається; код 77 (пропущено), коли жодного немає. Перевірено з LLDB на Windows x64 і GDB 16 на Linux x86-64.
Clause