Skip to content

excessive memory usage in IntelliSense with MSVC non-conforming preprocessor (OpenUSD/Pixar) #13205

Closed

Description

Environment

  • OS and Version: Windows 11 10.0.26100 x64
  • VS Code Version: 1.96.4 commit cd4ee3b1c348a
  • C/C++ Extension Version: 1.22.11 (ms-vscode.cpptools); 1.3.0 (ms-vscode.cpptools-extension-pack)
  • If using SSH remote, specify OS of remote machine:

Bug Summary and Steps to Reproduce

Bug Summary:
On a very large C++ codebase, cpptools-srv.exe will consume memory, starving out the rest of the system:

Image

I have C_Cpp: Max Memory set, but it does not appear to be used:

Image

Steps to reproduce:

  1. Open a very large C++ directory, potentially multiple in multiple instances of VSCode
  2. Wait
  3. Observe cpptools-srv.exe processes consuming significant amounts of memory

Expected behavior:

  1. Max memory limit is enforced

Configuration and Logs

{
    "configurations": [
        {
            "name": "Win32",
            "intelliSenseMode": "msvc-x64",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "compileCommands": "${workspaceFolder}/_build/windows-x86_64/release/compile_commands-full.json",
            "includePath": [
                "${default}",
                "${workspaceFolder}/include"
            ]
        },
    ],
    "version": 4
}



-------- Diagnostics - 1/27/2025, 5:55:42 PM
Version: 1.22.11
Current Configuration:
{
    "name": "Win32",
    "includePath": [
        "c:/src/<redacted>/**"
    ],
    "defines": [
        "_DEBUG",
        "UNICODE",
        "_UNICODE"
    ],
    "windowsSdkVersion": "10.0.22621.0",
    "compilerPath": "cl.exe",
    "cStandard": "c17",
    "cppStandard": "c++17",
    "intelliSenseMode": "windows-msvc-x64",
    "compilerPathInCppPropertiesJson": "cl.exe",
    "intelliSenseModeIsExplicit": false,
    "cStandardIsExplicit": false,
    "cppStandardIsExplicit": false,
    "mergeConfigurations": false,
    "compilerPathIsExplicit": false,
    "browse": {
        "path": [
            "c:/src/kit/**",
            "${workspaceFolder}"
        ],
        "limitSymbolsToIncludedHeaders": true
    }
}
Modified Settings:
{
    "C_Cpp.maxMemory": 4000
}
Additional Tracked Settings:
{
    "editorTabSize": 4,
    "editorInsertSpaces": true,
    "editorAutoClosingBrackets": "languageDefined",
    "filesEncoding": "utf8",
    "filesAssociations": {},
    "filesExclude": {
        "**/.git": true,
        "**/.svn": true,
        "**/.hg": true,
        "**/CVS": true,
        "**/.DS_Store": true,
        "**/Thumbs.db": true
    },
    "filesAutoSaveAfterDelay": false,
    "editorInlayHintsEnabled": true,
    "editorParameterHintsEnabled": true,
    "searchExclude": {
        "**/node_modules": true,
        "**/bower_components": true,
        "**/*.code-search": true
    },
    "workbenchSettingsEditor": "ui"
}
cpptools version (native): 1.22.11.0
Current database path: C:\USERS\REDACTED\APPDATA\LOCAL\MICROSOFT\VSCODE-CPPTOOLS\D04989D4087A51DA9667FC71DEFAB4CC\.BROWSE.VC.DB
No active translation units.

------- Workspace parsing diagnostics -------
Number of files discovered (not excluded): 155248
Number of files parsed: 30183

Other Extensions

No response

Additional context

No response

