Dotnet logo

.NET Tools

Essential productivity kit for .NET and game developers

.NET Tools How-To's

Unreal Debugging Improvements in Rider 2024.2

Rider 2024.2 includes massive improvements to the native debugger, significantly improving the experience of evaluating native code while debugging. There are performance improvements, better handling of optimised code, and support for evaluating operators on smart pointers and string types. The changes will make a big difference to your day-to-day debugging of Unreal games with JetBrains Rider. Let’s take a look at some of the details.

View Blueprints in callstack

It can sometimes be tricky to know exactly where your code is being called from, especially if the callstack just shows anonymous memory addresses. Fortunately, Rider can help here, by resolving those memory addresses to Blueprint frames, and showing you which Blueprint your code is being called from. This feature first appeared in 2024.1, but has had a few updates in the new release. It’s really helpful when Rider can tell you how your code is being used!

You’ll notice in the screenshot that there’s another option in the filter drop-down menu. The Show C++ Frames from Unreal Engine option allows you to show or hide the frames from the engine, so you can ignore some of the massive call stacks from the engine, and concentrate on your code.

Evaluation improvements

The most significant updates in Rider 2024.2 are the improvements to evaluation in the debugger. We’ve addressed a large number of the most pressing issues with native code evaluation, and these changes should have a dramatic effect on your quality of life when using Rider’s debugger. Let’s dive in!

Firstly, Rider will now correctly evaluate operators on STL and Unreal smart pointers, collection containers, and strings. In other words, given the Unreal smart pointer TSharedPtr or TObjectPtr, it’s now possible to use the arrow operator -> to access members of the underlying pointer, or to dereference the object with the * operator. You can also use the !, != and == operators, just as you’d expect.

You can also use the [] operator to access elements of array-like containers, such as TArray.

Rider now supports the == and != operators to compare string literals against string types such as std::string, std::wstring, and FString.

Furthermore, all of these operators can be used not just in evaluation and watches, but as conditions in a conditional breakpoint.

Another huge quality of life improvement is that the Add to Watches action now works much more reliably, and in many more scenarios. When looking at an object in the debugger, you’re typically looking at a custom, user-friendly view of the object provided by the Natvis framework. For example, instead of showing the internal values of an STL or Unreal container, the debugger can show the items inside the container, showing the data you’d expect. This custom view means it is very complex to add a custom watch to one of these synthetic object members, but Rider 2024.2 now generates the correct expression when right-clicking to add a watch.

This release also improves evaluation for optimised code. The compiler can optimise away huge chunks of data, which can make it harder to understand the current context while debugging. But the compiler doesn’t always optimise away entire objects – only data that isn’t being used. Rider can now show objects with partial data, providing much more context to help you debug. And when data or objects aren’t available, the sometimes cryptic internal error messages have been replaced with more friendly text that explains why all or part of an object can’t be shown.

Modules view

Rider 2024.2 brings support for the Modules view to the native debugger. This feature was first introduced in Rider 2023.3 for .NET, and it now works with your Unreal games, too. The Modules view is a new tab in the Debug tool window that shows a list of all the loaded .dll or .so files, along with details of the path, and if the module has loaded symbols.

You can right click on a module and copy the module’s name or path, or navigate to the module location on disk.

Data breakpoints

We’ve also made significant improvements to data breakpoints in this release, making them much more responsive and reliable. They now work in many more scenarios than before, including on items generated from Natvis files, and will show a useful error message if there are problems. If you’re not already familiar with data breakpoints, check out the documentation. They are a great feature that will suspend execution when a value changes, such as an integer or pointer, which can be a very powerful way to debug complex behaviour.

Roadmap

The paint on Rider 2024.2 is barely dry, and yes, we’re already talking about the roadmap! We’ve got lots more planned over the next few releases for the native debugger and Unreal debugging in general. Specifically, we’re looking at more performance optimisations, better support for detaching from processes, and even more work on improving code evaluation.

We also want to bring stepping time visualisation to the native debugger, like Rider already does for .NET. Rider will show an inline hint while debugging, letting you know how long it takes to execute your code between two breakpoints, or when stepping over single lines of code. This can help you spot when a section of code is taking longer than expected, and might indicate a performance issue that you want to investigate further.

Another feature we want to bring from the .NET debugger is the collections visualiser, which provides a rich interface for inspecting, sorting, and filtering collections of objects.

And finally, we’re making great progress on debugging support for games consoles, and look forward to sharing that in future releases.

There are lots of great improvements to the native debugger in Rider 2024.2, we encourage you to upgrade and try it for yourself on your own projects.

For more information on game development in Rider, check out the recording of this livestream on how Rider’s AI Assistant can help with understanding a codebase, dealing with boilerplate, and learning APIs and frameworks.

image description