Windows Mobile 6.0 Advanced Exception Filtering

Posted by Javier Flores Assad

During development of Windows Mobile 6.0 we realized that gaining more control over the debugger was a key factor in our journey of “completing” the debugging experience of anyone using our debugging tools. Sometimes you want the debugger to do different things depending what process is involved, sometimes it depends on the type or the handling level of an exception… For all these reasons we decided to put a little bit more brains into exception filtering and coded some goodies for the WM6.0 developers using the kernel debugger.

The WM6.0 advanced exception filtering feature of the kernel debugger will allow you to give the debugger a set of rules to evaluate when an exception occurs and decide if a break should occur or the exception should be ignored.


Turning ON and OFF the Advanced Exception Filtering feature

The exception filtering feature is turned OFF by default when debugger is loaded (when kd.dll loads), the debugger will react to any exception with a “break”.

To turn ON exception filtering from Platform Builder 5.*

1. Break into the debugger.

2. Go to the debug menu and click on “advanced commands”.

a. This will bring up a small dialog waiting for your commands

3. Type fex on in the text field and click the execute button

a. This will turn exception filtering ON

fex_screenshot1

To turn ON exception filtering from Platform Builder 6.* (Visual Studio 2005 with PB)

1. Break into the debugger.

2. fex and any other advanced commands are available to you at the "target control window" when you are in a break state.

3. simply type .fex on in the target control window (a "." before the command is required)

4. all other fex commands are accessible in the same way in PB 6

Now that exception filtering is ON you can see the status and more information in the debug output:

fex_screenshot2

When you turn ON the exception filtering feature the debugger will ignore exceptions by default (instead of breaking by default)

, this means that you need to add rules for the cases that you want the debugger to break on. For these reason we hardcoded 3 critical rules

To turn OFF exception filtering from Platform Builder 5.*

1. Break into the debugger.

2. Go to the debug menu and click on “advanced commands”.

3. Type fex off in the text field and click the execute button

To turn ON exception filtering from Platform Builder 6.* (Visual Studio 2005 with PB)

1. Break into the debugger.

2. fex and any other advanced commands are available to you at the "target control window" when you are in a break state.

3. simply type .fex off in the target control window (a "." before the command is required)

4. all other fex commands are accessible in the same way in PB 6

You can turn ON or OFF the exception filtering feature at anytime all the times you want.


Exception filtering logic

Once you turn ON the exception filtering rules you will be able to see a set of 25 rules (most of them empty).

The logic behind exception filtering is very simple:

1. All rules are evaluated every time an exception occurs

2. Each rule gets a specific score depending how many of its fields match with the description of the exception. A rule that applies to a broad set of exceptions receives lower scores; a rule that applies to specific conditions receives higher scores.

3. The rule with the highest score determines if the debugger should break (allow break) or if the exception should be ignored (ignore exception).

4. If two or more rules have the same highest score then the one with the lowest index (first found) is applied.

5. If no rule applies to the current exception then the debugger ignores the exception (does not generate a break)


Rule fields explained

Each rule is composed by 5 fields (values): an index in the set of rules, an exception code, a process name, a handling level and the desired debugger action.

0. Index: Represents the place in the table were this rule is stored. This value is used to refer to the rule when you need to modify it or delete it. Also, in the case of a tie of highest scores, this value is used to get which rule will be applied, the rule with the lowest index is applied in the case of a tie.

1. Exception code: Represents the exception to which the rule is applied. By using the all keyword you can specify that this rule applies to all exceptions or by using a specific exception code you can specify that the rule only applies to a certain type of exception. Example: 0xC00000FD represents “Stack overflows”. For a complete list of exceptions refer to: public\common\sdk\inc\ntstatus.h in your WM6.0 SDK installation.

2. Process name: Represents the process to which the rule is applied. By using the all keyword you can specify that this rule applies to all processes or by using a specific name you can specify that the rule only applies to a certain process name.

