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

Я пишу это для Android (только ARM), но я верю, что этот принцип одинаков и для универсального Linux.

Я пытаюсь захватить трассировку стека из обработчика сигнала, чтобы я мог записать его, когда мое приложение падает. Это то, что я придумал, используя <unwind.h>,
Инициализация:

struct sigaction signalhandlerDescriptor;
memset(&signalhandlerDescriptor, 0, sizeof(signalhandlerDescriptor));
signalhandlerDescriptor.sa_flags = SA_SIGINFO;
signalhandlerDescriptor._u._sa_sigaction = signalHandler;
sigaction(SIGSEGV, &signalhandlerDescriptor, 0);

Сам код:

struct BacktraceState
{
void** current;
void** end;
void* pc;
};

inline _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)
{
BacktraceState* state = static_cast<BacktraceState*>(arg);
state->pc = (void*)_Unwind_GetIP(context);
if (state->pc)
{
if (state->current == state->end)
return _URC_END_OF_STACK;
else
*state->current++ = reinterpret_cast<void*>(state->pc);
}
return _URC_NO_REASON;
}

inline size_t captureBacktrace(void** addrs, size_t max, unsigned long pc)
{
BacktraceState state = {addrs, addrs + max, (void*)pc};
_Unwind_Backtrace(unwindCallback, &state);
personality_routine();

return state.current - addrs;
}

inline void dumpBacktrace(std::ostream& os, void** addrs, size_t count)
{
for (size_t idx = 0; idx < count; ++idx) {
const void* addr = addrs[idx];
const char* symbol = "";

Dl_info info;
if (dladdr(addr, &info) && info.dli_sname) {
symbol = info.dli_sname;
}

int status = -3;
char * demangledName = abi::__cxa_demangle(symbol, 0, 0, &status);
os << "#" << idx << ": " << addr << "  " << (status == 0 ? demangledName : symbol) << "\n";
free(demangledName);
}
}

void signalHandler(int sig, siginfo_t *siginfo, void *uctx)
{
ucontext * context = (ucontext*)uctx;
unsigned long PC = context->uc_mcontext.arm_pc;
unsigned long SP = context->uc_mcontext.arm_sp;

Logger() << __PRETTY_FUNCTION__ << "Fatal signal:" << sig;
const size_t maxNumAddresses = 50;
void* addresses[maxNumAddresses];
std::ostringstream oss;

const size_t actualNumAddresses = captureBacktrace(addresses, maxNumAddresses, PC);
dumpBacktrace(oss, addresses, actualNumAddresses);
Logger() << oss.str();
exit(EXIT_FAILURE);
}

