microsoft / microsoft/vscode-cpptools

GDB hangs on breakpoint, infinitely traversing pretty printers

Open
#14,487 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

debugger help wanted
Dominant language
TypeScript
Stars
6.2k
Forks
1.7k
Avg merge
14h 46m
Merged PRs (30d)
61

Description

Environment
  • OS and version: Linux Mint 22.3 (inside Distrobox)
  • VS Code: 1.114.0 (e7fb5e96c0730b9deb70b33781f98e2f35975036)
  • C/C++ extension: 1.32.2
  • GDB version: 15.1
Bug Summary and Steps to Reproduce

Bug Summary:
As far as I understand, IDE + GDB should not call "children" method of a pretty-printer unless the user has asked to expand the value in GUI. Moreover, calling "children" method recursively is unacceptable, because linked data structures can be expanded infinitely.
And this is exactly what I see happening in this case: GDB recursively traverses the data structure and hangs because it has loops.

Steps to reproduce:

  1. Clone this repository: https://github.com/stgatilov/vscode-gdb-pretty-printers-hangs-repro-code
  2. Open it in VS Code.
  3. Build Debug build.
  4. Put breakpoint at the only line of badfunc.
  5. Run debugging using the included configuration "Launch with pretty printers".
  6. When debugger stops at the breakpoint, GDB hangs trying to evaluate local variable entity.
Debugger Configurations
{
            "name": "(gdb) Launch with pretty printers",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/build/proj",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                },
                {
                    "description": "Initialize pretty printer",
                    "text": "source ${workspaceFolder}/printers.py"
                }
            ],
            "logging": {
                "engineLogging": true,
                "trace": true,
                "traceResponse": true
            }            
        },
