Как получить правильную строку кода с обратной трассировкой в ​​C++?

Я взял этот код и изменил его, чтобы он выглядел следующим образом:

std::string Backtrace(int skip = 1)
{
    void *callstack[128];
    const int nMaxFrames = sizeof(callstack) / sizeof(callstack[0]);
    char buf[1024];
    int nFrames = backtrace(callstack, nMaxFrames);
    char **symbols = backtrace_symbols(callstack, nFrames);

    string message = "";
    for (int i = skip; i < nFrames; i++) {
        Dl_info info;
        if (dladdr(callstack[i], &info)) {
            char *demangled = nullptr;
            int status;
            demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
            if(demangled != nullptr)
                message += string(demangled) + ": " +
                    to_string((char *)callstack[i] - (char *)info.dli_saddr) + "\n";
            free(demangled);
        }
    }
    free(symbols);
    if (nFrames == nMaxFrames)
        message += "[truncated]\n";
    return message;
}

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

Когда я запускаю этот код (в состоянии, которое гарантированно вызовет проблему), я получаю следующую трассировку стека:

DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT, unsigned int, VkDebugUtilsMessengerCallbackDataEXT const*, void*): 146
vk::DispatchLoaderStatic::vkQueueSubmit(VkQueue_T*, unsigned int, VkSubmitInfo const*, VkFence_T*) const: 50
Display::UpdateFrame(): 1088
RenderingPipeline::RenderFrame(vk::Buffer&, vk::Buffer&, Image&, unsigned int): 63
RenderHandler::RenderHandler(Window*, HardwareInterface*, Display*, Memory*): 784

Моя цель — попытаться напечатать как можно больше релевантной информации. (файл, функция, строка). Теперь я подумал, что инструкция: (char *)callstack[i] - (char *)info.dli_saddr), которую я скопировал из исходного скрипта, даст мне строку, в которой был вызван код, но, например, файл, в котором определяется Display::UpdateFrame(), не имеет даже 1000 строк, поэтому тривиально это число не не номер вызывающего кода в исходном файле.

Есть ли способ получить эту информацию с помощью трассировки стека, аналогично тому, как это делает GDB?

то есть, если функция была вызвана в исходном коде в

File: Display.hpp

Function: Display::UpdateFrame()

Line: 227

Могу ли я получить эту информацию во время выполнения с помощью stacktrace?


person Makogan    schedule 31.08.2019    source источник
comment
Выстрел в темноте: показанный номер строки включает расширенные заголовочные файлы препроцессора над ним?   -  person Weak to Enuma Elish    schedule 01.09.2019
comment
Возможно, но у меня нет простого способа проверить это. Хотя, если он включает заголовки, я бы ожидал гораздо большего числа   -  person Makogan    schedule 01.09.2019
comment
Возможно, вы сможете получить дополнительную помощь по вашему запросу, направив его в список рассылки gdb. Не могу придумать лучшего места, чтобы спросить, как сделать то, что делает gdb, возможно, через какой-то интерфейс API, а не напрямую спросить людей, которые написали gdb. Насколько я могу судить, я не вижу такого API в Fedora, но, возможно, вам повезет.   -  person Sam Varshavchik    schedule 01.09.2019
comment
Это номера строк или смещения байтов машинной инструкции, которая привела к сбою?   -  person user1118321    schedule 01.09.2019
comment
Я не уверен, это определенно не номера строк. Таким образом, исключая смещения байтов?   -  person Makogan    schedule 01.09.2019


Ответы (1)


Backtrace() возвращает смещения в байтах относительно начала некоторого раздела ELF. Чтобы получить номера строк и имена функций, вам нужно использовать библиотеку, которая может считывать отладочную информацию вашей программы, а затем выяснять, какому исходному файлу/номеру строки/функции соответствует данное смещение.

Вот пример того, как это сделать (написанный мной), используя libbfd (при условии, что вы работаете в Linux):

https://github.com/CarloWood/libmemleak/blob/master/src/addr2line.c

person Carlo Wood    schedule 01.09.2019