Beispiel #1
0
        private void RestartWithx64()
        {
            if (Environment.Is64BitProcess)
            {
                return;
            }

            if (IsChild)
            {
                Console.WriteLine("Recursion detected. Do not start any further child process. Please file an issue at https://github.com/Alois-xx/MemAnalyzer/issues");
                return;
            }

            // If csv output file was already opened close it to allow child process to write to it.
            if (OutputStringWriter.CsvOutput == true)
            {
                OutputStringWriter.Output.Close();
                OutputStringWriter.Output    = Console.Out;
                OutputStringWriter.CsvOutput = false;
            }

            string exe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "MemAnalyzerx64.exe");

            // if -debug is specified do not deploy the exe into AppData. Instead start it from the location where where the main exe is located
            if (!IsDebug)
            {
                string appDir = GetToolDeployDirectory();
                Directory.CreateDirectory(appDir);
                exe = Path.Combine(appDir, "MemAnalyzerx64.exe");
            }

            if (!File.Exists(exe))
            {
                File.WriteAllBytes(exe, Binaries.MemAnalyzerx64);
                // Deploy app.config
                File.WriteAllText(exe + ".config", Binaries.App);
            }

            DebugPrinter.Write("Starting x64 child {0}", exe);
            ProcessStartInfo info = new ProcessStartInfo(exe, GetQuotedArgs() + " -child")
            {
                UseShellExecute        = false,
                WindowStyle            = ProcessWindowStyle.Hidden,
                CreateNoWindow         = true,
                RedirectStandardOutput = true
            };
            var p = Process.Start(info);

            Console.CancelKeyPress += (a, b) => p.Kill();

            string output = null;

            while ((output = p.StandardOutput.ReadLine()) != null)
            {
                Console.WriteLine(output);
            }
            p.WaitForExit();
            DebugPrinter.Write($"Setting return code from x64 process {p.ExitCode} to our own return code.");
            ReturnCode = p.ExitCode;
        }
Beispiel #2
0
        public ClrHeap GetHeap(DataTarget target)
        {
            var clrVersion = target.ClrVersions.FirstOrDefault();

            if (clrVersion == null)
            {
                return(null);
            }

            ClrRuntime runtime = null;

            try
            {
                runtime = clrVersion.CreateRuntime();
                return(runtime.GetHeap());
            }
            catch (Exception ex)
            {
                DebugPrinter.Write("Got from CreateRuntime or GetHeap: {0}", ex);

                // If it is a target architecture mismatch hide error message and start x64 to try again later.
                if (Environment.Is64BitProcess)
                {
                    Console.WriteLine("Error: Is the dump file opened by another process (debugger)? If yes close the debugger first.");
                    Console.WriteLine("       If the dump comes from a different computer with another CLR version {0} that you are running on your machine you need to download the matching mscordacwks.dll first. Check out " + DacCollection + " and download the matching version/s.", clrVersion.Version);
                    Console.WriteLine("       Then set _NT_SYMBOL_PATH=PathToYourDownloadedMscordackwks.dll  e.g. _NT_SYMBOL_PATH=c:\\temp\\mscordacwks in the shell where you did execute MemAnalyzer and then try again.");
                    Console.WriteLine();
                }
                throw;
            }
        }
Beispiel #3
0
        /// <summary>
        /// Query can be 100;N#1 or N#1 or 100
        /// </summary>
        /// <param name="query">Query string to parse</param>
        private void ParseTopNQuery(string query, out int n, out int minCount)
        {
            var    parts     = query.Replace(" ", "").Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            string condition = null;

            minCount = 0;
            if (int.TryParse(parts[0], out n) == false)
            {
                condition = parts[0];
            }
            else if (parts.Length > 1)
            {
                condition = parts[1];
            }

            if (condition != null)
            {
                // cannot use > sign because the shell will interpret it was file redirection character.
                if (condition.IndexOf("N", StringComparison.OrdinalIgnoreCase) == 0 && condition.IndexOf('#') == 1)
                {
                    int.TryParse(condition.Substring(2), out minCount);
                    DebugPrinter.Write($"Condition: {condition}, MinValue: {minCount}");
                }
                else
                {
                    Console.WriteLine($"Warning condition {condition} of query {query} could not be parsed.");
                    Console.WriteLine("A query contains ddd;N#ddd2 where ddd is the TopN limit and dddd2 will print only types which have an instance count > ddd2");
                }
            }

            DebugPrinter.Write($"N: {n}, MinCount: {minCount}");
        }
Beispiel #4
0
        /// <summary>
        /// Save VMMap data to output file
        /// </summary>
        /// <param name="pid"></param>
        /// <param name="outFile"></param>
        public static void SaveVMmapDataToFile(int pid, string outFile)
        {
            var info = new ProcessStartInfo(VmMap, $"-accepteula -p {pid} {outFile}")
            {
                CreateNoWindow = true,
                WindowStyle    = ProcessWindowStyle.Hidden,
            };

            try
            {
                var p = Process.Start(info);
                p.WaitForExit();

                // Looks like a bug in VMMap where the x64 child process is still writing the output data but the
                // x86 parent process has already exited.
                for (int i = 0; i < 10 && !File.Exists(outFile); i++)
                {
                    DebugPrinter.Write($"VMMap has exited but output file {outFile} does not yet exist");
                    Thread.Sleep(2000);
                }
            }
            catch (Exception ex)
            {
                NoVMMap = true; // do not try again if not present.
                DebugPrinter.Write($"Could not start VMMap: {info.FileName} {info.Arguments}. Got: {ex}");
            }
        }
