microsoft / microsoft/vscode-cpptools
GDB hangs on breakpoint, infinitely traversing pretty printers
Nobody has claimed this yet.
- 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:
- Clone this repository: https://github.com/stgatilov/vscode-gdb-pretty-printers-hangs-repro-code
- Open it in VS Code.
- Build Debug build.
- Put breakpoint at the only line of
badfunc. - Run debugging using the included configuration "Launch with pretty printers".
- 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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