microsoft / microsoft/terminal
Rare hang / infinite loop writing line containing emoji unicode characters
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 105k
- Forks
- 9.6k
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 29
Description
Windows Terminal version
1.22.11141.0
Windows build number
10.0.26100.3775
Other Software
Output coming from a node.js script
Steps to reproduce
Unfortunately I cannot reproduce this consistently. I had noticed this hanging on occasion, and would have to close the window, and OpenConsole.exe would remain spinning a core until I terminated the process. Eventually I decided to grab a full memory dump file next time it occurred, though I have not been able to reproduce again. I do still have the full memory dump available.
The underlying node.js script is simply logging operations to the console.
In this case, it is writing the text: "countByTag 🎵 Oblivion - Grimes [1]: 0.215ms\r\n"
Expected Behavior
Text should output without hanging the console.
Actual Behavior
The console hangs and becomes unresponsive. A simple analysis of the memory dump follows:
OpenConsole!WriteCharsLegacy is writing the text "countByTag 🎵 Oblivion - Grimes [1]: 0.215ms\r\n"
this calls _writeCharsLegacyUnprocessed which appears to get stuck in the while loop while (!state.text.empty())
Presumably the first textBuffer.Replace call in this loop succeeds, because by the time there is a hang the state has advanced to the first emoji character in the string; that is, state.text is now pointing to the first emoji / unicode character in the text: "🎵 Oblivion - Grimes [1]: 0.215ms\r\n"
Note that the emoji is made of two unicode chars, which I have a hunch is the underlying issue.
state.columnBegin is 126, .columnLimit is 127, columnEnd is 127, columnBeginDirty is 126, columnEndDirty is 127 (all decimal numbers here). The TextBuffer.width is also 127
I grabbed two dumps a few seconds apart and both are in pretty much the same area, one was in ROW::WriteHelper::Finish and one was ROW::ReplaceText, but notably there does not seem to be any change/progress in the state variable.
I think somehow textBuffer.Replace is not advancing the text for some reason due to an edge case here, which causes it to never be empty, and therefore spin forever.
I can provide a dump file securely if desired.
stack trace:
# Child-SP RetAddr Call Site
00 (Inline Function) --------`-------- OpenConsole!ROW::WriteHelper::Finish(void)+0x3c [C:\__w\1\s\src\buffer\out\Row.cpp @ 860]
01 0000003f`b86ff0a0 00007ff7`e129e447 OpenConsole!ROW::ReplaceText(
struct RowWriteState * state = 0x00000000`00000000)+0x188 [C:\__w\1\s\src\buffer\out\Row.cpp @ 620]
02 0000003f`b86ff160 00007ff7`e1294e0a OpenConsole!TextBuffer::Replace(
int row = 0n26,
class TextAttribute * attributes = 0x000001de`0311adec,
struct RowWriteState * state = 0x0000003f`b86ff1e8)+0x37 [C:\__w\1\s\src\buffer\out\textBuffer.cpp @ 531]
03 0000003f`b86ff1b0 00007ff7`e1294ffa OpenConsole!_writeCharsLegacyUnprocessed(
class SCREEN_INFORMATION * screenInfo = 0x000001de`03110430,
class std::basic_string_view<wchar_t,std::char_traits<wchar_t> > * text = <Value unavailable error>,
<Unimplemented error> psScrollY = <Unimplemented error>)+0x9a [C:\__w\1\s\src\host\_stream.cpp @ 133]
04 0000003f`b86ff250 00007ff7`e12957f6 OpenConsole!WriteCharsLegacy(
class SCREEN_INFORMATION * screenInfo = 0x000001de`03110430,
class std::basic_string_view<wchar_t,std::char_traits<wchar_t> > * text = 0x0000003f`b86ff378 "countByTag 🎵 Oblivion - Grimes [1]: 0.215ms
",
<Unimplemented error> psScrollY = <Unimplemented error>)+0x14a [C:\__w\1\s\src\host\_stream.cpp @ 195]
05 0000003f`b86ff350 00007ff7`e129588c OpenConsole!DoWriteConsole(
wchar_t * pwchBuffer = 0x000001de`03166510 "countByTag ???",
unsigned int64 * pcbBuffer = 0x0000003f`b86ff3f0,
class SCREEN_INFORMATION * screenInfo = 0x000001de`03110430,
class std::unique_ptr<WriteData,std::default_delete<WriteData> > * waiter = 0x0000003f`b86ff460 empty)+0xe6 [C:\__w\1\s\src\host\_stream.cpp @ 436]
06 0000003f`b86ff3d0 00007ff7`e129618a OpenConsole!WriteConsoleWImplHelper(
class SCREEN_INFORMATION * context = 0x000001de`03110430,
class std::basic_string_view<wchar_t,std::char_traits<wchar_t> > * buffer = 0x0000003f`b86ff450 "countByTag 🎵 Oblivion - Grimes [1]: 0.215ms
",
unsigned int64 * read = 0x0000003f`b86ff510,
class std::unique_ptr<WriteData,std::default_delete<WriteData> > * waiter = 0x0000003f`b86ff460 empty)+0x5c [C:\__w\1\s\src\host\_stream.cpp @ 478]
07 0000003f`b86ff430 00007ff7`e12d6b89 OpenConsole!ApiRoutines::WriteConsoleWImpl(
class SCREEN_INFORMATION * context = <Value unavailable error>,
class std::basic_string_view<wchar_t,std::char_traits<wchar_t> > * buffer = 0x0000003f`b86ff500 "countByTag 🎵 Oblivion - Grimes [1]: 0.215ms
",
unsigned int64 * read = 0x0000003f`b86ff510,
class std::unique_ptr<IWaitRoutine,std::default_delete<IWaitRoutine> > * waiter = 0x0000003f`b86ff4f0 empty)+0x8a [C:\__w\1\s\src\host\_stream.cpp @ 705]
08 0000003f`b86ff4a0 00007ff7`e12de4cd OpenConsole!ApiDispatchers::ServerWriteConsole(
struct _CONSOLE_API_MSG * m = 0x0000003f`b86ff670,
int * pbReplyPending = 0x0000003f`b86ff5b8)+0x229 [C:\__w\1\s\src\server\ApiDispatchers.cpp @ 392]
09 0000003f`b86ff570 00007ff7`e12d4607 OpenConsole!ApiSorter::ConsoleDispatchRequest(
struct _CONSOLE_API_MSG * Message = 0x0000003f`b86ff670)+0xbd [C:\__w\1\s\src\server\ApiSorter.cpp @ 174]
0a (Inline Function) --------`-------- OpenConsole!IoDispatchers::ConsoleDispatchRequest(void)+0x8 [C:\__w\1\s\src\server\IoDispatchers.cpp @ 582]
0b 0000003f`b86ff5e0 00007ff7`e126f136 OpenConsole!IoSorter::ServiceIoOperation(
struct _CONSOLE_API_MSG * pMsg = <Value unavailable error>,
struct _CONSOLE_API_MSG ** ReplyMsg = <Value unavailable error>)+0x67 [C:\__w\1\s\src\server\IoSorter.cpp @ 100]
0c 0000003f`b86ff630 00007ff9`1aede8d7 OpenConsole!ConsoleIoThread(
void * lpParameter = <Value unavailable error>)+0x1f6 [C:\__w\1\s\src\host\srvinit.cpp @ 990]
0d 0000003f`b86ff8b0 00007ff9`1c2b14fc kernel32!BaseThreadInitThunk+0x17
0e 0000003f`b86ff8e0 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
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 _stream.cpp in _writeCharsLegacyUnprocessed, then trace TextBuffer::Replace and ROW::ReplaceText in Row.cpp, especially the reported while loop and boundary state. Use the supplied stack trace and memory-dump details to investigate the emoji case. Done means console writes containing the reported emoji no longer spin indefinitely, with coverage for the boundary condition if an existing test location is identified.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- cli, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100