Esempio n. 1
0
        static void Main(string[] args)
        {
            if (args.Length < 1) {
                Usage();
                return;
            }

            int samples = 10;
            int sampleInterval = 1000;

            var state = ParseState.Unknown;
            foreach (var arg in args.Skip(1)) {
                switch (state) {
                    case ParseState.Unknown:
                        if (arg.ToLower() == "/s") {
                            state = ParseState.Samples;
                        } else if (arg.ToLower() == "/i") {
                            state = ParseState.Interval;
                        } else {
                            Usage();
                            return;
                        }
                        break;
                    case ParseState.Samples:
                        if (!Int32.TryParse(arg, out samples)) {
                            Usage();
                            return;
                        }
                        state = ParseState.Unknown;
                        break;
                    case ParseState.Interval:
                        if (!Int32.TryParse(arg, out sampleInterval)) {
                            Usage();
                            return;
                        }
                        state = ParseState.Unknown;
                        break;
                    default:
                        break;
                }
            }

            string pidOrProcess = args[0];

            var stats = new Dictionary<int, List<ThreadSnapshot>>();
            var debugger = new MDbgEngine();
            int pid = -1;

            var processes = Process.GetProcessesByName(pidOrProcess);
            if (processes.Length < 1) {
                try {
                    pid = Int32.Parse(pidOrProcess);
                } catch {
                    Console.WriteLine("Error: could not find any processes with that name or pid");
                    return;
                }
            } else {
                if (processes.Length > 1) {
                    Console.WriteLine("Warning: multiple processes share that name, attaching to the first");
                }
                pid = processes[0].Id;
            }

            MDbgProcess attached = null;
            try {
                attached = debugger.Attach(pid);
            } catch(Exception e) {
                Console.WriteLine("Error: failed to attach to process: " + e);
                return;
            }

            attached.Go().WaitOne();

            for (int i = 0; i < samples; i++) {

                foreach (MDbgThread thread in attached.Threads) {
                    var snapshot = ThreadSnapshot.GetThreadSnapshot(thread);
                    List<ThreadSnapshot> snapshots;
                    if (!stats.TryGetValue(snapshot.Id, out snapshots)) {
                        snapshots = new List<ThreadSnapshot>();
                        stats[snapshot.Id] = snapshots;
                    }

                    snapshots.Add(snapshot);
                }

                attached.Go();
                Thread.Sleep(sampleInterval);
                attached.AsyncStop().WaitOne();
            }

            attached.Detach().WaitOne();

            // perform basic analysis to see which are the top N stack traces observed,
            //  weighted on cost

            Dictionary<Guid, long> costs = new Dictionary<Guid,long>();
            Dictionary<Guid, string> stacks = new Dictionary<Guid, string>();

            foreach (var stat in stats.Values)
            {
                long prevTime = -1;
                foreach (var snapshot in stat)
                {
                    long time = snapshot.KernelTime + snapshot.UserTime;
                    if (prevTime != -1)
                    {
                        foreach (var tuple in snapshot.StackHashes)
                        {
                            if (costs.ContainsKey(tuple.Item1))
                            {
                                costs[tuple.Item1] += time - prevTime;
                            }
                            else
                            {
                                costs[tuple.Item1] = time - prevTime;
                                stacks[tuple.Item1] = tuple.Item2;
                            }
                        }
                    }
                    prevTime = time;
                }
            }

            Console.WriteLine("Most expensive stacks");
            Console.WriteLine("------------------------------------");
            foreach (var group in costs.OrderByDescending(p => p.Value).GroupBy(p => p.Value))
            {
                List<string> stacksToShow = new List<string>();

                foreach (var pair in group.OrderByDescending(p => stacks[p.Key].Length))
                {
                    if (!stacksToShow.Any(s => s.Contains(stacks[pair.Key])))
                    {
                        stacksToShow.Add(stacks[pair.Key]);
                    }
                }

                foreach (var stack in stacksToShow)
                {
                    Console.WriteLine(stack);
                    Console.WriteLine("===> Cost ({0})", group.Key);
                    Console.WriteLine();
                }
            }

            var offenders = stats.Values
               .Select(_ => ThreadSnapshotStats.FromSnapshots(_))
               .OrderBy(stat => stat.TotalKernelTime + stat.TotalUserTime)
               .Reverse();

            foreach (var stat in offenders) {
                Console.WriteLine("------------------------------------");
                Console.WriteLine(stat.ThreadId);
                Console.WriteLine("Kernel: {0} User: {1}", stat.TotalKernelTime, stat.TotalUserTime);
                foreach (var method in stat.CommonStack) {
                    Console.WriteLine(method);
                }
                Console.WriteLine("Other Stacks:");
                var prev = new List<string>();
                foreach (var trace in stats[stat.ThreadId].Select(_ => _.StackTrace)) {
                    if (!prev.SequenceEqual(trace)) {
                        Console.WriteLine();
                        foreach (var method in trace) {
                            Console.WriteLine(method);
                        }
                    } else {
                        Console.WriteLine("<skipped>");
                    }
                    prev = trace;
                }
                Console.WriteLine("------------------------------------");

            }
        }
