How to Write and Debug Annotations


Many annotations are straightforward and obvious to write, but some can take a little effort to get right. PREfast attempts to diagnose any errors it sees in annotations, but it cannot check and report every conceivable error. It is always a good idea to write a small test case to confirm that annotations behave as you expect.

A good test case should report any expected errors and should not report instances of correct usage. Simple annotations such as __in do not benefit from test cases, but annotations that involve sizes-in particular, annotations that use the __part modifier-often benefit from test cases because writing the test cases often forces you to think about corner cases.

Examples of Annotation Test Cases

The example in Listing 23-34 shows a set of very simple test cases for the myfun function, which takes three parameters: mode, p1, and p2. The value of mode determines valid values for p1 and p2, so the test cases use the __drv_when conditional annotation to specify how to enforce correct usage of myfun.

Listing 23-34: Example of annotation test cases for a function

image from book
 // Annotate the prototype. __drv_when(mode==1, drv_arg(p1, __null)) __drv_when(mode==2, drv_arg(p2, __null)) __drv_when(mode <= 0 || mode > 2,      __drv_reportError("bad mode value")) void myfun(__in int mode, __in struct s *p1, __in struct s *p2); 
image from book

Starting with the first annotation, the prototype establishes the following usage requirements:

  • If mode is 1, p1 must be NULL.

  • If mode is 2, p2 must be NULL.

  • If mode is negative or greater than 2, issue a "bad mode value" error message.

In Listing 23-35, the test cases call the function correctly and incorrectly. Incorrect calls cause PREfast to issue a warning.

Listing 23-35: Example of code that exercises annotation test cases

image from book
 // A correct use void dummy1(__in struct s *a, __in struct s *b) {     myfun(0, a, b); } // An incorrect use: expect a warning void dummy2(__in struct s *a, __in struct s *b) {     myfun(1, a, b); } // An incorrect use: expect a warning void dummy3(__in struct s *a, __in struct s *b) {     myfun(2, a, b); } // A correct use void dummy4(__in struct s *a, __in struct s *b) {      myfun(1, NULL, b); } // A correct use void dummy5(__in struct s *a, __in struct s *b) {      myfun(2, a, NULL); } // An incorrect use: expect a warning void dummy6(__in struct s *a, __in struct s *b) {      myfun(14, a, b); } 
image from book

Tips for Writing Annotation Test Cases

Consider these tips when writing annotation test cases:

  • It is usually not necessary for the test case code to actually do anything. Often a single function call inside a dummy function is sufficient.

  • If you need pointers to structures, it is often enough to simply have the dummy function take those pointers as parameters, with the appropriate __in, __out, or other annotations that are required for the test.

  • It is not necessary to link or run annotation test cases. It is only necessary to use PREfast to compile them.

  • If you are writing several test cases, it is usually a good idea to put each test case in a separate dummy function. PREfast tries to minimize noise by suppressing duplicate errors within a function. Often when you are checking for multiple ways to trigger the error, the duplicate suppression logic suppresses the additional instances.

  • Functions that return void are usually better for test cases because they are not required to meet the compiler's requirement that the function return a value.

  • Remember to give each function a distinct name.

  • Remember that annotations are independent. It is usually unnecessary to check for unexpected interactions of unrelated annotations.




Developing Drivers with the Microsoft Windows Driver Foundation
Developing Drivers with the Windows Driver Foundation (Pro Developer)
ISBN: 0735623740
EAN: 2147483647
Year: 2007
Pages: 224

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net