Fix line number issues in SOS (#734)
authorMike McLaughlin <mikem@microsoft.com>
Tue, 14 Jan 2020 08:22:12 +0000 (00:22 -0800)
committerGitHub <noreply@github.com>
Tue, 14 Jan 2020 08:22:12 +0000 (00:22 -0800)
Fix line number issues in SOS

The change is to subtract 1 from the IP used to get the source/line info when the stack frame is a leaf node. For hardware exceptions, the IP has already been adjusted (+1) by the runtime so they cancel out.

Fixes in SOS the same issues reported in https://github.com/dotnet/coreclr/issues/27765 and https://github.com/dotnet/coreclr/issues/25740 for the runtime's unhandled exception message.

Add some more PrintException -lines testing for thrown and hardware exceptions.

Gracefully fail with runtime module size == 0.

Fix "clrmodules" failure our vendors found.

Add line number (LineNum) check testcase

15 files changed:
debuggees.sln
src/SOS/SOS.NETCore/SymbolReader.cs
src/SOS/SOS.UnitTests/Debuggees/LineNums/LineNums.csproj [new file with mode: 0644]
src/SOS/SOS.UnitTests/Debuggees/LineNums/Program.cs [new file with mode: 0644]
src/SOS/SOS.UnitTests/SOS.cs
src/SOS/SOS.UnitTests/Scripts/DivZero.script
src/SOS/SOS.UnitTests/Scripts/LineNums.script [new file with mode: 0644]
src/SOS/SOS.UnitTests/Scripts/SimpleThrow.script
src/SOS/Strike/disasm.h
src/SOS/Strike/exts.h
src/SOS/Strike/runtime.cpp
src/SOS/Strike/strike.cpp
src/SOS/Strike/util.cpp
src/SOS/Strike/util.h
src/Tools/dotnet-dump/Analyzer.cs

index 74b27053cf00f76e330edc9f9b48a01813b33eb4..abffc6dacdc3e8686e71960d42ef575f454e7a17 100644 (file)
@@ -35,6 +35,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SymbolTestApp", "src\SOS\SO
 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
@@ -579,6 +581,46 @@ Global
                {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
@@ -599,6 +641,7 @@ Global
                {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}
index 50231e29ba093e099ade6ab891426fbf5025251f..1f17a22843b8edcd276a01a029d2ec0da5e67923 100644 (file)
@@ -19,6 +19,7 @@ using System.Reflection.Metadata.Ecma335;
 using System.Reflection.PortableExecutable;
 using System.Runtime.InteropServices;
 using System.Threading;
+using System.Threading.Tasks;
 
 namespace SOS
 {
@@ -309,7 +310,7 @@ 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);
                 }
diff --git a/src/SOS/SOS.UnitTests/Debuggees/LineNums/LineNums.csproj b/src/SOS/SOS.UnitTests/Debuggees/LineNums/LineNums.csproj
new file mode 100644 (file)
index 0000000..a951e54
--- /dev/null
@@ -0,0 +1,8 @@
+<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>
diff --git a/src/SOS/SOS.UnitTests/Debuggees/LineNums/Program.cs b/src/SOS/SOS.UnitTests/Debuggees/LineNums/Program.cs
new file mode 100644 (file)
index 0000000..721fd9c
--- /dev/null
@@ -0,0 +1,29 @@
+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");
+            }
+        }
+    }
+}
index b667cde6266bb13f97676baf18740a7af6440e4d..338abcec29e59d2c578d45ecfa1b0f2a686e5ae5 100644 (file)
@@ -144,6 +144,12 @@ public class SOS
         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)
     {
index 47379e38deec53193a6bc34476ac161f9ed21188..be215a4a99ed13fd17e34d35d2ecaa3a49570e0c 100644 (file)
@@ -10,7 +10,6 @@ LOADSOS
 
 # 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+
@@ -24,6 +23,22 @@ VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+[Dd]iv[Zz]ero.*!C\.Main(\(.*\))?\+0x<HEXVAL>\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
diff --git a/src/SOS/SOS.UnitTests/Scripts/LineNums.script b/src/SOS/SOS.UnitTests/Scripts/LineNums.script
new file mode 100644 (file)
index 0000000..cfbc537
--- /dev/null
@@ -0,0 +1,58 @@
+# 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*
index 278a45f3daeb6cca54d8d398de5ef39aff192572..c2bf379c881a2804e5279451ea93d7e566ba8367 100644 (file)
@@ -33,6 +33,18 @@ VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+[Ss]imple[Tt]hrow.*!(\$0_)?UserObject\.UseObject
 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
@@ -54,6 +66,6 @@ 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+(\*\*\* 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*\]
index 1eb3920cb027184e457d02e35f56191b8fd98e76..fb2ee3e6400915c319afa2a0f0d736893a90eb0a 100644 (file)
@@ -125,7 +125,6 @@ eTargetType GetFinalTarget(DWORD_PTR callee, DWORD_PTR* finalMDorIP);
 #ifndef THUMB_CODE
 #define THUMB_CODE 1
 #endif
-#define STACKWALK_CONTROLPC_ADJUST_OFFSET 2
 
 #ifdef SOS_TARGET_X86
 
@@ -177,6 +176,8 @@ public:
 
     virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
 
+    int StackWalkIPAdjustOffset() const { return 1; }
+
 private:
     X86Machine()  {}
     ~X86Machine() {}
@@ -244,6 +245,8 @@ public:
 
     virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
 
+    int StackWalkIPAdjustOffset() const { return 2; }
+
 private:
     ARMMachine()  {}
     ~ARMMachine() {}
@@ -313,6 +316,8 @@ public:
 
     virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
 
+    int StackWalkIPAdjustOffset() const { return 1; }
+
 private:
     AMD64Machine()  {}
     ~AMD64Machine() {}
@@ -378,6 +383,8 @@ public:
 
     virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
 
+    int StackWalkIPAdjustOffset() const { return 4; }
+
 private:
     ARM64Machine()  {}
     ~ARM64Machine() {}
index cefec80d91333cdea1c217cadcb9fb4360d698ec..2d630b87bdf596968e466aa91434ef8100c47c7a 100644 (file)
@@ -390,6 +390,8 @@ public:
     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()           {}
index cafe21bd1ce09099fda81d34a5385f038989715e..04d48bfa0974ad016ff117092ee5dd15b7116dea 100644 (file)
@@ -85,8 +85,16 @@ HRESULT Runtime::CreateInstance(bool isDesktop, Runtime **ppRuntime)
         }
         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;
index 27814e649ac57656320b3919df0aaef403c57962..4efa101bbc1d50bc6deb8dd310d037481eaa36a4 100644 (file)
@@ -2384,13 +2384,15 @@ size_t FormatGeneratedException (DWORD_PTR dataPtr,
     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';
@@ -2464,7 +2466,28 @@ size_t FormatGeneratedException (DWORD_PTR dataPtr,
             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);
             }
@@ -13439,6 +13462,8 @@ class ClrStackImpl
 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)))
@@ -13481,7 +13506,9 @@ public:
             
         TableOutput out(3, POINTERSIZE_HEX, AlignRight);
         out.WriteRow("Child SP", "IP", "Call Site");
-                
+
+        int frameNumber = 0;
+        int internalFrames = 0;
         do
         {
             if (IsInterrupt())
@@ -13516,6 +13543,8 @@ public:
                 // 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));
 
@@ -13534,8 +13563,27 @@ public:
                 }
                 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).
index 4bde52925deb7ec176c06d1ec087db8247715989..6cdae98c5134703345b53c929cd2f614a3de3395 100644 (file)
@@ -5169,7 +5169,7 @@ WString GetFrameFromAddress(TADDR frameAddr, IXCLRDataStackWalk *pStackWalk, BOO
     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;
@@ -5242,7 +5242,9 @@ WString MethodNameFromIP(CLRDATA_ADDRESS ip, BOOL bSuppressLines, BOOL bAssembly
 
         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("]");
         }
index 92666b8a154e6f45329a432486a9ea29983a12a4..28b6cccda185069e18b892329b98a21b2b4a6899 100644 (file)
@@ -2589,7 +2589,7 @@ typedef struct _CROSS_PLATFORM_CONTEXT {
 
 
 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);
 
index b48a75810256efc89dd8c8de7ec903703484f95e..339901db5493ed0b6b189f3d5ba0c5ee87c2a7cd 100644 (file)
@@ -183,7 +183,10 @@ namespace Microsoft.Diagnostics.Tools.Dump
             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)
             {
@@ -240,7 +243,7 @@ namespace Microsoft.Diagnostics.Tools.Dump
 
                 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;
             }