Как получить след стека вызовов? (глубоко встроенный, никакая поддержка библиотеки)

Я хочу, чтобы мои обработчики исключений и функции отладки смогли распечатать следы стека вызовов, в основном точно так же, как след () библиотечная функция в glibc. К сожалению, моя библиотека C (Newlib) не обеспечивает такой вызов.

У меня есть что-то вроде этого:

#include  // GCC's internal unwinder, part of libgcc
_Unwind_Reason_Code trace_fcn(_Unwind_Context *ctx, void *d)
{
    int *depth = (int*)d;
    printf("\t#%d: program counter at %08x\n", *depth, _Unwind_GetIP(ctx));
    (*depth)++;
    return _URC_NO_REASON;
}

void print_backtrace_here()
{
    int depth = 0;
    _Unwind_Backtrace(&trace_fcn, &depth);
}

который в основном работает, но получающиеся трассировки не всегда завершены. Например, если я делаю

int func3() { print_backtrace_here(); return 0; }
int func2() { return func3(); }
int func1() { return func2(); }
int main()  { return func1(); }

след только показывает func3 () и основной (). (Это - лицевая сторона. игрушечный пример, но я проверил дизассемблирование и подтвердил, что эти функции - все здесь полностью и не оптимизированные или встроенные.)

Обновление: Я попробовал этот код следа старой системы ARM7, но с тем же (или по крайней мере, максимально эквивалентные) сценарий параметров компилятора и компоновщика и это печатают корректный, полный след (т.е. func1, и func2 не отсутствуют), и действительно это даже следы, мимо основные в код инициализации начальной загрузки. Таким образом, по-видимому, проблема не со сценарием компоновщика или параметрами компилятора. (Кроме того, подтвержденный от дизассемблирования, что никакой указатель кадра не используется в этом ARM7, тестируют любого).

Код компилируется с-fomit-frame-pointer, но моя платформа (чистый металл Кора ARM M3) определяет ABI, который не использует указатель кадра так или иначе. (Предыдущая версия этой системы использовала старый ABI APCS на ARM7 с принудительными стековыми фреймами и указателем кадра и следом как тот здесь, который работал отлично).

Целая система компилируется с-fexception, который гарантирует необходимые метаданные, которые использует _Unwind, включен в файл ELF. (_Unwind разработан для обработки исключений, я думаю).

Так, мой вопрос: существует ли "стандартный", принятый способ получить надежные следы во встроенных системах с помощью GCC?

Я не возражаю иметь необходимость бездельничать со сценариями компоновщика и кодом crt0 при необходимости, но не хочу должным быть делать любые возможности к самому набору инструментальных средств.

Спасибо!

20
задан hugov 4 August 2010 в 20:24
поделиться

3 ответа

gcc возвращает оптимизацию. В func1 () и func2 () он не вызывает func2 () / func3 () - вместо этого он переходит к func2 () / func3 (), поэтому func3 () может немедленно вернуться к main ().

В вашем случае func1 () и func2 () не нуждаются в настройке кадра стека, но если они это сделают (например, для локальных переменных), gcc все равно может выполнить оптимизацию, если вызов функции является последней инструкцией - затем он очищает стек перед переходом к func3 ().

Взгляните на сгенерированный код ассемблера, чтобы увидеть его.


Изменить / обновить:

Чтобы убедиться, что это причина, сделайте что-нибудь после вызова функции, что не может быть переупорядочено компилятором (например, используя возвращаемое значение). Или просто попробуйте скомпилировать с -O0.

7
ответ дан 30 November 2019 в 00:43
поделиться

Поскольку платформы ARM не используют указатель кадра, вы никогда не узнаете, насколько велик стековый кадр, и не можете просто развернуть стек за пределы единственного возвращаемого значения в R14.

При исследовании сбоя, для которого у нас нет отладочных символов, мы просто выгружаем весь стек и ищем ближайший символ к каждому элементу в диапазоне инструкций. Он действительно генерирует массу ложных срабатываний, но все же может быть очень полезен для расследования сбоев.

Если вы запускаете чистые исполняемые файлы ELF, вы можете отделить символы отладки из исполняемого файла выпуска. Затем gdb может помочь вам выяснить, что происходит, из стандартного дампа ядра unix

7
ответ дан 30 November 2019 в 00:43
поделиться

Содержит ли ваш исполняемый файл отладочную информацию, полученную при компиляции с опцией -g? Я думаю, это необходимо для получения полной трассировки стека без указателя кадра.

Вам может понадобиться -gdwarf-2, чтобы убедиться, что он использует формат, включающий информацию о размотке.

0
ответ дан 30 November 2019 в 00:43
поделиться