Add SDK Symbols tests (#19528)
Co-authored-by: Viktor Hofer <viktor.hofer@microsoft.com>
This commit is contained in:
parent
bf738fd99f
commit
94d9faf698
4 changed files with 162 additions and 37 deletions
|
@ -78,48 +78,23 @@ namespace Microsoft.DotNet.UnifiedBuild.Tasks
|
|||
|
||||
foreach (string file in Directory.GetFiles(SdkLayoutPath, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (file.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase) &&
|
||||
!file.EndsWith(".resources.dll", StringComparison.InvariantCultureIgnoreCase))
|
||||
if (PdbUtilities.FileInSdkLayoutRequiresAPdb(file, out string guid))
|
||||
{
|
||||
string guid = string.Empty;
|
||||
using var pdbStream = File.OpenRead(file);
|
||||
using var peReader = new PEReader(pdbStream);
|
||||
try
|
||||
string debugId = GetDebugId(guid, file);
|
||||
if (!allPdbGuids.ContainsKey(debugId))
|
||||
{
|
||||
// Check if pdb is embedded
|
||||
if (peReader.ReadDebugDirectory().Any(entry => entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var debugDirectory = peReader.ReadDebugDirectory().First(entry => entry.Type == DebugDirectoryEntryType.CodeView);
|
||||
var codeViewData = peReader.ReadCodeViewDebugDirectoryData(debugDirectory);
|
||||
guid = $"{codeViewData.Guid.ToString("N").Replace("-", string.Empty)}";
|
||||
filesWithoutPDBs.Add(file.Substring(SdkLayoutPath.Length + 1));
|
||||
}
|
||||
catch (Exception e) when (e is BadImageFormatException || e is InvalidOperationException)
|
||||
else
|
||||
{
|
||||
// Ignore binaries without debug info
|
||||
continue;
|
||||
}
|
||||
// Copy matching pdb to symbols path, preserving sdk binary's hierarchy
|
||||
string sourcePath = (string)allPdbGuids[debugId]!;
|
||||
string destinationPath =
|
||||
file.Replace(SdkLayoutPath, SdkSymbolsLayoutPath)
|
||||
.Replace(Path.GetFileName(file), Path.GetFileName(sourcePath));
|
||||
|
||||
if (guid != string.Empty)
|
||||
{
|
||||
string debugId = GetDebugId(guid, file);
|
||||
if (!allPdbGuids.ContainsKey(debugId))
|
||||
{
|
||||
filesWithoutPDBs.Add(file.Substring(SdkLayoutPath.Length + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy matching pdb to symbols path, preserving sdk binary's hierarchy
|
||||
string sourcePath = (string)allPdbGuids[debugId]!;
|
||||
string destinationPath =
|
||||
file.Replace(SdkLayoutPath, SdkSymbolsLayoutPath)
|
||||
.Replace(Path.GetFileName(file), Path.GetFileName(sourcePath));
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)!);
|
||||
File.Copy(sourcePath, destinationPath, true);
|
||||
}
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)!);
|
||||
File.Copy(sourcePath, destinationPath, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
|
||||
namespace Microsoft.DotNet.UnifiedBuild.Tasks
|
||||
{
|
||||
public static class PdbUtilities
|
||||
{
|
||||
// Checks if a file in Sdk layout requires an external Pdb.
|
||||
// Also returns the Pdb GUID, if one was found in PE.
|
||||
public static bool FileInSdkLayoutRequiresAPdb(string file, out string guid)
|
||||
{
|
||||
guid = string.Empty;
|
||||
|
||||
// Files under packs/ are used for build only, no need for Pdbs
|
||||
return !file.Contains(Path.DirectorySeparatorChar + "packs" + Path.DirectorySeparatorChar) ?
|
||||
FileHasCompanionPdbInfo(file, out guid) :
|
||||
false;
|
||||
}
|
||||
|
||||
// Checks if a file has debug data indicating an external companion Pdb.
|
||||
// Also returns the Pdb GUID, if one was found in PE.
|
||||
private static bool FileHasCompanionPdbInfo(string file, out string guid)
|
||||
{
|
||||
guid = string.Empty;
|
||||
|
||||
if (file.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase) &&
|
||||
!file.EndsWith(".resources.dll", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
using var pdbStream = File.OpenRead(file);
|
||||
using var peReader = new PEReader(pdbStream);
|
||||
try
|
||||
{
|
||||
// Check if pdb is embedded
|
||||
if (peReader.ReadDebugDirectory().Any(entry => entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var debugDirectory = peReader.ReadDebugDirectory().First(entry => entry.Type == DebugDirectoryEntryType.CodeView);
|
||||
var codeViewData = peReader.ReadCodeViewDebugDirectoryData(debugDirectory);
|
||||
guid = $"{codeViewData.Guid.ToString("N").Replace("-", string.Empty)}";
|
||||
}
|
||||
catch (Exception e) when (e is BadImageFormatException || e is InvalidOperationException)
|
||||
{
|
||||
// Ignore binaries without debug info
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return guid != string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,10 @@
|
|||
<VSTestUseMSBuildOutput>false</VSTestUseMSBuildOutput>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="$(TasksDir)Microsoft.DotNet.UnifiedBuild.Tasks\PdbUtilities.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TestUtilities\TestUtilities.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Microsoft.DotNet.UnifiedBuild.Tasks;
|
||||
|
||||
namespace Microsoft.DotNet.SourceBuild.SmokeTests;
|
||||
|
||||
public class SymbolsTests : SdkTests
|
||||
{
|
||||
private static string SymbolsTestsRoot { get; } = Path.Combine(Directory.GetCurrentDirectory(), nameof(SymbolsTests));
|
||||
|
||||
public SymbolsTests(ITestOutputHelper outputHelper) : base(outputHelper) { }
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that all symbols have valid sourcelinks.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void VerifySdkSymbols()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(SymbolsTestsRoot))
|
||||
{
|
||||
Directory.Delete(SymbolsTestsRoot, true);
|
||||
}
|
||||
Directory.CreateDirectory(SymbolsTestsRoot);
|
||||
|
||||
string symbolsRoot = Directory.CreateDirectory(Path.Combine(SymbolsTestsRoot, "symbols")).FullName;
|
||||
|
||||
// We are validating dotnet-symbols-sdk-*.tar.gz which contains source-built sdk symbols
|
||||
Utilities.ExtractTarball(
|
||||
Utilities.GetFile(Path.GetDirectoryName(Config.SourceBuiltArtifactsPath)!, "dotnet-symbols-sdk-*.tar.gz"),
|
||||
symbolsRoot,
|
||||
OutputHelper);
|
||||
|
||||
IList<string> failedFiles = VerifySdkFilesHaveMatchingSymbols(symbolsRoot, Config.DotNetDirectory);
|
||||
|
||||
if (failedFiles.Count > 0)
|
||||
{
|
||||
OutputHelper.WriteLine($"Did not find PDBs for the following SDK files:");
|
||||
foreach (string file in failedFiles)
|
||||
{
|
||||
OutputHelper.WriteLine(file);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.True(failedFiles.Count == 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(SymbolsTestsRoot, true);
|
||||
}
|
||||
}
|
||||
|
||||
private IList<string> VerifySdkFilesHaveMatchingSymbols(string symbolsRoot, string sdkRoot)
|
||||
{
|
||||
Assert.True(Directory.Exists(sdkRoot), $"Path, with SDK files to validate, does not exist: {sdkRoot}");
|
||||
|
||||
var failedFiles = new ConcurrentBag<string>();
|
||||
|
||||
IEnumerable<string> allFiles = Directory.GetFiles(sdkRoot, "*", SearchOption.AllDirectories);
|
||||
Parallel.ForEach(allFiles, file =>
|
||||
{
|
||||
if (PdbUtilities.FileInSdkLayoutRequiresAPdb(file, out string guid))
|
||||
{
|
||||
string symbolFile = Path.ChangeExtension(file.Replace(sdkRoot, symbolsRoot), ".pdb");
|
||||
if (!File.Exists(symbolFile))
|
||||
{
|
||||
failedFiles.Add(file);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return failedFiles.ToList();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue