문제

단계적 릴리스 코드에 대한 의미가 있습니까? 일부 코드 라인이 생략된다는 것을 알았습니다. 즉, 일부 메소드 호출. 또한 변수 미리보기에는 일부 변수가 표시되지 않으며 다른 변수는 다른 사람들에게 유효하지 않은 (실제가 아님) 값을 보여 주므로 모두 오해의 소지가 있습니다.

WindBG CrashDump 파일을 Visual Studio에로드하면 스텝 노출과 동일한 스택과 변수 부분보기를 제공하기 때문에이 질문을하고 있습니다. 최적화없이 응용 프로그램을 다시 컴파일하는 것을 제외하고 Crashdump Analyze 경험을 향상시키는 방법이 있습니까?

Windows, Visual Studio 2005, 관리되지 않은 C ++

도움이 되었습니까?

해결책

최적화없이 관심있는 파일 만 재 컴파일 :)

일반적으로 :

  • 인터리브 분해 모드로 전환하십시오. 분해를 통해 단일 단계를 밟으면 건너 뛸 수있는 기능 호출을 진행하고 상감 코드를 더욱 분명하게 만들 수 있습니다.
  • 변수에서 값을 얻는 대체 방법을 찾으십시오. 디버거가 직접 보여줄 수 없습니다. 그들이 논쟁으로 통과 된 경우, 콜 스택을 찾아보십시오. 종종 발신자에서 볼 수 있다는 것을 알게 될 것입니다. 그들이 어떤 물체의 getters를 통해 검색된 경우, 그 물체를 조사하십시오. 코드에 의해 생성 된 어셈블리를 한눈에 보관하여 저장된 위치를 계산합니다. 등. 기타 실패 및 최적화를 비활성화 / printf () 추가가 타이밍을 디버깅에 영향을 미치기에 충분히 왜곡하면 더미 글로벌 변수를 추가하고 관심 섹션에 대한 관심 가치로 설정하십시오.

다른 팁

예 - 빌드 용 .pdb와 충돌로 인한 .dmp 파일이있는 경우 정확한 고장 시점에서 디버거를 열고 해당 시점에서 앱 상태를 검사 할 수 있습니다.

몇몇 사람들이 언급했듯이 - 일부 변수는 최적화되지만 약간 창의적 / 호기심이 많은 경우 해당 값을 얻는 방법을 찾을 수 있습니다.

코드의 루트 충돌 핸들러를 빌드하여 다음과 같은 것을 사용하여 모든 Windows 풍미 (Windows 앱을 작성한다고 가정)에서 자동으로 작동하는 .dmp 파일을 생성 할 수 있습니다.

// capture the unhandled exception hook - we will create a mini dump for ourselves
// NOTE: according to docs, if a debugger is present, this API won't succeed (ie. debug builds ignore this)
MiniDumper::Install(
    true,
    filename,
    "Please send a copy of this file, along with a brief description of the problem, to [insert your email address here] so that we might fix this issue."
);

위의 내용은 아래에 쓴 미니 덤퍼 클래스가 필요합니다.

#pragma once
#include <dbghelp.h>
#include "DynamicLinkLibrary.h"
#include "FileName.h"

//////////////////////////////////////////////////////////////////////////
// MiniDumper
//
//  Provides a mechanism whereby an application will generate its own mini dump file anytime
//  it throws an unhandled exception (or at the client's request - see GenerateMiniDump, below).
//
//  Warning: the C-runtime will NOT invoke our unhandled handler if you are running a debugger
//  due to the way that the SetUnhandledExceptionFilter() API works (q.v.)
//
//  To use this facility, simply call MiniDumper::Install - for example, during CWinApp initialization.
//
//  Once this has been installed, all current and future threads in this process will be covered.
//  This is unlike the StructuredException and CRTInvalidParameter classes, which must be installed for
//  for each thread for which you wish to use their services.
//
class MiniDumper
{
public:
    // install the mini dumper (and optionally, hook the unhandled exception filter chain)
    // @param filename is the mini dump filename to use (please include a path)
    // @return success or failure
    // NOTE: we can be called more than once to change our options (unhook unhandled, change the filename)
    static bool Install(bool bHookUnhandledExceptionFilter, const CFilename & filenameMiniDump, const CString & strCustomizedMessage, DWORD dwMiniDumpType = MiniDumpNormal)
    {
        return GetSingleton().Initialize(bHookUnhandledExceptionFilter, filenameMiniDump, strCustomizedMessage, dwMiniDumpType); 
    }

    // returns true if we've been initialized (but doesn't indicate if we have hooked the unhandled exception filter or not)
    static bool IsInitialized() { return g_bInstalled; }

    // returns true if we've been setup to intercept unhandled exceptions
    static bool IsUnhandledExceptionHooked() { return g_bInstalled && GetSingleton().m_bHookedUnhandledExceptionFilter; }

    // returns the filename we've been configured to write to if we're requested to generate a mini dump
    static CFilename GetMiniDumpFilename() { return g_bInstalled ? GetSingleton().m_filenameMiniDump : ""; }

    // you may use this wherever you have a valid EXCEPTION_POINTERS in order to generate a mini dump of whatever exception just occurred
    // use the GetExceptionInformation() intrinsic to obtain the EXCEPTION_POINTERS in an __except(filter) context
    // returns success or failure
    // DO NOT hand the result of GenerateMiniDump to your __except(filter) - instead use a proper disposition value (q.v. __except)
    // NOTE: you *must* have already installed MiniDumper or this will only error
    static bool GenerateMiniDump(EXCEPTION_POINTERS * pExceptionPointers);

private:

    // based on dbghelp.h
    typedef BOOL (WINAPI * MINIDUMPWRITEDUMP_FUNC_PTR)(
        HANDLE hProcess, 
        DWORD dwPid, 
        HANDLE hFile, 
        MINIDUMP_TYPE DumpType,
        CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
        CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
        CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
        );

    // data we need to pass to our mini dump thread
    struct ExceptionThreadData
    {
        ExceptionThreadData(EXCEPTION_POINTERS * exceptionPointers, bool bUnhandled, DWORD threadID = ::GetCurrentThreadId())
            : pExceptionPointers(exceptionPointers)
            , dwThreadID(threadID)
            , bUnhandledException(bUnhandled)
        {
        }

        EXCEPTION_POINTERS *    pExceptionPointers;
        DWORD                   dwThreadID;
        bool                    bUnhandledException;
    };

    // our unhandled exception filter (called automatically by the run time if we've been installed to do so)
    static LONG CALLBACK UnhandledExceptionFilter(EXCEPTION_POINTERS * pExceptionPointers);

    // creates a new thread in which to generate our mini dump (so we don't run out of stack)
    static bool ExecuteMiniDumpThread(EXCEPTION_POINTERS * pExceptionPointers, bool bUnhandledException);

    // thread entry point for generating a mini dump file
    static DWORD WINAPI MiniDumpThreadProc(LPVOID lpParam);

    // obtains the one and only instance
    static MiniDumper & GetSingleton();

    // flag to indicate if we're installed or not
    static bool g_bInstalled;

    // create us
    MiniDumper() 
        : m_pPreviousFilter(NULL)
        , m_pWriteMiniDumpFunction(NULL)
        , m_bHookedUnhandledExceptionFilter(false)
    {
    }

    // install our unhandled exception filter
    bool Initialize(bool bHookUnhandledExceptionFilter, const CFilename & filenameMiniDump, const CString & strCustomizedMessage, DWORD dwMiniDumpType);

    // generates a mini dump file
    bool GenerateMiniDumpFile(ExceptionThreadData * pData);

    // handle an unhandled exception
    bool HandleUnhandledException(ExceptionThreadData * pData);

    bool                            m_bHookedUnhandledExceptionFilter;
    CFilename                       m_filenameMiniDump;
    CString                         m_strCustomizedMessage;
    DWORD                           m_dwMiniDumpType;
    MINIDUMPWRITEDUMP_FUNC_PTR      m_pWriteMiniDumpFunction;
    LPTOP_LEVEL_EXCEPTION_FILTER    m_pPreviousFilter;
};

그리고 그 구현 :

#include "StdAfx.h"
#include "MiniDumper.h"

using namespace Toolbox;

//////////////////////////////////////////////////////////////////////////
// Static Members

bool MiniDumper::g_bInstalled = false;

// returns true if we were able to create a mini dump for this exception
bool MiniDumper::GenerateMiniDump(EXCEPTION_POINTERS * pExceptionPointers)
{
    // obtain the mini dump in a new thread context (which will have its own stack)
    return ExecuteMiniDumpThread(pExceptionPointers, false);
}

// this is called from the run time if we were installed to hook the unhandled exception filter
LONG CALLBACK MiniDumper::UnhandledExceptionFilter(EXCEPTION_POINTERS * pExceptionPointers)
{
    // attempt to generate the mini dump (use a separate thread to ensure this one is frozen & we have a fresh stack to work with)
    ExecuteMiniDumpThread(pExceptionPointers, true);

    // terminate this process, now
    ::TerminateProcess(GetCurrentProcess(), 0xFFFFFFFF);

    // carry on as normal (we should never get here due to TerminateProcess, above)
    return EXCEPTION_CONTINUE_SEARCH;
}

bool MiniDumper::ExecuteMiniDumpThread(EXCEPTION_POINTERS * pExceptionPointers, bool bUnhandledException)
{
    // because this may have been created by a stack overflow
    // we may be very very low on stack space
    // so we'll create a new, temporary stack to work with until we fix this situation
    ExceptionThreadData data(pExceptionPointers, bUnhandledException);
    DWORD dwScratch;
    HANDLE hMiniDumpThread = ::CreateThread(NULL, 0, MiniDumpThreadProc, &data, 0, &dwScratch);
    if (hMiniDumpThread)
    {
        VERIFY(::WaitForSingleObject(hMiniDumpThread, INFINITE) == WAIT_OBJECT_0);
        VERIFY(::GetExitCodeThread(hMiniDumpThread, &dwScratch));
        VERIFY(::CloseHandle(hMiniDumpThread));
        return AsBool(dwScratch);
    }

    return false;
}

DWORD WINAPI MiniDumper::MiniDumpThreadProc(LPVOID lpParam) 
{
    // retrieve our exception context from our creator
    ExceptionThreadData * pData = (ExceptionThreadData *)lpParam;

    // generate the actual mini dump file in this thread context - with our own stack
    if (pData->bUnhandledException)
        return GetSingleton().HandleUnhandledException(pData);
    else
        return GetSingleton().GenerateMiniDumpFile(pData);
}

bool MiniDumper::HandleUnhandledException(ExceptionThreadData * pData)
{
    // generate the actual mini dump file first - hopefully we get this even if the following errors
    const bool bMiniDumpSucceeded = GenerateMiniDumpFile(pData);

    // try to inform the user of what's happened
    CString strMessage = FString("An Unhandled Exception occurred in %s\n\nUnfortunately, this requires that the application be terminated.", CFilename::GetModuleFilename());

    // create the mini dump file
    if (bMiniDumpSucceeded)
    {
        // let user know about the mini dump
        strMessage.AppendFormat("\n\nOn a higher note, we have saved some diagnostic information in %s", m_filenameMiniDump.c_str());
    }

    // append any custom message(s)
    if (!IsEmpty(m_strCustomizedMessage))
        strMessage.AppendFormat("\n\n%s", m_strCustomizedMessage);

    // cap it off with an apology
    strMessage.Append("\n\nThis application must be terminated now.  All unsaved data will be lost.  We are deeply sorry for the inconvenience.");

    // let the user know that things have gone terribly wrong
    ::MessageBox(GetAppWindow(), strMessage, "Internal Error - Unhandled Exception", MB_ICONERROR);

    // indicate success or not
    return bMiniDumpSucceeded;
}

//////////////////////////////////////////////////////////////////////////
// Instance Members

MiniDumper & MiniDumper::GetSingleton() 
{
    static std::auto_ptr<MiniDumper> g_pSingleton(new MiniDumper);
    return *g_pSingleton.get(); 
}

bool MiniDumper::Initialize(bool bHookUnhandledExceptionFilter, const CFilename & filenameMiniDump, const CString & strCustomizedMessage, DWORD dwMiniDumpType)
{
    // check if we need to link to the the mini dump function
    if (!m_pWriteMiniDumpFunction)
    {
        try
        {
            // attempt to load the debug helper DLL
            DynamicLinkLibrary dll("DBGHelp.dll", true);

            // get the function address we need
            m_pWriteMiniDumpFunction = (MINIDUMPWRITEDUMP_FUNC_PTR)dll.GetProcAddress("MiniDumpWriteDump", false);
        }
        catch (CCustomException &)
        {
            // we failed to load the dll, or the function didn't exist
            // either way, m_pWriteMiniDumpFunction will be NULL
            ASSERT(m_pWriteMiniDumpFunction == NULL);

            // there is nothing functional about the mini dumper if we have no mini dump function pointer
            return false;
        }
    }

    // record the filename to write our mini dumps to (NOTE: we don't do error checking on the filename provided!)
    if (!IsEmpty(filenameMiniDump))
        m_filenameMiniDump = filenameMiniDump;

    // record the custom message to tell the user on an unhandled exception
    m_strCustomizedMessage = strCustomizedMessage;

    // check if they're updating the unhandled filter chain
    if (bHookUnhandledExceptionFilter && !m_bHookedUnhandledExceptionFilter)
    {
        // we need to hook the unhandled exception filter chain
        m_pPreviousFilter = ::SetUnhandledExceptionFilter(&MiniDumper::UnhandledExceptionFilter);
    }
    else if (!bHookUnhandledExceptionFilter && m_bHookedUnhandledExceptionFilter)
    {
        // we need to un-hook the unhandled exception filter chain
        VERIFY(&MiniDumper::UnhandledExceptionFilter == ::SetUnhandledExceptionFilter(m_pPreviousFilter));
    }

    // set type of mini dump to generate
    m_dwMiniDumpType = dwMiniDumpType;

    // record that we've been installed
    g_bInstalled = true;

    // if we got here, we must have been successful
    return true;
}

