Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/SOS/Strike/dbgengservices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <stdint.h>
#include "hostservices.h"
#include "exts.h"
#include "symbols.h"

extern IMachine* GetTargetMachine(ULONG processorType);

Expand Down Expand Up @@ -860,6 +861,9 @@ DbgEngServices::SetCurrentThreadIdFromSystemId(
void
DbgEngServices::InitializeSymbolStoreFromSymPath()
{
// Clear the negative symbol lookup cache so modules are retried with the new path.
ClearSymbolLookupCache();

ISymbolService* symbolService = GetSymbolService();
if (symbolService != nullptr)
{
Expand Down
5 changes: 5 additions & 0 deletions src/SOS/Strike/managedcommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

#include "exts.h"
#include "symbols.h"

// Windows host only managed command stubs

Expand Down Expand Up @@ -142,6 +143,10 @@ DECLARE_API(ObjSize)
DECLARE_API(SetSymbolServer)
{
INIT_API_EXT();

// Clear negative symbol lookup cache so modules are retried with the new configuration.
ClearSymbolLookupCache();

return ExecuteManagedOnlyCommand("setsymbolserver", args);
}

Expand Down
34 changes: 33 additions & 1 deletion src/SOS/Strike/symbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@
#undef IfFailRet
#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)

// Tracks module PE addresses for which symbol loading has already failed,
// so we don't repeatedly hit symbol servers for the same module.
// Cleared when the symbol path changes (see ClearSymbolLookupCache).
static std::set<ULONG64> g_failedSymbolLookups;

void ClearSymbolLookupCache()
{
g_failedSymbolLookups.clear();
}

#ifndef FEATURE_PAL
HMODULE g_hmoduleSymBinder = nullptr;
ISymUnmanagedBinder3 *g_pSymBinder = nullptr;
Expand Down Expand Up @@ -314,6 +324,14 @@ HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataMod
ExtOut("LoadSymbols GetClrModuleImages FAILED 0x%08x\n", hr);
return hr;
}

// Skip modules that have already failed symbol loading to avoid repeated
// network round-trips to symbol servers (see dotnet/diagnostics#675).
if (moduleBase != 0 && g_failedSymbolLookups.find(moduleBase) != g_failedSymbolLookups.end())
{
return E_FAIL;
}

if (GetSymbolService() == nullptr || !HasPortablePDB(moduleBase))
{
hr = LoadSymbolsForWindowsPDB(pMD, moduleBase, pModuleName, FALSE);
Expand All @@ -328,6 +346,13 @@ HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataMod
#endif
}

// Skip modules that have already failed symbol loading to avoid repeated
// network round-trips to symbol servers (see dotnet/diagnostics#675).
if (moduleData.LoadedPEAddress != 0 && g_failedSymbolLookups.find(moduleData.LoadedPEAddress) != g_failedSymbolLookups.end())
{
return E_FAIL;
}

#ifndef FEATURE_PAL
if (GetSymbolService() == nullptr || !HasPortablePDB(moduleData.LoadedPEAddress))
{
Expand All @@ -340,14 +365,21 @@ HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataMod
}
#endif // FEATURE_PAL

return LoadSymbolsForPortablePDB(
hr = LoadSymbolsForPortablePDB(
pModuleName,
moduleData.IsInMemory,
moduleData.IsFileLayout,
moduleData.LoadedPEAddress,
moduleData.LoadedPESize,
moduleData.InMemoryPdbAddress,
moduleData.InMemoryPdbSize);

if (FAILED(hr) && moduleData.LoadedPEAddress != 0)
{
g_failedSymbolLookups.insert(moduleData.LoadedPEAddress);
}

return hr;
}

#ifndef FEATURE_PAL
Expand Down
4 changes: 4 additions & 0 deletions src/SOS/Strike/symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,7 @@ GetLineByOffset(
__out_ecount(cchFileName) WCHAR* pwszFileName,
___in ULONG cchFileName,
___in BOOL bAdjustOffsetForLineNumber = FALSE);

// Clears the negative cache for failed symbol lookups, allowing modules
// to be retried. Call when the symbol path changes.
void ClearSymbolLookupCache();
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,35 @@ public void SymbolPathTests()
symbolService.DisableSymbolStore();
}

[Fact]
public void OpenSymbolFile_ReturnsNull_ForInvalidPEStream()
{
SymbolService symbolService = new(this);

// OpenSymbolFile should return null (not throw) for non-PE data.
// The native SymbolReader::LoadSymbols layer caches these failures
// to avoid repeated symbol server lookups (dotnet/diagnostics#675),
// but that caching is in native C++ and cannot be tested here.
byte[] bogusData = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
using System.IO.MemoryStream stream = new(bogusData);

ISymbolFile result = symbolService.OpenSymbolFile("bogus.dll", isFileLayout: false, stream);
Assert.Null(result);
}

[Fact]
public void OpenSymbolFile_ReturnsNull_ForInvalidPdbStream()
{
SymbolService symbolService = new(this);

// A stream that is not a valid portable PDB should return null.
byte[] bogusData = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 };
using System.IO.MemoryStream stream = new(bogusData);

ISymbolFile result = symbolService.OpenSymbolFile(stream);
Assert.Null(result);
}
Comment thread
leculver marked this conversation as resolved.

#region IHost

public IServiceEvent OnShutdownEvent { get; } = new ServiceEvent();
Expand Down
Loading