Share via


スレッドをデバッグするためのヒント

この記事では、ネイティブ コードおよびマネージド コードのスレッド名の設定に関する情報など、スレッドのデバッグに役立つ情報を提供します。

C/C++ のヒント

ここでは、ネイティブ コード内のスレッドをデバッグするときに役立つヒントを紹介します。

  • [ウォッチ] ウィンドウまたは [クイック ウォッチ] ダイアログ ボックスで「@TIB」と入力すると、[スレッド情報ブロック] の内容を表示できます。

  • [ウォッチ] ウィンドウまたは [クイック ウォッチ] ダイアログ ボックスで「@Err」と入力すると、現在のスレッドの最終エラー コードを表示できます。

  • マルチスレッド アプリケーションのデバッグには、C ランタイム ライブラリ (CRT) 関数を使用できます。 詳細については、「_malloc_dbg」を参照してください。

C/C++ のスレッド名を設定する

スレッド名の設定は、Visual Studio のどのエディションでも実行できます。 スレッドの名前付けは、実行中のプロセスをデバッグするときに、[スレッド] ウィンドウで対象のスレッドを識別するために役立ちます。 スレッドにわかりやすい名前を付けると、クラッシュ ダンプ検査を介して事後デバッグを実行するときや、さまざまなツールを使用してパフォーマンス キャプチャを分析するときにも役立ちます。

スレッド名を設定する方法

スレッド名を設定するには、2 つの方法があります。 1 つ目は、SetThreadDescription 関数を使用する方法です。 2 つ目は、Visual Studio デバッガーがプロセスにアタッチされているときに特定の例外をスローする方法です。 それぞれのアプローチに利点と注意点があります。 SetThreadDescription の使用は、Windows 10 バージョン 1607 または Windows Server 2016 以降でサポートされています。

特記事項として、これらのメカニズムは互いに独立しているため、必要に応じて、両方のアプローチを同時に使用できます。

SetThreadDescription を使用してスレッド名を設定する

利点:

  • スレッド名は、SetThreadDescription が呼び出されたときにデバッガーがプロセスにアタッチされていたかどうかに関係なく、Visual Studio でデバッグするときに表示されます。
  • Visual Studio でクラッシュ ダンプを読み込んで事後デバッグを実行すると、スレッド名が表示されます。
  • スレッド名は、WinDbg デバッガーや Windows パフォーマンス アナライザー パフォーマンス アナライザーなどの他のツールを使用しているときにも表示されます。

注意事項:

  • スレッド名は、Visual Studio 2017 バージョン 15.6 以降のバージョンでのみ表示されます。
  • クラッシュ ダンプ ファイルの事後デバッグでは、Windows 10 バージョン 1607、Windows Server 2016 以降のバージョンの Windows 上でクラッシュが発生した場合にのみ、スレッド名が表示されます。

例:

#include <windows.h>
#include <processthreadsapi.h>

int main()
{
    HRESULT r;
    r = SetThreadDescription(
        GetCurrentThread(),
        L"ThisIsMyThreadName!"
    );

    return 0;
}

例外をスローしてスレッド名を設定する

プログラムでスレッド名を設定するもう 1 つの方法は、特別に構成された例外をスローして、目的のスレッド名を Visual Studio デバッガーに通知することです。

利点:

  • Visual Studio のすべてのバージョンで動作します。

注意事項:

  • デバッガーがアタッチされ、例外ベースの方法が使用されている場合にのみ動作します。
  • この方法を使用して設定されたスレッド名は、ダンプまたはパフォーマンス分析ツールでは使用できません。

例:

以下に示す SetThreadName 関数は、この例外ベースのアプローチを示しています。 スレッド名は自動的にスレッドにコピーされるので、SetThreadName の呼び出しが完了した後、threadName パラメーターのメモリを解放できることに注意します。

//
// Usage: SetThreadName ((DWORD)-1, "MainThread");
//
#include <windows.h>
const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
    DWORD dwType; // Must be 0x1000.
    LPCSTR szName; // Pointer to name (in user addr space).
    DWORD dwThreadID; // Thread ID (-1=caller thread).
    DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
void SetThreadName(DWORD dwThreadID, const char* threadName) {
    THREADNAME_INFO info;
    info.dwType = 0x1000;
    info.szName = threadName;
    info.dwThreadID = dwThreadID;
    info.dwFlags = 0;
#pragma warning(push)
#pragma warning(disable: 6320 6322)
    __try{
        RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
    }
    __except (EXCEPTION_EXECUTE_HANDLER){
    }
#pragma warning(pop)
}

マネージド コードのスレッド名を設定する

スレッド名の設定は、Visual Studio のどのエディションでも実行できます。 スレッド名を設定すると、[スレッド] ウィンドウでスレッドを追跡する際に役立ちます。

マネージド コードのスレッド名を設定するには、Name プロパティを使用します。

public class Needle
{
    // This method will be called when the thread is started.
    public void Baz()
    {
        Console.WriteLine("Needle Baz is running on another thread");
    }
}

public void Main()
{
    Console.WriteLine("Thread Simple Sample");
    Needle oNeedle = new Needle();
    // Create a Thread object.
    System.Threading.Thread oThread = new System.Threading.Thread(oNeedle.Baz);
    // Set the Thread name to "MyThread".
    oThread.Name = "MyThread";
    // Starting the thread invokes the ThreadStart delegate
    oThread.Start();
}