예제 #1
0
        } // end InterceptCtrlC()

        protected override void ProcessRecord()
        {
            if (!DbgProvider.IsInGuestMode)
            {
                throw new InvalidOperationException("This command is only intended to be used by the !dbgshell implementation.");
            }

            LogManager.Trace("WaitForBangDbgShellCommandCommand.ProcessRecord: DbgShell is going dormant.");

            using (var disposer = new ExceptionGuard())
            {
                disposer.Protect(SetDebuggerAndContextAndUpdateCallbacks());
                disposer.Protect(InterceptCtrlC());
                disposer.Protect(Debugger.SuspendEventOutput());   // don't want our "modload" output etc. spewing while we are dormant

                base.ProcessRecord();
                // We need to perform the wait on another thread, because we need to "pump"
                // on this thread, so that debug event callbacks can queue WriteObject calls
                // to this thread.
                _RegisterWaitForGuestModeEvent(disposer);

                // Give the user a hint about what's going on.
                var s = new ColorString(ConsoleColor.DarkGray,
                                        "(DbgShell will remain running in the background until you run !dbgshell again)").ToString(true);
                Host.UI.WriteLine(s);

                MsgLoop.Prepare();

                // This will allow the extension command to return (it tells the dbgeng
                // thread that it can return).
                DbgProvider.GuestModePassivate();

                MsgLoop.Run(); // This will complete when the guest mode event gets signaled.
            } // end using( disposer )
        } // end ProcessRecord()
예제 #2
0
        } // end BeginProcessing()

        protected override void ProcessRecord()
        {
            var inputCallbacks = Debugger.GetInputCallbacks() as DebugInputCallbacks;

            if (null != inputCallbacks)
            {
                inputCallbacks.UpdateCmdlet(this);
            }

            //var outputCallbacks = Debugger.GetOutputCallbacks() as DebugOutputCallbacks;
            //if( null != outputCallbacks )
            //    outputCallbacks.UpdateCmdlet( this );

            using (var disposer = new ExceptionGuard())
            {
                disposer.Protect(Debugger.SetCurrentCmdlet(this));
                disposer.Protect(InterceptCtrlC());

                MsgLoop.Prepare();

                string actualCommand = string.Join(" ", Command);
                Task   t             = Debugger.InvokeDbgEngCommandAsync(actualCommand, OutputPrefix, _ConsumeLine);
                Task   t2            = t.ContinueWith(async(x) =>
                {
                    DEBUG_STATUS execStatus = Debugger.GetExecutionStatus();
                    if (_StatusRequiresWait(execStatus))
                    {
                        LogManager.Trace("InvokeDbgExtensionCommand: Current execution status ({0}) indicates that we should enter a wait.",
                                         execStatus);

                        await WaitForBreakAsync();
                    }

                    disposer.Dispose();
                    SignalDone();
                }).Unwrap();

                MsgLoop.Run();

                Host.UI.WriteLine();
                Util.Await(t);   // in case it threw
                Util.Await(t2);  // in case it threw
            } // end using( disposer )
        } // end ProcessRecord()