3. Handling level: Represents the handling level to which the rule is applied. An exception can be either handled or unhandled depending if the exception occurs within a try/except (try | catch) block or not. We identify the handling level with a numerical value using 1 for handled exceptions and 4 for unhandled exceptions (values 2 and 3 are reserved values, currently not used). If you want the rule to apply only to unhandled exceptions you use the value of 4; if you want the rule to apply to handled exceptions then you use the value of 1. If a rule applies for handled exceptions it also applies for unhandled exceptions due to exception criticality implications and common sense.

4. Debugger action: Represents the action to be executed by the debugger if this rule happens to get the highest score when it is evaluated about the exception in the current context. The value can be either allow or ignore (depending if you want the debugger to allow a break to occur or to ignore the exception and continue) you can also use “a” or “i” (for short of allow or ignore).

Example #1:

If you want to tell the debugger: “break in any unhandled exceptions caused by any process

The rule to add would be: “all all 4 allow

Now, in addition to that: “well, actually, if the exception is a stack overflow from any process even if it is handled I actually want to break if that is the case”

The rule to be added would be: “0xC00000FD all 1 allow

Now, in addition to that: “actually, I would also like to break if an access violation occurs in cprog.exe even if it is handled”

The rule to be added would be: “0xC0000005 cprog.exe 1 allow”

Example #2:

“Well in the particular case of any access violation coming from tictactoe.exe even if it is unhandled I actually dont want you to break”

The rule to be added would be: “0xC0000005 tictactoe.exe 4 ignore”

Adding a rule using the FEX interface

You can have up to 25 rules in your set. Normally you can specify an extremely complete exception filtering rule-set by using less than 10 rules so 25 should be more than enough. If you are using a lot of rules most likely some of the rules are contradicting others or have the same meaning than others so make sure you understand what you are telling the debugger to do every time you add or remove a rule.

To add a new rule you need to have the exception filtering feature turned ON and follow the next steps:

1. Break into the debugger

2. Open the Advanced command window (from the debug menu in Platform Builder 5 anf the target control window in PB 6)

3. Type the command fex ? and then press execute to get more information regarding all the different options in the feature.

a. This will print information in the debug output window regarding how to use each command

4. As we can see from the help output, the command we need to use to add rules is “fex ar” (add rule)

5. Let’s say that we want to add the rules that we specified in the examples #1 and #2 above:

a. Type fex ar all all 4 allow and press execute

b. Type fex ar 0xC00000FD all 1 allow and press execute

c. Type fex ar 0xC0000005 cprog.exe 1 allow and press execute

d. Type fex ar 0xC0000005 tictactoe.exe 4 i and press execute

i. The reason to use “i” instead of the complete word “ignore” is the length limit of the command box

6. If you did it correct you should see the list of rules in the debug output including your new rules in it.

7. As soon as you un-break (hit GO or F5 in platform builder), the rule-set will become active for any future exceptions.

8. You can turn OFF the exception filtering feature and then turn it back ON and it will still have the last set of rules that you specified. If you need more help regarding “fex ar” use the “fex ar ?” command

Removing a rule using the FEX interface

To remove a rule you only need to know the index of the rule that you want to remove from the list. It doesn’t matter if by removing rules you leave empty slots in the middle of the list, the kernel debugger will check all the slots to make sure all rules receive a score before deciding to allow a break or ignore an exception.

1. Break into the debugger

2. Open the Advanced command window (from the debug menu in Platform Builder)

3. Type fex dr 5 and press the execute button to delete the rule at index 5 (in the example case, the rule for cprog.exe access violations will be removed)

Note: It is not recommended to delete the default rules (index 0,1,2) unless you know exactly what you are doing.

Changing the value of specific fields using the FEX interface

Due to length limitations in the advanced command windows, we provide a way to edit certain fields of a rule. This way you can have bigger process names without suffering from the length limitation.

The “fex cr” command is very simple; you need to specify the index of the rule you want to change, the index of the field inside the rule that you want to change and what is the new value for that field. These are the indexes corresponding to the fields inside any rule:

Index 0 = Index of the rule in the table

Index 1 = Exception Code

Index 2 = Process Name

Index 3 = Handling Level

Index 4 = Debugger action (allow or ignore)

Let’s say that you want to change the rule at index 5 of example #1. This is the rule regarding the access violations in cprog.exe. Let’s change it to be applied to poutlook.exe instead of cprog.exe. This means that you want to change the value in field #2 of the rule with index 5.

