Share via


Coding Your Own ASSERT Macros

PREfast for Drivers can report inaccurate warnings because it does not interpret ASSERT macros in the code correctly. PREfast for Drivers uses various heuristics to recognize ASSERTs, but because ASSERT macro code varies significantly, it can misinterpret the code.

Using __declspec(noreturn)

One strategy to help PREfast for Drivers to recognize your ASSERT macros is to implement the assert by using a function that is marked with the __declspec(noreturn) attribute. This attribute tells the compiler that the called function will not return.

However, many drivers have very complex assert macros that allow you to enable or disable them at runtime or continue from them after a break. If PREfast for Drivers can find a path through the ASSERT macro that does not go through a __declspec(noreturn) function, it can report false positive warnings about the condition that the ASSERT confirms is impossible.

Using __analysis_assume or __assume

ASSERT macros are also often coded to have no effect in a free build, but when you run PREfast for Drivers on the code in a checked build environment, PREfast for Drivers might report false-positive results.

The __assume compiler intrinsic tells the compiler that the expression in the assume is true at the point where the intrinsic appears and remains true until the expression is changed. The __analysis_assume macro is the preferred way to actually use __assume if the only purpose of using it is for PREfast. The __assume compiler can change generated code, but the __analysis_assume macro only affects PREfast.

PREfast for Drivers recognizes and understands the __assume instrinsic. Using this keyword can reduce the incidence of false-positive results caused by ASSERT macros.

Note   The __analysis_assert is an assertion that is only present during PREfast runs and can help with suppressing noise as well. Use it when you believe the analysis should terminate (on that path) when the assertion is false.

Example: Coding an ASSERT Macro

The following example demonstrates several methods of coding an ASSERT macro so that PREfast for Drivers interprets it correctly, without affecting the code that the compiler generates.

This example merely demonstrates the structure of the code. It is intended to be used as a template that is modified for use.

The _PREFAST_ macro in the example is defined when PREfast for Drivers is run. It is not defined when the code is being generated.

void __declspec(noreturn) __assertNoReturn(char *, int, char *);

#if defined(_PREFAST_)
// For PREfast runs
//
   #define myAssert(x) __assume(x)

#elif defined(DBG)
//
// For checked builds with a simple assert mechanism 
// (where DBG is the checked build symbol)
//
// The following function call will not cause false-positive
// results because __assertNoReturn will not return. More complex
// macros might return. In that case, use __assume.
//
   #define myAssert(x) ((!(x))?__assertNoReturn(__FILE__, __LINE__, #x):1)
#else
// Free build
   #define myAssert(x)   // Nothing
#endif

Coding Breakpoints

To include breakpoints in the assert body that do not cause additional false-positive results, call the DbgBreakPoint function or use the __debugbreak compiler intrinsic.

DbgBreakPoint will appear on the stack in the debugger. The __debugbreak intrinsic inserts a breakpoint inline in the code. It is the portable alternative to the x86-based __asm int 3;.

A breakpoint can appear in the assert body or in the function that is called from the assert to report the error. PREfast for Drivers treats __debugbreak() and __asm int 3 as non-returning functions, even though the code generator does not. PREfast for Drivers also recognizes any annotations of DbgBreakPoint. Because DbgBreakPoint can be used in contexts in which it will continue, it should not be marked for the compiler as a non-returning function.

 

 

Send comments about this topic to Microsoft

Build date: 5/3/2011