Проблема: если я получу регистрацию ПК, позвонив _Unwind_GetIP(context) в unwindCallbackЯ получаю полный след для стека обработчика сигналов. Это отдельный стек, и это явно не то, что я хочу. Поэтому я попытался поставить компьютер, взятый из ucontext в обработчике сигналов, и получил странный результат: я получаю одну запись стека, это правильная запись — функция, которая вызвала сигнал в первую очередь. Но он регистрируется дважды (даже адрес один и тот же, так что это не символическая ошибка поиска имени). Очевидно, этого недостаточно, мне нужен весь стек. И мне интересно, является ли этот результат просто случайным (т.е. он не должен работать в целом.

Теперь я прочитал, мне нужно также указать указатель стека, который я, очевидно, могу получить ucontextтак же, как ПК. Но я не знаю, что с этим делать. Нужно ли разматывать вручную вместо использования _Unwind_Backtrace? Если да, можете ли вы дать мне пример кода? Я искал лучшую часть дня, и все еще не мог найти ничего, что я мог бы скопировать и вставить в свой проект.

Для чего это стоит, вот libunwind источник, который содержит _Unwind_Backtrace определение. Я думал, что смогу что-то выяснить, если увижу его источник, но это намного сложнее, чем я ожидал.

27

Решение

Во-первых, вам нужно прочитать раздел о функциях «async signal safe»:

http://man7.org/linux/man-pages/man7/signal.7.html

Это весь набор функций, которые безопасно вызывать в обработчике сигналов. Самое худшее, что вы можете сделать, это вызвать все, что вызывает malloc () / free (), или сделать это самостоятельно.

Во-вторых, сначала запустите его за пределами обработчика сигнала.

В-третьих, это, вероятно, по поводу:

Как получить C ++ backtrace на Android

Android NDK: возвращаемся

1

Другие решения

Чтобы получить трассировку стека кода, вызвавшего SIGSEGV вместо трассировки стека обработчика сигнала, вы должны получить регистры ARM из ucontext_t и использовать их для размотки.

Но это трудно сделать с _Unwind_Backtrace(), Таким образом, если вы используете libc ++ (LLVM STL), лучше попробуйте предварительно скомпилировать libunwind для 32-битной ARM, в комплекте с современными Android NDK (в sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libunwind.a). Вот пример кода.


// This method can only be used on 32-bit ARM with libc++ (LLVM STL).
// Android NDK r16b contains "libunwind.a" for armeabi-v7a ABI.
// This library is even silently linked in by the ndk-build,
// so we don't have to add it manually in "Android.mk".
// We can use this library, but we need matching headers,
// namely "libunwind.h" and "__libunwind_config.h".
// For NDK r16b, the headers can be fetched here:
// https://android.googlesource.com/platform/external/libunwind_llvm/+/ndk-r16/include/
#if _LIBCPP_VERSION && __has_include("libunwind.h")
#include "libunwind.h"#endif

struct BacktraceState {
const ucontext_t*   signal_ucontext;
size_t              address_count = 0;
static const size_t address_count_max = 30;
uintptr_t           addresses[address_count_max] = {};

BacktraceState(const ucontext_t* ucontext) : signal_ucontext(ucontext) {}

bool AddAddress(uintptr_t ip) {
// No more space in the storage. Fail.
if (address_count >= address_count_max)
return false;

// Reset the Thumb bit, if it is set.
const uintptr_t thumb_bit = 1;
ip &= ~thumb_bit;

// Ignore null addresses.
if (ip == 0)
return true;

// Finally add the address to the storage.
addresses[address_count++] = ip;
return true;
}
};

void CaptureBacktraceUsingLibUnwind(BacktraceState* state) {
assert(state);

// Initialize unw_context and unw_cursor.
unw_context_t unw_context = {};
unw_getcontext(&unw_context);
unw_cursor_t  unw_cursor = {};
unw_init_local(&unw_cursor, &unw_context);

// Get more contexts.
const ucontext_t* signal_ucontext = state->signal_ucontext;
assert(signal_ucontext);
const sigcontext* signal_mcontext = &(signal_ucontext->uc_mcontext);
assert(signal_mcontext);

// Set registers.
unw_set_reg(&unw_cursor, UNW_ARM_R0, signal_mcontext->arm_r0);
unw_set_reg(&unw_cursor, UNW_ARM_R1, signal_mcontext->arm_r1);
unw_set_reg(&unw_cursor, UNW_ARM_R2, signal_mcontext->arm_r2);
unw_set_reg(&unw_cursor, UNW_ARM_R3, signal_mcontext->arm_r3);
unw_set_reg(&unw_cursor, UNW_ARM_R4, signal_mcontext->arm_r4);
unw_set_reg(&unw_cursor, UNW_ARM_R5, signal_mcontext->arm_r5);
unw_set_reg(&unw_cursor, UNW_ARM_R6, signal_mcontext->arm_r6);
unw_set_reg(&unw_cursor, UNW_ARM_R7, signal_mcontext->arm_r7);
unw_set_reg(&unw_cursor, UNW_ARM_R8, signal_mcontext->arm_r8);
unw_set_reg(&unw_cursor, UNW_ARM_R9, signal_mcontext->arm_r9);
unw_set_reg(&unw_cursor, UNW_ARM_R10, signal_mcontext->arm_r10);
unw_set_reg(&unw_cursor, UNW_ARM_R11, signal_mcontext->arm_fp);
unw_set_reg(&unw_cursor, UNW_ARM_R12, signal_mcontext->arm_ip);
unw_set_reg(&unw_cursor, UNW_ARM_R13, signal_mcontext->arm_sp);
unw_set_reg(&unw_cursor, UNW_ARM_R14, signal_mcontext->arm_lr);
unw_set_reg(&unw_cursor, UNW_ARM_R15, signal_mcontext->arm_pc);

unw_set_reg(&unw_cursor, UNW_REG_IP, signal_mcontext->arm_pc);
unw_set_reg(&unw_cursor, UNW_REG_SP, signal_mcontext->arm_sp);

// unw_step() does not return the first IP.
state->AddAddress(signal_mcontext->arm_pc);

// Unwind frames one by one, going up the frame stack.
while (unw_step(&unw_cursor) > 0) {
unw_word_t ip = 0;
unw_get_reg(&unw_cursor, UNW_REG_IP, &ip);

bool ok = state->AddAddress(ip);
if (!ok)
break;
}
}

void SigActionHandler(int sig, siginfo_t* info, void* ucontext) {
const ucontext_t* signal_ucontext = (const ucontext_t*)ucontext;
assert(signal_ucontext);

BacktraceState backtrace_state(signal_ucontext);
CaptureBacktraceUsingLibUnwind(&backtrace_state);
// Do something with the backtrace - print, save to file, etc.
}

Я также советую взглянуть на этот мой ответ, который содержит больше кода и больше информации:

https://stackoverflow.com/a/50027799/1016580

Если вы используете libstdc ++ (GNU STL), используйте решение Василия Галкина:

https://stackoverflow.com/a/30515756/1016580

, что совпадает с решением Дар Ху из другого поста:

https://stackoverflow.com/a/48593413/1016580

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector