Skip to content

Commit

Permalink
Get Debug ID for Full PDB format on Windows (#2222)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattjohnsonpint authored Mar 9, 2023
1 parent c534d5c commit 3eb4e33
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 40 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Fixes

- Get debug image for Full PDB format on Windows ([#2222](https://github.com/getsentry/sentry-dotnet/pull/2222))
- Fix debug files not uploading for `packages.config` nuget ([#2224](https://github.com/getsentry/sentry-dotnet/pull/2224))

### Dependencies
Expand Down
119 changes: 79 additions & 40 deletions src/Sentry/Internal/DebugStackTrace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,15 @@ internal void MergeDebugImagesInto(SentryEvent @event)
{
if (i != j)
{
relocations.Add(DebugStackTrace.GetRelativeAddressMode(i), DebugStackTrace.GetRelativeAddressMode(j));
relocations.Add(GetRelativeAddressMode(i), GetRelativeAddressMode(j));
}
found = true;
}
}

if (!found)
{
relocations.Add(DebugStackTrace.GetRelativeAddressMode(i), DebugStackTrace.GetRelativeAddressMode(@event.DebugImages.Count));
relocations.Add(GetRelativeAddressMode(i), GetRelativeAddressMode(@event.DebugImages.Count));
@event.DebugImages.Add(DebugImages[i]);
}
}
Expand Down Expand Up @@ -213,7 +213,7 @@ private SentryStackFrame InternalCreateFrame(StackFrame stackFrame, bool demangl

if (AddDebugImage(method.Module) is { } moduleIdx && moduleIdx != DebugImageMissing)
{
frame.AddressMode = DebugStackTrace.GetRelativeAddressMode(moduleIdx);
frame.AddressMode = GetRelativeAddressMode(moduleIdx);

try
{
Expand Down Expand Up @@ -369,9 +369,9 @@ private static void DemangleLambdaReturnType(SentryStackFrame frame)
}
}

private PEReader? TryReadAssembly(string assemblyName)
private static PEReader? TryReadAssembly(string assemblyName, SentryOptions options)
{
if (_options.AssemblyReader is { } reader)
if (options.AssemblyReader is { } reader)
{
return reader.Invoke(assemblyName);
}
Expand All @@ -382,77 +382,116 @@ private static void DemangleLambdaReturnType(SentryStackFrame frame)
private int? AddDebugImage(Module module)
{
var id = module.ModuleVersionId;

if (_debugImageIndexByModule.TryGetValue(id, out var idx))
{
return idx;
}

var debugImage = GetDebugImage(module, _options);
if (debugImage == null)
{
// don't try to resolve again
_debugImageIndexByModule.Add(id, DebugImageMissing);
return null;
}

idx = DebugImages.Count;
DebugImages.Add(debugImage);
_debugImageIndexByModule.Add(id, idx);

return idx;
}

internal static DebugImage? GetDebugImage(Module module, SentryOptions options)
{
var assemblyName = module.FullyQualifiedName;
using var peReader = TryReadAssembly(assemblyName);
using var peReader = TryReadAssembly(assemblyName, options);
if (peReader is null)
{
_options.LogDebug("Skipping DebugImage for module '{0}' because assembly wasn't found: '{1}'",
module.Name, assemblyName);
_debugImageIndexByModule.Add(id, DebugImageMissing); // don't try to resolve again
options.LogDebug("Skipping debug image for module '{0}' because assembly wasn't found: '{1}'",
module.Name, assemblyName);
return null;
}

string? codeId = null;
var headers = peReader.PEHeaders;
if (headers.PEHeader is { } peHeader)
{
codeId = $"{headers.CoffHeader.TimeDateStamp:X8}{peHeader.SizeOfImage:x}";
}
var codeId = headers.PEHeader is { } peHeader
? $"{headers.CoffHeader.TimeDateStamp:X8}{peHeader.SizeOfImage:x}"
: null;

string? debugId = null;
string? debugFile = null;
string? debugChecksum = null;

var debugDirs = peReader.ReadDebugDirectory();
foreach (var entry in debugDirs)
var debugDirectoryEntries = peReader.ReadDebugDirectory();

foreach (var entry in debugDirectoryEntries)
{
if (entry.Type == DebugDirectoryEntryType.PdbChecksum)
switch (entry.Type)
{
var checksum = peReader.ReadPdbChecksumDebugDirectoryData(entry);
var checksumHex = checksum.Checksum.AsSpan().ToHexString();
debugChecksum = $"{checksum.AlgorithmName}:{checksumHex}";
case DebugDirectoryEntryType.PdbChecksum:
{
var checksum = peReader.ReadPdbChecksumDebugDirectoryData(entry);
var checksumHex = checksum.Checksum.AsSpan().ToHexString();
debugChecksum = $"{checksum.AlgorithmName}:{checksumHex}";
break;
}

case DebugDirectoryEntryType.CodeView:
{
var codeView = peReader.ReadCodeViewDebugDirectoryData(entry);
debugFile = codeView.Path;

// Specification:
// https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md#codeview-debug-directory-entry-type-2
//
// See also:
// https://learn.microsoft.com/dotnet/csharp/language-reference/compiler-options/code-generation#debugtype
//
// Note: Matching PDB ID is stored in the #Pdb stream of the .pdb file.

if (entry.IsPortableCodeView)
{
// Portable PDB Format
// Version Major=any, Minor=0x504d
debugId = $"{codeView.Guid}-{entry.Stamp:x8}";
}
else
{
// Full PDB Format (Windows only)
// Version Major=0, Minor=0
debugId = $"{codeView.Guid}-{codeView.Age}";
}

break;
}
}
if (!entry.IsPortableCodeView)

if (debugId != null && debugChecksum != null)
{
continue;
// No need to keep looking, once we have both.
break;
}
var codeView = peReader.ReadCodeViewDebugDirectoryData(entry);

// Together 16B of the Guid concatenated with 4B of the TimeDateStamp field of the entry form a PDB ID that
// should be used to match the PE/COFF image with the associated PDB (instead of Guid and Age).
// Matching PDB ID is stored in the #Pdb stream of the .pdb file.
// See https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md#codeview-debug-directory-entry-type-2
debugId = $"{codeView.Guid}-{entry.Stamp:x8}";
debugFile = codeView.Path;
}

// well, we are out of luck :-(
if (debugId == null)
{
_options.LogInfo("Skipping DebugImage for module '{0}' because DebugId couldn't be determined", module.Name);
_debugImageIndexByModule.Add(id, DebugImageMissing); // don't try to resolve again
options.LogInfo("Skipping debug image for module '{0}' because the Debug ID couldn't be determined", module.Name);
return null;
}

idx = DebugImages.Count;
DebugImages.Add(new DebugImage
var debugImage = new DebugImage
{
Type = "pe_dotnet",
CodeId = codeId,
CodeFile = assemblyName,
DebugId = debugId,
DebugChecksum = debugChecksum,
DebugFile = debugFile,
ModuleVersionId = id,
});
_debugImageIndexByModule.Add(id, idx);
ModuleVersionId = module.ModuleVersionId,
};

return idx;
options.LogDebug("Got debug image for '{0}' having Debug ID: {1}", module.Name, debugId);

return debugImage;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@
}
],
DebugImages: [
{
Type: pe_dotnet,
ImageAddress: null,
ImageSize: null,
DebugId: ________-____-____-____-____________-_,
DebugChecksum: null,
DebugFile: mscorlib.pdb,
CodeId: ______________,
CodeFile: .../mscorlib.dll
},
{
Type: pe_dotnet,
ImageAddress: null,
Expand Down

0 comments on commit 3eb4e33

Please sign in to comment.