MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Concurrent;6using System.Collections.Generic;7using System.Collections.ObjectModel;8using System.Diagnostics;9using System.Diagnostics.CodeAnalysis;10using System.Globalization;11using System.IO;12using System.Linq;13using System.Management.Automation.Host;14using System.Management.Automation.Internal;15using System.Management.Automation.Internal.Host;16using System.Management.Automation.Language;17using System.Management.Automation.Runspaces;18using System.Runtime.CompilerServices;19using System.Text.RegularExpressions;20using System.Threading;21 22using Microsoft.PowerShell.Commands.Internal.Format;23 24namespace System.Management.Automation25{26 #region Event Args27 28 /// <summary>29 /// Possible actions for the debugger after hitting a breakpoint/step.30 /// </summary>31 public enum DebuggerResumeAction32 {33 /// <summary>34 /// Continue running until the next breakpoint, or the end of the script.35 /// </summary>36 Continue = 0,37 /// <summary>38 /// Step to next statement, going into functions, scripts, etc.39 /// </summary>40 StepInto = 1,41 /// <summary>42 /// Step to next statement, going over functions, scripts, etc.43 /// </summary>44 StepOut = 2,45 /// <summary>46 /// Step to next statement after the current function, script, etc.47 /// </summary>48 StepOver = 3,49 /// <summary>50 /// Stop executing the script.51 /// </summary>52 Stop = 4,53 }54 55 /// <summary>56 /// Arguments for the DebuggerStop event.57 /// </summary>58 public class DebuggerStopEventArgs : EventArgs59 {60 /// <summary>61 /// Initializes the DebuggerStopEventArgs.62 /// </summary>63 internal DebuggerStopEventArgs(InvocationInfo invocationInfo, List<Breakpoint> breakpoints)64 {65 this.InvocationInfo = invocationInfo;66 this.Breakpoints = new ReadOnlyCollection<Breakpoint>(breakpoints);67 this.ResumeAction = DebuggerResumeAction.Continue;68 }69 70 /// <summary>71 /// Constructor.72 /// </summary>73 /// <param name="invocationInfo"></param>74 /// <param name="breakpoints"></param>75 /// <param name="resumeAction"></param>76 public DebuggerStopEventArgs(77 InvocationInfo invocationInfo,78 Collection<Breakpoint> breakpoints,79 DebuggerResumeAction resumeAction)80 {81 this.InvocationInfo = invocationInfo;82 this.Breakpoints = new ReadOnlyCollection<Breakpoint>(breakpoints);83 this.ResumeAction = resumeAction;84 }85 86 /// <summary>87 /// Invocation info of the code being executed.88 /// </summary>89 public InvocationInfo InvocationInfo { get; internal set; }90 91 /// <summary>92 /// The breakpoint(s) hit.93 /// </summary>94 /// <remarks>95 /// Note there may be more than one breakpoint on the same object (line, variable, command). A single event is96 /// raised for all these breakpoints.97 /// </remarks>98 public ReadOnlyCollection<Breakpoint> Breakpoints { get; }99 100 /// <summary>101 /// This property must be set in the event handler to indicate the debugger what it should do next.102 /// </summary>103 /// <remarks>104 /// The default action is DebuggerAction.Continue.105 /// DebuggerAction.StepToLine is only valid when debugging an script.106 /// </remarks>107 public DebuggerResumeAction ResumeAction { get; set; }108 109 /// <summary>110 /// This property is used internally for remote debug stops only. It is used to signal the remote debugger proxy111 /// that it should *not* send a resume action to the remote debugger. This is used by runspace debug processing to112 /// leave pending runspace debug sessions suspended until a debugger is attached.113 /// </summary>114 internal bool SuspendRemote { get; set; }115 }116 117 /// <summary>118 /// Kinds of breakpoint updates.119 /// </summary>120 public enum BreakpointUpdateType121 {122 /// <summary>123 /// A breakpoint was set.124 /// </summary>125 Set = 0,126 /// <summary>127 /// A breakpoint was removed.128 /// </summary>129 Removed = 1,130 /// <summary>131 /// A breakpoint was enabled.132 /// </summary>133 Enabled = 2,134 /// <summary>135 /// A breakpoint was disabled.136 /// </summary>137 Disabled = 3138 }139 140 /// <summary>141 /// Arguments for the BreakpointUpdated event.142 /// </summary>143 public class BreakpointUpdatedEventArgs : EventArgs144 {145 /// <summary>146 /// Initializes the BreakpointUpdatedEventArgs.147 /// </summary>148 internal BreakpointUpdatedEventArgs(Breakpoint breakpoint, BreakpointUpdateType updateType, int breakpointCount)149 {150 this.Breakpoint = breakpoint;151 this.UpdateType = updateType;152 this.BreakpointCount = breakpointCount;153 }154 155 /// <summary>156 /// Gets the breakpoint that was updated.157 /// </summary>158 public Breakpoint Breakpoint { get; }159 160 /// <summary>161 /// Gets the type of update.162 /// </summary>163 public BreakpointUpdateType UpdateType { get; }164 165 /// <summary>166 /// Gets the current breakpoint count.167 /// </summary>168 public int BreakpointCount { get; }169 }170 171 #region PSJobStartEventArgs172 173 /// <summary>174 /// Arguments for the script job start callback event.175 /// </summary>176 public sealed class PSJobStartEventArgs : EventArgs177 {178 /// <summary>179 /// Job to be started.180 /// </summary>181 public Job Job { get; }182 183 /// <summary>184 /// Job debugger.185 /// </summary>186 public Debugger Debugger { get; }187 188 /// <summary>189 /// Job is run asynchronously.190 /// </summary>191 public bool IsAsync { get; }192 193 /// <summary>194 /// Constructor.195 /// </summary>196 /// <param name="job">Started job.</param>197 /// <param name="debugger">Debugger.</param>198 /// <param name="isAsync">Job started asynchronously.</param>199 public PSJobStartEventArgs(Job job, Debugger debugger, bool isAsync)200 {201 this.Job = job;202 this.Debugger = debugger;203 this.IsAsync = isAsync;204 }205 }206 207 #endregion208 209 #region Runspace Debug Processing210 211 /// <summary>212 /// StartRunspaceDebugProcessing event arguments.213 /// </summary>214 public sealed class StartRunspaceDebugProcessingEventArgs : EventArgs215 {216 /// <summary> The runspace to process </summary>217 public Runspace Runspace { get; }218 219 /// <summary>220 /// When set to true this will cause PowerShell to process this runspace debug session through its221 /// script debugger. To use the default processing return from this event call after setting222 /// this property to true.223 /// </summary>224 public bool UseDefaultProcessing225 {226 get;227 set;228 }229 230 /// <summary>231 /// Constructor.232 /// </summary>233 public StartRunspaceDebugProcessingEventArgs(Runspace runspace)234 {235 if (runspace == null) { throw new PSArgumentNullException(nameof(runspace)); }236 237 Runspace = runspace;238 }239 }240 241 /// <summary>242 /// ProcessRunspaceDebugEnd event arguments.243 /// </summary>244 public sealed class ProcessRunspaceDebugEndEventArgs : EventArgs245 {246 /// <summary>247 /// The runspace where internal debug processing has ended.248 /// </summary>249 public Runspace Runspace { get; }250 251 /// <summary>252 /// Constructor.253 /// </summary>254 /// <param name="runspace"></param>255 public ProcessRunspaceDebugEndEventArgs(Runspace runspace)256 {257 if (runspace == null) { throw new PSArgumentNullException(nameof(runspace)); }258 259 Runspace = runspace;260 }261 }262 263 #endregion264 265 #endregion266 267 #region Enums268 269 /// <summary>270 /// Defines debugging mode.271 /// </summary>272 [Flags]273 public enum DebugModes274 {275 /// <summary>276 /// PowerShell script debugging is disabled.277 /// </summary>278 None = 0x0,279 280 /// <summary>281 /// Default setting for original PowerShell script debugging.282 /// Compatible with PowerShell Versions 2 and 3.283 /// </summary>284 Default = 0x1,285 286 /// <summary>287 /// PowerShell script debugging.288 /// </summary>289 LocalScript = 0x2,290 291 /// <summary>292 /// PowerShell remote script debugging.293 /// </summary>294 RemoteScript = 0x4295 }296 297 /// <summary>298 /// Defines unhandled breakpoint processing behavior.299 /// </summary>300 internal enum UnhandledBreakpointProcessingMode301 {302 /// <summary>303 /// Ignore unhandled breakpoint events.304 /// </summary>305 Ignore = 1,306 307 /// <summary>308 /// Wait on unhandled breakpoint events until a handler is available.309 /// </summary>310 Wait311 }312 313 #endregion314 315 #region Debugger base class316 317 /// <summary>318 /// Base class for all PowerShell debuggers.319 /// </summary>320 public abstract class Debugger321 {322 #region Events323 324 /// <summary>325 /// Event raised when the debugger hits a breakpoint or a step.326 /// </summary>327 public event EventHandler<DebuggerStopEventArgs> DebuggerStop;328 329 /// <summary>330 /// Event raised when a breakpoint is updated.331 /// </summary>332 public event EventHandler<BreakpointUpdatedEventArgs> BreakpointUpdated;333 334 /// <summary>335 /// Event raised when nested debugging is cancelled.336 /// </summary>337 internal event EventHandler<EventArgs> NestedDebuggingCancelledEvent;338 339 #region Runspace Debug Processing Events340 341 /// <summary>342 /// Event raised when a runspace debugger needs breakpoint processing.343 /// </summary>344 public event EventHandler<StartRunspaceDebugProcessingEventArgs> StartRunspaceDebugProcessing;345 346 /// <summary>347 /// Event raised when a runspace debugger is finished being processed.348 /// </summary>349 public event EventHandler<ProcessRunspaceDebugEndEventArgs> RunspaceDebugProcessingCompleted;350 351 /// <summary>352 /// Event raised to indicate that the debugging session is over and runspace debuggers queued for353 /// processing should be released.354 /// </summary>355 public event EventHandler<EventArgs> CancelRunspaceDebugProcessing;356 357 #endregion358 359 #endregion360 361 #region Properties362 363 /// <summary>364 /// True when the debugger is stopped.365 /// </summary>366 protected bool DebuggerStopped367 {368 get;369 private set;370 }371 372 /// <summary>373 /// IsPushed.374 /// </summary>375 internal virtual bool IsPushed376 {377 get { return false; }378 }379 380 /// <summary>381 /// IsRemote.382 /// </summary>383 internal virtual bool IsRemote384 {385 get { return false; }386 }387 388 /// <summary>389 /// Returns true if the debugger is preserving a DebuggerStopEvent390 /// event. Use ReleaseSavedDebugStop() to allow event to process.391 /// </summary>392 internal virtual bool IsPendingDebugStopEvent393 {394 get { throw new PSNotImplementedException(); }395 }396 397 /// <summary>398 /// Returns true if debugger has been set to stepInto mode.399 /// </summary>400 internal virtual bool IsDebuggerSteppingEnabled401 {402 get { throw new PSNotImplementedException(); }403 }404 405 /// <summary>406 /// Returns true if there is a handler for debugger stops.407 /// </summary>408 internal bool IsDebugHandlerSubscribed409 {410 get { return (DebuggerStop != null); }411 }412 413 /// <summary>414 /// UnhandledBreakpointMode.415 /// </summary>416 internal virtual UnhandledBreakpointProcessingMode UnhandledBreakpointMode417 {418 get { throw new PSNotImplementedException(); }419 420 set { throw new PSNotImplementedException(); }421 }422 423 /// <summary>424 /// DebuggerMode.425 /// </summary>426 public DebugModes DebugMode { get; protected set; } = DebugModes.Default;427 428 /// <summary>429 /// Returns true if debugger has breakpoints set and430 /// is currently active.431 /// </summary>432 public virtual bool IsActive433 {434 get { return false; }435 }436 437 /// <summary>438 /// InstanceId.439 /// </summary>440 public virtual Guid InstanceId441 {442 get { return s_instanceId; }443 }444 445 /// <summary>446 /// True when debugger is stopped at a breakpoint.447 /// </summary>448 public virtual bool InBreakpoint449 {450 get { return DebuggerStopped; }451 }452 453 #endregion454 455 #region Protected Methods456 457 /// <summary>458 /// RaiseDebuggerStopEvent.459 /// </summary>460 /// <param name="args">DebuggerStopEventArgs.</param>461 [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]462 protected void RaiseDebuggerStopEvent(DebuggerStopEventArgs args)463 {464 try465 {466 DebuggerStopped = true;467 DebuggerStop.SafeInvoke<DebuggerStopEventArgs>(this, args);468 }469 finally470 {471 DebuggerStopped = false;472 }473 }474 475 /// <summary>476 /// IsDebuggerStopEventSubscribed.477 /// </summary>478 /// <returns>True if event subscription exists.</returns>479 protected bool IsDebuggerStopEventSubscribed()480 {481 return (DebuggerStop != null);482 }483 484 /// <summary>485 /// RaiseBreakpointUpdatedEvent.486 /// </summary>487 /// <param name="args">BreakpointUpdatedEventArgs.</param>488 [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]489 protected void RaiseBreakpointUpdatedEvent(BreakpointUpdatedEventArgs args)490 {491 BreakpointUpdated.SafeInvoke<BreakpointUpdatedEventArgs>(this, args);492 }493 494 /// <summary>495 /// IsDebuggerBreakpointUpdatedEventSubscribed.496 /// </summary>497 /// <returns>True if event subscription exists.</returns>498 protected bool IsDebuggerBreakpointUpdatedEventSubscribed()499 {500 return (BreakpointUpdated != null);501 }502 503 #region Runspace Debug Processing504 505 /// <summary/>506 protected void RaiseStartRunspaceDebugProcessingEvent(StartRunspaceDebugProcessingEventArgs args)507 {508 if (args == null) { throw new PSArgumentNullException(nameof(args)); }509 510 StartRunspaceDebugProcessing.SafeInvoke<StartRunspaceDebugProcessingEventArgs>(this, args);511 }512 513 /// <summary/>514 protected void RaiseRunspaceProcessingCompletedEvent(ProcessRunspaceDebugEndEventArgs args)515 {516 if (args == null) { throw new PSArgumentNullException(nameof(args)); }517 518 RunspaceDebugProcessingCompleted.SafeInvoke<ProcessRunspaceDebugEndEventArgs>(this, args);519 }520 521 /// <summary/>522 protected bool IsStartRunspaceDebugProcessingEventSubscribed()523 {524 return (StartRunspaceDebugProcessing != null);525 }526 527 /// <summary/>528 protected void RaiseCancelRunspaceDebugProcessingEvent()529 {530 CancelRunspaceDebugProcessing.SafeInvoke<EventArgs>(this, null);531 }532 533 #endregion534 535 #endregion536 537 #region Public Methods538 539 /// <summary>540 /// Evaluates provided command either as a debugger specific command541 /// or a PowerShell command.542 /// </summary>543 /// <param name="command">PowerShell command.</param>544 /// <param name="output">Output.</param>545 /// <returns>DebuggerCommandResults.</returns>546 public abstract DebuggerCommandResults ProcessCommand(PSCommand command, PSDataCollection<PSObject> output);547 548 /// <summary>549 /// Sets the debugger resume action.550 /// </summary>551 /// <param name="resumeAction">DebuggerResumeAction.</param>552 public abstract void SetDebuggerAction(DebuggerResumeAction resumeAction);553 554 /// <summary>555 /// Stops a running command.556 /// </summary>557 public abstract void StopProcessCommand();558 559 /// <summary>560 /// Returns current debugger stop event arguments if debugger is in561 /// debug stop state. Otherwise returns null.562 /// </summary>563 /// <returns>DebuggerStopEventArgs.</returns>564 public abstract DebuggerStopEventArgs GetDebuggerStopArgs();565 566 /// <summary>567 /// Sets the parent debugger, breakpoints and other debugging context information.568 /// </summary>569 /// <param name="parent">Parent debugger.</param>570 /// <param name="breakPoints">List of breakpoints.</param>571 /// <param name="startAction">Debugger mode.</param>572 /// <param name="host">Host.</param>573 /// <param name="path">Current path.</param>574 public virtual void SetParent(575 Debugger parent,576 IEnumerable<Breakpoint> breakPoints,577 DebuggerResumeAction? startAction,578 PSHost host,579 PathInfo path)580 {581 throw new PSNotImplementedException();582 }583 584 /// <summary>585 /// Sets the debugger mode.586 /// </summary>587 public virtual void SetDebugMode(DebugModes mode)588 {589 this.DebugMode = mode;590 }591 592 /// <summary>593 /// Returns IEnumerable of CallStackFrame objects.594 /// </summary>595 /// <returns></returns>596 public virtual IEnumerable<CallStackFrame> GetCallStack()597 {598 return new Collection<CallStackFrame>();599 }600 601 /// <summary>602 /// Get a breakpoint by id in the current runspace, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets.603 /// </summary>604 /// <param name="id">Id of the breakpoint you want.</param>605 public Breakpoint GetBreakpoint(int id) =>606 GetBreakpoint(id, runspaceId: null);607 608 /// <summary>609 /// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets.610 /// </summary>611 /// <param name="id">Id of the breakpoint you want.</param>612 /// <param name="runspaceId">The runspace id of the runspace you want to interact with. A null value will use the current runspace.</param>613 public virtual Breakpoint GetBreakpoint(int id, int? runspaceId) =>614 throw new PSNotImplementedException();615 616 /// <summary>617 /// Adds the provided set of breakpoints to the debugger, in the current runspace.618 /// </summary>619 /// <param name="breakpoints">Breakpoints.</param>620 public void SetBreakpoints(IEnumerable<Breakpoint> breakpoints) =>621 SetBreakpoints(breakpoints, runspaceId: null);622 623 /// <summary>624 /// Adds the provided set of breakpoints to the debugger.625 /// </summary>626 /// <param name="breakpoints">Breakpoints.</param>627 /// <param name="runspaceId">The runspace id of the runspace you want to interact with, null being the current runspace.</param>628 public virtual void SetBreakpoints(IEnumerable<Breakpoint> breakpoints, int? runspaceId) =>629 throw new PSNotImplementedException();630 631 /// <summary>632 /// Returns breakpoints in the current runspace, primarily for the Get-PSBreakpoint cmdlet.633 /// </summary>634 public List<Breakpoint> GetBreakpoints() =>635 GetBreakpoints(runspaceId: null);636 637 /// <summary>638 /// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet.639 /// </summary>640 /// <param name="runspaceId">The runspace id of the runspace you want to interact with. A null value will use the current runspace.</param>641 public virtual List<Breakpoint> GetBreakpoints(int? runspaceId) =>642 throw new PSNotImplementedException();643 644 /// <summary>645 /// Sets a command breakpoint in the current runspace in the debugger.646 /// </summary>647 /// <param name="command">The name of the command that will trigger the breakpoint. This value may not be null.</param>648 /// <param name="action">The action to take when the breakpoint is hit. If null, PowerShell will break into the debugger when the breakpoint is hit.</param>649 /// <param name="path">The path to the script file where the breakpoint may be hit. If null, the breakpoint may be hit anywhere the command is invoked.</param>650 /// <returns>The command breakpoint that was set.</returns>651 public CommandBreakpoint SetCommandBreakpoint(string command, ScriptBlock action, string path) =>652 SetCommandBreakpoint(command, action, path, runspaceId: null);653 654 /// <summary>655 /// Sets a command breakpoint in the debugger.656 /// </summary>657 /// <param name="command">The name of the command that will trigger the breakpoint. This value may not be null.</param>658 /// <param name="action">The action to take when the breakpoint is hit. If null, PowerShell will break into the debugger when the breakpoint is hit.</param>659 /// <param name="path">The path to the script file where the breakpoint may be hit. If null, the breakpoint may be hit anywhere the command is invoked.</param>660 /// <param name="runspaceId">The runspace id of the runspace you want to interact with. A value of null will use the current runspace.</param>661 /// <returns>The command breakpoint that was set.</returns>662 public virtual CommandBreakpoint SetCommandBreakpoint(string command, ScriptBlock action, string path, int? runspaceId) =>663 throw new PSNotImplementedException();664 665 /// <summary>666 /// Sets a line breakpoint in the current runspace in the debugger.667 /// </summary>668 /// <param name="path">The path to the script file where the breakpoint may be hit. This value may not be null.</param>669 /// <param name="line">The line in the script file where the breakpoint may be hit. This value must be greater than or equal to 1.</param>670 /// <param name="column">The column in the script file where the breakpoint may be hit. If 0, the breakpoint will trigger on any statement on the line.</param>671 /// <param name="action">The action to take when the breakpoint is hit. If null, PowerShell will break into the debugger when the breakpoint is hit.</param>672 /// <returns>The line breakpoint that was set.</returns>673 public LineBreakpoint SetLineBreakpoint(string path, int line, int column, ScriptBlock action) =>674 SetLineBreakpoint(path, line, column, action, runspaceId: null);675 676 /// <summary>677 /// Sets a line breakpoint in the debugger.678 /// </summary>679 /// <param name="path">The path to the script file where the breakpoint may be hit. This value may not be null.</param>680 /// <param name="line">The line in the script file where the breakpoint may be hit. This value must be greater than or equal to 1.</param>681 /// <param name="column">The column in the script file where the breakpoint may be hit. If 0, the breakpoint will trigger on any statement on the line.</param>682 /// <param name="action">The action to take when the breakpoint is hit. If null, PowerShell will break into the debugger when the breakpoint is hit.</param>683 /// <param name="runspaceId">The runspace id of the runspace you want to interact with. A null value will use the current runspace.</param>684 /// <returns>The line breakpoint that was set.</returns>685 public virtual LineBreakpoint SetLineBreakpoint(string path, int line, int column, ScriptBlock action, int? runspaceId) =>686 throw new PSNotImplementedException();687 688 /// <summary>689 /// Sets a variable breakpoint in the current runspace in the debugger.690 /// </summary>691 /// <param name="variableName">The name of the variable that will trigger the breakpoint. This value may not be null.</param>692 /// <param name="accessMode">The variable access mode that will trigger the breakpoint.</param>693 /// <param name="action">The action to take when the breakpoint is hit. If null, PowerShell will break into the debugger when the breakpoint is hit.</param>694 /// <param name="path">The path to the script file where the breakpoint may be hit. If null, the breakpoint may be hit anywhere the variable is accessed using the specified access mode.</param>695 /// <returns>The variable breakpoint that was set.</returns>696 public VariableBreakpoint SetVariableBreakpoint(string variableName, VariableAccessMode accessMode, ScriptBlock action, string path) =>697 SetVariableBreakpoint(variableName, accessMode, action, path, runspaceId: null);698 699 /// <summary>700 /// Sets a variable breakpoint in the debugger.701 /// </summary>702 /// <param name="variableName">The name of the variable that will trigger the breakpoint. This value may not be null.</param>703 /// <param name="accessMode">The variable access mode that will trigger the breakpoint.</param>704 /// <param name="action">The action to take when the breakpoint is hit. If null, PowerShell will break into the debugger when the breakpoint is hit.</param>705 /// <param name="path">The path to the script file where the breakpoint may be hit. If null, the breakpoint may be hit anywhere the variable is accessed using the specified access mode.</param>706 /// <param name="runspaceId">The runspace id of the runspace you want to interact with. A null value will use the current runspace.</param>707 /// <returns>The variable breakpoint that was set.</returns>708 public virtual VariableBreakpoint SetVariableBreakpoint(string variableName, VariableAccessMode accessMode, ScriptBlock action, string path, int? runspaceId) =>709 throw new PSNotImplementedException();710 711 /// <summary>712 /// Removes a breakpoint from the debugger in the current runspace.713 /// </summary>714 /// <param name="breakpoint">The breakpoint to remove from the debugger. This value may not be null.</param>715 /// <returns>True if the breakpoint was removed from the debugger; false otherwise.</returns>716 public bool RemoveBreakpoint(Breakpoint breakpoint) =>717 RemoveBreakpoint(breakpoint, runspaceId: null);718 719 /// <summary>720 /// Removes a breakpoint from the debugger.721 /// </summary>722 /// <param name="breakpoint">The breakpoint to remove from the debugger. This value may not be null.</param>723 /// <param name="runspaceId">The runspace id of the runspace you want to interact with. A null value will use the current runspace.</param>724 /// <returns>True if the breakpoint was removed from the debugger; false otherwise.</returns>725 public virtual bool RemoveBreakpoint(Breakpoint breakpoint, int? runspaceId) =>726 throw new PSNotImplementedException();727 728 /// <summary>729 /// Enables a breakpoint in the debugger in the current runspace.730 /// </summary>731 /// <param name="breakpoint">The breakpoint to enable in the debugger. This value may not be null.</param>732 /// <returns>The updated breakpoint if it was found; null if the breakpoint was not found in the debugger.</returns>733 public Breakpoint EnableBreakpoint(Breakpoint breakpoint) =>734 EnableBreakpoint(breakpoint, runspaceId: null);735 736 /// <summary>737 /// Enables a breakpoint in the debugger.738 /// </summary>739 /// <param name="breakpoint">The breakpoint to enable in the debugger. This value may not be null.</param>740 /// <param name="runspaceId">The runspace id of the runspace you want to interact with. A null value will use the current runspace.</param>741 /// <returns>The updated breakpoint if it was found; null if the breakpoint was not found in the debugger.</returns>742 public virtual Breakpoint EnableBreakpoint(Breakpoint breakpoint, int? runspaceId) =>743 throw new PSNotImplementedException();744 745 /// <summary>746 /// Disables a breakpoint in the debugger in the current runspace.747 /// </summary>748 /// <param name="breakpoint">The breakpoint to enable in the debugger. This value may not be null.</param>749 /// <returns>The updated breakpoint if it was found; null if the breakpoint was not found in the debugger.</returns>750 public Breakpoint DisableBreakpoint(Breakpoint breakpoint) =>751 DisableBreakpoint(breakpoint, runspaceId: null);752 753 /// <summary>754 /// Disables a breakpoint in the debugger.755 /// </summary>756 /// <param name="breakpoint">The breakpoint to enable in the debugger. This value may not be null.</param>757 /// <param name="runspaceId">The runspace id of the runspace you want to interact with. A null value will use the current runspace.</param>758 /// <returns>The updated breakpoint if it was found; null if the breakpoint was not found in the debugger.</returns>759 public virtual Breakpoint DisableBreakpoint(Breakpoint breakpoint, int? runspaceId) =>760 throw new PSNotImplementedException();761 762 /// <summary>763 /// Resets the command processor source information so that it is764 /// updated with latest information on the next debug stop.765 /// </summary>766 public virtual void ResetCommandProcessorSource()767 {768 throw new PSNotImplementedException();769 }770 771 /// <summary>772 /// Sets debugger stepping mode.773 /// </summary>774 /// <param name="enabled">True if stepping is to be enabled.</param>775 public virtual void SetDebuggerStepMode(bool enabled)776 {777 throw new PSNotImplementedException();778 }779 780 #endregion781 782 #region Internal Methods783 784 /// <summary>785 /// Breaks into the debugger.786 /// </summary>787 /// <param name="triggerObject">The object that triggered the breakpoint, if there is one.</param>788 internal virtual void Break(object triggerObject = null)789 {790 throw new PSNotImplementedException();791 }792 793 /// <summary>794 /// Returns script position message of current execution stack item.795 /// This is used for WDAC audit mode logging for script information enhancement.796 /// </summary>797 /// <returns>Script position message string.</returns>798 internal virtual string GetCurrentScriptPosition()799 {800 throw new PSNotImplementedException();801 }802 803 /// <summary>804 /// Passes the debugger command to the internal script debugger command processor. This805 /// is used internally to handle debugger commands such as list, help, etc.806 /// </summary>807 /// <param name="command">Command string.</param>808 /// <param name="output">Output collection.</param>809 /// <returns>DebuggerCommand containing information on whether and how the command was processed.</returns>810 internal virtual DebuggerCommand InternalProcessCommand(string command, IList<PSObject> output)811 {812 throw new PSNotImplementedException();813 }814 815 /// <summary>816 /// Creates a source list based on root script debugger source information if available, with817 /// the current source line highlighted. This is used internally for nested runspace debugging818 /// where the runspace command is run in context of a parent script.819 /// </summary>820 /// <param name="lineNum">Current source line.</param>821 /// <param name="output">Output collection.</param>822 /// <returns>True if source listed successfully.</returns>823 internal virtual bool InternalProcessListCommand(int lineNum, IList<PSObject> output)824 {825 throw new PSNotImplementedException();826 }827 828 /// <summary>829 /// Sets up debugger to debug provided job or its child jobs.830 /// </summary>831 /// <param name="job">832 /// Job object that is either a debuggable job or a container of833 /// debuggable child jobs.834 /// </param>835 /// <param name="breakAll">836 /// If true, the debugger automatically invokes a break all when it837 /// attaches to the job.838 /// </param>839 internal virtual void DebugJob(Job job, bool breakAll) =>840 throw new PSNotImplementedException();841 842 /// <summary>843 /// Removes job from debugger job list and pops the its844 /// debugger from the active debugger stack.845 /// </summary>846 /// <param name="job">Job.</param>847 internal virtual void StopDebugJob(Job job)848 {849 throw new PSNotImplementedException();850 }851 852 /// <summary>853 /// GetActiveDebuggerCallStack.854 /// </summary>855 /// <returns>Array of stack frame objects of active debugger.</returns>856 internal virtual CallStackFrame[] GetActiveDebuggerCallStack()857 {858 throw new PSNotImplementedException();859 }860 861 /// <summary>862 /// Method to add the provided runspace information to the debugger863 /// for monitoring of debugger events. This is used to implement nested864 /// debugging of runspaces.865 /// </summary>866 /// <param name="args">PSEntityCreatedRunspaceEventArgs.</param>867 internal virtual void StartMonitoringRunspace(PSMonitorRunspaceInfo args)868 {869 throw new PSNotImplementedException();870 }871 872 /// <summary>873 /// Method to end the monitoring of a runspace for debugging events.874 /// </summary>875 /// <param name="args">PSEntityCreatedRunspaceEventArgs.</param>876 internal virtual void EndMonitoringRunspace(PSMonitorRunspaceInfo args)877 {878 throw new PSNotImplementedException();879 }880 881 /// <summary>882 /// If a debug stop event is currently pending then this method will release883 /// the event to continue processing.884 /// </summary>885 internal virtual void ReleaseSavedDebugStop()886 {887 throw new PSNotImplementedException();888 }889 890 /// <summary>891 /// Sets up debugger to debug provided Runspace in a nested debug session.892 /// </summary>893 /// <param name="runspace">894 /// The runspace to debug.895 /// </param>896 /// <param name="breakAll">897 /// If true, the debugger automatically invokes a break all when it898 /// attaches to the runspace.899 /// </param>900 internal virtual void DebugRunspace(Runspace runspace, bool breakAll) =>901 throw new PSNotImplementedException();902 903 /// <summary>904 /// Removes the provided Runspace from the nested "active" debugger state.905 /// </summary>906 /// <param name="runspace">Runspace.</param>907 internal virtual void StopDebugRunspace(Runspace runspace)908 {909 throw new PSNotImplementedException();910 }911 912 /// <summary>913 /// Raises the NestedDebuggingCancelledEvent event.914 /// </summary>915 internal void RaiseNestedDebuggingCancelEvent()916 {917 // Raise event on worker thread.918 Threading.ThreadPool.QueueUserWorkItem(919 (state) =>920 {921 try922 {923 NestedDebuggingCancelledEvent.SafeInvoke<EventArgs>(this, null);924 }925 catch (Exception)926 {927 }928 });929 }930 931 #endregion932 933 #region Runspace Debug Processing Methods934 935 /// <summary>936 /// Adds the provided Runspace object to the runspace debugger processing queue.937 /// The queue will then raise the StartRunspaceDebugProcessing events for each runspace to allow938 /// a host script debugger implementation to provide an active debugging session.939 /// </summary>940 /// <param name="runspace">Runspace to debug.</param>941 internal virtual void QueueRunspaceForDebug(Runspace runspace)942 {943 throw new PSNotImplementedException();944 }945 946 /// <summary>947 /// Causes the CancelRunspaceDebugProcessing event to be raised which notifies subscribers that current debugging948 /// sessions should be cancelled.949 /// </summary>950 public virtual void CancelDebuggerProcessing()951 {952 throw new PSNotImplementedException();953 }954 955 #endregion956 957 #region Members958 959 internal const string CannotProcessCommandNotStopped = "Debugger:CannotProcessCommandNotStopped";960 961 internal const string CannotEnableDebuggerSteppingInvalidMode = "Debugger:CannotEnableDebuggerSteppingInvalidMode";962 963 private static readonly Guid s_instanceId = new Guid();964 965 #endregion966 }967 968 #endregion969 970 #region ScriptDebugger class971 972 /// <summary>973 /// Holds the debugging information for a Monad Shell session.974 /// </summary>975 internal sealed class ScriptDebugger : Debugger, IDisposable976 {977 #region constructors978 979 internal ScriptDebugger(ExecutionContext context)980 {981 _context = context;982 _inBreakpoint = false;983 _idToBreakpoint = new ConcurrentDictionary<int, Breakpoint>();984 // The string key is function context file path. The int key is sequencePoint index.985 _pendingBreakpoints = new ConcurrentDictionary<string, ConcurrentDictionary<int, LineBreakpoint>>(StringComparer.OrdinalIgnoreCase);986 _boundBreakpoints = new ConcurrentDictionary<string, Tuple<WeakReference, ConcurrentDictionary<int, LineBreakpoint>>>(StringComparer.OrdinalIgnoreCase);987 _commandBreakpoints = new ConcurrentDictionary<int, CommandBreakpoint>();988 _variableBreakpoints = new ConcurrentDictionary<string, ConcurrentDictionary<int, VariableBreakpoint>>(StringComparer.OrdinalIgnoreCase);989 _steppingMode = SteppingMode.None;990 _callStack = new CallStackList { _callStackList = new List<CallStackInfo>() };991 992 _runningJobs = new Dictionary<Guid, PSJobStartEventArgs>();993 _activeDebuggers = new ConcurrentStack<Debugger>();994 _debuggerStopEventArgs = new ConcurrentStack<DebuggerStopEventArgs>();995 _syncObject = new object();996 _syncActiveDebuggerStopObject = new object();997 998 _runningRunspaces = new Dictionary<Guid, PSMonitorRunspaceInfo>();999 }1000 1001 /// <summary>1002 /// Static constructor.1003 /// </summary>1004 static ScriptDebugger()1005 {1006 s_processDebugPromptMatch = StringUtil.Format(@"""[{0}:", DebuggerStrings.NestedRunspaceDebuggerPromptProcessName);1007 }1008 1009 #endregion constructors1010 1011 #region properties1012 1013 /// <summary>1014 /// True when debugger is stopped at a breakpoint.1015 /// </summary>1016 public override bool InBreakpoint1017 {1018 get1019 {1020 if (_inBreakpoint)1021 {1022 return _inBreakpoint;1023 }1024 1025 Debugger activeDebugger;1026 if (_activeDebuggers.TryPeek(out activeDebugger))1027 {1028 return activeDebugger.InBreakpoint;1029 }1030 1031 return false;1032 }1033 }1034 1035 internal override bool IsPushed1036 {1037 get { return (!_activeDebuggers.IsEmpty); }1038 }1039 1040 /// <summary>1041 /// Returns true if the debugger is preserving a DebuggerStopEvent1042 /// event. Use ReleaseSavedDebugStop() to allow event to process.1043 /// </summary>1044 internal override bool IsPendingDebugStopEvent1045 {1046 get1047 {1048 return ((_preserveDebugStopEvent != null) && !_preserveDebugStopEvent.IsSet);1049 }1050 }1051 1052 /// <summary>1053 /// Returns true if debugger has been set to stepInto mode.1054 /// </summary>1055 internal override bool IsDebuggerSteppingEnabled1056 {1057 get1058 {1059 return ((_context._debuggingMode == (int)InternalDebugMode.Enabled) &&1060 (_currentDebuggerAction == DebuggerResumeAction.StepInto) &&1061 (_steppingMode != SteppingMode.None));1062 }1063 }1064 1065 private bool? _isLocalSession;1066 1067 private bool IsLocalSession1068 {1069 get1070 {1071 // Remote debug sessions always have a ServerRemoteHost. Otherwise it is a local session.1072 _isLocalSession ??= !((_context.InternalHost.ExternalHost != null) &&1073 (_context.InternalHost.ExternalHost is System.Management.Automation.Remoting.ServerRemoteHost));1074 1075 return _isLocalSession.Value;1076 }1077 }1078 1079 /// <summary>1080 /// Gets or sets the object that triggered the current breakpoint.1081 /// </summary>1082 private object TriggerObject { get; set; }1083 1084 #endregion properties1085 1086 #region internal methods1087 1088 #region Reset Debugger1089 1090 /// <summary>1091 /// Resets debugger to initial state.1092 /// </summary>1093 internal void ResetDebugger()1094 {1095 SetDebugMode(DebugModes.None);1096 SetInternalDebugMode(InternalDebugMode.Disabled);1097 1098 _steppingMode = SteppingMode.None;1099 _inBreakpoint = false;1100 _idToBreakpoint.Clear();1101 _pendingBreakpoints.Clear();1102 _boundBreakpoints.Clear();1103 _commandBreakpoints.Clear();1104 _variableBreakpoints.Clear();1105 s_emptyBreakpointList.Clear();1106 _callStack.Clear();1107 _overOrOutFrame = null;1108 _commandProcessor = new DebuggerCommandProcessor();1109 1110 _currentInvocationInfo = null;1111 _inBreakpoint = false;1112 _psDebuggerCommand = null;1113 _savedIgnoreScriptDebug = false;1114 _isLocalSession = null;1115 1116 _nestedDebuggerStop = false;1117 _debuggerStopEventArgs.Clear();1118 _lastActiveDebuggerAction = DebuggerResumeAction.Continue;1119 _currentDebuggerAction = DebuggerResumeAction.Continue;1120 _previousDebuggerAction = DebuggerResumeAction.Continue;1121 _nestedRunningFrame = null;1122 _nestedDebuggerStop = false;1123 _processingOutputCount = 0;1124 _preserveUnhandledDebugStopEvent = false;1125 1126 ClearRunningJobList();1127 ClearRunningRunspaceList();1128 _activeDebuggers.Clear();1129 1130 ReleaseSavedDebugStop();1131 1132 SetDebugMode(DebugModes.Default);1133 }1134 1135 #endregion1136 1137 #region Call stack management1138 1139 // Called from generated code on entering the script function, called once for each dynamicparam, begin, or end1140 // block, and once for each object written to the pipeline. Also called when entering a trap.1141 internal void EnterScriptFunction(FunctionContext functionContext)1142 {1143 Diagnostics.Assert(functionContext._executionContext == _context, "Wrong debugger is being used.");1144 1145 var invocationInfo = (InvocationInfo)functionContext._localsTuple.GetAutomaticVariable(AutomaticVariable.MyInvocation);1146 var newCallStackInfo = new CallStackInfo1147 {1148 InvocationInfo = invocationInfo,1149 File = functionContext._file,1150 DebuggerStepThrough = functionContext._debuggerStepThrough,1151 FunctionContext = functionContext,1152 IsFrameHidden = functionContext._debuggerHidden,1153 };1154 _callStack.Add(newCallStackInfo);1155 1156 if (_context._debuggingMode > 0)1157 {1158 var scriptCommandInfo = invocationInfo.MyCommand as ExternalScriptInfo;1159 if (scriptCommandInfo != null)1160 {1161 RegisterScriptFile(scriptCommandInfo);1162 }1163 1164 bool checkLineBp = CheckCommand(invocationInfo);1165 SetupBreakpoints(functionContext);1166 1167 if (functionContext._debuggerStepThrough && _overOrOutFrame == null && _steppingMode == SteppingMode.StepIn)1168 {1169 // Treat like step out, but only if we're not already stepping out1170 ResumeExecution(DebuggerResumeAction.StepOut);1171 }1172 1173 if (checkLineBp)1174 {1175 OnSequencePointHit(functionContext);1176 }1177 1178 if (_context.PSDebugTraceLevel > 1 && !functionContext._debuggerStepThrough && !functionContext._debuggerHidden)1179 {1180 TraceScriptFunctionEntry(functionContext);1181 }1182 }1183 }1184 1185 private void SetupBreakpoints(FunctionContext functionContext)1186 {1187 var scriptDebugData = _mapScriptToBreakpoints.GetValue(functionContext._sequencePoints,1188 _ => Tuple.Create(new Dictionary<int, List<LineBreakpoint>>(),1189 new BitArray(functionContext._sequencePoints.Length)));1190 functionContext._boundBreakpoints = scriptDebugData.Item1;1191 functionContext._breakPoints = scriptDebugData.Item2;1192 SetPendingBreakpoints(functionContext);1193 }1194 1195 // Called after exiting the script function, called once for each dynamicparam, begin, or end1196 // block, and once for each object written to the pipeline. Also called when leaving a trap.1197 internal void ExitScriptFunction()1198 {1199 // If it's stepping over to exit the current frame, we need to clear the _overOrOutFrame,1200 // so that we will stop at the next statement in the outer frame.