Beispiel #5
0
        /// <summary>
        /// Fixes issues with tools deployed side by side with the executable but the current working directory might
        /// be different.
        /// </summary>
        private static void AddProcessStartDirectoryToPath()
        {
            string path = Environment.GetEnvironmentVariable("PATH");

            path += ";" + Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
            Environment.SetEnvironmentVariable("PATH", path);
            DebugPrinter.Write($"Set Path={path}");
        }
Beispiel #6
0
        private bool CanLoadDump(string dumpFileName)
        {
            ProcessStartInfo info = new ProcessStartInfo(Assembly.GetEntryAssembly().Location, $"-f {dumpFileName} -vmmap")
            {
                UseShellExecute        = false,
                RedirectStandardOutput = true,
                CreateNoWindow         = true,
            };

            var p = Process.Start(info);

            Console.WriteLine("Verifying Dump if it is readable ...");
            Console.WriteLine(p.StandardOutput.ReadToEnd());
            DebugPrinter.Write($"Start child process {info.FileName} with args: {info.Arguments}");
            p.WaitForExit();
            return(p.ExitCode > 0);
        }
Beispiel #7
0
        static int Main(string[] args)
        {
            try
            {
                AppDomainResolverFromResources resolver = new AppDomainResolverFromResources(typeof(Binaries));
                var p = new Program(args);
                if (p.Parse())
                {
                    p.Run();
                }
            }
            catch (Exception ex)
            {
                ReturnCode = -1;
                Help("Got Exception: {0}", ex);
            }

            DebugPrinter.Write($"Returning value {ReturnCode} as exit code.");
            return(ReturnCode);
        }
Beispiel #8
0
        MemAnalyzerBase CreateAnalyzer(Actions action)
        {
            ClrHeap heap  = null;
            ClrHeap heap2 = null;

            // If only one dump file is opened then we can open the file with the debugger interface which only supports
            // one reader in one process. This is useful because otherwise we will lock the file and ClrMD will error
            // out if the dump file already open within a debugger which is pretty common.
            // But the drawback is that if we compare a live process against a memory dump it will fail and we get
            // always a diff of 0 back!
            var crashDumpOpenMode = TargetInformation.IsProcessCompare ? CrashDumpReader.ClrMD : CrashDumpReader.DbgEng;

            try
            {
                if (TargetInformation.DumpFileName1 != null)
                {
                    Target = DataTarget.LoadCrashDump(TargetInformation.DumpFileName1, crashDumpOpenMode);
                    heap   = GetHeap(Target);
                }
                if (TargetInformation.DumpFileName2 != null)
                {
                    Target2 = DataTarget.LoadCrashDump(TargetInformation.DumpFileName2, crashDumpOpenMode);
                    heap2   = GetHeap(Target2);
                }

                if (TargetInformation.Pid1 != 0 && Target == null)
                {
                    if (!NativeMethods.ProcessExists(TargetInformation.Pid1))
                    {
                        Console.WriteLine($"Error: Process {TargetInformation.Pid1} is not running.");
                        return(null);
                    }

                    // do not try wrong bitness when we know it will fail anyway
                    if (NativeMethods.IsWin64(TargetInformation.Pid1) && !Environment.Is64BitProcess)
                    {
                        DebugPrinter.Write($"Starting x64 because Pid {TargetInformation.Pid1} is Win64 process");
                        RestartWithx64();
                        return(null);
                    }

                    Target = DataTarget.AttachToProcess(TargetInformation.Pid1, 5000u);
                    heap   = GetHeap(Target);
                    if (heap == null)
                    {
                        Console.WriteLine($"Error: Could not get managed heap of process {TargetInformation.Pid1}. Most probably it is an unmanaged process.");
                    }
                }

                if (TargetInformation.Pid2 != 0 & Target2 == null)
                {
                    if (!NativeMethods.ProcessExists(TargetInformation.Pid2))
                    {
                        Console.WriteLine($"Error: Process {TargetInformation.Pid2} is not running.");
                        return(null);
                    }

                    // Cannot load data from processes with different bitness
                    if (TargetInformation.Pid1 != 0 && (NativeMethods.IsWin64(TargetInformation.Pid1) != NativeMethods.IsWin64(TargetInformation.Pid2)))
                    {
                        Console.WriteLine($"Error: Process {TargetInformation.Pid1} and {TargetInformation.Pid2} are of different bitness. You can dump each one separately to CSV files and compare the CSV files instead.");
                        return(null);
                    }

                    // do not try wrong bitness when we know it will fail anyway
                    if (NativeMethods.IsWin64(TargetInformation.Pid2) && !Environment.Is64BitProcess)
                    {
                        DebugPrinter.Write($"Starting x64 because Pid2 {TargetInformation.Pid2} is Win64 process");
                        RestartWithx64();
                        return(null);
                    }

                    Target2 = DataTarget.AttachToProcess(TargetInformation.Pid2, 5000u);
                    heap2   = GetHeap(Target2);
                }
            }
            catch (Exception ex)
            {
                // Default is a 32 bit process if runtime creation fails with InvalidOperationException or a DAC location failure
                // we try x64 as fall back. This will work if the bitness of the dump file is wrong.
                if (!Environment.Is64BitProcess && (ex is FileNotFoundException || ex is InvalidOperationException || ex is ClrDiagnosticsException ||
                                                    (ex is COMException && ex.HResult == HResultNotEnoughStorage)))
                {
                    RestartWithx64();
                }
                else
                {
                    if (ex is Win32Exception win32 && win32.NativeErrorCode == 5)
                    {
                        Console.WriteLine("Error: You need to run with elevated rights to access this process.");
                    }