예제 #3
0
        static int Main(string[] args)
        {
            // This allows "[Console]::WriteLine( [char] 0x2026 )" to work just as well as
            // "[char] 0x2026".
            Console.OutputEncoding = Encoding.UTF8;

            //
            // We've got four possibilities:
            //
            // 1) Normal mode: somebody just ran DbgShell.exe.
            // 2) Guest mode: DbgShell is being hosted by a debugger extension
            //    (DbgShellExt.dll).
            // 3) Guest mode cleanup: the debugger extension (DbgShellExt) is being
            //    unloaded.
            // 4) GuestAndHost mode: DbgShell.exe is the host, but !DbgShellExt.dbgshell
            //    was called.
            //

            DbgProvider.EntryDepth++;

            ExceptionGuard entryPointStateDisposer = new ExceptionGuard(
                (Disposable)(() => LogManager.Trace("Undoing entry point state changes.")));

            entryPointStateDisposer.Protect((Disposable)(() => DbgProvider.EntryDepth--));

            using ( entryPointStateDisposer )
            {
                if ((null != args) &&
                    (args.Length > 0) &&
                    (0 == StringComparer.Ordinal.Compare(args[0], c_guestMode)))
                {
                    LogManager.Trace("Main: GuestMode");
                    //
                    // In guest mode, the entry thread is used as the DbgEng thread, and a
                    // separate thread is started to run our Main. When the user runs "exit",
                    // we don't actually exit--we "pause" the main thread, and then return
                    // from here (returning control back to the debugger (like windbg)).
                    //

                    args = args.Skip(1).ToArray();

                    return(_GuestModeMainWrapper(entryPointStateDisposer, args));
                }
                else if ((null != args) &&
                         (args.Length > 0) &&
                         (0 == StringComparer.Ordinal.Compare(args[0], c_guestModeCleanup)))
                {
                    Util.Assert(args.Length == 1);   // there shouldn't be any other args

                    LogManager.Trace("Main: GuestModeCleanup");

                    //
                    // We're in guest mode and the debugger extension is being unloaded--time
                    // to clean up.
                    //
                    return(_GuestModeCleanup());
                }
                else if ((null != args) &&
                         (args.Length > 0) &&
                         (0 == StringComparer.Ordinal.Compare(args[0], c_guestAndHostMode)))
                {
                    LogManager.Trace("Main: GuestAndHostMode");
                    //
                    // GuestAndHost mode is pretty much just like the "reentrant" case in
                    // GuestMode. Because DbgShell.exe is already running and owns "main" and
                    // everything, we don't use any of the other guest mode machinery besides
                    // the CurrentActionQueue and EntryDepth.
                    //

                    args = args.Skip(1).ToArray();

                    return(_GuestAndHostModeMainWrapper(entryPointStateDisposer, args));
                }
                else
                {
                    LogManager.Trace("Main: Normal");
                    //
                    // "Normal" (non-guest) mode.
                    //
                    return(_NormalModeMainWrapper(args));
                } // end else "normal" mode

                // The stuff protected by the entryPointStateDisposer may not get disposed
                // here--it may have been transferred to a different thread via
                // entryPointStateDisposer.Transfer().
            } // end using( entryPointStateDisposer )
        }     // end Main()
