Avatar ·

Как собрать Rust проект в release режиме с отладочной информацией (debug symbols)?

Всем привет! Я начал изучать Rust и пишу небольшое консольное приложение для работы с файлами. Для отладки я обычно запускаю `cargo run`, но когда я хочу проверить производительность на реальных данных, я использую `cargo build --release`. Проблема в том, что при падении программы в release сборке я получаю совершенно бесполезный стектрейс (stack trace). Вместо нормальных имен функций и номеров строк я вижу только адреса памяти, а функция `backtrace` выводит что-то вроде `0x55a2b3c4d5e6` вместо `my_module::process_file`. Вот пример кода, который падает с ошибкой: ```rust use std::fs; fn main() { let data = fs::read_to_string("config.toml").unwrap(); let parsed: Vec = data .split(',') .map(|s| s.trim().parse::().unwrap()) .collect(); println!("{:?}", parsed); } ``` В debug режиме я вижу точное место падения: `called `Result::unwrap()` on an `Err` value: ParseIntError`. В release режиме я вижу только `thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value', src/main.rs:5`, но если я хочу получить полный backtrace через `RUST_BACKTRACE=1`, то он показывает искаженные имена и сырые адреса. Как правильно настроить сборку, чтобы в release профиле сохранялась отладочная информация, но при этом применялась оптимизация? Я слышал про профили в Cargo.toml, но не понимаю, какие ключи нужно указать. Заранее спасибо за помощь! <<>>

Решение: Настройка профиля release в Cargo.toml

Да, вы правы — стандартный release профиль в Cargo вырезает всю отладочную информацию (флаг strip = true по умолчанию). Это делается для минимизации размера бинарного файла. Однако Rust позволяет тонко настроить поведение сборки через секцию [profile.release] в файле Cargo.toml.

Чтобы получить release сборку с сохранением debug-символов, нужно явно указать debug = true или debug = 1 (второе — минимальный уровень для backtrace). Также стоит обратить внимание на флаг strip, который по умолчанию вырезает символы. Вот правильная конфигурация:


# Cargo.toml
[package]
name = "my_app"
version = "0.1.0"

[profile.release]
# Включаем отладочную информацию (уровень 2 — полная)
debug = true
# Отключаем автоматическое вырезание символов
strip = false
# Оптимизация остаётся на максимуме (по умолчанию 3)
opt-level = 3

После этого выполните cargo build --release. Теперь при падении программы с RUST_BACKTRACE=1 вы увидите читаемые имена функций, номера строк и даже сможете использовать внешние инструменты вроде gdb или lldb для отладки release-сборки.

Детальное объяснение параметров

  • debug = true (эквивалент debug = 2) — сохраняет полную DWARF-информацию: типы, переменные, номера строк. Размер бинарника вырастет в 2-3 раза, но это необходимо для детального бектрейса.
  • debug = 1 — минимальный уровень: только номера строк и имена функций. Этого достаточно для backtrace, но не для пошаговой отладки.
  • strip = false — запрещает Cargo удалять символы. По умолчанию в release стоит strip = "symbols", что делает backtrace бесполезным.

Пример до и после

До настройки (release по умолчанию):


$ RUST_BACKTRACE=1 cargo run --release
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value', src/main.rs:5
stack backtrace:
   0: 0x55a2b3c4d5e6 - <unknown>
   1: 0x55a2b3c4d5e7 - <unknown>
...

После настройки профиля:


$ RUST_BACKTRACE=1 cargo run --release
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value', src/main.rs:5
stack backtrace:
   0: std::panicking::begin_panic
   1: core::result::unwrap_failed
   2: my_app::main
...

Важные замечания

  • Не забудьте добавить RUST_BACKTRACE=1 в переменные окружения. В Windows можно использовать команду set RUST_BACKTRACE=1 && cargo run --release.
  • Если вы используете сторонние крейты (например, anyhow или color-eyre), убедитесь, что они тоже собраны с debug-символами. Для этого можно добавить debug = true в [profile.release.package.*] или глобально.
  • Для продакшена обычно используют debug = 1 и strip = "debuginfo" — это компромисс между размером и возможностью отладки.

Теперь вы можете отлаживать release сборки, не жертвуя производительностью!

<<>>

Альтернативный подход: раздельные профили для отладки и продакшена

Иногда не хочется менять глобальный профиль release, особенно если вы собираете бинарник для распространения. В таком случае можно создать отдельный профиль, например release-with-debug, и использовать его по необходимости.


# Cargo.toml
[profile.release-with-debug]
inherits = "release"   # наследуем все настройки из release
debug = true
strip = false

Теперь сборка выполняется командой:


cargo build --profile release-with-debug

Обратите внимание: в команде используется --profile, а не --release. Бинарник появится в папке target/release-with-debug/.

Использование переменной окружения CARGO_PROFILE

Если вам нужно быстро переключиться без правки файла, можно задать параметры через переменные окружения:


CARGO_PROFILE_RELEASE_DEBUG=true CARGO_PROFILE_RELEASE_STRIP=false cargo build --release

Это не требует изменения Cargo.toml и подходит для одноразовых отладочных сессий.

Минусы подхода

  • Придётся помнить, какой профиль используется для текущей сборки.
  • Размер бинарника будет больше, чем у обычного release.
  • В CI/CD системах нужно явно указывать профиль, иначе символы не попадут в артефакты.

Выбирайте тот способ, который удобнее для вашего рабочего процесса. Главное — не забывайте, что для окончательной поставки пользователям лучше использовать стандартный release профиль без отладочной информации.