The C++ logo, by Jeremy Kratz, licensed under CC0 1.0 Universal Public Domain Dedication

natstepfilter

Notes published the
3 - 4 minutes to read, 719 words

.natstepfilter files are one possible way to customize code stepping with the Visual Studio debugger.

One common way to use them is to "register" functions from external libraries, like the standard library, to avoid stepping into those functions. The official documentation 🗄️ shows that there are other possibilities.

Unfortunately, compared to natvis files, adding those files to our project is not sufficient.

One has to copy them either in the %VsInstallDirectory%\Common7\Packages\Debugger\Visualizers folder or in %USERPROFILE%\My Documents\<Visual Studio version>\Visualizers folder.

This is a big limitation in usability, there is already at least one open bug, and as one user reports

Asking user to copy is clunky for variety of reasons (not easy to find the folder, system folder needs admin rights, user folder may not exist already therefore increasing confusion, various versions of VS may be installed, unclear if needs to be copied to toolset or IDE version) but the biggest issue is indeed that the manual copy means it is unlikely to be updated correctly.

— developercommunity.visualstudio.com

As I thought about a good use-case for add_custom_target in CMake, it occurred to me that "installing" those files in the correct directory might be a good one, until Visual Studio improves the support for those files.

A minimal example showcasing how to implement such a feature for setting up the development environment might be similar to the following:

function(getVisualizersFolder VS_GENERATOR)
        if    ("${VS_GENERATOR}" STREQUAL "Visual Studio 16 2021")
                set(VisualizersFolder "$ENV{USERPROFILE}/Documents/Visual Studio 2021/Visualizers/" PARENT_SCOPE)
        elseif("${VS_GENERATOR}" STREQUAL "Visual Studio 16 2019")
                set(VisualizersFolder "$ENV{USERPROFILE}/Documents/Visual Studio 2019/Visualizers/" PARENT_SCOPE)
        elseif("${VS_GENERATOR}" MATCHES  "Visual Studio 15 2017")
                set(VisualizersFolder "$ENV{USERPROFILE}/Documents/Visual Studio 2017/Visualizers/" PARENT_SCOPE)
        else()
                message(WARNING "VS_GENERATOR ${VS_GENERATOR} not supported, VisualizersFolder not defined")
        endif()
endfunction()



add_executable(foo
    main.cpp
    bar.cpp
    bar.natstepfilter
)


if (NOT "$ENV{USERPROFILE}" STREQUAL "")
        getVisualizersFolder(${CMAKE_GENERATOR})
        get_target_property(NATSTEPFILTER foo SOURCES)
        list(FILTER NATSTEPFILTER INCLUDE REGEX ".*\.natstepfilter")
        set(output_files)
        foreach( file ${NATSTEPFILTER} )
                get_filename_component( filename ${file} NAME )
                list(APPEND output_files "${VisualizersFolder}/${filename}")
        endforeach()
        add_custom_command(
                OUTPUT "${output_files}"
                COMMAND ${CMAKE_COMMAND} -E copy ${NATSTEPFILTER} "${VisualizersFolder}"
                DEPENDS ${NATSTEPFILTER}
                VERBATIM
                COMMENT "Copy natstepfilter of foo to Visualizers directory"
        )
        add_custom_target(dev_install_natstepfilters
                DEPENDS "${output_files}"
        )
endif()

and an example of natstepfilter file:

<?xml version="1.0" encoding="utf-8"?>
<StepFilter xmlns="http://schemas.microsoft.com/vstudio/debugger/natstepfilter/2010">
	<Function><Name>fek::string_views.*</Name><Action>NoStepInto</Action></Function>
</StepFilter>

Good candidates for functions that one normally does not want to step into are:

  • external libraries (as most of the time one is searching for bugs in their own codebase)

  • simple functions without side effects (as input and output are already able to cover all code paths)

  • trivial functions like one-liners

In all cases, there is the implicit assumption that one can rely on those functions to be correct, otherwise not being able to step into them makes it harder to discover bugs.

This is true for third-party libraries, as at the beginning we mostly assume that our code is faulty, and this is the reason why most of the time only trivial and battle-tested functions, which functionalities are easily covered by tests, should be exempted by the step-into functionality (and for exceptional situations it is possible to side-step the .natstepfilter file).

After creating the project map, one can use cmake --build --target dev_install_natstepfilters from the command line (or "build" dev_install_natstepfilters from Visual Studio) to update the installed files.

Note that this should work on all "modern" localized Windows versions, on older Windows versions it would be necessary to use localized directory names, which makes the task harder.

Of course, this approach has limitations, for example, if there are multiple files with the same name. But at least it provides a way for updating the settings without leaving the IDE, which might also be good enough for most use cases.


Do you want to share your opinion? Or is there an error, some parts that are not clear enough?

You can contact me anytime.