이 작업을 수행하는 표준 C 함수가 없다는 것을 알고 있습니다. Windows와 * nix에서 이에 대한 기술이 무엇인지 궁금합니다. (Windows XP는 지금이 작업을 수행하는 가장 중요한 OS입니다.)
답변:
glibc는 backtrace () 함수를 제공합니다.
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
char ** backtrace_symbols (void *const *buffer, int size)
void backtrace_symbols_fd(void *const *buffer, int size, int fd)
예를 들어 출력을 stdout / err에 직접 보낼 수도 있습니다.
backtrace_symbols()
짜증나. 모든 심볼을 내 보내야하며 DWARF (디버깅) 심볼을 지원하지 않습니다. libbacktrace는 대부분의 경우 훨씬 더 나은 옵션입니다.
backtrace () 및 backtrace_symbols ()가 있습니다.
man 페이지에서 :
#include <execinfo.h>
#include <stdio.h>
...
void* callstack[128];
int i, frames = backtrace(callstack, 128);
char** strs = backtrace_symbols(callstack, frames);
for (i = 0; i < frames; ++i) {
printf("%s\n", strs[i]);
}
free(strs);
...
보다 편리한 / OOP 방식으로 이것을 사용하는 한 가지 방법은 예외 클래스 생성자에 backtrace_symbols ()의 결과를 저장하는 것입니다. 따라서 해당 유형의 예외를 throw 할 때마다 스택 추적이 있습니다. 그런 다음 인쇄 기능을 제공하십시오. 예를 들면 :
class MyException : public std::exception {
char ** strs;
MyException( const std::string & message ) {
int i, frames = backtrace(callstack, 128);
strs = backtrace_symbols(callstack, frames);
}
void printStackTrace() {
for (i = 0; i < frames; ++i) {
printf("%s\n", strs[i]);
}
free(strs);
}
};
...
try {
throw MyException("Oops!");
} catch ( MyException e ) {
e.printStackTrace();
}
따다!
참고 : 최적화 플래그를 활성화하면 결과 스택 추적이 부정확해질 수 있습니다. 이상적으로는 디버그 플래그를 켜고 최적화 플래그를 끈 상태에서이 기능을 사용하는 것이 좋습니다.
Windows의 경우 StackWalk64 () API (32 비트 Windows에서도)를 확인하십시오. UNIX의 경우 OS의 기본 방식을 사용하거나 가능한 경우 glibc의 backtrace ()로 대체해야합니다.
그러나 네이티브 코드에서 Stacktrace를 사용하는 것은 거의 좋은 생각이 아닙니다. 가능하지 않기 때문이 아니라 일반적으로 잘못된 것을 달성하려고하기 때문입니다.
대부분의 경우 사람들은 예외가 잡히면 어설 션이 실패하거나 최악의 경우와 같은 예외적 인 상황에서 스택 추적을 시도하거나 치명적인 "예외"또는 세분화 위반.
마지막 문제를 고려할 때 대부분의 API는 명시 적으로 메모리를 할당하거나 내부적으로 할당해야합니다. 프로그램이 현재있을 수있는 취약한 상태에서 그렇게하면 상황이 더욱 악화 될 수 있습니다. 예를 들어, 충돌 보고서 (또는 코어 덤프)는 문제의 실제 원인을 반영하지 않지만 문제를 처리하려는 실패한 시도를 반영합니다.
대부분의 사람들이 스택 추적을 얻을 때이를 시도하는 것처럼 보이는 것처럼 치명적인 오류 처리를 시도하고 있다고 가정합니다. 그렇다면 디버거 (개발 중)에 의존하고 프로덕션에서 프로세스 코어 덤프 (또는 Windows의 미니 덤프)를 허용합니다. 적절한 기호 관리와 함께 사후에 원인이되는 명령어를 파악하는 데 문제가 없어야합니다.
unwind 라이브러리를 사용해야합니다 .
unw_cursor_t cursor; unw_context_t uc;
unw_word_t ip, sp;
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
unsigned long a[100];
int ctr = 0;
while (unw_step(&cursor) > 0) {
unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_get_reg(&cursor, UNW_REG_SP, &sp);
if (ctr >= 10) break;
a[ctr++] = ip;
}
공유 라이브러리에서 호출하지 않으면 접근 방식도 잘 작동합니다.
addr2line
Linux 에서 명령을 사용 하여 해당 PC의 소스 기능 / 줄 번호를 가져올 수 있습니다 .
이를 수행하는 플랫폼 독립적 인 방법은 없습니다.
가장 가까운 방법은 최적화없이 코드를 실행하는 것입니다. 이렇게하면 (Visual C ++ 디버거 또는 GDB를 사용하여) 프로세스에 연결하고 사용 가능한 스택 추적을 얻을 수 있습니다.
Solaris에는 Linux에도 복사 된 pstack 명령이 있습니다.
스택을 뒤로 걸어 가면됩니다. 그러나 실제로는 각 함수의 시작 부분에서 호출 스택에 식별자를 추가하고 끝에서 팝업 한 다음 내용을 인쇄하는 것이 더 쉽습니다. 약간의 PITA이지만 잘 작동하며 결국 시간을 절약 할 수 있습니다.
지난 몇 년 동안 Ian Lance Taylor의 libbacktrace를 사용해 왔습니다. 모든 심볼을 내 보내야하는 GNU C 라이브러리의 함수보다 훨씬 깨끗합니다. libunwind보다 역 추적 생성에 더 많은 유틸리티를 제공합니다. 마지막으로 .NET Framework와 같은 외부 도구를 필요로하는 접근 방식이 ASLR에 의해 패배하지 않습니다 addr2line
.
Libbacktrace는 처음에는 GCC 배포의 일부 였지만 이제 작성자가 BSD 라이선스에 따라 독립 실행 형 라이브러리로 사용할 수 있습니다.
https://github.com/ianlancetaylor/libbacktrace
글을 쓰는 시점에서 libbacktrace에서 지원하지 않는 플랫폼에서 역 추적을 생성해야하는 경우가 아니면 다른 것을 사용하지 않을 것입니다.