una puntero programacion programa pilas pila microprocesadores estructura estatica eliminar elemento ejemplos dinamica dev datos como colas ats c++ linux android-ndk arm backtrace

c++ - programacion - ¿Cómo desenrollar la pila para volver atrás para el puntero de pila especificado(SP)?



programacion ats pilas (2)

Estoy escribiendo esto para Android (solo ARM), pero creo que el principio es el mismo para Linux genérico también.

Estoy tratando de capturar el seguimiento de la pila desde dentro del manejador de señal, para poder registrarlo cuando mi aplicación falla. Esto es lo que he encontrado usando <unwind.h> .
Inicialización:

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

El código en sí:

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); }

Problema: si obtengo el registro de PC llamando a _Unwind_GetIP(context) en unwindCallback , obtengo el seguimiento completo de la pila del manejador de señales . Que es una pila separada, y obviamente eso no es lo que quiero. Así que intenté suministrar la PC del ucontext en el controlador de señales y obtuve un resultado extraño: recibí una entrada de pila, es la entrada correcta, la función que causó la señal en primer lugar. Pero se registra dos veces (incluso la dirección es la misma, por lo que no es un error de búsqueda de nombre simbólico). Obviamente, eso no es lo suficientemente bueno, necesito toda la pila. Y me pregunto si este resultado es meramente accidental (es decir, no debería funcionar en general).

Ahora, leo que también necesito suministrar el puntero de pila, que aparentemente puedo obtener de ucontext , igual que la PC. Pero no sé qué hacer con eso. ¿Tengo que desenrollar manualmente en lugar de usar _Unwind_Backtrace ? Si es así, ¿puedes darme un código de muestra? He estado buscando la mayor parte del día y todavía no pude encontrar nada que pudiera copiar y pegar en mi proyecto.

Para lo que vale, aquí está la fuente libunwind que contiene la definición _Unwind_Backtrace . Pensé que podría resolver algo si veo su origen, pero es mucho más complicado de lo que esperaba.


Para obtener stacktrace del código que causó SIGSEGV en lugar de stacktrace del manejador de señales, debe obtener registros ARM de ucontext_t y usarlos para desenrollar.

Pero es difícil hacerlo con _Unwind_Backtrace() . Por lo tanto, si usa libc ++ (LLVM STL), intente mejor libunwind precompilado para libunwind de 32 bits, incluido con NDK de Android moderno (en sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libunwind.a ). Aquí hay un código de ejemplo.

// 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. }

También aconsejo echar un vistazo a esta mi respuesta que contiene más código y más información:

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

Si usa libstdc ++ (GNU STL), use la solución de Vasily Galkin:

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

, que es lo mismo que la solución de Dar Hoo de otro post:

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


Primero, debe leer la sección sobre funciones de "seguridad de señal asíncrona":

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

Ese es el conjunto completo de funciones que es seguro llamar en un manejador de señales. Lo peor que puedes hacer es llamar a cualquier cosa que llame a malloc () / free () debajo del capó, o hacerlo tú mismo.

Segundo, haz que funcione fuera de un manejador de señales primero.

En tercer lugar, estos son probablemente a propósito:

Cómo obtener el backtrace de C ++ en Android

NDK de Android: obteniendo el backtrace