Ускорение трассировки стека без разборки имен при кросс-компиляции - PullRequest
0 голосов
/ 23 января 2019

Для генерации трассировки стека я использую boost::stacktrace. У меня есть агент сборки в Debian, который компилирует версию программы для Unix и Windows. Версия Unix компилируется с использованием установленной нативной g++, а кросс-компиляция Windows создается с использованием mingw-w64. Я использую libbacktrace бэкэнд для обеих компиляций. И boost, и libbacktrace сами скомпилированы на компьютере Debian с использованием одного и того же компилятора mingw-w64.

В моем CMakeLists.txt я указываю: add_definitions(-DBOOST_STACKTRACE_USE_BACKTRACE)

Трассировка стека генерируется так:

namespace foo {
    class Bar {
    public:
        void fooBar() {
            std::cout << boost::stacktrace::stacktrace() << std::endl;
        }
    };
}

int main(int argc, char *argv[]) {
    foo::Bar bar;
    bar.fooBar();
}

Это приводит к следующему выводу (с -g и без оптимизации) при компиляции на моем компьютере (элементарная ОС) и при загрузке с сервера сборки на моей машине с Unix.

 0# foo::Bar::fooBar() in /home/cromon/CLionProjects/test-proj/cmake-build-debug/test-proj
 1# main at /home/cromon/CLionProjects/test-proj/main.cpp:31
 2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
 3# _start in /home/cromon/CLionProjects/test-proj/cmake-build-debug/test-proj

Это вывод с двоичным файлом, созданным на сервере сборки на моей машине с Unix:

 0# foo::Bar::fooBar() in ./test-proj
 1# main at /opt/teamcity/2018.2/TeamCity/buildAgent/work/d79789e141c5605f/test-proj/main.cpp:31
 2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
 3# _start in ./test-proj

Теперь, если я использую двоичный файл, который был скомпилирован с помощью mingw в Unix на моей машине с Windows, можно получить следующий вывод

0# ZN5boost10stacktrace16basic_stacktraceISaINS0_5frameEEE4initEyy at /opt/teamcity/boost/1_69/windows/include/boost-1_69/boost/stacktrace/stacktrace.hpp:75
1# ZN3foo3Bar6fooBarEv at /opt/teamcity/2018.2/TeamCity/buildAgent/work/eb975d0a928ba129/test_proj/main.cpp:22
2# main at /opt/teamcity/2018.2/TeamCity/buildAgent/work/eb975d0a928ba129/test_proj/main.cpp:31
3# _tmainCRTStartup at ./mingw-w64-crt/crt/crtexe.c:336
4# mainCRTStartup at ./mingw-w64-crt/crt/crtexe.c:214
5# register_frame_ctor in C:\WINDOWS\System32\KERNEL32.DLL
6# register_frame_ctor in C:\WINDOWS\SYSTEM32\ntdll.dll

Я также попытался перебрать кадры и запустить на них boost::core::demangle, но это не удалось.

Пока что я вижу только одно различие между средой компиляции в Unix и средой выполнения на моей машине с Windows. В Windows у меня g++ версии 8.2.0, а в Unix 6.3.0. Это вызывает какие-то проблемы? Что еще может привести к сбою деманглинга только в Windows при кросс-компиляции?

1 Ответ

0 голосов
/ 24 января 2019

TL / DR: libbacktrace по какой-то причине удаляет лидирующие подчеркивания и неправильно увеличивает вызовы backtrace_pcinfo -> Будет создана проблема для libbacktrace и boost (исправлено в моих локальных компиляциях)

Более подробный ответ:

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

Объяснение очень высокого уровня, как работает boost :: stacktrace, когда выбран бэкэнд libbacktrace:

  • Вызовите backtrace_pcinfo, это будет считывать (при первом вызове) и искать в отладочной информации DWARF (по крайней мере, в реализации mingw)
  • Если предыдущий вызов был успешным, он вернется сюда, поскольку получит более чем достаточно информации (функция, файл, строка)
  • Если он вернул ноль, он попытается вызвать backtrace_syminfo. Это (для mingw) будет искать таблицу символов coff
  • Если он вернул ноль, вы получите распечатанный необработанный адрес, в противном случае как минимум имя функции (без файла / строки)

Первая ошибка / неожиданное поведение, которое я смог отследить до libbacktrace (каламбур). Реализация в gcc / pecoff.c # 440 по какой-то причине удаляет начальное подчеркивание, если оно присутствует. По этой причине всем именам функций не хватает начального подчеркивания, и они не могут быть разделены. Я думаю, что это можно считать ошибкой, или, по крайней мере, я не вижу другой причины, кроме неудачной попытки печати красивых символов.

Теперь внимательный читатель, вероятно, помнит из моего вопроса, что я использую отладочную информацию, поэтому он не должен попадать в таблицу символов coff, а вместо этого должен использовать отладочную информацию DWARF. И именно здесь находится ошибка с boost.

backtrace_pcinfo должен вызываться с двумя обратными вызовами. Первый получит информацию о разрешенном символе (если есть), а второй - обратный вызов ошибки. Когда символ найден, вызывается первый обратный вызов, и его возвращаемое значение возвращается из backtrace_pcinfo или в коде:

возврат backtrace_pcinfo: return state->fileline_fn (state, pc, callback, error_callback, data);

fileline_fn для mingw - это карликовая реализация dwarf_fileline, которая возвращается так:

ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
        data, &found);
if (ret != 0 || found)
  return ret;

dwarf_lookup_pc теперь возвращает результат обратного вызова при обнаружении символа: return callback (data, pc, filename, lineno, function->name);

Теперь давайте посмотрим, как обеспечивает обратный вызов:

inline int libbacktrace_full_callback(void *data, uintptr_t /*pc*/, const char *filename, int lineno, const char *function) {
    pc_data& d = *static_cast<pc_data*>(data);
    if (d.filename && filename) {
        *d.filename = filename;
    }
    if (d.function && function) {
        *d.function = function;
    }
    d.line = lineno;
    return 0;
}

Поскольку возвращаемое значение напрямую распространяется на backtrace_pcinfo, это означает, что эта проверка всегда будет выполняться в случае or независимо от того, было ли что-либо найдено:

        ::backtrace_pcinfo(
            state,
            reinterpret_cast<uintptr_t>(addr),
            boost::stacktrace::detail::libbacktrace_full_callback,
            boost::stacktrace::detail::libbacktrace_error_callback,
            &data
        ) 
        ||
        ::backtrace_syminfo(
            state,
            reinterpret_cast<uintptr_t>(addr),
            boost::stacktrace::detail::libbacktrace_syminfo_callback,
            boost::stacktrace::detail::libbacktrace_error_callback,
            &data
        );

Это означает, что он всегда будет использовать реализацию, которая каким-то образом удаляет начальное пространство. Я изменил обратный вызов для возврата 1 и теперь все в порядке.

...