1. Break into the debugger

2. Open the Advanced command window (from the debug menu in Platform Builder)

3. Type fex cr 5 2 poutlook.exe and press the execute button. This will change the rule from cprog.exe to poutlook.exe

Note: The index of the rule cannot be changed using “fex cr”, trying to do so will result in an error message.

Note: Trying to set an invalid value in any field could result in the formation of an incorrect rule. Please check the rule list every time you add, change or delete a rule to verify that the kernel debugger understood what you tried to say.

Note: The kernel debugger is not going to do any kind of logic validation regarding your rule-set. The kernel debugger will do exactly what the rule-set says even if it doesn’t make any sense.

List of good practices using exception filtering

Exception filtering is a very powerful feature. By using it correctly you can save a lot of time, you can unblock your team from seeing the same exception that you already know about and you can target your debugging efforts to catch a specific exception that you care about or that you are looking for. BUT… you need to be careful with it, review the following list and always take it into account before using this feature:

1. Do not ignore exceptions just because you want to.

a. Ignoring an exception could put at risk the stability and reliability of your product.

b. Before ignoring an exception, make sure you understand the exception and understand why it is OK to ignore it.

c. Make sure you document that a certain exception is blocking your debugging efforts and that you are going to ignore it to see what is beyond it.

2. Deciding to ignore an exception depends on a list of considerations:

a. The code owner is telling you that it is OK to ignore the exception and gives you a valid reason.

b. The exception is handled (occurs inside a try block)…. Verify it is truly handled:

i. Even if the exception is in a try block, you want to make sure you understand it well and the exceptions that could occur inside the try block are well documented.

ii. Some exceptions are not acceptable even inside try blocks (such as divisions by zero, stack overflows or invalid instruction exceptions)

iii. Sometimes there are some leaks resulting from the try block itself, make sure that you check that there is no leaks of any kind resulting from the exception.

iv. Try blocks are NOT the solution to everything. Make sure you are using a try block for the correct reason and not just because you think it needs one.

v. Except (catch) blocks should be as specific as possible. You should never catch all exceptions unless you really know what you are doing.

c. If you are ignoring exceptions because you are looking for a particular one that is perfectly understandable. However, sometimes the exception that you are looking for can be a side effect of another one that you think you don’t care about.

3. Start your debugging without any exception filtering. Enable filters as you need them.

a. This will help you minimize the range of issues that you are bypassing

b. This will help you keep your filtering rule-set clean and understandable

4. It is recommended that you never ignore “unhandled” exceptions.

How to know which rule will have the highest score

Easy, the more specific a rule is highest its score will be during a match. Let’s explain it with one example: Having the following filtering rules:

A handled exception of type access violation occurs in cprog.exe, the kernel debugger sees the exception and evaluates the rules:

1. Rules 0, 1, 2, 4 refer to a specific exception types that are not access violations. Then those rules are discarded receiving a score of 0.

2. Rule 6 refers to a specific process (tictactoe.exe), in this case the exception occurred in cprog.exe. Then the rule is discarded receiving score of 0.

3. Rule 3 refers to all processes and all exception types particularly if the exceptions are unhandled. The rule applies to cprog.exe indirectly and to access violations indirectly (because the rule applies to all processes and all exceptions). The rule receives a positive score but since it is being applied because of generalization the score is low.

4. Rule 7 refers to all exception types that occur in cprog.exe (so it applies to access violations indirectly and it applies to cprog.exe specifically). The rule receives a higher score than Rule #3 because it is more specific in its process name.

5. Rule 5 refers to access violations in cprog.exe if the exceptions are handled. The rule matches perfectly the exception that occurred in the system. This rule gets a higher score than Rule #7 because is more specific to the current situation.

6. The kernel debugger applies rule #5: Allows the debug break.

If you pay attention to the declaration of the set of rules you will see that rule #7 is actually useless. why?... since the default debugger action is to "ignore the exception" if no rule matches it (when exception filtering is turned on) the debugger will ignore such exception anyway.

OK.... I think that's it =)

Happy Debugging!

-Javier