Debug meta info not sent when using `PublishSingleFile=true`
See original GitHub issuePackage
Sentry
.NET Flavor
.NET
.NET Version
7.0.2013
OS
Any (not platform specific)
SDK Version
3.31.0
Steps to Reproduce
Create a simple console app:
using Sentry;
SentrySdk.Init(options =>
{
options.Dsn = "...";
options.Debug = true;
});
try
{
throw new Exception("Test");
}
catch (Exception exception)
{
SentrySdk.CaptureException(exception);
}
In the csproj, configure to upload symbols and sources to Sentry (and authenticate with sentry-cli login
).
<PropertyGroup>
<SentryOrg>...</SentryOrg>
<SentryProject>...</SentryProject>
<SentryUploadSymbols>true</SentryUploadSymbols>
<SentryUploadSources>true</SentryUploadSources>
</PropertyGroup>
Compile and publish with:
dotnet publish -c Release -p:PublishSingleFile=true
Run the app from its published folder:
./bin/Release/net7.0/osx-arm64/publish/MyConsoleApp
Expected Result
The event generated and shown in the console debug output should contain a debug_meta
section, including the debug_id
.
In Sentry, the source context should be visible, and the debug images section should show the symbols were found.
Actual Result
The event is missing the debug_meta
section when -p:PublishSingleFile=true
is used. Thus, source context is not shown.
Line numbers will still be shown if the .pdb
file is present (which it is by default in the publish
folder), but if you delete it - or ship the executable app without the pdb file, then client-side symbolication won’t occur. Server-side symbolication also won’t occur because the debug_meta
section is missing from the event.
Issue Analytics
- State:
- Created 4 months ago
- Comments:10 (6 by maintainers)
Top GitHub Comments
OK, ILSpy is cunning. Here’s what it’s doing:
Determine whether it’s a bundle
If it can’t load the assembly from a file, it then checks to see if it can load it from a bundle
var bundle = LoadedPackage.FromBundle(fileName);
Basically it loads the whole package into into a
MemoryMappedFile
and then it hunts for a bundle signature in that memory stream. If it finds one, it can then return thebundleHeaderOffset
which is what is used to lookup all the other bundle entries.Get the other stuff in the bundle
Most of the work there happens in the
SingleFileBundle.ReadManifest(Stream stream)
method. It’s this that enumerates all of the entries (which include resource files being bundled with the single file executable, but also any bundled assemblies) with useful stuff like the offset, within the file/stream where that entry is kept.Load details for specific entries/assemblies
In ILSpy at least, this happens when you try to expand the node representing an embedded assembly in the ILSpy UI. That’s where the
PackageFolderTreeNode.LoadChildrenForFolder
method gets invoked, and there’s some specific logic in there to handle dlls (I think folders are only relevant for resources - not for embedded assemblies).This is where the offsets for bundled assemblies that were collected from the package manifest are used to load the bundled assemblies from memory - which happens in
LoadedPackage.ResolveFileName(string name)
.There’s a bit of inception going on at this point. The
LoadedAssembly
constructor is called. One of the parameters that gets passed in isTask.Run(entry.TryOpenStream)
… In the case of bundled assemblies, the concrete implementaion of TryOpenStream that gets called is eventuallyBundleEntry.TryOpenStream
. This method is critical as it’s where the logic to decompress bundles is implemented, if necessary. Otherwise, if the assembly hasn’t been bundled compressed, a plain vanillaUnmanagedMemoryStream
gets returned starting at the appropriate entry offset.Finally, once that Task completes and hands back a stream for the assembly we want, this gets used in the
LoadedAssembly
constructor in a call toLoadAsync
… which is the same method that loaded our single file executable… only this time, the branch of code that gets executed is not that dealing with bundles but the one that loads vanilla assemblies from a memory stream.Thankfully, ILSpy also has an MIT License… so it’d be OK to copy/reuse whichever bits of this logic were appropriate.
Sounds like we’re on the right path. Cool!
If we are just learning from ILSpy and using the same approach, that’s fine. If you actually need to copy code from the ILSpy project, please put it in its own subdirectory and add an attribution file. For example, see
/src/Sentry/Internal/FastSerialization
- which is another bit of code we’ve internalized. Thanks.