예제 #4
0
        } // end _NormalModeMainWrapper()

        private static int _GuestAndHostModeMainWrapper(ExceptionGuard entryPointStateDisposer,
                                                        string[] args)
        {
            // We should have two things:
            //    args[ 0 ]: "shareConsole"
            //    args[ 1 ]: command args should all should get passed in one big string.
            //
            // Unlike plain guest mode, we know we are "sharing" the console (because
            // DbgShell.exe is the host!), but it's convenient for the caller to still
            // pass the console ownership arg.
            Util.Assert(2 == args.Length);

            if (0 != StringComparer.Ordinal.Compare(args[0], c_guestModeShareConsole))
            {
                var msg = "Unexpected arg: I expected to see \"shareConsole\".";
                Util.Fail(msg);
                throw new Exception(msg);
            }

            //
            // We're getting called on the dbgeng thread, which should be a background
            // thread, but the API we use from DbgShellExt.dll (RunAssembly) causes this
            // thread to get marked as non-background, so we need to put that back.
            //
            Thread.CurrentThread.IsBackground = true;

            try
            {
                // We'll always pretend it's the first time for the purpose of parsing
                // arguments--on the one hand, -NoProfile doesn't make sense (it can't
                // /ever/ make sense in guestAndHostMode; the profile was run or not
                // when DbgShell.exe started), but we don't want to warn about it.
                //
                // TODO: Is that the right decision?

                bool   noProfile;
                bool   inBreakpointCommand;
                string command = _ParseGuestModeArgs(args[1],
                                                     /* firstTime = */ true,
                                                     out noProfile,
                                                     out inBreakpointCommand);

                if (inBreakpointCommand && !DbgProvider.IsInBreakpointCommand)
                {
                    DbgProvider.IsInBreakpointCommand = true;
                    entryPointStateDisposer.Protect((Disposable)(() => DbgProvider.IsInBreakpointCommand = false));
                }

                // Re-entrant case: DbgShell is already running, but something called
                // the debugger "!dbgshell" extension command (for instance, a
                // breakpoint command).
                LogManager.Trace("_GuestAndHostModeMainWrapper: re-entering (entryDepth: {0}).", DbgProvider.EntryDepth);
                if (null == DbgProvider.CurrentActionQueue)
                {
                    var msg = "(_GuestAndHostModeMainWrapper) DbgShell is already running, but I don't have a CurrentActionQueue to inject commands.";
                    Util.Fail(msg);
                    throw new Exception(msg);
                }
                var transferTicket = entryPointStateDisposer.Transfer();
                DbgProvider.CurrentActionQueue.PostCommand((cii) =>
                {
                    // TODO: BUGBUG: I need to do something here to separate CTRL+C
                    // handling for 'command' versus the cmdlet that is running the
                    // CurrentActionQueue. That is, if we post a command here that
                    // does a naked "Get-Content", it will prompt you for a path, and
                    // if you then do a CTRL+C, I want it to only cancel 'command',
                    // not the entire pipeline including the Resume-Exection command
                    // or whatever it is that is running the CurrentActionQueue.
                    //
                    // Besides just to behave as the user wants/expects, it throws a
                    // wrench in things for execution commands to get canceled--it's
                    // very important, so they do their own CTRL+C handling.
                    using (transferTicket.Redeem())
                    {
                        LogManager.Trace("Executing injected (reentrant) commands:");
                        LogManager.Trace(command.Replace("\n", "\n > "));
                        cii.InvokeScript(command,
                                         false,
                                         PipelineResultTypes.Error |
                                         PipelineResultTypes.Output,
                                         sm_noInput);
                    }     // end using( new disposer )
                });
                transferTicket.CommitTransfer();

                LogManager.Trace("_GuestAndHostModeMainWrapper: returning {0}.",
                                 Util.FormatErrorCode(sm_exitCode));
                return(sm_exitCode);
            }
            finally
            {
                LogManager.Trace("_GuestAndHostModeMainWrapper: leaving.");
            }
        } // end _GuestAndHostModeMainWrapper()
