⁉️

__debugbreak() は無視されることがある

2025/01/27に公開

はじめに

この記事の結論は __debugbreak関数は__nop関数を呼び出してからの使用がベター ということです。

https://learn.microsoft.com/ja-jp/cpp/intrinsics/nop?view=msvc-170

https://learn.microsoft.com/ja-jp/cpp/intrinsics/debugbreak?view=msvc-170

事例

ことの発端は、以下のようなアサーションマクロで発生しました。

// 記事のために改行しているが、本来は一行で記述している
#define DEBUG_ASSERT(expr, error)\
{\
    if(!(expr))\
    {\
        LOG_FATAL("{}: {}, {}, {}", error, __FUNCTION__, __FILE__, __LINE__);\
        __debugbreak();\
    }\
}

正常な挙動であれば、expr式がfalseの場合、コンソール出力と共にデバッガーが一時停止するはずです。しかし、実際にはLOG_FATALが出力を行うだけで、__dubugbreakはブレイクポイントをトリガーさせない場合が存在しました。

// false なのに 停止しない
DEBUG_ASSERT(false, "assertion failed");

解決策

コンパイラのバグでいつか直るだろうと思って諦めつつ、UnrealEngineのコードを眺めていたところ、解説付きの模範解答ページを発見しました☺️

Source/Runtime/Core/Public/Windows/WindowsPlatform.h
// Q: Why is there a __nop() before __debugbreak()?
// A: VS' debug engine has a bug where it will silently swallow explicit
// breakpoint interrupts when single-step debugging either line-by-line or
// over call instructions. This can hide legitimate reasons to trap. Asserts
// for example, which can appear as if the did not fire, leaving a programmer
// unknowingly debugging an undefined process.
#define PLATFORM_BREAK() (__nop(), __debugbreak())

どうやらデバッガーのバグらしいですが、__nopの副作用がどのような影響を与えているのかは分かりませんでした。組み込み系? アセンブラ? の知識が必要になってきそうなのでこの辺でやめておきますが、いずれ触れてみたい分野です。

Discussion