A debugger is a very useful development tool to understand what happens in a model. In RiskAgility Financial Modeller (RiskAgility FM), the debugger is fully integrated and easy to use. When used together with the Dependency Viewer, in most cases it is no longer necessary to use log-stream statements in the code to debug models and model results.

This tutorial will cover two specific uses of the debugger:

  • Understanding unexpected model results
  • Resolving runtime errors

Understanding unexpected model results

In this example we will look at a conventional with-profits (CWP) model. As a starting point, we will assume we are testing the model and have found that the cost of guarantees were not what we expected. We now would like to understand why.

As a first step, we can use the Dependency Viewer to determine which column in the model ultimately gives the unexpected answer. In our case, this was a column called “pay_cost_of_guar_dth_pp_e”. The next step of course is to find out why this column gives an unexpected answer.

When using MoSes, this next step often consisted of adding a log stream, recompiling, running the model, checking the output, adding more log streams, recompiling, running the model, checking the output, and so on, until the problematic part of the code was found. Once the error had been understood and properly corrected, the log streams would no longer be required (and would probably lead to confusion and increased run time if left in the final version). There was then a further stage of removing all the log streams.

With the new integrated debugger in RiskAgility FM, this process can be greatly simplified. There may still be some cases where a log stream is the right approach, but in most cases we would expect that using the debugger is much more time efficient.

Breakpoints

In order to understand what happens in our “pay_cost_of_ guar_dth_pp_e” column, we add a breakpoint to the code. When the projection is run in Debug mode, the model execution will stop (break) at the breakpoint and will allow us to investigate what is happening in the code at that time in the projection. Adding a breakpoint is very easy: just open the formula you would like to look at and click into the grey margin on the left-hand side. A red circle will appear at the location of the breakpoint (Figure 1).

Figure 1.

Towers Watson Media

Adding a breakpoint 

The model will stop the first time the breakpoint is reached. Let’s assume we actually want to investigate what happens in the column at time 12 when the bonus is declared. To achieve this, we add a condition to the breakpoint by right-clicking on the breakpoint and choosing “Condition…”. The syntax for the conditions that can be used is similar to the syntax used in “if” statements. In our case we would like the model to stop “if (t == 12)”, so we enter the condition “t == 12” (Figure 2).

Figure 2. Adding a condition to the breakpoint

Towers Watson Media

Debugging models in RiskAgility FM

To run the model in Debug mode, right click on the projection in the Run Page and select “Debug” instead of “Submit” (Figure 3).

Figure 3. A projection can be run in Debug mode by right-clicking on the projection and selecting “Debug”

Towers Watson Media

Note that in a standard non-debug run, that is one that was started by choosing “Submit”, the breakpoints will be ignored. Therefore, it is not necessary to remove the breakpoints after the investigation, although this can be easily done in a central location via the Debug menu (“Delete/Disable All Breakpoints”).

When the breakpoint is reached during the debug run, the user will be presented with the debug view (Figure 4), which is divided into three areas:

  1. Towards the top of the screen, a view of the model code can be seen. Here, the yellow arrow highlights the line of code that is just about to be executed.
  2. On the lower left-hand side is a view of the values of variables. The variables that are shown depend on which of the four tabs (Autos/Locals/Watch 1/Watch 2) is selected. The “Locals” tab shows all local variables available in the current formula. Since line 4 of the code has not been executed yet, the value of the variable min_db has not yet been set and therefore has a random value.
  3. Finally on the lower right-hand side is the call stack, which we will look at later.
Figure 4. Debug view

Figure 4. Debug view
Click to enlarge

Stepping through the code

The model execution has now been interrupted and we have a number of options to proceed. The most important ones are shown in the Debug menu (Figure 5).

Figure 5. Debug menu

Towers Watson Media

We can continue running the model until we reach the next breakpoint (or in this case the same breakpoint for the next policy), stop debugging altogether, or step through the code. To step through the code, we will be using the “Step Over” functionality (which has the keyboard shortcut F10).

