diff --git a/.github/workflows/component-common.yml b/.github/workflows/component-common.yml index 80f6ea8e7b..4909d6736a 100644 --- a/.github/workflows/component-common.yml +++ b/.github/workflows/component-common.yml @@ -8,6 +8,7 @@ on: - stylecop.json - '*.props' - '*.ruleset' + - nuget.config - .config/dotnet-tools.json - .github/workflows/component-shared-workflow.yml - .github/workflows/component-common.yml diff --git a/.github/workflows/component-configuration.yml b/.github/workflows/component-configuration.yml index f0cae5d49c..3f6cc30272 100644 --- a/.github/workflows/component-configuration.yml +++ b/.github/workflows/component-configuration.yml @@ -8,6 +8,7 @@ on: - stylecop.json - '*.props' - '*.ruleset' + - nuget.config - .config/dotnet-tools.json - .github/workflows/component-shared-workflow.yml - .github/workflows/component-configuration.yml diff --git a/.github/workflows/component-connectors.yml b/.github/workflows/component-connectors.yml index 375c7677f9..43f173b6f4 100644 --- a/.github/workflows/component-connectors.yml +++ b/.github/workflows/component-connectors.yml @@ -8,6 +8,7 @@ on: - stylecop.json - '*.props' - '*.ruleset' + - nuget.config - .config/dotnet-tools.json - .github/workflows/component-shared-workflow.yml - .github/workflows/component-connectors.yml diff --git a/.github/workflows/component-discovery.yml b/.github/workflows/component-discovery.yml index 12bf4088a7..7e991eb1db 100644 --- a/.github/workflows/component-discovery.yml +++ b/.github/workflows/component-discovery.yml @@ -8,6 +8,7 @@ on: - stylecop.json - '*.props' - '*.ruleset' + - nuget.config - .config/dotnet-tools.json - .github/workflows/component-shared-workflow.yml - .github/workflows/component-discovery.yml diff --git a/.github/workflows/component-logging.yml b/.github/workflows/component-logging.yml index 1a453d26a8..1ad817653a 100644 --- a/.github/workflows/component-logging.yml +++ b/.github/workflows/component-logging.yml @@ -8,6 +8,7 @@ on: - stylecop.json - '*.props' - '*.ruleset' + - nuget.config - .config/dotnet-tools.json - .github/workflows/component-shared-workflow.yml - .github/workflows/component-logging.yml diff --git a/.github/workflows/component-management.yml b/.github/workflows/component-management.yml index e3a01e34f8..da124fbeea 100644 --- a/.github/workflows/component-management.yml +++ b/.github/workflows/component-management.yml @@ -8,6 +8,7 @@ on: - stylecop.json - '*.props' - '*.ruleset' + - nuget.config - .config/dotnet-tools.json - .github/workflows/component-shared-workflow.yml - .github/workflows/component-management.yml diff --git a/.github/workflows/component-security.yml b/.github/workflows/component-security.yml index cfe3a805f5..25658195ec 100644 --- a/.github/workflows/component-security.yml +++ b/.github/workflows/component-security.yml @@ -8,6 +8,7 @@ on: - stylecop.json - '*.props' - '*.ruleset' + - nuget.config - .config/dotnet-tools.json - .github/workflows/component-shared-workflow.yml - .github/workflows/component-security.yml diff --git a/nuget.config b/nuget.config index f5253c498c..d363dbb2bd 100644 --- a/nuget.config +++ b/nuget.config @@ -1,8 +1,17 @@ - + - + + - + + + + + + + + + diff --git a/src/Management/src/Endpoint/Actuators/ThreadDump/EventPipeThreadDumper.cs b/src/Management/src/Endpoint/Actuators/ThreadDump/EventPipeThreadDumper.cs index 4270877b40..8fd30e2691 100644 --- a/src/Management/src/Endpoint/Actuators/ThreadDump/EventPipeThreadDumper.cs +++ b/src/Management/src/Endpoint/Actuators/ThreadDump/EventPipeThreadDumper.cs @@ -155,7 +155,7 @@ private async Task> GetThreadsFromEventPipeSessionAsync(EventPi List results = ReadStackSource(stackSource, symbolReader).ToList(); - _logger.LogTrace("Finished thread walk."); + _logger.LogTrace("Finished thread walk, found {Count} results.", results.Count); return results; } finally @@ -237,6 +237,8 @@ private IEnumerable ReadStackSource(MutableTraceEventStackSource sta stackSource.ForEach(sample => { + _logger.LogTrace("Analyzing sample: {Sample}", sample); + StackSourceCallStackIndex stackIndex = sample.StackIndex; while (!stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false).StartsWith(ThreadIdTemplate, StringComparison.Ordinal)) @@ -257,6 +259,14 @@ private IEnumerable ReadStackSource(MutableTraceEventStackSource sta } }); + // Workaround for Sonar bug, which incorrectly flags the code as unreachable. +#pragma warning disable S2589 // Boolean expressions should not be gratuitous + if (samplesForThread.Count == 0) + { + _logger.LogWarning("No managed samples found."); + } +#pragma warning restore S2589 // Boolean expressions should not be gratuitous + // For every thread recorded in our trace, use the first stack. foreach ((int threadId, List samples) in samplesForThread) { @@ -300,7 +310,7 @@ private IEnumerable GetStackTrace(int threadId, StackSourceSa while (!frameName.StartsWith(ThreadIdTemplate, StringComparison.Ordinal)) { - SourceLocation? sourceLocation = stackSource.GetSourceLine(frameIndex, symbolReader); + SourceLocation? sourceLocation = stackSource.GetSourceLine(frameIndex, symbolReader) ?? LegacyGetSourceLine(stackSource, frameIndex, symbolReader); StackTraceElement stackElement = GetStackTraceElement(frameName, sourceLocation); yield return stackElement; @@ -311,6 +321,94 @@ private IEnumerable GetStackTrace(int threadId, StackSourceSa } } + // Much of this code is from PerfView/TraceLog.cs + private SourceLocation? LegacyGetSourceLine(TraceEventStackSource stackSource, StackSourceFrameIndex frameIndex, SymbolReader reader) + { + TraceLog log = stackSource.TraceLog; + uint codeAddress = (uint)frameIndex - (uint)StackSourceFrameIndex.Start; + + if (codeAddress >= log.CodeAddresses.Count) + { + return null; + } + + var codeAddressIndex = (CodeAddressIndex)codeAddress; + TraceModuleFile moduleFile = log.CodeAddresses.ModuleFile(codeAddressIndex); + + if (moduleFile == null) + { + string hexAddress = log.CodeAddresses.Address(codeAddressIndex).ToString("X", CultureInfo.InvariantCulture); + _logger.LogTrace("LegacyGetSourceLine: Could not find moduleFile {HexAddress}.", hexAddress); + return null; + } + + MethodIndex methodIndex = log.CodeAddresses.MethodIndex(codeAddressIndex); + + if (methodIndex == MethodIndex.Invalid) + { + string hexAddress = log.CodeAddresses.Address(codeAddressIndex).ToString("X", CultureInfo.InvariantCulture); + _logger.LogTrace("LegacyGetSourceLine: Could not find method for {HexAddress}.", hexAddress); + return null; + } + + int methodToken = log.CodeAddresses.Methods.MethodToken(methodIndex); + + if (methodToken == 0) + { + string hexAddress = log.CodeAddresses.Address(codeAddressIndex).ToString("X", CultureInfo.InvariantCulture); + _logger.LogTrace("LegacyGetSourceLine: Could not find method for {HexAddress}.", hexAddress); + return null; + } + + int ilOffset = log.CodeAddresses.ILOffset(codeAddressIndex); + + if (ilOffset < 0) + { + ilOffset = 0; + } + + string? pdbFileName = null; + + if (moduleFile.PdbSignature != Guid.Empty) + { + pdbFileName = reader.FindSymbolFilePath(moduleFile.PdbName, moduleFile.PdbSignature, moduleFile.PdbAge, moduleFile.FilePath, + moduleFile.ProductVersion, true); + } + + if (pdbFileName == null) + { + string simpleName = Path.GetFileNameWithoutExtension(moduleFile.FilePath); + + if (simpleName.EndsWith(".il", StringComparison.Ordinal)) + { + simpleName = Path.GetFileNameWithoutExtension(simpleName); + } + + pdbFileName = reader.FindSymbolFilePath($"{simpleName}.pdb", Guid.Empty, 0); + } + + if (pdbFileName != null) + { + ManagedSymbolModule symbolReaderModule = reader.OpenSymbolFile(pdbFileName); + + if (symbolReaderModule != null) + { + if (moduleFile.PdbSignature != Guid.Empty && symbolReaderModule.PdbGuid != moduleFile.PdbSignature) + { + _logger.LogTrace("ERROR: The PDB opened does not match the PDB desired. PDB GUID = {PdbGuid}, DESIRED GUID = {DesiredGuid}", + symbolReaderModule.PdbGuid, moduleFile.PdbSignature); + + return null; + } + + symbolReaderModule.ExePath = moduleFile.FilePath; + return symbolReaderModule.SourceLocationForManagedCode((uint)methodToken, ilOffset); + } + } + + return null; + } + private static StackTraceElement GetStackTraceElement(string frameName, SourceLocation? sourceLocation) { if (string.IsNullOrEmpty(frameName)) diff --git a/src/Management/test/Endpoint.Test/Actuators/ThreadDump/EventPipeThreadDumperTest.cs b/src/Management/test/Endpoint.Test/Actuators/ThreadDump/EventPipeThreadDumperTest.cs index 01346608b3..9ddf7acb2d 100644 --- a/src/Management/test/Endpoint.Test/Actuators/ThreadDump/EventPipeThreadDumperTest.cs +++ b/src/Management/test/Endpoint.Test/Actuators/ThreadDump/EventPipeThreadDumperTest.cs @@ -35,6 +35,12 @@ public async Task Can_resolve_source_location_from_pdb() StackTraceElement? backgroundThreadFrame = threads.SelectMany(thread => thread.StackTrace) .FirstOrDefault(frame => frame.MethodName == "BackgroundThreadCallback(class System.Object)"); + if (backgroundThreadFrame == null) + { + string logs = loggerProvider.GetAsText(); + throw new InvalidOperationException($"Failed to find expected stack frame. Captured log:{System.Environment.NewLine}{logs}"); + } + backgroundThreadFrame.Should().NotBeNull(); backgroundThreadFrame.IsNativeMethod.Should().BeFalse(); backgroundThreadFrame.ModuleName.Should().Be(GetType().Assembly.GetName().Name);