bool MiniDumper::GenerateMiniDumpFile(ExceptionThreadData * pData)
{
    // NOTE: we don't check this before now because this allows us to generate an exception in a different thread context (rather than an exception while processing an exception in the main thread)
    ASSERT(g_bInstalled);
    if (!g_bInstalled)
        return false;

    HANDLE hFile = ::CreateFile(m_filenameMiniDump.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        // indicate failure
        return false;
    }
    else
    {
        // NOTE: don't use exception_info - its a #define!!!
        Initialized<_MINIDUMP_EXCEPTION_INFORMATION> ex_info;
        ex_info.ThreadId = pData->dwThreadID;
        ex_info.ExceptionPointers = pData->pExceptionPointers;

        // generate our mini dump
        bool bStatus = FALSE != ((*m_pWriteMiniDumpFunction)(GetCurrentProcess(), GetCurrentProcessId(), hFile, (MINIDUMP_TYPE)m_dwMiniDumpType, &ex_info, NULL, NULL));

        // close the mini dump file
        ::CloseHandle(hFile);

        return bStatus;
    }
}

이것이 드롭 인 솔루션이 아니라는 사실에 대해 사과드립니다. 내 Toolbox 라이브러리의 다른 부분에 의존성이 있습니다. 그러나 코드에서 자동으로 "충돌 미니 덤프 캡처"를 작성하는 방법에 대한 올바른 아이디어를 제공하는 데 큰 도움이 될 것이라고 생각합니다. 그러면 .DSP 파일과 결합하여 개발주기의 일반 부분 - .DMP가 들어 오면 릴리스 빌드에서 저장된 .pdb (배포하지 않음)로 디버거를 발사 할 수 있으며 충돌 조건을 상당히 디버깅 할 수 있습니다. 용이하게.

위의 코드는 여러 다른 소스의 아말감입니다 - 책을 디버깅하는 코드 스 니펫, MSDN 문서 등의 코드 스 니펫 등. 그러나 나는 위의 코드 중 하나가 나 자신을 제외한 모든 사람에 의해 크게 만들어 졌다고 생각하지 않습니다.

적어도 IA64 덤프가 아닙니다 ...

완전한 덤프와 개인 기호를 넘어서 할 수있는 일은 많지 않습니다. 최신 컴파일러는 코드와 함께 현장의 날을 가지고 있으며 특히 인식 할 수 있습니다. LTCG.

유용한 두 가지가 있습니다.

  1. '이것'이 실제로 가리키는 것에 좋은 앵커를 얻을 때까지 스택을 걸어 가십시오. 대부분의 경우 객체 메소드 프레임에있을 때 '이것은'레지스트리 옵트미즈로 인해 신뢰할 수 없습니다. 일반적으로 몇몇은 스택을 호출하고 올바른 주소가있는 객체를 얻을 수 있으며 Crash Point까지 멤버 참조로 회원 참조를 탐색 할 수 있으며 'this'에 대한 올바른 값을 가질 수 있습니다.

  2. UF (WINDBG의 어셈블리 기능 명령). 이 작은 도우미는 정상적인 해리보기보다 더 관리하기 쉬운 형태의 함수 분해를 나열 할 수 있습니다. 점프와 코드 재 돌리기를 따라 가기 때문에 UF 출력의 논리를 따르는 것이 더 쉽습니다.

가장 중요한 것은 기호 파일 (*.pdb)을 갖는 것입니다. 기본적으로 활성화되지 않은 릴리스 빌드를 위해 생성 할 수 있습니다.

그런 다음 최적화로 인해 코드가 다시 주문 될 수 있으므로 디버깅이 약간 육포 될 수 있음을 알아야합니다. 또한 일부 중간 변수가 최적화되었을 수 있습니다. 일반적으로 데이터의 동작과 가시성은 약간의 제한이있을 수 있습니다.

Visual Studio C ++ 2008을 사용하면 *.dmp 파일을 자동으로 디버깅 할 수 있습니다. 나는 그것이 vs 2005에서도 작동한다고 생각합니다. 이전 컴파일러의 경우 Windbg를 사용해야 할까봐 두려워합니다 ...

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