Skip to content

Commit 228155e

Browse files
Copilotelinor-fung
andcommitted
Include full requesting assembly chain instead of just the parent
Walk the inner exception chain to build the full dependency path. When A→B→C→D and D fails, D's FusionLog now shows: Requesting assembly: 'C' Requesting assembly: 'B' Requesting assembly: 'A' At each Throw(AssemblySpec*) level, the current parent assembly name is appended to every inner EEFileLoadException's chain. The managed FormatRequestingAssemblyChain helper formats each newline-separated name with the "Requesting assembly:" resource string. Co-authored-by: elinor-fung <47805090+elinor-fung@users.noreply.github.com>
1 parent 1863344 commit 228155e

4 files changed

Lines changed: 73 additions & 16 deletions

File tree

src/coreclr/System.Private.CoreLib/src/System/BadImageFormatException.CoreCLR.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ private BadImageFormatException(string? fileName, int hResult)
1515
}
1616

1717
// Do not delete: this is invoked from native code.
18-
// Used when the requesting assembly is known, to provide assembly load dependency context.
19-
private BadImageFormatException(string? fileName, string? requestingAssemblyName, int hResult)
18+
// Used when the requesting assembly chain is known, to provide assembly load dependency context.
19+
private BadImageFormatException(string? fileName, string? requestingAssemblyChain, int hResult)
2020
: base(null)
2121
{
2222
HResult = hResult;
2323
_fileName = fileName;
24-
if (requestingAssemblyName is not null)
25-
_fusionLog = SR.Format(SR.IO_FileLoad_RequestingAssembly, requestingAssemblyName);
24+
if (requestingAssemblyChain is not null)
25+
_fusionLog = IO.FileLoadException.FormatRequestingAssemblyChain(requestingAssemblyChain);
2626
SetMessageField();
2727
}
2828
}

src/coreclr/System.Private.CoreLib/src/System/IO/FileLoadException.CoreCLR.cs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,44 @@ private FileLoadException(string? fileName, int hResult)
1818
}
1919

2020
// Do not delete: this is invoked from native code.
21-
// Used when the requesting assembly is known, to provide assembly load dependency context.
22-
private FileLoadException(string? fileName, string? requestingAssemblyName, int hResult)
21+
// Used when the requesting assembly chain is known, to provide assembly load dependency context.
22+
// The requestingAssemblyChain parameter is a newline-separated list of assembly display names,
23+
// from immediate parent to root ancestor.
24+
private FileLoadException(string? fileName, string? requestingAssemblyChain, int hResult)
2325
: base(null)
2426
{
2527
HResult = hResult;
2628
FileName = fileName;
27-
if (requestingAssemblyName is not null)
28-
FusionLog = SR.Format(SR.IO_FileLoad_RequestingAssembly, requestingAssemblyName);
29+
if (requestingAssemblyChain is not null)
30+
FusionLog = FormatRequestingAssemblyChain(requestingAssemblyChain);
2931
_message = FormatFileLoadExceptionMessage(FileName, HResult);
3032
}
3133

34+
internal static string FormatRequestingAssemblyChain(string requestingAssemblyChain)
35+
{
36+
int newlineIndex = requestingAssemblyChain.IndexOf('\n');
37+
if (newlineIndex < 0)
38+
return SR.Format(SR.IO_FileLoad_RequestingAssembly, requestingAssemblyChain);
39+
40+
var result = new System.Text.StringBuilder();
41+
int start = 0;
42+
while (start < requestingAssemblyChain.Length)
43+
{
44+
int end = requestingAssemblyChain.IndexOf('\n', start);
45+
string name = end >= 0
46+
? requestingAssemblyChain.Substring(start, end - start)
47+
: requestingAssemblyChain.Substring(start);
48+
49+
if (result.Length > 0)
50+
result.AppendLine();
51+
result.Append(SR.Format(SR.IO_FileLoad_RequestingAssembly, name));
52+
53+
start = end >= 0 ? end + 1 : requestingAssemblyChain.Length;
54+
}
55+
56+
return result.ToString();
57+
}
58+
3259
internal static string FormatFileLoadExceptionMessage(string? fileName, int hResult)
3360
{
3461
string? format = null;

src/coreclr/System.Private.CoreLib/src/System/IO/FileNotFoundException.CoreCLR.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ private FileNotFoundException(string? fileName, int hResult)
1515
}
1616