예제 #5
0
        private static int _GuestModeMainWrapper(ExceptionGuard entryPointStateDisposer,
                                                 string[] args)
        {
            // We should have two things:
            //    args[ 0 ]: either "consoleOwner" or "shareConsole" (windbg versus ntsd, for instance)
            //    args[ 1 ]: command args should all should get passed in one big string.
            Util.Assert(2 == args.Length);

            bool shareConsole = false;

            if (0 == StringComparer.Ordinal.Compare(args[0], c_guestModeShareConsole))
            {
                shareConsole = true;
            }
            else
            {
                if (0 != StringComparer.Ordinal.Compare(args[0], c_guestModeConsoleOwner))
                {
                    var msg = "Unexpected arg: I expected to see \"consoleOwner\".";
                    Util.Fail(msg);
                    throw new Exception(msg);
                }
            }

            bool firstTime      = null == sm_altMainThread;
            bool alreadyRunning = DbgProvider.EntryDepth > 1;

            try
            {
                bool   noProfile;
                bool   inBreakpointCommand;
                string command = _ParseGuestModeArgs(args[1],
                                                     firstTime,
                                                     out noProfile,
                                                     out inBreakpointCommand);

                if (inBreakpointCommand && !DbgProvider.IsInBreakpointCommand)
                {
                    DbgProvider.IsInBreakpointCommand = true;
                    entryPointStateDisposer.Protect((Disposable)(() => DbgProvider.IsInBreakpointCommand = false));
                }

                if (firstTime)
                {
                    Util.Assert(!alreadyRunning);
                    ColorConsoleHost.GuestModeInjectCommand(command);

                    LogManager.Trace("_GuestModeMainWrapper: initial entry.");
                    // This is the first time we've been run, so we need to start everything
                    // up, pretty much the same as when running as a normal EXE.
                    sm_altMainThread = new Thread(_AltMainThread);

                    DbgProvider.SetGuestMode(shareConsole);

                    string[] mainArgs = null;
                    if (noProfile)
                    {
                        mainArgs = new string[] { "-NoProfile" }
                    }
                    ;

                    sm_altMainThread.Start(mainArgs);

                    // This will get signaled when things have been initialized enough to
                    // where we can start the guest mode thread activity.
                    DbgProvider.GuestModeEvent.WaitOne();
                    DbgProvider.GuestModeEvent.Reset();
                }
                else
                {
                    if (alreadyRunning)
                    {
                        // Re-entrant case: DbgShell is already running, but something called
                        // the debugger "!dbgshell" extension command (for instance, a
                        // breakpoint command).
                        LogManager.Trace("_GuestModeMainWrapper: re-entering (entryDepth: {0}).", DbgProvider.EntryDepth);
                        if (null == DbgProvider.CurrentActionQueue)
                        {
                            Util.Fail("DbgShell is already running, but I don't have a CurrentActionQueue to inject commands.");
                            throw new Exception("DbgShell is already running, but I don't have a CurrentActionQueue to inject commands.");
                        }
                        var transferTicket = entryPointStateDisposer.Transfer();
                        DbgProvider.CurrentActionQueue.PostCommand((cii) =>
                        {
                            using (transferTicket.Redeem())
                            {
                                LogManager.Trace("Executing injected (reentrant) commands:");
                                LogManager.Trace(command.Replace("\n", "\n > "));
                                cii.InvokeScript(command,
                                                 false,
                                                 PipelineResultTypes.Error |
                                                 PipelineResultTypes.Output,
                                                 sm_noInput);
                            }     // end using( new disposer )
                        });
                        transferTicket.CommitTransfer();
                    }
                    else
                    {
                        LogManager.Trace("_GuestModeMainWrapper: resuming.");
                        ColorConsoleHost.GuestModeInjectCommand(command);

                        // We already have an alternate main thread, which is "paused" (blocked),
                        // waiting to resume.

                        DbgProvider.GuestModeResume();
                    }
                }

                if (!alreadyRunning)
                {
                    LogManager.Trace("_GuestModeMainWrapper: entering GuestModeGuestThreadActivity.");
                    // The guest mode thread activity is to pump the DbgEngThread (this thread is
                    // the thread that the debugger extension was called on, and we need to use
                    // this thread to interact with dbgeng).
                    //
                    // (if alreadyRunning, then we are already running the
                    // GuestModeGuestThreadActivity (it's below us on the stack))
                    DbgProvider.GuestModeGuestThreadActivity();
                }

                LogManager.Trace("_GuestModeMainWrapper: returning {0}.",
                                 Util.FormatErrorCode(sm_exitCode));
                return(sm_exitCode);
            }
            finally
            {
                LogManager.Trace("_GuestModeMainWrapper: leaving.");
            }
        } // end _GuestModeMainWrapper()
