문제

MS VC++에서 답변을 찾고 있습니다.

불행하게도 C++ 예외가 매우 광범위하게 사용되는 대규모 C++ 응용 프로그램을 디버깅할 때.때로는 실제로 원하는 것보다 조금 늦게 예외를 포착할 때도 있습니다.

의사 코드의 예:

FunctionB()
{
    ...
    throw e;
    ...
}

FunctionA()
{
    ...
    FunctionB()
    ...
}

try
{
    Function A()
}
catch(e)
{
    (<--- breakpoint)
    ...
}

디버깅할 때 중단점을 사용하여 예외를 포착할 수 있습니다.하지만 예외가 발생한 경우 다시 추적할 수 없습니다. FunctionA() 또는 FunctionB(), 또는 다른 기능.(광범위한 예외 사용과 위 예제의 거대한 버전을 가정).

내 문제에 대한 한 가지 해결책은 호출 스택을 결정하고 저장하는 것입니다. 예외 생성자에서 (즉.잡히기 전에).그러나 이렇게 하려면 이 기본 예외 클래스에서 모든 예외를 파생시켜야 합니다.또한 많은 코드가 필요하고 프로그램 속도가 느려질 수도 있습니다.

작업을 덜 필요로 하는 더 쉬운 방법이 있습니까?대규모 코드 베이스를 변경하지 않고도 가능합니까?

다른 언어에 이 문제에 대한 더 나은 해결책이 있습니까?

도움이 되었습니까?

해결책

예외가 어디서 발생했는지에만 관심이 있다면 다음과 같은 간단한 매크로를 작성할 수 있습니다.

#define throwException(message) \
    {                           \
        std::ostringstream oss; \
        oss << __FILE __ << " " << __LINE__ << " "  \
           << __FUNC__ << " " << message; \
        throw std::exception(oss.str().c_str()); \
    }

파일 이름, 줄 번호 및 함수 이름을 예외 텍스트에 추가합니다(컴파일러가 해당 매크로를 제공하는 경우).

그런 다음 다음을 사용하여 예외를 발생시킵니다.

throwException("An unknown enum value has been passed!");

다른 팁

코드에서 중단점을 지적했습니다.디버거에 있으므로 예외 클래스의 생성자에 중단점을 설정하거나 발생한 모든 예외를 중단하도록 Visual Studio 디버거를 설정할 수 있습니다(디버그->예외 C++ 예외를 클릭하고 발생 및 포착되지 않은 옵션 선택).

많은 어려운 디버깅 질문을 다루는 John Robbins가 쓴 훌륭한 책이 있습니다.책 이름은 Microsoft .NET 및 Microsoft Windows용 애플리케이션 디버깅.제목에도 불구하고 이 책에는 기본 C++ 응용 프로그램 디버깅에 대한 많은 정보가 포함되어 있습니다.

이 책에는 발생한 예외에 대한 호출 스택을 가져오는 방법에 대한 긴 섹션이 있습니다.내 기억이 정확하다면 그의 조언 중 일부는 다음과 같습니다. 구조적 예외 처리 (SEH) C++ 예외 대신(또는 추가로).나는 정말로 그 책을 충분히 추천할 수 없다.

예외 개체 생성자에 중단점을 넣습니다.예외가 발생하기 전에 중단점을 얻게 됩니다.

예외의 원인을 찾을 수 있는 방법이 없습니다. ~ 후에 던질 때 해당 정보를 포함하지 않으면 잡힌다.예외를 포착할 때쯤에는 스택이 이미 해제되어 스택의 이전 상태를 재구성할 방법이 없습니다.

생성자에 스택 추적을 포함하라는 제안이 최선의 방법입니다.예, 구축하는 동안 시간이 소요되지만 이것이 문제가 될 만큼 자주 예외를 발생시켜서는 안 됩니다.모든 예외를 새로운 기반에서 상속받는 것도 필요한 것보다 많을 수 있습니다.관련 예외를 상속하고(고마워요, 다중 상속) 이를 별도로 포착할 수 있습니다.

당신은 사용할 수 있습니다 스택트레이스64 추적을 작성하는 함수입니다(다른 방법도 있다고 생각합니다).확인해 보세요 이 기사 예를 들어 코드.

GCC 라이브러리를 사용하여 C++에서 이를 수행하는 방법은 다음과 같습니다.

#include <execinfo.h> // Backtrace
#include <cxxabi.h> // Demangling

vector<Str> backtrace(size_t numskip) {
    vector<Str> result;
    std::vector<void*> bt(100);
    bt.resize(backtrace(&(*bt.begin()), bt.size()));
    char **btsyms = backtrace_symbols(&(*bt.begin()), bt.size());
    if (btsyms) {
        for (size_t i = numskip; i < bt.size(); i++) {
            Aiss in(btsyms[i]);
            int idx = 0; Astr nt, addr, mangled;
            in >> idx >> nt >> addr >> mangled;
            if (mangled == "start") break;
            int status = 0;
            char *demangled = abi::__cxa_demangle(mangled.c_str(), 0, 0, &status);

            Str frame = (status==0) ? Str(demangled, demangled+strlen(demangled)) : 
                                      Str(mangled.begin(), mangled.end());
            result.push_back(frame);

            free(demangled);
        }
        free(btsyms);
    }
    return result;
}

