Debugging C++Builder 64-Bit Windows Applications

From RAD Studio
Jump to: navigation, search

Go Up to C++Builder 64-bit Windows Application Development


C++ LLDB-based Debugger for WIN64

RAD Studio 10.4 introduces a new debugger for C++ for Win64, based on LLDB. The compiler uses the DWARF version 4 format for debug information.

The new RAD Studio LLDB-based debugger offers improved stability when debugging, and via both a new debugger technology and the debug information generated provides improved evaluation, inspection, and other debugger features. Additionally, the debugger has support for evaluating complex types such as STL collections or strings via formatters.

Formatters

A common issue when debugging C++ applications is inspecting or evaluating complex types. Evaluating the contents of a std::map (for example) requires knowledge of the layout of the type, and the array access operator [] may not be available to the debugger if it is never called in code or if it is always inlined and so not callable. Similar problems exist for other types including strings, and possibly even your own types.

This is solved through the use of formatters. A formatter is a small Python script that assists in debugging a specific type. RAD Studio ships with formatters for common types, and you can add your own for your own types if needed.

The following formatters are provided for common STL and Delphi types.

* std::string and std::wstring 
* String (UnicodeString), AnsiString, UTF8String, WideString. 
* std::vector 
* std::deque 
* std::stack 
* std::map 
* std::shared_ptr

Custom formatters

To add your own formatter, create a new python file and edit the bin\Windows\lldb\.lldbinit file to contain a line referring to your new Python script. You can find more information about writing your own formatters here:

C++Builder 64-Bit Windows

C++Builder 64-bit Windows compiler (BCC64) generates debug information in DWARF format, which is different from the format used by BCC32 and BCCOSX. However, in general, debugging C++ 64-bit Windows applications is similar to debugging C++ 32-bit Windows applications. There are some differences, as described here:

  • Some debugger features are not supported. Debugging of properties, closures, class methods and other Delphi language extensions is not currently supported.
    For example, on the Debug Inspector, the Methods and Properties tabs are not displayed for C++ 64-bit Windows applications.
  • Unicode, code pages, and localization are not fully supported.
    For example, Unicode is not supported in identifier names, and code pages are not supported by the C++ 64-bit Windows debugger.
  • When evaluating a 64-bit Windows register, the register name must be prefixed with $, such as $rax.
    See Evaluate/Modify.
  • Function calls that throw exceptions are handled as follows:
    • If a function contains a try/except/catch block, and a C++ or OS/SEH exception is raised during execution, the function call finishes correctly, but the result is undefined or 0. In this case, the internal exception block is not executed because the exception is handled directly by the debugger.
    • If a function contains a try/except/catch block, and no language or OS/SEH exceptions are raised, the function call finishes fine and the result is correct, depending on the function.
Note: RAD Studio allows you to debug BCC64X Apps as any other BCC64 or BCC32 Apps.

Call Stack Differences

Some values might be displayed differently in the 64-bit evaluator than in the 32-bit evaluator. For example, the call stack is displayed without function parameters and values.

The call stack typically contains two copies of each constructor and destructor. For example, the call stack might contain:

 :0000000000401244 ; MyClass::~MyClass
 :0000000000401229 ; MyClass::~MyClass
 :0000000000401187 ; main
 :000000000040ef90 ; _startup
Note: Clang implements the Itanium ABI, which describes three constructors and three destructors that chain call each other. However, Clang implemented only two of the three constructors, and C++Builder added the third kind for Delphi-style classes. See the Itanium ABI doc or this post: http://stackoverflow.com/questions/6921295/dual-emission-of-constructor-symbols.

Reducing Linker Memory Usage with Split DWARF

Note: The Split DWARF feature is supported in C++ Windows 64-bit only. This feature reduces the amount of data to be processed and therefore reduces linker errors in the linker. For more information on how to handle linker errors, see Handling Linker Out of Memory Errors.

RAD Studio 10.4.2 introduces a new feature in order to help reduce the amount of data the linker needs to process, especially when linking applications built in debug mode. This feature is known as Split DWARF and splits the debug information to a separate .dwo file (DWARF object) sitting parallel to the normal object file containing compiled code. The linker then only links executable code and small amounts of other information, thus reducing memory strain.

Note: The Split Dwarf feature is off by default.

Enabling Split DWARF in the IDE or msbuild

Open your C++ project options dialog. Navigate to Building > C++ Compiler > Debugger and ensure the Target platform is set to one of the Windows 64-bit targets.

  1. Enable the ‘Use Split DWARF’ checkbox.
  2. Specify a folder for the debug information files in the 'DWO output directory' setting. This must be an absolute path, not a relative path or a path using environment variables.

For example, c:\myproject\win64debug is ok, but ..\win64debug is not.

To disable this feature, deselect the checkbox in Project Options > Building > C++ Compiler > Debugging. These settings will be used when building from within the IDE, or building with msbuild on the command line.

Enabling Split DWARF on the Command Line

To manually enable Split DWARF on a pure command-line build, such as one using BCC64:

1. Specify the Split DWARF setting on the compiler’s command line.

"-enable-split-dwarf"     
AND     
"-split-dwarf-file AFilename.dwo"     

By doing this the compiler creates the AFilename.dwo file and emits the location of that .dwo file into the .o object file. Note that this step does not yet mean that the .dwo file contains the debug information.

2. Use the "-dwo-dir <directory>" to specify the directory where the compiler writes out the .dwo file. Ensure this is an absolute path.

3. Run the objcopy.exe tool on the .o file. This action removes all the dwarf sections from the normal object (.o) file and places them in a separate .dwo file. This needs to be done for each object (.o file) and needs to match the name you specify in step 1.

objcopy --split-dwo=AFilename.dwo AFilename.o 

4. Finally, link the .o files in order to create your EXE or DLL as you normally would - this step is not modified. The files will be smaller than normal as there is less debug information in them, each object contains the name and the location of the .dwo. As described in step 2, this is the location for your specific machine and must be a specific absolute path.

Issues Loading Debug Information When Using Split DWARF

When using Split-Dwarf for Win64 debugging, source files that are not in the same directory as the project file may get incorrect directory information generated into the object file. This will mean that symbols from those files (types, local variables, and parameters) will not be available. You can recognize this when debugging by seeing that the blue dots in the editor indicating line numbers are visible, and you will be able to place a breakpoint, but you will not be able to evaluate or inspect expressions or symbols.

To work around this, specify an absolute path in the 'DWO output directory' field of the C++ Compiler > Debugging > Use Split Dwarf project option, or the -dwo-dir command-line option if building on the command line. See above for information on these settings.

Evaluating Function Calls Such as strcmp()

Evaluation for a function call such as strcmp(str, "ABC") could return an error as follows:

#include <system.hpp>
int main()
{ 
    char *str = "ABC";
    return strcmp(str, "ABC"); 
}

error: 'strcmp' has unknown return type; cast the call to its declared return type
error: 1 errors parsing expression

In the Evaluate/Modify window, you need to cast the return type for strcmp():

   (int) strcmp(str, "ABC"); 

See strcmp, _mbscmp, wcscmp.

Note: If you encounter issues debugging, our Support team may ask you to provide debugger logs. Read how to enable logging for RAD Studio debuggers.

See Also