Debugger Logs
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (301) ->~\"Breakpoint 2, badfunc (entity=55555556d2b0 GameEntity(0): \\\"my_entity_0\\\" = {...}) at /home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp:21\\n\"\n"},"seq":421}
1: (301) ->~"Breakpoint 2, badfunc (entity=55555556d2b0 GameEntity(0): \"my_entity_0\" = {...}) at /home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp:21\n"
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (301) ->~\"21\\t    entity->renderParams.color[2] += 1e-3f;\\n\"\n"},"seq":423}
1: (301) ->~"21\t    entity->renderParams.color[2] += 1e-3f;\n"
--> E (output): {"type":"event","event":"output","body":{"category":"stdout","output":"Breakpoint 2, badfunc (entity=55555556d2b0 GameEntity(0): \"my_entity_0\" = {...}) at /home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp:21\n"},"seq":425}
Breakpoint 2, badfunc (entity=55555556d2b0 GameEntity(0): "my_entity_0" = {...}) at /home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp:21
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (301) ->*stopped,reason=\"breakpoint-hit\",disp=\"keep\",bkptno=\"2\",frame={addr=\"0x0000555555555195\",func=\"badfunc\",args=[{name=\"entity\",value=\"55555556d2b0 GameEntity(0): \\\"my_entity_0\\\" = {...}\"}],file=\"/home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp\",fullname=\"/home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp\",line=\"21\",arch=\"i386:x86-64\"},thread-id=\"1\",stopped-threads=\"all\",core=\"10\"\n"},"seq":426}
1: (301) ->*stopped,reason="breakpoint-hit",disp="keep",bkptno="2",frame={addr="0x0000555555555195",func="badfunc",args=[{name="entity",value="55555556d2b0 GameEntity(0): \"my_entity_0\" = {...}"}],file="/home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp",fullname="/home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp",line="21",arch="i386:x86-64"},thread-id="1",stopped-threads="all",core="10"
--> E (output): {"type":"event","event":"output","body":{"category":"stdout","output":"21\t    entity->renderParams.color[2] += 1e-3f;\n"},"seq":429}
21	    entity->renderParams.color[2] += 1e-3f;
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (302) <-1025-stack-list-frames 0 1000\n"},"seq":431}
1: (302) <-1025-stack-list-frames 0 1000
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (302) ->1025^done,stack=[frame={level=\"0\",addr=\"0x0000555555555195\",func=\"badfunc\",file=\"/home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp\",fullname=\"/home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp\",line=\"21\",arch=\"i386:x86-64\"},frame={level=\"1\",addr=\"0x00005555555553d0\",func=\"main\",file=\"/home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp\",fullname=\"/home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp\",line=\"44\",arch=\"i386:x86-64\"}]\n"},"seq":433}
1: (302) ->1025^done,stack=[frame={level="0",addr="0x0000555555555195",func="badfunc",file="/home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp",fullname="/home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp",line="21",arch="i386:x86-64"},frame={level="1",addr="0x00005555555553d0",func="main",file="/home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp",fullname="/home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp",line="44",arch="i386:x86-64"}]
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (302) ->(gdb)\n"},"seq":435}
1: (302) ->(gdb)
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (302) 1025: elapsed time 0\n"},"seq":437}
1: (302) 1025: elapsed time 0
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (303) Send Event AD7BreakpointEvent\n"},"seq":439}
1: (303) Send Event AD7BreakpointEvent
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"Execute debugger commands using \"-exec <command>\", for example \"-exec info registers\" will list registers in use (when GDB is the debugger)\n"},"seq":441}
Execute debugger commands using "-exec <command>", for example "-exec info registers" will list registers in use (when GDB is the debugger)
--> E (stopped): {"type":"event","event":"stopped","body":{"reason":"breakpoint","threadId":2015535,"allThreadsStopped":true,"source":{"name":"main.cpp","path":"/home/stgatilov/cppdev/gdb_pp_hang_repro/main.cpp","sources":[],"checksums":[]},"line":21,"column":1},"seq":443}
<--   C (threads-12): {"command":"threads","type":"request","seq":12}
--> R (threads-12): {"type":"response","request_seq":12,"success":true,"command":"threads","body":{"threads":[{"id":2015535,"name":"proj [2015535]"}]},"seq":446}
<--   C (stackTrace-13): {"command":"stackTrace","arguments":{"threadId":2015535,"startFrame":0,"levels":20},"type":"request","seq":13}
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (323) <-1026-list-features\n"},"seq":449}
1: (323) <-1026-list-features
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (324) ->1026^done,features=[\"frozen-varobjs\",\"pending-breakpoints\",\"thread-info\",\"data-read-memory-bytes\",\"breakpoint-notifications\",\"ada-task-info\",\"language-option\",\"info-gdb-mi-command\",\"undefined-command-error-code\",\"exec-run-start-option\",\"data-disassemble-a-option\",\"simple-values-ref-types\",\"python\"]\n"},"seq":451}
1: (324) ->1026^done,features=["frozen-varobjs","pending-breakpoints","thread-info","data-read-memory-bytes","breakpoint-notifications","ada-task-info","language-option","info-gdb-mi-command","undefined-command-error-code","exec-run-start-option","data-disassemble-a-option","simple-values-ref-types","python"]
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (324) ->(gdb)\n"},"seq":453}
1: (324) ->(gdb)
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (325) 1026: elapsed time 1\n"},"seq":455}
1: (325) 1026: elapsed time 1
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (326) <-1027-stack-list-arguments 2 0 1\n"},"seq":457}
1: (326) <-1027-stack-list-arguments 2 0 1
Other Extensions

No response

Additional Information

You can uncomment print statements in printers.py and confirm that children methods of pretty printers are called endlessly while GDB hangs. The last command that is sent to GDB is -stack-list-arguments 2 0 1, and GDB never responds to it.

According to GDB-MI docs, print-values = 2 or --simple-values means: "print the name, type and value for simple data types, and the name and type for arrays, structures and unions."
I suspect that pointers are also considered "simple data types", even when they are covered by pretty printers and have children. So GDB tries to return the value of a pointer by deep-printing it. Since the children graph in this case has cycles, deep-printing hangs.

The context: I'm trying to rewrite ~1000 LOC of natvis into GDB pretty printers for TheDarkMod game, so that Linux programmers could enjoy debugging too. It looks like GDB normally assumes that children graph of any value is a tree and it can be deep-printed easily. But I feel such a restriction heavily limits the usefulness of natvis/pretty printers. Sadly, this issue looks like a blocker in using GDB pretty printers reliably.

As an unrelated issue, the limited natvis support also hangs on this repro project. To reproduce, run "Launch with natvis" configuration, then stop at the same breakpoint and look into Locals, then expand entity and renderParams.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked repro project, its main.cpp breakpoint in badfunc, printers.py, and the “Launch with pretty printers” configuration; reproduce the hang and inspect the logged -stack-list-arguments request. Compare the pretty-printer children calls with the extension’s debugger requests, and consider the issue resolved when the breakpoint no longer hangs on the cyclic value while preserving the intended Locals behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, python, typescript, vscode
Domain
developer-experience, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.