Development Tip

C에서 스택 추적을 어떻게 잡을 수 있습니까?

yourdevel 2020. 10. 9. 12:27
반응형

C에서 스택 추적을 어떻게 잡을 수 있습니까?


이 작업을 수행하는 표준 C 함수가 없다는 것을 알고 있습니다. Windows 및 * nix에서 이에 대한 기술이 무엇인지 궁금합니다. (Windows XP는 지금이 작업을 수행하는 가장 중요한 OS입니다.)


우리는 이것을 프로젝트에 사용했습니다.

https://www.codeproject.com/kb/threads/stackwalker.aspx

코드는 약간 지저분한 IMHO이지만 잘 작동합니다. Windows에만 해당됩니다.


glibc는 backtrace () 함수를 제공합니다.

http://www.gnu.org/software/libc/manual/html_node/Backtraces.html


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 

...



try {
   throw MyException("Oops!");
} catch ( MyException e ) {
    e.printStackTrace();
}

따다!

참고 : 최적화 플래그를 활성화하면 결과 스택 추적이 부정확해질 수 있습니다. 이상적으로는 디버그 플래그를 켜고 최적화 플래그를 끈 상태에서이 기능을 사용하는 것이 좋습니다.


Windows의 경우 StackWalk64 () API (32 비트 Windows에서도)를 확인하십시오. UNIX의 경우 OS의 기본 방식을 사용하거나 가능한 경우 glibc의 backtrace ()로 대체해야합니다.

그러나 네이티브 코드에서 Stacktrace를 사용하는 것은 거의 좋은 생각이 아닙니다. 가능하지 않기 때문이 아니라 일반적으로 잘못된 것을 달성하려고하기 때문입니다.

대부분의 경우 사람들은 예외가 잡히면 어설 션이 실패하거나 최악의 경우와 같은 예외적 인 상황에서 스택 추적을 시도하거나 치명적인 "예외"또는 세분화 위반.

마지막 문제를 고려할 때 대부분의 API는 명시 적으로 메모리를 할당해야하거나 내부적으로 할당 할 수 있습니다. 프로그램이 현재있을 수있는 취약한 상태에서 그렇게하면 상황이 더욱 악화 될 수 있습니다. 예를 들어, 충돌 보고서 (또는 코어 덤프)는 문제의 실제 원인을 반영하지 않지만 실패한 문제 처리 시도를 반영합니다.

대부분의 사람들이 스택 추적을 얻을 때이를 시도하는 것처럼 보이는 것처럼 치명적인 오류 처리를 시도하고 있다고 가정합니다. 그렇다면 디버거 (개발 중)에 의존하고 프로덕션에서 프로세스 코어 덤프 (또는 Windows의 미니 덤프)를 허용합니다. 적절한 기호 관리와 함께 사후에 원인이되는 명령어를 파악하는 데 문제가 없어야합니다.


이를 수행하는 플랫폼 독립적 인 방법은 없습니다.

The nearest thing you can do is to run the code without optimizations. That way you can attach to the process (using the visual c++ debugger or GDB) and get a usable stack trace.


For Windows, CaptureStackBackTrace() is also an option, which requires less preparation code on the user's end than StackWalk64() does. (Also, for a similar scenario I had, CaptureStackBackTrace() ended up working better (more reliably) than StackWalk64().)


You should be using the unwind library.

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

Your approach also would work fine unless you make a call from a shared library.

You can use the addr2line command on Linux to get the source function / line number of the corresponding PC.


Solaris has the pstack command, which was also copied into Linux.


May I point you to my article. It's only a few lines of code.

Post Mortem Debugging

Although I currently have problems with the x64 implementation of this.


For the past few years I have been using Ian Lance Taylor's libbacktrace. It is much cleaner than the functions in the GNU C library which require exporting all the symbols. It provides more utility for the generation of backtraces than libunwind. And last but not least, it is not defeated by ASLR as are approaches requiring external tools such as addr2line.

Libbacktrace was initially part of the GCC distribution, but it is now made available by the author as a standalone library under a BSD license:

https://github.com/ianlancetaylor/libbacktrace

At the time of writing, I would not use anything else unless I need to generate backtraces on a platform which is not supported by libbacktrace.


You can do it by walking the stack backwards. In reality, though, it's frequently easier to add an identifier onto a call stack at the beginning of each function and pop it at the end, then just walk that printing the contents. It's a bit of a PITA, but it works well and will save you time in the end.

참고URL : https://stackoverflow.com/questions/105659/how-can-one-grab-a-stack-trace-in-c

반응형