Debugging Synergy .NET Code
This topic includes the following sections:
- Remote debugging
- Local variables and the Locals window
- Determining whether a system option is set
- Examining the contents of a dynamic memory handle
- Synergy types in the Visual Studio Memory Usage Tool
- Watch and Quickwatch windows
- SET WATCH functionality in Visual Studio
The Visual Studio .NET debugger is used for debugging .NET Framework applications on Windows. See Visual Studio documentation for more information on debugging, and note the following:
- The active configuration/platform combination determines which build will be debugged. The configuration/platform combination or the profile (for .NET Core or .NET Standard) determines which debug settings are used. See Configurations, platforms, and profiles.
- When an assembly is built, the compiler creates a program database file (.pdb). Program database files store debugging and project state information. If you want to obtain traceback information for a distributed assembly, you must distribute the program database file for the assembly. Note that only module traceback information is provided unless the assembly is compiled with the “Debug” or “Debug (full)” option set for “Debug/Optimize code” on the Build page of project properties.
- The _DEBUG system define enables you to include code based on whether a program is compiled in debug mode. See Compile-time defines for more information.
- No information displays for a define when you hover over it. (This is true for any language that supports defines—e.g., C++.)
- If a breakpoint doesn’t work, use Visual Studio’s Modules window to load necessary symbols, or at least find out why the symbols weren’t loaded. Select Debug > Windows > Modules from the Visual Studio menu (which opens the Modules window), and then find the entry for the missing symbols. If the path and filename are correct, but the status column indicates that the symbols aren’t loaded, you can instruct Visual Studio to load them by right-clicking the entry and selecting Load Symbols from the context menu. If the Modules window indicates that the path or file isn’t correct, you’ll need to figure out why the file couldn’t be located and resolve the issue.
Remote debugging
Remote debugging is supported for Synergy .NET. This means you can debug a Synergy .NET program running on a remote Windows desktop system or server. You don’t need to have a Visual Studio project for the program, but the program must be built in debug mode, SDI must be installed on the development machine, and the source file (.dbl) with the code you want to step through must be on the development machine. Note the following:
- To use remote debugging, the remote system must have Remote Tools for Visual Studio.
- To examine Synergy variables as you debug remotely, you must also install the Synergy .NET Remote Debugger on the remote system. Synergy .NET Remote Debugger is a set of libraries that enable the Visual Studio Remote Debugger to work with Synergy types.
See Microsoft documentation for more information on remote debugging.
Local variables and the Locals window
- Make sure both of the following Visual Studio debugging options (Tools > Options > Debugging > General) are selected: “Enable property evaluation and other implicit function calls” and “Call string-conversion function on objects in variables windows.” If either is unselected, local variables will be displayed with internal names.
- If the “Unwind the call stack on unhandled exceptions debugging” option is cleared (Tools > Options > Debugging), the pseudovariable $exception is displayed in the Locals window for an uncaught exception. Expand $exception to see more information.
- Local fields are not initialized until you step past the PROC statement.
- If the debugger is running slowly, it may be that too many variables are displayed in the Locals window (rather than being hidden in collapsed nodes of the Locals window). The debugger continually evaluates displayed variables, but doesn’t evaluate variables in collapsed nodes. For example, if the Globals node is expanded, the debugger may need to evaluate scores of variables, especially if there are a large number of unnamed common variables. (These are displayed directly under the Globals node rather than in subnodes, which could be collapsed.) To prevent this, keep Locals window nodes collapsed, and instead use the Watch or QuickWatch window or hover over a variable to get information about it.
- C# literal suffixes are supported. If you click on a local variable in the Locals window and its value is one of the following types, you’ll see the suffix (e.g., .m). Do not remove these suffixes.
Literal
Suffix
float
.f
double
.d
decimal
.m
unsigned
.u
long
.l
Determining whether a system option is set
To determine whether a system option is set for a Synergy .NET program, enter the following in the Visual Studio Immediate window, where OPTION is the number of the system option (see System Options):
Synergex.SynergyDE.SysRoutines.f_option(Synergex.SynergyDE.IntegerDesc.CreateLiteral(OPTION), Synergex.SynergyDE.VariantDesc.notPassedInteger);*
If the option is set, this returns 1. If the option is not set, it returns 0.
Examining the contents of a dynamic memory handle
1. | Open the Locals window, expand the dm variable, locate the name of the dynamic memory handle you want to inspect, and note the integer value assigned to it. |
2. | In the Immediate window, run the following command, where ## is the number assigned to the dynamic memory handle: |
Synergex.SynergyDE.SysRoutines.hatml(##).ToString()
The value for the dynamic memory handle will be displayed below the command you entered in the Immediate window.
If your dynamic memory handle contains null values, you will need to write the results to a file. For example:
System.IO.File.WriteAllText("filename", Synergex.SynergyDE.SysRoutines.hatml(##).ToString())
where filename is the path and name of the file you want to create and ## is the number assigned to the dynamic memory handle.
Synergy types in the Visual Studio Memory Usage Tool
The Visual Studio Memory Usage Tool displays information on Synergy types, but it uses different names for these types:
- A Synergy alpha, integer, decimal, or implied decimal will have a name with one of the following formats (where Type is Alpha, Integer, Decimal, or ImpliedDecimal):
Synergex.SynergyDE.TypeDesc
Synergex.SynergyDE.PinTypeDesc
Synergex.SynergyDE.RefTypeDesc
For example, a decimal will appear as either Synergex.SynergyDE.DecimalDesc, Synergex.SynergyDE.PinDecimalDesc, or Synergex.SynergyDE.RefDecimalDesc.
- Structures appear as GenBox<string1,string2> or GenCouple<string1,string2>, where string1 and string2 are strings that include the structure name. For example, the following is for a boxed structure named struct1 in namespace ns1:
Synergex.SynergyDE.Gen_Box<ns1.$$_struct1, ns1.$$_wrap_struct1>
The next example is for a boxed structure named struct2 in namespace ns1:
Synergex.SynergyDE.Gen_Couple<ns1.$$_struct2, ns1.$$_wrap_struct2>
Watch and Quickwatch windows
Synergy DBL Integration includes several Synergy-specific commands for use in the Visual Studio Watch and QuickWatch windows:
Command |
Information displayed |
---|---|
^ADDR(expression) |
The address for a descriptor type variable specified by expression. This works only for .NET types in Synergy records and i, d, and a Synergy types. It is used in a procedure that determines what changed a variable. See SET WATCH functionality in Visual Studio below. |
Show channels |
Channel information |
Show handles |
Handle information |
Note the following:
- If you set conditional breakpoints in a Watch window, only the == operator and value comparisons (not reference comparisons) are supported.
- If a path to a static type contains a mix of imported and specified (manually entered) namespaces, it may not be evaluated in the Watch and QuickWatch windows. For example, it may not be evaluated if a namespace is defined in a file because the namespace is assumed to be imported for the entire file.
- If you use the unsupported Synergy.SynergyDE.tklib.dll library, you will not be able to directly examine UI Toolkit globals such as g_select in the debugger. Instead, find the property name for the .define in tools.def, and enter that property name (case-sensitive) in the Watch or QuickWatch window. For example, the property name entry in tools.def for g_select is
.define g_select Synergex.SynergyDE.UIToolkit.GlobalState.t_selection
So, to examine g_select you would enter the following in the Watch or QuickWatch window: Synergex.SynergyDE.UIToolkit.GlobalState.t_selection.
For information on the Synergy.SynergyDE.tklib.dll library, see Using UI Toolkit with the .NET Framework.
SET WATCH functionality in Visual Studio
In traditional Synergy, if a variable’s value changes, you can find out what changed it by using WATCH variable or SET WATCH variable. But for Synergy .NET, this functionality is not available as the default in the Visual Studio debugger. Instead you must use the SOS debugging extension (which is installed with Visual Studio) as outlined in the following procedure. The SOS command !CLRStack dumps current stack traces for all threads at the point the variable changes. By examining these stack traces, you can find out which routine is writing to the variable.
1. | Open your Synergy .NET project in Visual Studio, and locate or create a programmatic break or pause in execution (e.g., a point where the program waits for user input) that happens before the variable’s value is changed. You’ll need a programmatic break or pause to give you time to select Break All in step 7. |
2. | Set a Visual Studio breakpoint at or before the programmatic break. |
3. | Start debugging the program (Debug > Start Debugging). |
^ADDR(my_alpha)
Note (or copy) the address in the Value column of the Watch or QuickWatch window. You will use the hexadecimal form of this address to set a data breakpoint in step 8. (If a decimal value is displayed in the Value column, you can right-click on the value and select Hexadecimal Display from the context menu.)
5. | Detach from the process (Debug > Detach All). |
6. | Reattach to the process in native mode: Select Debug > Attach to Process, select the process for your program in the Available Processes area of the Attach to Process window, and then click Select. In the Select Code Type window, select Native and clear all other options. Then click OK to close the Select Code Type window, and click Attach in the Attach to Process window. |
Detaching and reattaching is necessary because the debugger runs in managed mode by default but must run in native mode for the data breakpoint set in step 8 to work correctly.
8. | Set a data breakpoint (Debug > New Breakpoint > New Data Breakpoint) using the hexadecimal address you got in step 4. For example, if the hexadecimal address from that step is 0x0288F0C0, enter this in the Address field of the New Breakpoint window. |
|
9. | When the breakpoint is set, continue program execution (Debug > Continue). |
10. | When the debugger breaks at the breakpoint, you’ll see the message “The following breakpoint was hit....” Click OK in this message box. |
11. | Select Debug > Save Dump As, and then use the Save Dump As window to save a .dmp file. |
12. | Stop the debugging session (Debug > Stop Debugging). |
13. | In Visual Studio, select File > Open > File, and then in the Open File window, select the file you saved in the previous step and click Open. |
14. | A window opens with the title of the .dmp file. In the Actions section of this window, select Debug with Mixed. |
15. | Enter the following in the Immediate Window: |
.load sos
This loads the SOS debugger extension.
16. | Enter the following case-sensitive SOS command in the Immediate Window: |
!CLRStack
This writes the stack traces to the Immediate Window.
Note that the program’s debugging session is over, so you won’t be able to continue debugging it.