예제 #6
0
        // I think this should be for kernel-mode only.
        // [Parameter( Mandatory = false )]
        // public SwitchParameter IgnoreInaccessibleMemory { get; set; }



        protected override void ProcessRecord()
        {
            // Support relative paths in PS.
            string dumpFileResolved = SessionState.Path.GetUnresolvedProviderPathFromPSPath(DumpFile);

            DEBUG_FORMAT flags = DEBUG_FORMAT.DEFAULT;

            flags |= DEBUG_FORMAT.USER_SMALL_FULL_MEMORY |
                     DEBUG_FORMAT.USER_SMALL_HANDLE_DATA |
                     DEBUG_FORMAT.USER_SMALL_UNLOADED_MODULES |
                     DEBUG_FORMAT.USER_SMALL_INDIRECT_MEMORY |
                     DEBUG_FORMAT.USER_SMALL_DATA_SEGMENTS |
                     DEBUG_FORMAT.USER_SMALL_PROCESS_THREAD_DATA |
                     DEBUG_FORMAT.USER_SMALL_PRIVATE_READ_WRITE_MEMORY |
                     DEBUG_FORMAT.USER_SMALL_FULL_MEMORY_INFO |
                     DEBUG_FORMAT.USER_SMALL_THREAD_INFO |
                     DEBUG_FORMAT.USER_SMALL_CODE_SEGMENTS |
                     DEBUG_FORMAT.USER_SMALL_FULL_AUXILIARY_STATE;  // TODO: Need to learn what this means


            // I think this should be for kernel-mode only.
            // if( IgnoreInaccessibleMemory )
            // {
            //     flags |= DEBUG_FORMAT.USER_SMALL_IGNORE_INACCESSIBLE_MEM;
            // }

            // TODO: Figure out these things, and see if there are new ones I don't know about:
            //       DEBUG_FORMAT.USER_SMALL_FILTER_MEMORY |
            //       DEBUG_FORMAT.USER_SMALL_FILTER_PATHS |

            //       DEBUG_FORMAT.USER_SMALL_NO_OPTIONAL_DATA |

            //       DEBUG_FORMAT.USER_SMALL_NO_AUXILIARY_STATE |


            if (!AllowClobber)
            {
                flags |= DEBUG_FORMAT.NO_OVERWRITE;
            }

            if (Compress)
            {
                flags |= DEBUG_FORMAT.WRITE_CAB;
            }

            if (CompressWithSymbols)
            {
                flags |= DEBUG_FORMAT.WRITE_CAB;
                flags |= DEBUG_FORMAT.CAB_SECONDARY_FILES;
                // TODO: What is CAB_SECONDARY_ALL_IMAGES for? DbgEng.h says
                //
                //    "When creating a CAB with secondary images do searches
                //    for all image files, regardless of whether they're
                //    needed for the current session or not."
                //
                // but I don't know what it means for an image file to be "needed" for the
                // current session versus not.
            }

            if ((Compress || CompressWithSymbols) &&
                !DumpFile.EndsWith(".cab", StringComparison.OrdinalIgnoreCase))
            {
                WriteWarning("Output will be compressed, but output file name does not end in '.cab'.");
                WriteWarning("You will need to rename the resulting file to end with '.cab' if you want to be able to mount the dump file with Mount-DbgDumpFile.");
            }

            // TODO: Just let the API fail instead of doing this pre-check?
            if (File.Exists(dumpFileResolved))
            {
                if (!AllowClobber)
                {
                    // is there a better "already exists" exception?
                    WriteError(new InvalidOperationException(Util.Sprintf("The file '{0}' already exists. Use -AllowClobber to overwrite it.",
                                                                          DumpFile)),
                               "DumpFileAlreadyExists",
                               ErrorCategory.ResourceExists,
                               DumpFile);
                    return;
                }
            }

            using (var disposer = new ExceptionGuard())
            {
                disposer.Protect(Debugger.HandleDbgEngOutput((x) => SafeWriteVerbose(x.TrimEnd())));
                disposer.Protect(new CtrlCInterceptor(_CtrlCHandler));

                try
                {
                    MsgLoop.Prepare();

                    var task = Debugger.WriteDumpAsync(DumpFile,
                                                       flags,
                                                       Comment,
                                                       CancelTS.Token);
                    task.ContinueWith((_t) => { disposer.Dispose(); SignalDone(); });

                    MsgLoop.Run();

                    Util.Await(task);   // in case it threw
                }
                catch (DbgProviderException dpe)
                {
                    if ((dpe.HResult == DebuggerObject.E_UNEXPECTED) &&
                        CancelTS.IsCancellationRequested)
                    {
                        WriteWarning("Dump file creation canceled.");
                    }
                    else
                    {
                        WriteError(dpe);
                    }
                }
            } // end using( dbgeng output, CTRL-C )
        }     // end ProcessRecord()