Skip to content

Loading debug symbols is not thread-safe on MSVC #14952

Description

@HertzDevil

On Windows, when debug symbols are loaded concurrently from multiple threads, such as when raising an exception or calling caller, most of those threads will fail. A snippet like:

4.times do
  Thread.new do
    puts "#{caller.inspect}\n"
  end
end
puts "#{caller.inspect}\n"
sleep 0.1

could produce results like:

["???", "???", "???"]
["???", "???", "???"]
["???", "???", "???"]
["???", "???", "???"]
["usr\\test.cr:27 in '__crystal_main'", "src\\crystal\\main.cr:118 in 'main_user_code'", "src\\crystal\\main.cr:104 in 'main'", "src\\crystal\\main.cr:130 in 'main'", "src\\crystal\\system\\win32\\wmain.cr:37 in 'wmain'", "D:\\a\\_work\\1\\s\\src\\vctools\\crt\\vcstartup\\src\\startup\\exe_common.inl:288 in '__scrt_common_main_seh'", "C:\\WINDOWS\\System32\\KERNEL32.DLL +75133 in 'BaseThreadInitThunk'", "C:\\WINDOWS\\SYSTEM32\\ntdll.dll +372520 in 'RtlUserThreadStart'"]
["???", "???", "???"]
Unable to load debug information["???", "???", "???"]
Invalid memory access (C0000005) at address : SymInitializeW: The parameter is incorrect. (RuntimeError)
0x["???", "???", "???"]
1f565411000
  from ???
  from ???[0x7ff73a44789c] 
  from ???
  from ???
???
  from ???
  from [0x1fffffff0000000d] ???
[0x7ff73a446163] ???
???
[["???"]
0xd00000000] ???
[0x52933d3fd4da2e04] ???
[0xe9fe19dd54ddbdb6] ???
[0x7ff73a4475e6] ???
[0x1f5653d0d00] ???
[0x6258e96d0000000d] ???
[0x1a36ad96b7e88ea] ???
[0x1f5653dcd80] ???
[0xab200000001] ???
[0x100000001] ???
[0x1] ???
[0x1f5653dcd80] ???
[0x97980500979805] ???
[0x1f5653d0d00] ???

This is also reproducible with fibers under -Dpreview_mt:

4.times do
  spawn do
    puts "#{caller.inspect}\n"
  end
end
puts "#{caller.inspect}\n"
sleep 0.1

All related Win32 functions are single-threaded; replacing Exception::CallStack.@@sym_loaded with an Atomic::Flag is not enough, because those alternate threads still end up calling LibC.StackWalk64 to unwind their stacks before LibC.SymInitializeW returns. Therefore, all threads must await the completion of Exception::CallStack.load_debug_info_impl on whatever thread it is called.

To achieve this, #14905 or something like InitOnceExecuteOnce should be used instead.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions