Sto scrivendo questo per Android (solo ARM), ma credo che il principio sia lo stesso anche per Linux generico.Come srotolare lo stack per ottenere il backtrace per il puntatore dello stack specificato (SP)?
Sto cercando di acquisire la traccia dello stack dall'interno del gestore di segnale, in modo che possa effettuare il log quando la mia app si arresta in modo anomalo. Questo è quello che ho trovato usando <unwind.h>
.
inizializzazione:
struct sigaction signalhandlerDescriptor;
memset(&signalhandlerDescriptor, 0, sizeof(signalhandlerDescriptor));
signalhandlerDescriptor.sa_flags = SA_SIGINFO;
signalhandlerDescriptor._u._sa_sigaction = signalHandler;
sigaction(SIGSEGV, &signalhandlerDescriptor, 0);
Il codice stesso:
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: se ottengo il registro PC chiamando _Unwind_GetIP(context)
in unwindCallback
, ottengo la traccia completa per il gestore del segnale pila. Che è uno stack separato, e ovviamente non è quello che voglio. Così ho provato a fornire il PC prelevato dal gestore di segnale ucontext
e ho ottenuto un risultato strano: ottengo una voce di stack, è la voce corretta - la funzione che ha causato il segnale in primo luogo. Ma è registrato due volte (anche l'indirizzo è lo stesso, quindi non è un nome simbolico per cercare bug). Ovviamente, non è abbastanza buono - ho bisogno dell'intero stack. E mi chiedo se questo risultato è puramente accidentale (vale a dire che non dovrebbe funzionare in generale.
Ora, ho letto ho bisogno di fornire anche lo stack pointer, che mi pare di poter ottenere da ucontext
, stesso PC. Ma io non so cosa fare con esso. Devo rilassarsi manualmente invece di usare _Unwind_Backtrace
? Se sì, puoi darmi codice di esempio? Ho cercato la parte migliore di un giorno, e ancora non riuscivo a trovare qualcosa che potrebbe copiare e incollare nel mio progetto.
Per quello che vale, ecco la fonte libunwind che contiene _Unwind_Backtrace
definizione. Pensavo di poter capire qualcosa se vedo la sua fonte, ma è molto più complicato di quanto mi aspettassi.
FWIW, il modo in cui la Dalvik VM raccoglie tracce di stack nativi da altri thread inizia qui: https://android.googlesource.com /platform/dalvik/+/kitkat-release/vm/interp/Stack.cpp#1389. L'implementazione sta inviando segnali ad altri thread per indurli a fare la raccolta. – fadden
@fadden: sfortunatamente utilizza 'corkscrew/backtrace.h', che non è disponibile nell'NDK e ho sentito che la libreria è stata rimossa da Android 5. –
Su una nota correlata - Assicurati che' -fno- omit-frame-pointer' è passato nel 'CFLAGS' al compilatore per permettere di tracciare il grafo delle chiamate. I puntatori di frame saranno disattivati automaticamente dai flag di ottimizzazione (ad esempio. -O2'). – TheCodeArtist