Résumé de la méthode d'acquisition de backtrace au moment du débogage
En utilisant LD_PRELOAD
, vous pouvez intégrer ultérieurement une trace arrière à n'importe quel endroit sans reconstruire la cible.
Omis car ce sera l'utilisation de gdb
Uniquement disponible lorsque SEGV se produit dans le programme.
comment utiliser
$ LD_PRELOAD=/lib/libSegFault.so hoge
# or
$ catchsegv hoge
__builtin_return_address ()
Vous pouvez obtenir l'adresse de l'appelant en utilisant __builtin_return_address ()
.
En augmentant le nombre d'arguments, vous pouvez vous référer à l'appelant ci-dessus.
test.c
#include <stdio.h>
#define __USE_GNU
#include <dlfcn.h>
void hoge() {
Dl_info info;
dladdr(__builtin_return_address(0), &info);
fprintf(stderr, "%s : __builtin_return_address => %p\n", __func__, __builtin_return_address(0));
fprintf(stderr, "%s : Dl_info.dli_fname => %s\n", __func__, info.dli_fname);
fprintf(stderr, "%s : Dl_info.dli_fbase => %p\n", __func__, info.dli_fbase);
fprintf(stderr, "%s : Dl_info.dli_sname => %s\n", __func__, info.dli_sname);
fprintf(stderr, "%s : Dl_info.dli_saddr => %p\n", __func__, info.dli_saddr);
dladdr(__builtin_return_address(1), &info);
fprintf(stderr, "%s : __builtin_return_address => %p\n", __func__, __builtin_return_address(1));
fprintf(stderr, "%s : Dl_info.dli_fname => %s\n", __func__, info.dli_fname);
fprintf(stderr, "%s : Dl_info.dli_fbase => %p\n", __func__, info.dli_fbase);
fprintf(stderr, "%s : Dl_info.dli_sname => %s\n", __func__, info.dli_sname);
fprintf(stderr, "%s : Dl_info.dli_saddr => %p\n", __func__, info.dli_saddr);
}
int main(int argc, char const* argv[])
{
hoge();
Dl_info info;
dladdr(__builtin_return_address(0), &info);
fprintf(stderr, "%s : __builtin_return_address => %p\n", __func__, __builtin_return_address(0));
fprintf(stderr, "%s : Dl_info.dli_fname => %s\n", __func__, info.dli_fname);
fprintf(stderr, "%s : Dl_info.dli_fbase => %p\n", __func__, info.dli_fbase);
fprintf(stderr, "%s : Dl_info.dli_sname => %s\n", __func__, info.dli_sname);
fprintf(stderr, "%s : Dl_info.dli_saddr => %p\n", __func__, info.dli_saddr);
return 0;
}
Créer et exécuter des résultats
$ gcc test.c -ldl -rdynamic && ./a.out
hoge : __builtin_return_address => 0x4007d7
hoge : Dl_info.dli_fname => ./a.out
hoge : Dl_info.dli_fbase => 0x400000
hoge : Dl_info.dli_sname => (null)
hoge : Dl_info.dli_saddr => (nil)
hoge : __builtin_return_address => 0x7f8889a6c76d
hoge : Dl_info.dli_fname => /lib/x86_64-linux-gnu/libc.so.6
hoge : Dl_info.dli_fbase => 0x7f8889a4b000
hoge : Dl_info.dli_sname => __libc_start_main
hoge : Dl_info.dli_saddr => 0x7f8889a6c680
main : __builtin_return_address => 0x7f8889a6c76d
main : Dl_info.dli_fname => /lib/x86_64-linux-gnu/libc.so.6
main : Dl_info.dli_fbase => 0x7f8889a4b000
main : Dl_info.dli_sname => __libc_start_main
main : Dl_info.dli_saddr => 0x7f8889a6c680
Notez qu'avec cette méthode, SEGV se produira si vous accédez à une adresse qui n'existe pas lorsque vous augmentez le nombre et revenez en arrière.
Lors du référencement d'un cadre de pile qui n'existe pas(SEGV se produit)
#include <stdio.h>
int main(int argc, char const* argv[])
{
printf("%p\n", __builtin_return_address(0));
printf("%p\n", __builtin_return_address(1));
return 0;
}
Créer et exécuter des résultats
$ gcc test.c && ./a.out
0x7f6c9450076d
Segmentation fault (core dumped)
Un exemple d'utilisation de backtrace ()
et backtrace_symbols ()
de la glibc pour afficher une trace arrière.
test.cpp
// backtrace, backtrace_symbols
#include <execinfo.h>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <string>
using RetVec = std::vector<std::string>;
RetVec my_backtrace() {
auto trace_size = 10;
void* trace[trace_size];
auto size = backtrace(trace, trace_size);
auto symbols = backtrace_symbols(trace, size);
RetVec result(symbols, symbols + size);
free(symbols);
return result;
}
RetVec hoge2() {
return my_backtrace();
}
RetVec hoge1() {
return hoge2();
}
int main(int argc, char const* argv[])
{
auto vec = hoge1();
int count = vec.size();
for (auto v : vec) {
count--;
fprintf(stderr, "%s : backtrace [%d] %s\n",
__func__,
count,
v.c_str()
);
}
return 0;
}
Créer et exécuter des résultats
$ g++ -g -std=c++11 -o test test.cpp -rdynamic
$ ./test
main : backtrace [5] ./test(_Z12my_backtracev+0x97) [0x4021ff]
main : backtrace [4] ./test(_Z5hoge2v+0x18) [0x4022ac]
main : backtrace [3] ./test(_Z5hoge1v+0x18) [0x4022ca]
main : backtrace [2] ./test(main+0x1c) [0x4022ec]
main : backtrace [1] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7fc27153076d]
main : backtrace [0] ./test() [0x402079]
Vous pouvez également obtenir une trace en utilisant libunwind.
(Libunwind est une bibliothèque qui contrôle les chaînes d'appels, voir "BINARY HACKS # 73")
Il y avait un cas où backtrace ()
ne fonctionnait pas bien sur la carte ARM, donc je l'ai implémenté ici aussi.
python
// backtrace, backtrace_symbols
#include <execinfo.h>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <string>
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <dlfcn.h>
#define UNW_LOCAL_ONLY
#include <libunwind.h>
void my_backtrace() {
unw_cursor_t cursor;
unw_context_t context;
unw_getcontext(&context);
unw_init_local(&cursor, &context);
auto count = 0;
do {
unw_word_t offset, pc;
char fname[64];
unw_get_reg(&cursor, UNW_REG_IP, &pc);
fname[0] = '\0';
(void) unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);
Dl_info info;
dladdr((void*)pc, &info);
fprintf(stderr, "%s : backtrace [%d] %s(%s+0x%lx) [%p]\n",
__func__,
count,
info.dli_fname,
fname,
offset,
(void*)pc
);
count++;
} while (unw_step(&cursor) > 0);
}
void hoge2() {
my_backtrace();
}
void hoge1() {
hoge2();
}
int main(int argc, char const* argv[])
{
hoge1();
return 0;
}
Créer et exécuter des résultats
$ g++ -g -std=c++11 -o test test.cpp -rdynamic -lunwind -ldl
$ ./test
my_backtrace : backtrace [0] ./test(_Z12my_backtracev+0x29) [0x400c81]
my_backtrace : backtrace [1] ./test(_Z5hoge2v+0x9) [0x400d92]
my_backtrace : backtrace [2] ./test(_Z5hoge1v+0x9) [0x400d9d]
my_backtrace : backtrace [3] ./test(main+0x14) [0x400db3]
my_backtrace : backtrace [4] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7f12a138d76d]
my_backtrace : backtrace [5] ./test(_start+0x29) [0x400b69]
Binary Hacks a beaucoup d'explications pour les exemples ci-dessus.
backtrace() The GNU C Library: Backtraces Return Address - Using the GNU Compiler Collection (GCC) [Demain est toujours frais et sans erreur. @ Memorandum-Backtrace en langage C (trace de pile)] (http://www35.atwiki.jp/futoyama/pages/101.html) Afficher la trace en C ++ - Days of memos (2010-10-15) How to generate a stacktrace when my gcc C++ app crashes - Stack Overflow
libunwind libunwind documentation c - GCC return address of calling function in ARM architecture - Stack Overflow Coding Relic: Pre-mortem Backtracing Backtrace using ptrace in ARM Linux environment-blog of crimsonwoods
Recommended Posts