natstepfilter
.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.
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.