1717
// Do not delete: this is invoked from native code.
18-
// Used when the requesting assembly is known, to provide assembly load dependency context.
19-
private FileNotFoundException(string? fileName, string? requestingAssemblyName, int hResult)
18+
// Used when the requesting assembly chain is known, to provide assembly load dependency context.
19+
private FileNotFoundException(string? fileName, string? requestingAssemblyChain, int hResult)
2020
: base(null)
2121
{
2222
HResult = hResult;
2323
FileName = fileName;
24-
if (requestingAssemblyName is not null)
25-
FusionLog = SR.Format(SR.IO_FileLoad_RequestingAssembly, requestingAssemblyName);
24+
if (requestingAssemblyChain is not null)
25+
FusionLog = FileLoadException.FormatRequestingAssemblyChain(requestingAssemblyChain);
2626
SetMessageField();
2727
}
2828
}

src/coreclr/vm/clrex.cpp

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1715,7 +1715,7 @@ void DECLSPEC_NORETURN EEFileLoadException::Throw(AssemblySpec *pSpec, HRESULT
17151715
StackSString name;
17161716
pSpec->GetDisplayName(0, name);
17171717

1718-
// Extract the requesting assembly name for diagnostic purposes
1718+
// Extract the requesting assembly chain for diagnostic purposes
17191719
{
17201720
FAULT_NOT_FATAL();
17211721

@@ -1726,9 +1726,39 @@ void DECLSPEC_NORETURN EEFileLoadException::Throw(AssemblySpec *pSpec, HRESULT
17261726
Assembly *pParentAssembly = pSpec->GetParentAssembly();
17271727
if (pParentAssembly != NULL)
17281728
{
1729-
StackSString requestingName;
1730-
pParentAssembly->GetDisplayName(requestingName);
1731-
pException->SetRequestingAssembly(requestingName);
1729+
StackSString parentName;
1730+
pParentAssembly->GetDisplayName(parentName);
1731+
1732+
// Set the requesting assembly for this exception
1733+
pException->SetRequestingAssembly(parentName);
1734+
1735+
// Walk the inner exception chain and append this parent to each
1736+
// inner exception's chain, so that every exception in the chain
1737+
// carries the full dependency path from its perspective
1738+
EEFileLoadException *pInnerFLE = NULL;
1739+
Exception *pWalk = inner2;
1740+
int depth = 0;
1741+
while (pWalk != NULL && depth < 20)
1742+
{
1743+
if (EEFileLoadException::CheckType(pWalk))
1744+
{
1745+
pInnerFLE = (EEFileLoadException*)pWalk;
1746+
if (!pInnerFLE->m_requestingAssemblyName.IsEmpty())
1747+
{
1748+
StackSString updatedChain;
1749+
updatedChain.Set(pInnerFLE->m_requestingAssemblyName);
1750+
updatedChain.Append(W("\n"));
1751+
updatedChain.Append(parentName);
1752+
pInnerFLE->SetRequestingAssembly(updatedChain);
1753+
}
1754+
pWalk = pInnerFLE->m_innerException;
1755+
}
1756+
else
1757+
{
1758+
break;
1759+
}
1760+
depth++;
1761+
}
17321762
}
17331763

17341764
STRESS_LOG3(LF_EH, LL_INFO100, "EX_THROW_WITH_INNER Type = 0x%x HR = 0x%x, "

0 commit comments

Comments
 (0)