Esempio n. 2
0
        /// <summary>Starts the debugging process. This is synchronised on the Debugger
        /// object it is called on.</summary>
        /// <remarks>
        /// Assumes that the DebuggerSync object has been set up correctly and that something
        /// is listening to the BackgroundWorker events. It will lock indefinitely if something
        /// doesn't tell it to continue by calling set on the DebuggerSync.ContinueDebugExecution
        /// object.
        /// </remarks>
        public void Run()
        {
            lock (this)
            {
                int pid = DebugProcess.GetDebugProcessId();

                CommandReceiver obj = DebugProcess.GetCommandReceiver();

                // Get Assembly Search paths. These are used for finding
                // any assemblies that are used by the template.
                List<string> assemblySearchPaths = new List<string>();

                foreach (string assemblyLocation in AssemblyLocations)
                {
                    string dir = Path.GetDirectoryName(assemblyLocation).ToLower();

                    if (assemblySearchPaths.BinarySearch(dir) < 0)
                    {
                        assemblySearchPaths.Add(dir);
                        assemblySearchPaths.Sort();
                    }
                }
                obj.ExecuteCommand(new RunFunctionCommand(CompiledAssemblyLocation, TestNamespace, TestClassname,
                                                          TestFunctionName, ArgumentList, UserOptions, AaprojXml, IsFunctionStatic));

                // This is a race condition, but it is required because we can't create the breakpoints
                // before attaching to the process.
                MDbgEngine debugger = new MDbgEngine();
                proc = debugger.Attach(pid);

                _CurrentlyRunningBreakpoints.Clear();

                foreach (int bp in _Breakpoints)
                {
                    _CurrentlyRunningBreakpoints.Add(bp,
                                                     proc.Breakpoints.CreateBreakpoint(CodeFilename, bp));
                }

                proc.Breakpoints.CreateBreakpoint("Commands.cs", LAST_LINE_IN_MAIN);
                proc.Breakpoints.CreateBreakpoint(CodeFilename, LAST_LINE_IN_FUNCTION);

                _CurrentlyRunningProcess = proc;

                while (proc.IsAlive)
                {
                    // Let the debuggee run and wait until it hits a debug event.
                    switch (_DebuggerSync.NextDebugAction)
                    {
                        case DebugActionType.Continue:
                            proc.Go().WaitOne();
                            break;
                        case DebugActionType.StepInto:
                            proc.StepInto(false).WaitOne();
                            break;
                        case DebugActionType.StepOver:
                            proc.StepOver(false).WaitOne();
                            break;
                        case DebugActionType.StepOut:
                            proc.StepOut().WaitOne();
                            break;
                        case DebugActionType.Stop:
                            _CurrentlyRunningProcess = null;
                            proc.Breakpoints.DeleteAll();
                            proc.Detach();
                            break;
                    }

                    if (!proc.IsAlive)
                    {
                        proc = null;
                        break;
                    }

                    // Get a DebugInformation object filled with the info we need.
                    DebugInformation di = GetDebugInformation(proc);

                    // If this is the last line, we need to stop debugging.
                    if (di.StartLineNumber == LAST_LINE_IN_MAIN)
                    {
                        di.Stopped = false;
                        di.StopReason = StopReason.DebuggerFinished;
                        di.StopReasonText = "Debugger Finished";
                        _DebuggerSync.DebugBackgroundWorker.ReportProgress(99, di);
                        _CurrentlyRunningProcess = null;
                        proc.Breakpoints.DeleteAll();
                        proc.Detach();

                        break;
                    }
                    // Any other lines in the main function should just be skipped.
                    if (di.SourceFile == "Commands.cs")
                    {
                        _DebuggerSync.NextDebugAction = DebugActionType.Continue;
                        continue;
                    }

                    if (di.StartLineNumber == LAST_LINE_IN_FUNCTION)
                    {
                        _DebuggerSync.NextDebugAction = DebugActionType.StepOut;
                        continue;
                    }
                    // If an exception was thrown, report it then stop debugging.
                    if (di.ExceptionThrown)
                    {
                        di.Stopped = false;
                        _DebuggerSync.DebugBackgroundWorker.ReportProgress(99, di);
                        _CurrentlyRunningProcess = null;
                        proc.Breakpoints.DeleteAll();
                        proc.Detach();

                        break;
                    }

                    di.Stopped = true;
                    _DebuggerSync.DebugBackgroundWorker.ReportProgress(50, di);
                    _DebuggerSync.ContinueDebugExecution.WaitOne();
                    continue;
                }
            }
            _CurrentlyRunningProcess = null;
        }
