質問

リリースコードをステップ実行する意味はありますか?いくつかのコード行が省略されている、つまりメソッド呼び出しがいくつかあることに気付きました。また、変数のプレビューでは一部の変数が表示されず、他の一部の変数に対して無効な(実数ではない)値が表示されるため、まったく誤解を招きます。

WinDbgクラッシュダンプファイルをVisual Studioに読み込むと、ステップ実行と同じスタックと変数の部分ビューが表示されるため、この質問をしています。最適化せずにアプリケーションを再コンパイルする以外に、クラッシュダンプ分析のエクスペリエンスを改善する方法はありますか?

Windows、Visual Studio 2005、アンマネージC ++

役に立ちましたか?

解決

最適化せずに目的のファイルのみを再コンパイルします:)

一般:

  • インターリーブされた逆アセンブリモードに切り替えます。逆アセンブリをシングルステップ実行すると、スキップされた関数呼び出しにステップインでき、インラインコードがより明確になります。
  • デバッガが直接表示できない変数の値を取得する別の方法を探します。それらが引数として渡された場合は、コールスタックを検索します-多くの場合、呼び出し元に表示されます。それらが何らかのオブジェクトからゲッターを介して取得された場合、そのオブジェクトを調べます。それらを計算するコードによって生成されたアセンブリを見て、それらが格納されている場所を見つけます。他のすべてが失敗し、最適化を無効にする/ printf()を追加することでデバッグに影響を与えるほどタイミングが歪む場合は、ダミーのグローバル変数を追加し、対象セクションへのエントリで対象の値に設定します。

他のヒント

はい-ビルド用の.pdbとクラッシュの.dmpファイルがある場合、正確な障害点でデバッガーを開き、その時点でアプリの状態を調べることができます。

いくつか指摘したように、いくつかの変数は最適化されますが、少し創造的/好奇心re盛であれば、それらの値を取得する方法があります。

次のようなコードを使用して、すべてのWindowsフレーバーで動作する.dmpファイルを自動的に生成するコードのルートクラッシュハンドラーを構築できます(Windowsアプリを作成している場合):

// 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."
);

上記には、以下で記述したMiniDumperクラスが必要です。

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

これはドロップインソリューションではないことをおaび申し上げます。ツールボックスライブラリの他の部分に依存関係があります。しかし、「クラッシュミニダンプをキャプチャ」する方法を組み込む方法については、正しいアイデアを提供するのに大いに役立つと思います。コードから自動的に作成します。これを.dspファイルと組み合わせて、開発サイクルの通常の部分にすることができます。これにより、.dmpが入ったときに、保存された.pdbでデバッガを起動できます。リリースビルド(配布しない!)を使用すると、クラッシュ状態を非常に簡単にデバッグできます。

上記のコードは、さまざまなソースの融合です-デバッグブック、MSDNドキュメントなどからのコードスニペット。属性を省略した場合、問題はありません。ただし、上記のコードのいずれかが自分以外の誰かによって大幅に作成されたとは思わない。

少なくともIA64ダンプではありません...

実際には、完全なダンプシンボルとプライベートシンボルを持つ以外にできることはあまりありません。最近のコンパイラには、コードを使用してフィールドデイがあり、特に LTCG

次の2つが有用であることがわかりました。

  1. 「this」が実際に指し示すものに対する適切なアンカーが得られるまで、スタックを歩いてください。ほとんどの場合、オブジェクトメソッドフレーム「this」はレジストリの最適化のために信頼できません。通常、スタックを数回呼び出して、正しいアドレスを持つオブジェクトを取得し、クラッシュポイントまでメンバー参照ごとにナビゲートし、「this」の正しい値を取得できます

  2. uf(Windbgの逆アセンブリ関数コマンド)。この小さなヘルパーは、通常のディスアセンブリビューよりも扱いやすい形式で関数ディスアセンブルをリストできます。ジャンプとコードの再配置に続くので、uf出力のロジックに従うのが簡単です。

最も重要なことは、シンボルファイル(* .pdb)を持つことです。リリースビルド用に生成できます。デフォルトではアクティブではありません。

次に、最適化のためにコードの順序が変更される可能性があるため、デバッグが少しぎくしゃくする可能性があることを知っておく必要があります。また、いくつかの中間変数は最適化されている可能性があります。一般的に、データの動作と可視性にはいくつかの制限があります。

Visual Studio C ++ 2008では、*。dmpファイルを自動的にデバッグできます。 VS 2005でも動作すると思います。古いコンパイラの場合、WinDbgを使用する必要があると思います...(もちろん、WinDbgの* .pdbファイルも指定してください。そうしないと、情報がかなり制限されます)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top