What is ReadyToRun (R2R)?
.NET offers a variety of deployment methods and execution environments, and I plan to write a separate post to summarize them later.
By default, .NET code is compiled natively at runtime by the JIT (Just-in-Time) compiler. However, when distributed using the ReadyToRun method, some parts are pre-compiled natively, while the rest is compiled at runtime.
There are no significant limitations other than an increase in the size of the assembly, making it easy to implement.
Unable to Debug R2R Mini Dumps
Typically, when a game server crashes, a full dump that includes the entire memory is created to identify the cause.
Since writing the entire memory takes a long time and there is a chance of failure in the process, a mini dump containing only minimal information, such as the call stack, is created first, followed by a full dump.
In our project, we were storing not just PDB files but also binaries like DLLs and EXEs on the symbol server. However, there was an issue where the binaries corresponding to the mini dumps with the ReadyToRun deployment option could not be retrieved from the symbol server.
Identifying the Cause
The full dump could be debugged normally. Although I didn't investigate in detail, it seemed that the R2R binaries were loaded into the full dump memory, so there were no issues there.
Upon checking, I found that the larger R2R binaries were not being uploaded to the symbol server. The symstore failed to upload the file due to type recognition issues.
Error message:
skip (not a known file type. ErrorLevel is 13)
I tried uploading the binaries before they increased in size to the symbol server, but the keys used by symstore and the debugger contain size information, making this binary unusable.
TimeDataStamp(4bytes) + SizeOfImage(3bytes)
Portable Executable - Wikipedia
When R2R is applied, the binary size increases, causing the latter part to change.
- Before: CF33DFB3388000
- After: CF33DFB37d2000
While it was possible to manually find the R2R binary corresponding to the mini dump and debug it alongside the dump file, or to use only the full dump for debugging, it was inconvenient.
Attempt 1 - Failure
You can specify which type of mini dump to create using MINIDUMP_TYPE.
MINIDUMP_TYPE (minidumpapiset.h) - Win32 apps | Microsoft Learn
I thought that including the loaded R2R binaries in the mini dump might work.
Using MiniDumpWithModuleHeaders would include the binaries, but the debugger did not recognize them. Even if it did, the size increased, negating the purpose of the mini dump.
Attempt 2 - Solution
Symstore did not upload the R2R binaries regardless of the options provided. Since the files I wanted to upload were not symbol files but simple binaries, I thought uploading them directly to the symbol server would not cause issues for the debugger.
Since the build script was written in Python, I used the following Python code to retrieve the symbol key from the PE header information and upload it directly to the symbol server.
pip install pefile
def get_symbol_key(filename):
pe = pefile.PE(filename)
return format(pe.FILE_HEADER.TimeDateStamp, 'x').upper() + format(pe.OPTIONAL_HEADER.SizeOfImage, 'x').upper()
Now, the binaries can be found correctly from the mini dump.
Issues When Distributing as SingleFile
By default, .NET executables are merely bootstrap files, and the actual code is loaded and executed from DLLs.
To simplify distribution, there is an option called SingleFile that bundles everything into a single executable. However, the issue arises when using SingleFile with ReadyToRun, as the larger DLL files could not be found in the output folder.
Example output folder:
- Output/Release
- Program.exe (bundled file)
- Output/Release/Win-x64
- Program.dll (original size before R2R application)
Since C# is deterministically compiled, building twice with SingleFile on/off allows for the bundled file to be distributed while the R2R-applied DLL can be manually uploaded to the symbol server.
To save build time, I added a process to extract the sub-binaries from the SingleFile distributed file and upload them to the symbol server.
Install ilspycmd:
dotnet tool install --global ilspycmd
Usage:
ilspycmd -d -o output_folder Program.exe (bundled file)