From 6fa79d3ecf24a0b0fdd5b218c2be6a646b83714b Mon Sep 17 00:00:00 2001 From: Mikhail Filippov Date: Fri, 8 Jun 2018 11:11:55 +0300 Subject: [PATCH] Add support dump to dir passed by THREAD_DUMP_DIR environment variable. Migrate to new csproj. Create NuGet package. --- .gitignore | 2 + README.md | 6 +- src/ClrStack.sln | 22 +++--- src/ClrStack.x64/ClrStack.x64.csproj | 14 ++++ src/ClrStack.x64/Program.cs | 70 +++++++++++++++++++ src/ClrStack.x86/ClrStack.x86.csproj | 19 ++++++ src/ClrStack/ClrStack.csproj | 91 ++++++++----------------- src/ClrStack/Program.cs | 39 ----------- src/ClrStack/Properties/AssemblyInfo.cs | 19 ------ src/ClrStack/packages.config | 4 -- 10 files changed, 150 insertions(+), 136 deletions(-) create mode 100644 src/ClrStack.x64/ClrStack.x64.csproj create mode 100644 src/ClrStack.x64/Program.cs create mode 100644 src/ClrStack.x86/ClrStack.x86.csproj delete mode 100644 src/ClrStack/Program.cs delete mode 100644 src/ClrStack/Properties/AssemblyInfo.cs delete mode 100644 src/ClrStack/packages.config diff --git a/.gitignore b/.gitignore index 3e759b7..460ed95 100644 --- a/.gitignore +++ b/.gitignore @@ -328,3 +328,5 @@ ASALocalRun/ # MFractors (Xamarin productivity tool) working folder .mfractor/ + +build/ diff --git a/README.md b/README.md index 33908ba..7045e71 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ -Usage: ClrStack.exe PID \ No newline at end of file +**Tool for capturing managed stack traces from .NET applications.** + +```Usage: ClrStack.exe PID``` + +You could pass THREAD_DUMP_DIR environment variable with path to existing directory to write thread dump in file. \ No newline at end of file diff --git a/src/ClrStack.sln b/src/ClrStack.sln index cf7ca20..cce98d9 100644 --- a/src/ClrStack.sln +++ b/src/ClrStack.sln @@ -1,19 +1,21 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClrStack", "ClrStack\ClrStack.csproj", "{2ECDA63E-31B6-4562-B88D-FDC51DCA1959}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClrStack.x64", "ClrStack.x64\ClrStack.x64.csproj", "{4C4DD06E-14DF-4B70-B28B-3D4A4EB0DAC0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClrStack.x86", "ClrStack.x86\ClrStack.x86.csproj", "{DEBBF0D6-F59A-45BC-AC4F-5FC7CE976C71}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClrStack", "ClrStack\ClrStack.csproj", "{A5A25E3F-D2AE-4130-91D4-56A8D0AA8201}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 + Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2ECDA63E-31B6-4562-B88D-FDC51DCA1959}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2ECDA63E-31B6-4562-B88D-FDC51DCA1959}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2ECDA63E-31B6-4562-B88D-FDC51DCA1959}.Release|x64.ActiveCfg = Release|x64 - {2ECDA63E-31B6-4562-B88D-FDC51DCA1959}.Release|x64.Build.0 = Release|x64 - {2ECDA63E-31B6-4562-B88D-FDC51DCA1959}.Release|x86.ActiveCfg = Release|x86 - {2ECDA63E-31B6-4562-B88D-FDC51DCA1959}.Release|x86.Build.0 = Release|x86 + {4C4DD06E-14DF-4B70-B28B-3D4A4EB0DAC0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C4DD06E-14DF-4B70-B28B-3D4A4EB0DAC0}.Release|Any CPU.Build.0 = Release|Any CPU + {DEBBF0D6-F59A-45BC-AC4F-5FC7CE976C71}.Release|Any CPU.ActiveCfg = Release|x86 + {DEBBF0D6-F59A-45BC-AC4F-5FC7CE976C71}.Release|Any CPU.Build.0 = Release|x86 + {A5A25E3F-D2AE-4130-91D4-56A8D0AA8201}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5A25E3F-D2AE-4130-91D4-56A8D0AA8201}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/ClrStack.x64/ClrStack.x64.csproj b/src/ClrStack.x64/ClrStack.x64.csproj new file mode 100644 index 0000000..210067c --- /dev/null +++ b/src/ClrStack.x64/ClrStack.x64.csproj @@ -0,0 +1,14 @@ + + + Exe + net461 + 1.1.0 + x64 + ..\build + ClrStack + false + + + + + \ No newline at end of file diff --git a/src/ClrStack.x64/Program.cs b/src/ClrStack.x64/Program.cs new file mode 100644 index 0000000..b9faf21 --- /dev/null +++ b/src/ClrStack.x64/Program.cs @@ -0,0 +1,70 @@ +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.Diagnostics.Runtime; + +namespace ClrStack +{ + internal static class Program + { + private const string ThreadDumpDirEnvVar = "THREAD_DUMP_DIR"; + + public static void Main(string[] args) + { + if (args.Length != 1 || + !int.TryParse(args[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var pid)) + { + Console.Error.WriteLine("Usage: ClrStack.exe [PID]"); + return; + } + var threadDumpDir = Environment.GetEnvironmentVariable(ThreadDumpDirEnvVar); + if (!string.IsNullOrEmpty(threadDumpDir) && !Directory.Exists(threadDumpDir)) + { + Console.Error.WriteLine($"Path [{threadDumpDir}] in THREAD_DUMP_DIR environment vaiable not exists or not directory"); + return; + } + + var output = new StringBuilder(); + try + { + using (var target = DataTarget.AttachToProcess(pid, 5000, AttachFlag.NonInvasive)) + { + var clrVersion = target.ClrVersions.FirstOrDefault(); + if (clrVersion == null) + { + output.AppendLine($"CLR not found in process: {pid}"); + return; + } + + var runtime = clrVersion.CreateRuntime(); + + foreach (var clrThread in runtime.Threads) + { + if (!clrThread.IsAlive) + continue; + output.AppendLine($"Thread #{clrThread.ManagedThreadId}:"); + + foreach (var frame in clrThread.StackTrace) + output.AppendLine($"\tat {frame}"); + } + } + } + catch (Exception ex) + { + output.AppendLine($"Cannot capture stack trace from process[{pid}]. Error: {ex.Message}"); + } + + if (string.IsNullOrEmpty(threadDumpDir)) + { + Console.Write(output.ToString()); + } + else + { + var fileName = $"{DateTime.Now:yyyy-MM-dd_HH_mm_ss.fff}.tdump"; + File.WriteAllText(Path.Combine(threadDumpDir, fileName), output.ToString(), Encoding.UTF8); + } + } + } +} \ No newline at end of file diff --git a/src/ClrStack.x86/ClrStack.x86.csproj b/src/ClrStack.x86/ClrStack.x86.csproj new file mode 100644 index 0000000..c80f990 --- /dev/null +++ b/src/ClrStack.x86/ClrStack.x86.csproj @@ -0,0 +1,19 @@ + + + Exe + net461 + 1.1.0 + x86 + ..\build + ClrStack + false + + + + + + + Program.cs + + + \ No newline at end of file diff --git a/src/ClrStack/ClrStack.csproj b/src/ClrStack/ClrStack.csproj index 94febd6..f3de377 100644 --- a/src/ClrStack/ClrStack.csproj +++ b/src/ClrStack/ClrStack.csproj @@ -1,64 +1,29 @@ - - - - - Debug - AnyCPU - {2ECDA63E-31B6-4562-B88D-FDC51DCA1959} - Exe - Properties - ClrStack - ClrStack - v4.6.1 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - bin\x64\Release\ - x64 - false - - - pdbonly - true - TRACE - prompt - 4 - bin\x86\Release\ - x86 - true - - - - ..\packages\Microsoft.Diagnostics.Runtime.0.9.180305.01\lib\net40\Microsoft.Diagnostics.Runtime.dll - True - - - - - - - - - - - - - + + + Library + net461 + 1.1.0 + Mikhail Filippov + Tool for capturing managed stack traces from .NET applications. + https://github.com/mfilippov/clrstack/blob/master/LICENSE + https://github.com/mfilippov/clrstack + https://github.com/mfilippov/clrstack + git + Mikhail Filippov + ..\build + False + + + + + + + + + False + + + False + + \ No newline at end of file diff --git a/src/ClrStack/Program.cs b/src/ClrStack/Program.cs deleted file mode 100644 index ef43223..0000000 --- a/src/ClrStack/Program.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Globalization; -using System.Linq; -using Microsoft.Diagnostics.Runtime; - -namespace ClrStack -{ - internal static class Program - { - public static void Main(string[] args) - { - if (args.Length != 1 || !int.TryParse(args[0], NumberStyles.Integer, CultureInfo.InvariantCulture,out var pid)) - { - Console.Error.WriteLine("Usage: ClrStack.exe [PID]"); - return; - } - using (var target = DataTarget.AttachToProcess(pid, 1000, AttachFlag.NonInvasive)) - { - var clrVersion = target.ClrVersions.FirstOrDefault(); - if (clrVersion == null) - { - Console.WriteLine($"CLR not found in process: {pid}"); - return; - } - var runtime = clrVersion.CreateRuntime(); - - foreach (var clrThread in runtime.Threads) - { - if (!clrThread.IsAlive) - continue; - Console.WriteLine($"Thread #{clrThread.ManagedThreadId}:"); - - foreach (var frame in clrThread.StackTrace) - Console.WriteLine($"\tat {frame}"); - } - } - } - } -} \ No newline at end of file diff --git a/src/ClrStack/Properties/AssemblyInfo.cs b/src/ClrStack/Properties/AssemblyInfo.cs deleted file mode 100644 index fd6e8af..0000000 --- a/src/ClrStack/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("ClrStack")] -[assembly: AssemblyDescription("Tool for capture thread dump from .NET process")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Mikhail Filippov")] -[assembly: AssemblyProduct("ClrStack")] -[assembly: AssemblyCopyright("Copyright © Mikhail Filippov 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] - - -[assembly: Guid("2ECDA63E-31B6-4562-B88D-FDC51DCA1959")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/src/ClrStack/packages.config b/src/ClrStack/packages.config deleted file mode 100644 index bde5b3b..0000000 --- a/src/ClrStack/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file