After pressing F10, the yellow arrow moves to the next non-blank line in the code, line 6, and the variable min_db has been calculated. Variables that have recently updated are highlighted in red in the Locals view (Figure 6).

Figure 6. Debug view after stepping over line

Figure 6. Debug view after stepping over line
Click to enlarge

Another very useful feature of the debugger is the ability to hover over column or variable names in the code to reveal their value. For example hovering over the variable pay_ gtd_ben_method_dth reveals that its value is “max_dth_ben” (Figure 7).

Figure 7. Hovering the mouse over a variable reveals its value

Towers Watson Media

The tooltip showing the value can be pinned by clicking on the pin on the right end of the tooltip, so that it will not disappear when the mouse is moved away.

For columns, all values for all time periods are shown as a list (Figure 8) with values that have not yet been calculated shown as “≠”.

Figure 8. All values of a column that have been calculated can be inspected

Towers Watson Media

Note that for sliding columns, only a small number of values are retained in memory at any point in time, so only the available values would be shown.

Continuing stepping through the code, we arrive at line 9 (Figure 9).

Figure 9. Code extract

Towers Watson Media

Let’s assume now that we have identified that there may be an issue with the terminal bonus rate and we therefore want to step into the calculation of pay_tb_rate_dth_pp(t). The easiest way to do this is to place the cursor on the column name pay_tb_rate_dth_pp and press F12 (“Go To Definition”). This will take us into the column code and we can add a breakpoint (Figure 10).

Figure 10. Add a breakpoint to investigate preceding columns15 Insights FM

Towers Watson Media

Once we press “Continue” (or F5), the model will next stop at line one of pay_tb_rate_dth_pp(t) where we set the breakpoint.

Note that RiskAgility FM only calculates the value for each time period of a column once, in order to reduce model run time. Therefore, if the value pay_tb_rate_dth_pp(12) has been calculated before, then the code in the column at time 12 would not be executed again and hence the breakpoint would not be hit.

Now stepping through the code by pressing F10, we can observe each and every calculation and follow what is happening in the code. We can see the intermediate results either in the Locals window (Figure 11) or by hovering the mouse over the code.

As we can see in the Locals window, the value of pay_tb_ method_dth_w is “prop_uas”. So it is quite unexpected that we end up in line 21, which we expected to be the code for the “table” method. However, on closer inspection of the code, we notice that there are no curly brackets ({ }) in lines 17 and 22. Therefore, although the indention would suggest that this should only be executed when the pay_tb_method_ dth_w is set to “table”, a coding error has meant that line 21 is always executed.

Using the debugger to step through the code meant that this issue was relatively easy to identify, which may have been a very laborious task using log_streams.

Figure 11. Code and Locals window in debug view

Towers Watson Media

2. Resolving runtime errors

Another important use of the debugger is to resolve runtime errors. In this section, we present a simple example where a model gives a runtime error and we want to understand the source of the error.

The build process

RiskAgility FM is a flexible platform on which many different types of models can be easily built, including cash-flow projection models. The platform hides a lot of standard functionality from users so that users can concentrate on defining the cash-flows and do not need advanced programming knowledge.

Before a model can be run, it needs to go through the build process (this was called “Generate and Compile” in MoSes). This is either triggered explicitly from the “Build” menu or automatically when a projection is run and there have been changes to the model. The build process roughly consists of two stages:

  1. The code is “generated”, that is the user code (formulae in columns, scalars, etc.) is combined with the core code (all the complexities that are usually not visible to the user).
  2. The generated code is compiled, that is translated from human-readable C++ language into machine language.

Using the debugger means watching the program as it runs, so the debugger needs to link up the human-readable source code and the compiled code by adding more information to the compiled code. As this additional information will slow down the program slightly, it is not included by default. Instead there are two versions of the compiled code, one for “normal” runs and one for debug runs. As a result, RiskAgility FM will start a new build when a debug run is started.

