EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SymbolTestDll", "src\SOS\SOS.UnitTests\Debuggees\SymbolTestApp\SymbolTestDll\SymbolTestDll.csproj", "{8C27904A-47C0-44C7-B191-88FF34580CBE}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LineNums", "src\SOS\SOS.UnitTests\Debuggees\LineNums\LineNums.csproj", "{84881FB8-37E1-4D9B-B27E-9831C30DCC04}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Checked|Any CPU = Checked|Any CPU
{8C27904A-47C0-44C7-B191-88FF34580CBE}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{8C27904A-47C0-44C7-B191-88FF34580CBE}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
{8C27904A-47C0-44C7-B191-88FF34580CBE}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Checked|Any CPU.Build.0 = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Checked|ARM.ActiveCfg = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Checked|ARM.Build.0 = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Checked|ARM64.ActiveCfg = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Checked|ARM64.Build.0 = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Checked|x64.ActiveCfg = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Checked|x64.Build.0 = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Checked|x86.ActiveCfg = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Checked|x86.Build.0 = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Debug|ARM.Build.0 = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Debug|x64.Build.0 = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Debug|x86.Build.0 = Debug|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Release|Any CPU.Build.0 = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Release|ARM.ActiveCfg = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Release|ARM.Build.0 = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Release|ARM64.Build.0 = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Release|x64.ActiveCfg = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Release|x64.Build.0 = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Release|x86.ActiveCfg = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.Release|x86.Build.0 = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.RelWithDebInfo|ARM64.ActiveCfg = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.RelWithDebInfo|ARM64.Build.0 = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
{B50D14DB-8EE5-47BD-B412-62FA5C693CC7} = {C3072949-6D24-451B-A308-2F3621F858B0}
{112FE2A7-3FD2-4496-8A14-171898AD5CF5} = {C3072949-6D24-451B-A308-2F3621F858B0}
{8C27904A-47C0-44C7-B191-88FF34580CBE} = {C3072949-6D24-451B-A308-2F3621F858B0}
+ {84881FB8-37E1-4D9B-B27E-9831C30DCC04} = {C3072949-6D24-451B-A308-2F3621F858B0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {46465737-C938-44FC-BE1A-4CE139EBB5E0}
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Threading;
+using System.Threading.Tasks;
namespace SOS
{
}
}
}
- catch (Exception ex) when (ex is BadInputFormatException || ex is InvalidVirtualAddressException)
+ catch (Exception ex) when (ex is BadInputFormatException || ex is InvalidVirtualAddressException || ex is TaskCanceledException)
{
s_tracer.Error("{0}/{1:X16}: {2}", moduleFilePath, address, ex.Message);
}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <Optimize>true</Optimize>
+ <TargetFramework Condition="'$(BuildProjectFramework)' != ''">$(BuildProjectFramework)</TargetFramework>
+ <TargetFrameworks Condition="'$(BuildProjectFramework)' == ''">netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
+ </PropertyGroup>
+</Project>
--- /dev/null
+using System;
+using System.Runtime.CompilerServices;
+
+namespace LineNums
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ Foo();
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static void Foo()
+ {
+ Bar();
+ Bar();
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static void Bar()
+ {
+ while (true)
+ {
+ throw new Exception("This should be line #25");
+ }
+ }
+ }
+}
await RunTest(config, "SimpleThrow", "SimpleThrow.script", testTriage: true);
}
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task LineNums(TestConfiguration config)
+ {
+ await RunTest(config, "LineNums", "LineNums.script", testTriage: true);
+ }
+
[SkippableTheory, MemberData(nameof(Configurations))]
public async Task NestedExceptionTest(TestConfiguration config)
{
# Verifying that PrintException gives us the right exception in the format above.
SOSCOMMAND:PrintException
-
VERIFY:Exception object:\s+<HEXVAL>\s+
VERIFY:Exception type:\s+System\.DivideByZeroException\s+
VERIFY:Message:\s+(<Invalid Object>|Attempted to divide by zero\.)\s+
VERIFY:(StackTraceString: <none>\s+)?\s+
VERIFY:HResult:\s+80020012\s+
+SOSCOMMAND:PrintException -lines
+VERIFY:Exception object:\s+<HEXVAL>\s+
+VERIFY:Exception type:\s+System\.DivideByZeroException\s+
+VERIFY:Message:\s+(<Invalid Object>|Attempted to divide by zero\.)\s+
+VERIFY:InnerException:\s+<none>\s+
+VERIFY:StackTrace \(generated\):\s+
+VERIFY:\s+SP\s+IP\s+\s+Function\s+
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+[Dd]iv[Zz]ero.*!C\.DivideByZero(\(.*\))?\+0x<HEXVAL>\s+
+VERIFY:\[.*[\\|/]Debuggees[\\|/](dotnet.+[\\|/])?DivZero[\\|/]DivZero\.cs @ 12\s*\]\s*
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+[Dd]iv[Zz]ero.*!C\.F3(\(.*\))?\+0x<HEXVAL>\s+
+VERIFY:\[.*[\\|/]Debuggees[\\|/](dotnet.+[\\|/])?DivZero[\\|/]DivZero\.cs @ 21\s*\]\s*
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+[Dd]iv[Zz]ero.*!C\.F2(\(.*\))?\+0x<HEXVAL>\s+
+VERIFY:\[.*[\\|/]Debuggees[\\|/](dotnet.+[\\|/])?DivZero[\\|/]DivZero\.cs @ 33\s*\]\s*
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+[Dd]iv[Zz]ero.*!C\.Main(\(.*\))?\+0x<HEXVAL>\s+
+VERIFY:\[.*[\\|/]Debuggees[\\|/](dotnet.+[\\|/])?DivZero[\\|/]DivZero\.cs @ 54\s*\]\s*
+
# Verify that Threads (clrthreads) works
IFDEF:DOTNETDUMP
SOSCOMMAND:clrthreads
--- /dev/null
+# Test source line numbers for corner cases
+
+CONTINUE
+
+LOADSOS
+
+SOSCOMMAND:PrintException
+VERIFY:Exception object:\s+<HEXVAL>\s+
+VERIFY:Exception type:\s+System\.Exception\s+
+VERIFY:Message:\s+(<Invalid Object>|This should be line #25)\s+
+VERIFY:InnerException:\s+<none>\s+
+VERIFY:StackTrace\s+\(generated\):\s+
+VERIFY:\s+SP\s+IP\s+Function\s+
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+LineNums.*!LineNums\.Program\.Bar.*\+0x<HEXVAL>\s+
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+LineNums.*!LineNums\.Program\.Foo.*\+0x<HEXVAL>\s+
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+LineNums.*!LineNums\.Program\.Main.*\+0x<HEXVAL>\s+
+VERIFY:HResult:\s+80131500
+
+SOSCOMMAND:PrintException -lines
+VERIFY:Exception object:\s+<HEXVAL>\s+
+VERIFY:Exception type:\s+System\.Exception\s+
+VERIFY:Message:\s+(<Invalid Object>|This should be line #25)\s+
+VERIFY:InnerException:\s+<none>\s+
+VERIFY:StackTrace\s+\(generated\):\s+
+VERIFY:\s+SP\s+IP\s+Function\s+
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+LineNums.*!LineNums\.Program\.Bar.*\+0x<HEXVAL>\s+
+VERIFY:\[.*[\\|/]Debuggees[\\|/]LineNums[\\|/]Program\.cs @ 25\s*\]\s*
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+LineNums.*!LineNums\.Program\.Foo.*\+0x<HEXVAL>\s+
+VERIFY:\[.*[\\|/]Debuggees[\\|/]LineNums[\\|/]Program\.cs @ 16\s*\]\s*
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+LineNums.*!LineNums\.Program\.Main.*\+0x<HEXVAL>\s+
+VERIFY:\[.*[\\|/]Debuggees[\\|/]LineNums[\\|/]Program\.cs @ 10\s*\]\s*
+
+# Verify that Threads (clrthreads) works
+IFDEF:DOTNETDUMP
+SOSCOMMAND:clrthreads
+ENDIF:DOTNETDUMP
+!IFDEF:DOTNETDUMP
+SOSCOMMAND:Threads
+ENDIF:DOTNETDUMP
+VERIFY:\s*ThreadCount:\s+<DECVAL>\s+
+VERIFY:\s+UnstartedThread:\s+<DECVAL>\s+
+VERIFY:\s+BackgroundThread:\s+<DECVAL>\s+
+VERIFY:\s+PendingThread:\s+<DECVAL>\s+
+VERIFY:\s+DeadThread:\s+<DECVAL>\s+
+VERIFY:\s+Hosted Runtime:\s+no\s+
+VERIFY:\s+ID\s+OSID\s+ThreadOBJ\s+State.*\s+
+VERIFY:\s+<DECVAL>\s+<DECVAL>\s+<HEXVAL>\s+<HEXVAL>.*\s+
+
+# Verify that ClrStack with no options works
+SOSCOMMAND:ClrStack
+VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
+VERIFY:\s+Child\s+SP\s+IP\s+Call Site\s+
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+LineNums\.Program\.Bar\(\).*\s+
+VERIFY:\[.*[\\|/]Debuggees[\\|/]LineNums[\\|/]Program\.cs @ 25\s*\]\s*
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+LineNums\.Program\.Foo\(\).*\s+
+VERIFY:\[.*[\\|/]Debuggees[\\|/]LineNums[\\|/]Program\.cs @ 16\s*\]\s*
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+LineNums\.Program\.Main\(System.String\[\]\).*\s+
+VERIFY:\[.*[\\|/]Debuggees[\\|/]LineNums[\\|/]Program\.cs @ 10\s*\]\s*
VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+[Ss]imple[Tt]hrow.*!(\$0_)?Simple\.Main.*\+0x<HEXVAL>\s+
VERIFY:HResult:\s+80131509
+SOSCOMMAND:PrintException -lines
+VERIFY:Exception object:\s+<HEXVAL>\s+
+VERIFY:Exception type:\s+System\.InvalidOperationException\s+
+VERIFY:Message:\s+(<Invalid Object>|Throwing an invalid operation\.\.\.\.)\s+
+VERIFY:InnerException:\s+<none>\s+
+VERIFY:StackTrace\s+\(generated\):\s+
+VERIFY:\s+SP\s+IP\s+Function\s+
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+[Ss]imple[Tt]hrow.*!(\$0_)?UserObject\.UseObject.*\+0x<HEXVAL>\s*
+VERIFY:\[.*[\\|/]Debuggees[\\|/](dotnet.+[\\|/])?SimpleThrow[\\|/]UserObject\.cs @ 16\s*\]\s*
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+[Ss]imple[Tt]hrow.*!(\$0_)?Simple\.Main.*\+0x<HEXVAL>\s+
+VERIFY:\[.*[\\|/]Debuggees[\\|/](dotnet.+[\\|/])?SimpleThrow[\\|/]SimpleThrow\.cs @ 9\s*\]\s*
+
# Verify that Threads (clrthreads) works
IFDEF:DOTNETDUMP
SOSCOMMAND:clrthreads
VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
VERIFY:\s+Child\s+SP\s+IP\s+Call Site\s+
VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+(\*\*\* WARNING: Unable to verify checksum for SimpleThrow.exe\s*)?UserObject\.UseObject.*\s+
-VERIFY:[.*[\\|/]Debuggees[\\|/](dotnet.+[\\|/])?SimpleThrow[\\|/]UserObject\.cs @ 18\s*\]
+VERIFY:[.*[\\|/]Debuggees[\\|/](dotnet.+[\\|/])?SimpleThrow[\\|/]UserObject\.cs @ 16\s*\]
VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+Simple\.Main\(\)\s+
VERIFY:[.*[\\|/]Debuggees[\\|/](dotnet.+[\\|/])?SimpleThrow[\\|/]SimpleThrow\.cs @ 9\s*\]
#ifndef THUMB_CODE
#define THUMB_CODE 1
#endif
-#define STACKWALK_CONTROLPC_ADJUST_OFFSET 2
#ifdef SOS_TARGET_X86
virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+ int StackWalkIPAdjustOffset() const { return 1; }
+
private:
X86Machine() {}
~X86Machine() {}
virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+ int StackWalkIPAdjustOffset() const { return 2; }
+
private:
ARMMachine() {}
~ARMMachine() {}
virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+ int StackWalkIPAdjustOffset() const { return 1; }
+
private:
AMD64Machine() {}
~AMD64Machine() {}
virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+ int StackWalkIPAdjustOffset() const { return 4; }
+
private:
ARM64Machine() {}
~ARM64Machine() {}
typedef void (*printfFtn)(const char* fmt, ...);
// Dumps the GCInfo
virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const = 0;
+ // The amount of bytes to adjust the IP for software exception throw instructions (the STACKWALK_CONTROLPC_ADJUST_OFFSET define in the runtime)
+ virtual int StackWalkIPAdjustOffset() const = 0;
protected:
IMachine() {}
}
if (SUCCEEDED(hr))
{
- *ppRuntime = new Runtime(isDesktop, moduleIndex, moduleAddress, moduleSize);
- OnUnloadTask::Register(CleanupRuntimes);
+ if (moduleSize > 0)
+ {
+ *ppRuntime = new Runtime(isDesktop, moduleIndex, moduleAddress, moduleSize);
+ OnUnloadTask::Register(CleanupRuntimes);
+ }
+ else
+ {
+ ExtOut("Runtime (%s) module size == 0\n", runtimeModuleName);
+ hr = E_INVALIDARG;
+ }
}
}
return hr;
UINT bytes,
__out_ecount_opt(bufferLength) WCHAR *wszBuffer,
size_t bufferLength,
- BOOL bAsync,
+ BOOL bAsync, // hardware exception if true
BOOL bNestedCase = FALSE,
BOOL bLineNumbers = FALSE)
{
UINT count = bytes / sizeof(StackTraceElement);
size_t Length = 0;
+ _ASSERTE(g_targetMachine != nullptr);
+
if (wszBuffer && bufferLength > 0)
{
wszBuffer[0] = L'\0';
WCHAR filename[MAX_LONGPATH] = W("");
ULONG linenum = 0;
if (bLineNumbers &&
- SUCCEEDED(GetLineByOffset(TO_CDADDR(ste.ip), &linenum, filename, _countof(filename))))
+ // To get the source line number of the actual code that threw an exception, the IP needs
+ // to be adjusted in certain cases.
+ //
+ // The IP of the stack frame points to either:
+ //
+ // 1) The instruction that caused a hardware exception (div by zero, null ref, etc).
+ // 2) The instruction after the call to an internal runtime function (FCALL like IL_Throw,
+ // IL_Rethrow, JIT_OverFlow, etc.) that caused a software exception.
+ // 3) The instruction after the call to a managed function (non-leaf node).
+ //
+ // #2 and #3 are the cases that need to adjust IP because they point after the call instruction
+ // and may point to the next (incorrect) IL instruction/source line. We distinguish these from
+ // #1 by the bAsync flag which is set to true for hardware exceptions and that it is a leaf node
+ // (i == 0).
+ //
+ // When the IP needs to be adjusted it is a lot simpler to decrement IP instead of trying to figure
+ // out the beginning of the instruction. It is enough for GetLineByOffset to return the correct line number.
+ //
+ // The unmodified IP is displayed (above by DumpMDInfoBuffer) which points after the exception in most
+ // cases. This means that the printed IP and the printed line number often will not map to one another
+ // and this is intentional.
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(bAsync && i == 0 ? ste.ip : ste.ip - g_targetMachine->StackWalkIPAdjustOffset()), &linenum, filename, _countof(filename))))
{
swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s [%s @ %d]\n"), so.String(), filename, linenum);
}
public:
static void PrintThread(ULONG osID, BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bFull, BOOL bDisplayRegVals)
{
+ _ASSERTE(g_targetMachine != nullptr);
+
// Symbols variables
ULONG symlines = 0; // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
TableOutput out(3, POINTERSIZE_HEX, AlignRight);
out.WriteRow("Child SP", "IP", "Call Site");
-
+
+ int frameNumber = 0;
+ int internalFrames = 0;
do
{
if (IsInterrupt())
// Print the method/Frame info
if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
{
+ internalFrames++;
+
// Skip the instruction pointer because it doesn't really mean anything for method frames
out.WriteColumn(1, bFull ? String("") : NativePtr(ip));
}
else
{
+ // To get the source line number of the actual code that threw an exception, the IP needs
+ // to be adjusted in certain cases.
+ //
+ // The IP of stack frame points to either:
+ //
+ // 1) Currently executing instruction (if you hit a breakpoint or are single stepping through).
+ // 2) The instruction that caused a hardware exception (div by zero, null ref, etc).
+ // 3) The instruction after the call to an internal runtime function (FCALL like IL_Throw,
+ // JIT_OverFlow, etc.) that caused a software exception.
+ // 4) The instruction after the call to a managed function (non-leaf node).
+ //
+ // #3 and #4 are the cases that need IP adjusted back because they point after the call instruction
+ // and may point to the next (incorrect) IL instruction/source line. We distinguish these from #1
+ // or #2 by either being non-leaf node stack frame (#4) or the present of an internal stack frame (#3).
+ bool bAdjustIPForLineNumber = frameNumber > 0 || internalFrames > 0;
+ frameNumber++;
+
+ // The unmodified IP is displayed which points after the exception in most cases. This means that the
+ // printed IP and the printed line number often will not map to one another and this is intentional.
out.WriteColumn(1, InstructionPtr(ip));
- out.WriteColumn(2, MethodNameFromIP(ip, bSuppressLines, bFull, bFull));
+ out.WriteColumn(2, MethodNameFromIP(ip, bSuppressLines, bFull, bFull, bAdjustIPForLineNumber));
// Print out gc references. refCount will be zero if bGC is false (or if we
// failed to fetch gc reference information).
return frameOutput;
}
-WString MethodNameFromIP(CLRDATA_ADDRESS ip, BOOL bSuppressLines, BOOL bAssemblyName, BOOL bDisplacement)
+WString MethodNameFromIP(CLRDATA_ADDRESS ip, BOOL bSuppressLines, BOOL bAssemblyName, BOOL bDisplacement, BOOL bAdjustIPForLineNumber)
{
ULONG linenum;
WString methodOutput;
ArrayHolder<WCHAR> wszFileName = new WCHAR[MAX_LONGPATH];
if (!bSuppressLines &&
- SUCCEEDED(GetLineByOffset(TO_CDADDR(ip), &linenum, wszFileName, MAX_LONGPATH)))
+ // If the IP needs to be adjusted, it is a lot simpler to decrement IP instead of trying to figure out
+ // the beginning of the instruction. It is enough for GetLineByOffset to return the correct line number.
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(bAdjustIPForLineNumber ? ip - g_targetMachine->StackWalkIPAdjustOffset() : ip), &linenum, wszFileName, MAX_LONGPATH)))
{
methodOutput += WString(W(" [")) + wszFileName + W(" @ ") + Decimal(linenum) + W("]");
}
WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj = true);
-WString MethodNameFromIP(CLRDATA_ADDRESS methodDesc, BOOL bSuppressLines = FALSE, BOOL bAssemblyName = FALSE, BOOL bDisplacement = FALSE);
+WString MethodNameFromIP(CLRDATA_ADDRESS methodDesc, BOOL bSuppressLines = FALSE, BOOL bAssemblyName = FALSE, BOOL bDisplacement = FALSE, BOOL bAdjustIPForLineNumber = FALSE);
HRESULT GetGCRefs(ULONG osID, SOSStackRefData **ppRefs, unsigned int *pRefCnt, SOSStackRefError **ppErrors, unsigned int *pErrCount);
WString GetFrameFromAddress(TADDR frameAddr, IXCLRDataStackWalk *pStackwalk = NULL, BOOL bAssemblyName = FALSE);
string dacFilePath = GetDacFile(clrInfo);
try
{
- runtime = clrInfo.CreateRuntime(dacFilePath);
+ // Ignore the DAC version mismatch that can happen on Linux because the clrmd ELF dump
+ // reader returns 0.0.0.0 for the runtime module that the DAC is matched against. This
+ // will be fixed in clrmd 2.0 but not 1.1.
+ runtime = clrInfo.CreateRuntime(dacFilePath, ignoreMismatch: RuntimeInformation.IsOSPlatform(OSPlatform.Linux));
}
catch (DllNotFoundException ex)
{
if (_dacFilePath == null)
{
- throw new FileNotFoundException("Could not find matching DAC for this runtime: {0}", clrInfo.ModuleInfo.FileName);
+ throw new FileNotFoundException($"Could not find matching DAC for this runtime: {clrInfo.ModuleInfo.FileName}");
}
_isDesktop = clrInfo.Flavor == ClrFlavor.Desktop;
}