Esempio n. 3
0
        public static MDbgProcess AttachToProcess(int pid, MDbgEngine debugger)
        {
            debugger.Options.SymbolPath = "";
            string ver = null;
            try
            {
                ver = CorDebugger.GetDebuggerVersionFromPid(pid);
            }
            catch (Exception ex)
            {
                //a problem getting the version doesn't mean that we can't attach, try with the default ver
                ver = CorDebugger.GetDefaultDebuggerVersion();
            }

            MDbgProcess proc = debugger.Attach(pid, null, ver);
            DebuggerUtils.DrainAttach(debugger, proc);
            return proc;
        }
Esempio n. 4
0
        public static void AttachCmd(int pid, MDbgEngine debugger)
        {
            const string versionArg = "ver";
            const string continuationEventArg = "attachEvent";
            const string pidArg = "pid";
            //ArgParser ap = new ArgParser(arguments, versionArg + ":1;" + continuationEventArg + ":1;" + pidArg + ":1");

            //if (ap.Count > 1)
            //{
            //    throw new MDbgShellException("Wrong # of arguments.");
            //}

            //if (!ap.Exists(0) && !ap.OptionPassed(pidArg))
            //{
            //    WriteOutput("Please choose some process to attach");
            //    ProcessEnumCmd("");
            //    return;
            //}

            //int pid;
            //if (ap.Exists(0))
            //{
            //    pid = ap.AsInt(0);
            //    if (ap.OptionPassed(pidArg))
            //    {
            //        //WriteOutput("Do not specify pid option when also passing pid as last argument");
            //        return;
            //    }
            //}
            //else
            //{
            //    Debug.Assert(ap.OptionPassed(pidArg)); // verified above
            //    pid = ap.GetOption(pidArg).AsInt;
            //}

            //
            // Do some sanity checks to give useful end-user errors.
            //

            // Can't attach to ourselves!
            if (Process.GetCurrentProcess().Id == pid)
            {
                throw new Exception("Cannot attach to myself!");
            }

            // Can't attach to a process that we're already debugging.
            // ICorDebug may enforce this, but the error may not be very descriptive for an end-user.
            // For example, ICD may propogate an error from the OS, and the OS may return
            // something like AccessDenied if another debugger is already attached.
            // This only checks for cases where this same instance of MDbg is already debugging
            // the process of interest.
            foreach (MDbgProcess procOther in debugger.Processes)
            {
                if (pid == procOther.CorProcess.Id)
                {
                    throw new Exception("Can't attach to process " + pid + " because it's already being debugged");
                }
            }

            // Get the OS handle if there was one
            ///SafeWin32Handle osEventHandle = null;
            ///if (ap.OptionPassed(continuationEventArg))
            ///{
            ///    osEventHandle = new SafeWin32Handle(new IntPtr(ap.GetOption(continuationEventArg).AsHexOrDecInt));
            ///}

            // determine the version to attach to
            string version = MdbgVersionPolicy.GetDefaultAttachVersion(pid);
            //}
            if (version == null)
            {
                throw new Exception("Can't determine what version of the CLR to attach to in process " +
                    pid + ". Use -ver to specify a version");
            }

            // attach
            MDbgProcess p;
            //p = debugger.Attach(pid, osEventHandle, version);
            p = debugger.Attach(pid, null, version);

            p.Go().WaitOne();

            //if (osEventHandle != null)
            //{
            //    osEventHandle.Dispose();
            //}
        }
Esempio n. 5
0
        private static Dictionary<int, List<ThreadSnapshot>> CollectStats(SamplingParams sampling)
        {
            var stats = new Dictionary<int, List<ThreadSnapshot>>();
            var debugger = new MDbgEngine();

            MDbgProcess attached = null;
            try {
                attached = debugger.Attach(sampling.Pid);
            }
            catch (Exception e) {
                Console.WriteLine("Error: failed to attach to process: " + e);
                return stats;
            }

            attached.Go().WaitOne();

            for (int i = 0; i < sampling.Samples; i++) {
                foreach (MDbgThread thread in attached.Threads) {
                    try {
                        var snapshot = ThreadSnapshot.GetThreadSnapshot(thread);
                        List<ThreadSnapshot> snapshots;
                        if (!stats.TryGetValue(snapshot.Id, out snapshots))
                        {
                            snapshots = new List<ThreadSnapshot>();
                            stats[snapshot.Id] = snapshots;
                        }

                        snapshots.Add(snapshot);
                    }
                    catch (Exception ex) {
                        Console.WriteLine("Exception getting sample #{0} from PID {1} : {2}", i, sampling.Pid, ex.ToString());
                    }

                }

                attached.Go();
                Thread.Sleep(sampling.SampleInterval);
                attached.AsyncStop().WaitOne();
            }

            attached.Detach().WaitOne();

            return stats;
        }