One of the consequences of the above feature is that the calls to some of the code that is normally hidden from the user is visible in the call stack.

In this example, we receive an error when we run the model (Figure 12).

To investigate this error, we run the model in debug mode. As before, this is done by selecting ‘Debug’ instead of ‘Submit’ when right-clicking on the projection on the run page.

The model will run again, however, in Debug mode, and as soon as an error is encountered, a window will be shown (Figure 13).

This gives us some high-level details about the reason the program has stopped. In most cases we can safely ignore the text in this window and click on “Break”. The debugger will now take us to the place in the code where the error occurred. However, this is not necessarily where the actual problem is and may not be in the user code. Therefore, we will first have a look at the call stack, which shows how the model execution arrived at the piece of code where the error occurred.

Figure 12. A projection can be run in Debug mode by right-clicking on the projection and selecting “Debug”

Towers Watson Media

Figure 13. Window showing that a run-time error has been encountered 17 Insights FM

Towers Watson Media

The call stack

The call stack shows us which part of the code that we are currently looking at, indicated by the yellow arrow, and also how we got there. The idea behind a call stack is that every time a function is called, the function name is put on top of the stack. Once the execution of the function has finished, the name will be taken off the stack.

To illustrate this idea, suppose Function A calls Function B, then Function B calls Function C. Then the stack will look like this:

  • Function C
  • Function B
  • Function A

Knowing that Function C is on top of Function A, means that Function A has some indirect dependence on Function C.

The call stack is the one place where users will see calls to the part of the code that is normally hidden from the user (“core code”). It can look a bit daunting at first sight, but is easy to interpret with a bit of background information, see the box “The build process”. The list in the call stack includes code in RiskAgility FM objects like columns, scalars and external functions as well as the core code. An example of the call stack is presented in Figure 14.

Figure 14.

Towers Watson Media

User code entries can be identified easily, since they will always begin with the name of the model class in capital letters, then “UDF::” and then the name of the model class again followed by the column name.

In Figure 15, the same call stack is presented again, with entries related to user code highlighted in yellow. We can now easily see that the error occurred in the column dec_rate_pup(t), which is in the model class cwp_pol. Furthermore, we can identify that this column has been called from dec_off_surr_pp, which in turn has been called by dec_pop_pp_e, and so on.

Figure 15.

Towers Watson Media

If the call stack is shorter than expected and not showing any user code, open the RiskAgility FM options from the Tools menu, go to the Debugging/Symbols page and tick the “Microsoft Symbol Server” option (Figure 16).

Figure 16. Set up RiskAgility FM to load missing symbol files from a Microsoft server

Towers Watson Media

A double-click within the Call Stack window on the entry dec_rate_pup opens the code for this column and an arrow shows the line of code where the problem is. Hovering over the variables in that line, we can quickly identify the problem: the prem_freq is zero (Figure 17).

Figure 17. Identifying the issue

Towers Watson Media

Our first thought is that the data might contain an error here, so we’d like to find out the record that has caused the error. In this example, we can identify the record by the policy number. We can use a watch to achieve this.

Watches 

 Watches can be accessed from the debug view (Figure 4). Clicking on the “Watch 1” tab on the lower-left-hand window, we select items to identify the value at that point in the model execution simply by typing their name into the Name column. These values will be updated in real time as we step through the code. In this example, we add the variables “polnum” and “prem_type”, which are both linked to the data (Figure 18).

Figure 18

Towers Watson Media

Now that we know the policy number, we could easily check the data file to assess whether the frequency is valid. In this example, this is a single premium policy and hence a premium frequency of zero is justified. Therefore, the solution to the ‘Floating point Division by Zero’ error is to amend the code to handle the single premium case.

Summary

The new debugger integrated with RiskAgility FM has greatly improved the functionality of the platform when compared to MoSes. The debugger now allows the user to step through the code as the model is being executed and observe the values in columns, variables and so on, in real time. It also allows users to pause model execution when an error occurs and investigate the error. These tools are invaluable when developing models in RiskAgility FM.