调试器状态管理类
DebuggerContext
00 Remarks
这个类型是整个调试器的核心, 其内部不包含任何的控制台交互代码, 控制台的交互逻辑位于 ConsoleDebuggerFrontend 之中, 使得GUI宿主程序能够通过订阅 DebuggerContext.OnBreakpointHit 事件 并且调用 DebuggerContext.Resume() 函数的方式来复用同一套调试器内核.
在整个环境链之上, 所有的 Environment 对象都是共享 同一个 DebuggerContext 实例的, 请参见 Environment 的构造函数之中的相关实现
01 Syntax
02 Methods
| Name | Overloads | Summary |
|---|---|---|
| Start | 1 | 启动调试会话 |
| Stop | 1 | 结束调试会话, 让脚本程序继续正常的执行下去 |
| EnterBlock | 1 | 进入一个新的代码块 |
| ExitBlock | 1 | 离开当前的代码块 |
| ShouldPause | 1 | 检查是否应该暂停执行 |
| TryHitBreakpoint | 1 | 判断当前的表达式之上是否命中了某一个断点 |
| evaluateCondition | 1 | 对条件断点的条件表达式进行求值 |
| Pause | 1 | 进入暂停状态, 等待用户的指令 |
| Resume | 1 | 设置下一步的调试动作并且唤醒被阻塞住的执行线程 |
| EvaluateWithoutDebug | 1 | 在临时挂起调试状态的情况下执行一段R#程序 |
| AddBreakpoint | 1 | 添加一个断点 |
| RemoveBreakpoint | 1 | 移除掉指定位置上的断点 |
| SetEnabled | 1 | 设置指定位置上的断点的启用状态 |
| ClearBreakpoints | 1 | 清除掉所有的断点 |
| ListBreakpoints | 1 | 列出当前所注册的全部的断点 |
03 Properties
| Name | Overloads | Summary |
|---|---|---|
| breakpoints | 1 | 断点的集合管理器 |
| inspector | 1 | 运行时的检查器, 提供查看变量/调用堆栈以及表达式求值等功能 |
| CurrentAction | 1 | 当前的调试动作 |
| IsDebugging | 1 | 是否处于调试模式? |
| CurrentEnvironment | 1 | 用于外部(例如UI界面)访问当前的变量环境 |
| stackDepth | 1 | 当前的代码块嵌套深度 |
| isOwnerThread | 1 | 当前的调用是否发生在主线程之上? |
04 Fields
| Name | Overloads | Summary |
|---|---|---|
| _depth | 1 | DebuggerContext.stackDepth 的后备字段 |
| baselineDepth | 1 | 在执行单步操作的时候所记录下来的基准深度 |
| resumeSignal | 1 | 用于阻塞执行线程的信号量 |
| ownerThreadId | 1 | 创建这个调试器上下文对象的线程的编号 |
05 Events
| Name | Overloads | Summary |
|---|---|---|
| OnBreakpointHit | 2 | 触发断点时的回调事件(可以用来弹窗或者在控制台之中做交互) |
06 Members
Boolean)启动调试会话
| Name | Type | Description |
|---|---|---|
breakOnEntry | Boolean | 是否在脚本的第一条语句处就暂停下来? 默认为True, 便于用户在脚本开始执行之前设置断点 |
结束调试会话, 让脚本程序继续正常的执行下去
进入一个新的代码块
这个函数只应该被 ExecutableLoop.Execute() 所调用
离开当前的代码块
这个函数只应该在 ExecutableLoop.Execute() 的 Finally 代码块之中被调用, 以保证在发生了异常或者是通过 return/break 提前退出的时候深度计数器不会失衡
检查是否应该暂停执行
| Name | Type | Description |
|---|---|---|
expr | Expression | 当前即将被执行的表达式 |
env | Environment | 当前的运行时环境 |
hit | Breakpoint | 如果本次暂停是由断点所触发的话, 则通过这个参数返回对应的断点对象 |
判断当前的表达式之上是否命中了某一个断点
对条件断点的条件表达式进行求值
当条件表达式的求值过程之中发生了错误的时候, 这个函数会返回True(即安全失败, 仍然中断执行), 避免因为条件表达式本身存在问题而导致断点被静默的忽略掉
进入暂停状态, 等待用户的指令
这是一个阻塞调用. 函数会先触发 DebuggerContext.OnBreakpointHit 事件 来通知宿主程序更新其变量监视界面, 然后阻塞住当前的执行线程, 直到宿主程序调用了 DebuggerContext.Resume() 为止
设置下一步的调试动作并且唤醒被阻塞住的执行线程
这个函数是由宿主程序或者是控制台前端所在的线程来调用的
| Name | Type | Description |
|---|---|---|
action | DebugAction | 下一步所要执行的调试动作 |
在临时挂起调试状态的情况下执行一段R#程序
在暂停点上对监视表达式或者是条件断点的条件表达式进行求值的时候, 其内部同样会经由 ExecutableLoop.Execute() 来执行, 如果不做特殊处理的话就会再一次的触发暂停逻辑, 从而使得执行线程重入 DebuggerContext.Pause() 而造成死锁.
所以在这里通过临时的将 DebuggerContext.IsDebugging 置为False 的方式来保证求值过程本身不会被调试器所拦截
String, Int32, String)添加一个断点
| Name | Type | Description |
|---|---|---|
file | String | 脚本文件的路径 |
line | Int32 | 源代码的行号 |
condition | String | 可选的条件表达式R#源代码 |
String, Int32)移除掉指定位置上的断点
String, Int32, Boolean)设置指定位置上的断点的启用状态
清除掉所有的断点
列出当前所注册的全部的断点
断点的集合管理器
运行时的检查器, 提供查看变量/调用堆栈以及表达式求值等功能
当前的调试动作
是否处于调试模式?
当这个属性值为False的时候, 程序的执行不会受到调试器的任何影响, 在 ExecutableLoop 的热点循环之中仅存在一次布尔判断的开销
用于外部(例如UI界面)访问当前的变量环境
当前的代码块嵌套深度
因为所有的函数体/循环体/分支体最终都是通过 ExecutableLoop.Execute() 来执行其内部的语句列表的, 所以在该函数的出入口处对这个计数器做自增自减操作之后, 这个计数器的数值就天然的等于当前的代码块的嵌套深度了
当前的调用是否发生在主线程之上?
因为需要通过 Interlocked 来进行原子操作, 所以在这里必须要声明为一个可以按引用传递的字段
在执行单步操作的时候所记录下来的基准深度
用于阻塞执行线程的信号量
初始状态为无信号, 使得 DebuggerContext.Pause() 能够阻塞住执行线程, 直到宿主程序调用 DebuggerContext.Resume() 将其置位为止
创建这个调试器上下文对象的线程的编号
因为 DebuggerContext 在整个环境链之上是共享的同一个实例, 而 parLapply/parSapply/%dopar% 等并行操作会在多个工作线程之上同时执行 R#的表达式, 如果不加以限制的话, 多个工作线程会同时在这里等待同一个信号量 而造成死锁. 所以在这里记录下创建者线程(即主线程)的编号, 使得只有主线程才会真正的进入暂停状态
触发断点时的回调事件(可以用来弹窗或者在控制台之中做交互)
这个事件是在执行线程之上被同步触发的, 事件的处理程序返回之后, 执行线程会继续阻塞在 DebuggerContext.Pause() 之中, 直到有其他的代码调用了 DebuggerContext.Resume() 为止