예외 생성자는 간단히 이 함수를 호출하고 스택 추적을 저장할 수 있습니다.매개변수가 필요합니다. numskip 왜냐하면 나는 스택 추적에서 예외 생성자를 잘라내는 것을 좋아하기 때문입니다.

이를 수행하는 표준 방법은 없습니다.

또한 호출 스택은 일반적으로 예외가 발생하는 시점에 기록되어야 합니다. 던져진;일단 그랬어 잡았다 스택이 펼쳐졌기 때문에 던진 시점에 무슨 일이 일어났는지 더 이상 알 수 없습니다.

Win32/Win64의 VC++에서는 ~할 것 같다 컴파일러 내장 _ReturnAddress()의 값을 기록하고 예외 클래스 생성자가 __declspec(noinline)인지 확인하여 충분히 사용 가능한 결과를 얻습니다.디버그 기호 라이브러리와 함께 SymGetLineFromAddr64를 사용하여 반환 주소에 해당하는 함수 이름(및 .pdb에 포함되어 있는 경우 줄 번호)을 얻을 수 있다고 생각합니다.

네이티브 코드에서는 다음을 설치하여 콜스택을 탐색해 볼 수 있습니다. 벡터 예외 처리기.VC++는 SEH 예외 위에 C++ 예외를 구현하며 벡터 예외 처리기는 프레임 기반 처리기보다 먼저 제공됩니다.그러나 벡터 예외 처리로 인해 발생하는 문제는 진단하기 어려울 수 있으므로 주의해야 합니다.

또한 Mike Stall은 몇 가지 경고를 했습니다. 관리 코드가 있는 앱에서 사용하는 방법에 대해 알아보세요.마지막으로 읽어보세요 Matt Pietrek의 기사 이 작업을 시도하기 전에 SEH 및 벡터 예외 처리를 이해했는지 확인하세요.(중요한 문제를 추적하는 데 도움이 되는 추가한 코드에 대한 중요한 문제를 추적하는 것만큼 기분 나쁜 일은 없습니다.)

MSDev에서는 예외가 발생했을 때 중단점을 설정할 수 있다고 생각합니다.

또는 예외 객체의 생성자에 중단점을 두십시오.

IDE에서 디버깅하는 경우 디버그->예외로 이동하여 C++ 예외에 대해 Thrown을 클릭합니다.

다른 언어?음, Java에서는 e.printStackTrace()를 호출합니다.그보다 훨씬 간단하지는 않습니다.

누군가 관심이 있을 경우를 대비하여 동료가 이메일을 통해 나에게 이 질문에 답했습니다.

Artem은 다음과 같이 썼습니다.

모든 전역 변수 등을 포함한 전체 프로그램 상태를 볼 수 있는 더 나은 크래시 덤프를 수행할 수 있는 MiniDumpWriteDump()에 대한 플래그가 있습니다.호출 스택의 경우 최적화로 인해 더 좋아질 수 있을지 의문입니다...(어쩌면 일부) 최적화를 끄지 않는 한.

그리고 인라인 기능을 비활성화하고 전체 프로그램을 최적화하는 것도 꽤 도움이 될 것 같아요.

실제로 덤프 유형은 다양합니다. 충분히 작은 유형을 선택할 수도 있지만 여전히 더 많은 정보가 있습니다.http://msdn.microsoft.com/en-us/library/ms680519(VS.85).aspx

이러한 유형은 호출 스택에 도움이 되지 않으며 볼 수 있는 변수의 양에만 영향을 미칩니다.

이러한 덤프 유형 중 일부는 우리가 사용하는 dbghelp.dll 버전 5.1에서 지원되지 않는 것으로 나타났습니다.최신 6.9 버전으로 업데이트할 수 있습니다. 방금 MS 디버깅 도구에 대한 EULA를 확인했습니다. 최신 dbghelp.dll은 여전히 ​​재배포해도 괜찮습니다.

나는 내 자신의 예외를 사용합니다.매우 간단하게 처리할 수 있습니다. 텍스트도 포함되어 있습니다.나는 다음 형식을 사용합니다.

throw Exception( "comms::serial::serial( )", "Something failed!" );

또한 두 번째 예외 형식이 있습니다.

throw Exception( "comms::serial::serial( )", ::GetLastError( ) );

그런 다음 FormatMessage를 사용하여 DWORD 값에서 실제 메시지로 변환됩니다.어디서/어떤 형식을 사용하면 무슨 일이 일어났는지, 어떤 기능에서 발생했는지 확인할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top