Activity

  1. sean-mcmanus commented on Jan 28, 2025

    @sean-mcmanus
    Contributor

    The settings are behaving as expected -- they don't limit the memory usage of a single cpptools-srv process.

    When you run the C/C++: Log Diagnostics command after opening a source file -- what memory usage is reported per TU? Over 15 GB? If so, that seems like it might be a bug. Your logging indicates no active files have been started.

    The cpptools-srv process only has memory for the current TU (source file and headers), so having a "large code base" should not make a difference.

  2. sean-mcmanus commented on Jan 28, 2025

    @sean-mcmanus
    Contributor

    Josh Kriegshauser (@jkriegshauser) Also, your repro steps don't indicate that you have opened any files? No cpptools-srv process should launch if you don't open any files.

  3. jkriegshauser commented on Jan 28, 2025

    @jkriegshauser
    Author

    Oh, I didn't realize it was per TU. I opened one VSCode instance with two source windows open. I have several source files open in the left source window and only one in the right source window. Active, the left source file is a .cpp file with 10,978 lines and in the right, a .cpp file with 1,028 lines.

    I have 3 cpptools-srv.exe processes running each consuming nearly 15 GB of RAM each:

    Image

    Though every time I do C/C++: Log Diagnostics it says No active translation units. I even bound it to a key and execute the key with the larger source file active and get the same message:

    -------- Diagnostics - 1/28/2025, 8:41:03 AM
    Version: 1.22.11
    Current Configuration:
    {
        "name": "Win32",
        "includePath": [
            "c:/src/kit/**"
        ],
        "defines": [
            "_DEBUG",
            "UNICODE",
            "_UNICODE"
        ],
        "windowsSdkVersion": "10.0.22621.0",
        "compilerPath": "cl.exe",
        "cStandard": "c17",
        "cppStandard": "c++17",
        "intelliSenseMode": "windows-msvc-x64",
        "compilerPathInCppPropertiesJson": "cl.exe",
        "intelliSenseModeIsExplicit": false,
        "cStandardIsExplicit": false,
        "cppStandardIsExplicit": false,
        "mergeConfigurations": false,
        "compilerPathIsExplicit": false,
        "browse": {
            "path": [
                "c:/src/redacted/**",
                "${workspaceFolder}"
            ],
            "limitSymbolsToIncludedHeaders": true
        }
    }
    Modified Settings:
    {
        "C_Cpp.maxMemory": 4000
    }
    Additional Tracked Settings:
    {
        "editorTabSize": 4,
        "editorInsertSpaces": true,
        "editorAutoClosingBrackets": "languageDefined",
        "filesEncoding": "utf8",
        "filesAssociations": {},
        "filesExclude": {
            "**/.git": true,
            "**/.svn": true,
            "**/.hg": true,
            "**/CVS": true,
            "**/.DS_Store": true,
            "**/Thumbs.db": true
        },
        "filesAutoSaveAfterDelay": false,
        "editorInlayHintsEnabled": true,
        "editorParameterHintsEnabled": true,
        "searchExclude": {
            "**/node_modules": true,
            "**/bower_components": true,
            "**/*.code-search": true
        },
        "workbenchSettingsEditor": "ui"
    }
    cpptools version (native): 1.22.11.0
    Current database path: C:\USERS\REDACTED\APPDATA\LOCAL\MICROSOFT\VSCODE-CPPTOOLS\D04989D4087A51DA9667FC71DEFAB4CC\.BROWSE.VC.DB
    No active translation units.
    
    ------- Workspace parsing diagnostics -------
    Number of files discovered (not excluded): 157125
    Number of files parsed: 2
    
  4. sean-mcmanus commented on Jan 29, 2025

    @sean-mcmanus
    Contributor

    Josh Kriegshauser (@jkriegshauser) Those might be dangling processes. Can you kill them? Does your C/C++ logging with C_Cpp.loggingLevel set to "Debug" indicate the IntelliSense process is shutting down or crashing?

  5. jkriegshauser commented on Jan 29, 2025

    @jkriegshauser
    Author

    I had 13 cpptools-srv processes running, the most using ~26 GB RAM. I killed them all with taskkill and 5 restarted. I see some debug logging happening now after opening it. When I killed the processes I saw this appear in the logs:

    IntelliSense process crash detected: handle_initialize
    IntelliSense process crash detected: handle_initialize
    IntelliSense process crash detected: handle_initialize
    loggingLevel has changed to: Debug
    IntelliSense process crash detected: handle_update_intellisense
    IntelliSense process crash detected: handle_update_intellisense
    IntelliSense process crash detected: handle_update_intellisense
    IntelliSense process crash detected: handle_initialize
    Update IntelliSense time (sec): 3776.863
    IntelliSense process crash detected: handle_contains
    IntelliSense process crash detected: handle_contains
    IntelliSense process crash detected: handle_contains
    IntelliSense process crash detected: handle_contains
    IntelliSense process crash detected: handle_contains
    
  6. sean-mcmanus commented on Jan 29, 2025

    @sean-mcmanus
    Contributor

    Josh Kriegshauser (@jkriegshauser) Are the crash logging from your killing the processes or from the processes actually crashing? If it's an actual crash you could potentially get a crash call stack via attaching a debugger before it crashes (https://github.com/microsoft/vscode-cpptools/wiki/Attaching-debugger-to-cpptools-or-cpptools%E2%80%90srv). It sounds like our IntelliSense parser may be hitting some bug while processing your TU. If it's using CPU while memory is increasing, attaching a debugger and hitting a breakpoint might show what processing is causing the issue if it's stuck in some loop that is allocating memory.

  7. jkriegshauser commented on Jan 29, 2025

    @jkriegshauser
    Author

    I think those were from me killing the process. I have some huge memory processes:

    Image

    I used procdump to get mini-dumps of them and the biggest ones have threads doing this:

    	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!expand_top_level_pcc_macro()	Unknown
     	cpptools-srv.exe!macro_invocation(struct a_symbol *,int *)	Unknown
     	cpptools-srv.exe!get_token(void)	Unknown
     	cpptools-srv.exe!scan_nonmember_declaration(struct a_decl_parse_state *,struct a_source_range *)	Unknown
     	cpptools-srv.exe!declaration(int,int,int,int,struct a_param_id *,struct a_source_range *,struct a_decl_parse_state *)	Unknown
     	cpptools-srv.exe!process_translation_unit(char const *,int,struct an_exported_template_file *)	Unknown
     	cpptools-srv.exe!cfe_main(int,char * * const)	Unknown
     	cpptools-srv.exe!cfe_main_exception_handler(int,char * * const)	Unknown
     	cpptools-srv.exe!invoke_edge_compiler()	Unknown
     	cpptools-srv.exe!edge_compiler_main(int,char const * * const)	Unknown
     	cpptools-srv.exe!preparse(int,char const * *,class a_scout_store *,class edge::translation_unit *)	Unknown
     	cpptools-srv.exe!a_compiler_thread::compiler_thread_routine(class a_compiler_thread *)	Unknown
     	cpptools-srv.exe!msvc::thread_helper_t::thread_entry(void *)	Unknown
     	cpptools-srv.exe!thread_start<unsigned int (__cdecl*)(void *),1>()	Unknown
     	kernel32.dll!BaseThreadInitThunk()	Unknown
     	ntdll.dll!RtlUserThreadStart()	Unknown
    

    Unfortunately the symbols are minimal so I don't see any local variables or anything. But it definitely looks like there's some recursion happening. Would be nice to know what macro is kicking it off

  8. sean-mcmanus commented on Jan 29, 2025

    @sean-mcmanus
    Contributor

    Josh Kriegshauser (@jkriegshauser) I've sent an email to the team involved. Are you able to reduce the TU to locate the macro that is triggering the excessive/infinite recursion?

  9. jkriegshauser commented on Jan 29, 2025

    @jkriegshauser
    Author

    Sean McManus (@sean-mcmanus) not easily. I'm not even sure which file it's parsing. I looked at strings in the minidump that I gathered and didn't see any .obj or .cpp files, but several headers. Having PDB files with local variables might help. This tool isn't open-source I take it?

  10. bobbrow commented on Feb 25, 2025

    @bobbrow
    Contributor

    No, the tool is not open source and we don't release binaries with full symbols. I looked at the stack and the place in the parser where I see a potential for recursion appears to be related to clang's __is_identifier and __has_warning macros though it could still be something else. It's possible a search through your workspace folder may turn up some usage of these macros that is not handled correctly by the parser which results in this infinite or extremely inefficient recursion. Our enforcement of process memory usage only happens after each IntelliSense operation completes and it appears that this one never completes to give us the opportunity to trim it.

    To reproduce this, I would recommend only opening one cpp file at a time and watching Task Manager for a runaway process. The stack indicates that the recursion is happening during process initialization (the first parse through the code), so you shouldn't need to interact with the file, just watch for runaway memory consumption. Once you have identified a translation unit that reproduces this, you can delete content from it until it gets into a state where it does not eat up an unreasonable amount of memory (e.g. the 266MB process in your screenshot). When it stops reproducing, you'll want to revert your last edit because the code you just deleted appears to be important for the repro. You will need to terminate the runaway process after each edit so that the extension can try parsing it again. When you terminate a runaway process, you will need to interact with the file at least once (hover, type, etc) so that the extension can detect that the process "crashed" and can respawn it.

    Some strategies for edits that work when doing this include:

    • delete all/half the code in the current file except the #includes
    • delete half of the remaining #includes
    • start deleting from the bottom of the file so that you don't have to think as much about missing dependencies adding parsing errors that might change the repro.

    Repeat these steps until you have something small or safe enough to share with us (e.g. no proprietary code) and we can take over from there.

  11. jkriegshauser commented on Feb 25, 2025

    @jkriegshauser
    Author

    Thanks Bob Brown (@bobbrow), I'll see if I can carve out some time to do so. I have a sneaking suspicion that it has something to do with OpenUSD (we're using Tag v24.05) include files, since when I wrap those includes with #ifndef __INTELLISENSE__ the problem corrects itself, though I don't get any intellisense on USD includes (of course, with intellisense not completing I get nothing).

    I opened a .cpp file that includes at least the following OpenUSD includes:

    #include <pxr/base/arch/export.h>
    #include "pxr/base/tf/staticTokens.h"
    #include "pxr/pxr.h"
    #include "pxr/base/tf/debug.h"
    #include "pxr/base/vt/value.h"
    #include "pxr/usd/ndr/declare.h"
    #include "pxr/usd/sdf/types.h"
    #include "pxr/usd/sdf/valueTypeName.h"
    #include "pxr/usd/sdr/shaderProperty.h"
    #include "pxr/usd/ndr/declare.h"
    #include "pxr/usd/ndr/nodeDiscoveryResult.h"
    #include "pxr/usd/usd/prim.h"
    #include "pxr/usd/sdf/assetPath.h"
    #include "pxr/base/tf/singleton.h"
    #include "pxr/base/tf/instantiateSingleton.h"
    #include "pxr/base/tf/stringUtils.h"
    #include "pxr/usd/ar/resolver.h"
    #include "pxr/usd/sdr/registry.h"
    #include "pxr/usd/usdShade/nodeDefAPI.h"
    

    and it's up to 4.5GB so far with a peak of 8.8GB with no intellisense help to support thus far

  12. bobbrow commented on Mar 5, 2025

    @bobbrow
    Contributor

    Our IntelliSense team was talking about an issue with these Pixar headers in Visual Studio today and I learned that there is also a Developer Community ticket or two covering this issue. They haven't reduced it down to a specific macro or macros yet, but my understanding is that there are some particularly deep macro expansions going on in this codebase (which matches the stack you sent earlier). I had thought that maybe this was a case of an infinite loop, but it appears to be worse than that unfortunately.

    I suspected pxr/base/tf/preprocessorUtilsLite.h given some of the deep recursion in those definitions, but opening preprocessorUtilsLite.cpp doesn't suffer from this issue. I'll keep trying to help them find a root cause as I find time.

  13. jkriegshauser commented on Mar 5, 2025

    @jkriegshauser
    Author

    Bob Brown (@bobbrow) thanks for the update. I haven't had a chance to try to whittle it down either given that it's a very time-consuming process 😞

  14. bobbrow commented on Mar 5, 2025

    @bobbrow
    Contributor

    @bobbrow thanks for the update. I haven't had a chance to try to whittle it down either given that it's a very time-consuming process 😞

    I understand. If you do have time, we'll accept the help. But I also think we might have enough to go on at this point. I can reproduce a significant (but temporary) memory increase just pasting that list of headers into an empty source file and that might be enough.

  15. bobbrow commented on Mar 7, 2025

    @bobbrow
    Contributor

    Hi Josh Kriegshauser (@jkriegshauser),

    I have some good news to share. We discovered that the Pixar headers support both MSVC's conforming and non-conforming preprocessors. When you configure your project and IntelliSense to use the newer standards conforming preprocessor, these headers do not require the deep recursion and memory usage in the IntelliSense process.

    Can you try adding this into your configurations in c_cpp_properties.json and let me know if that addresses the issue?

                "compilerArgs": [
                    "/Zc:preprocessor"
                ],
    

    This will instruct IntelliSense to emulate MSVC's standards conforming preprocessor which will take the faster path to compile the sources.

    EDIT: for reference, this is the path we want to trigger: https://github.com/PixarAnimationStudios/OpenUSD/blob/1ff329aac66e0cab154cb9217f5d70c12e21f712/pxr/base/arch/defines.h#L93-L103

  16. jkriegshauser commented on Mar 7, 2025

    @jkriegshauser
    Author

    Awesome! That definitely seems to help!

    I can actually use Intellisense in files that include Pixar headers now! 🎉

  17. changed the title [-]cpptools-srv using too much memory[/-] [+]excessive memory usage in IntelliSense with MSVC non-conforming preprocessor (OpenUSD/Pixar)[/+] on Mar 7, 2025
  18. bobbrow commented on Mar 7, 2025

    @bobbrow
    Contributor

    That's great to hear. I'm going to close this issue as mitigated and write a Q&A topic for it. The issue with the non-conforming preprocessor mode will be tracked by the Visual Studio C++ team.

  19. nv-scristiano commented on Mar 18, 2025

    @nv-scristiano

    the offending macro is at include\pxr\base\tf\staticTokens.h

    #define TF_DECLARE_PUBLIC_TOKENS(...) _TF_DECLARE_PUBLIC_TOKENS_EXPAND( _TF_DECLARE_PUBLIC_TOKENS_EVAL(_TF_DECLARE_PUBLIC_TOKENS_EXPAND( TF_PP_VARIADIC_SIZE(__VA_ARGS__) ))(__VA_ARGS__) )

    Another temporary workaround for the issue is changing it to :

    #ifdef __INTELLISENSE__
    #define TF_DECLARE_PUBLIC_TOKENS(...)
    #else
    #define TF_DECLARE_PUBLIC_TOKENS(...) _TF_DECLARE_PUBLIC_TOKENS_EXPAND( _TF_DECLARE_PUBLIC_TOKENS_EVAL(_TF_DECLARE_PUBLIC_TOKENS_EXPAND( TF_PP_VARIADIC_SIZE(__VA_ARGS__) ))(__VA_ARGS__) )
    #endif
  20. locked and limited conversation to collaborators on Apr 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions