bp、bu、bm(设置断点)

bpbubm 命令设置一个或多个软件断点。 可以组合位置、条件和选项来设置不同类型的软件断点。

用户模式

[~Thread] bp[ID] [Options] [Address [Passes]] ["CommandString"] 
[~Thread] bu[ID] [Options] [Address [Passes]] ["CommandString"] 
[~Thread] bm [Options] SymbolPattern [Passes] ["CommandString"]

内核模式

bp[ID] [Options] [Address [Passes]] ["CommandString"] 
bu[ID] [Options] [Address [Passes]] ["CommandString"] 
bm [Options] SymbolPattern [Passes] ["CommandString"]

参数

线
指定断点适用的线程。 有关语法的详细信息,请参阅线程语法。 只能在用户模式下指定线程。 如果未指定线程,断点将应用于所有线程。

ID
指定标识断点的十进制数。

调试器在创建断点时分配 ID,但可以使用 br(断点重新编号)命令对其进行更改。 可以使用 ID 在后面的调试器命令中引用断点。 若要显示断点的 ID,请使用 bl(断点列表)命令。

在命令中使用 ID 时,请勿在命令(bpbu)和 ID 号之间键入空格。

ID 参数始终是可选的。 如果未指定 ID,调试器将使用第一个可用的断点编号。 在内核模式下,只能设置 32 个断点。 在用户模式下,可以设置任意数量的断点。 在任一情况下,ID 号的值均没有限制。 如果用方括号 ([]) 将 ID 括起来,ID 可以包含任何表达式。 有关语法的详细信息,请参阅数字表达式语法

选项 指定断点选项。 可以指定以下任意数量的选项,除非另有说明:

/1
创建一个“一次性”断点。 触发此断点后,会从断点列表中将其删除。

/p EProcess
(仅内核模式)指定与此断点关联的进程。 EProcess 应该是 EPROCESS 结构的实际地址,而不是 PID。 只有在此进程的上下文中遇到断点时,才会触发该断点。

/t EThread
(仅内核模式)指定与此断点关联的线程。 EThread 应该是 ETHREAD 结构的实际地址,而不是线程 ID。 只有在此线程的上下文中遇到断点时,才会触发该断点。 如果使用 /p EProcess/t EThread,则可以按任意顺序输入它们。

/c MaxCallStackDepth
仅当调用堆栈深度小于 MaxCallStackDepth 时,才会激活断点。 不能将此选项与 /C 一起使用。

/C MinCallStackDepth
仅当调用堆栈深度大于 MinCallStackDepth 时,才会激活断点。 不能将此选项与 /c 一起使用。

/a
(仅限 bm)设置所有指定位置的断点,无论它们位于数据空间还是代码空间中。 由于数据断点可能导致程序故障,因此仅在已知安全的位置上使用此选项。

/d
(仅限 bm)将断点位置转换为地址。 因此,如果移动代码,断点仍会保留在相同的地址上,而不是根据 SymbolPattern 设置。 使用 /d 避免在加载或卸载模块时重新评估对断点的更改。

/(
(仅限 bm)包括 SymbolString 定义的符号字符串中的参数列表信息。

此功能可以对名称相同但参数列表不同的重载函数设置断点。 例如,bm /(myFunc 在 myFunc(int a) myFunc(char a)上设置断点)。 如果没有“/(”,则 myFunc 上设置的断点会失败,因为它不指示断点适用于哪个 myFunc 函数。

/w dx 对象表达式根据 dx 对象表达式返回的布尔值设置条件断点。 该参数是一个数据模型 (dx) 表达式,其值为 true(符合条件 - 中断)或 false(不符合条件 - 不中断)。

此示例基于 localVariable 的值设置条件断点。

bp /w "localVariable == 4" mymodule!myfunction

此示例演示如何使用 JavaScript 设置断点。

bp /w "@$scriptContents.myFunc(localVariable)" @rip

有关调试器对象的详细信息,请参阅 dx(显示调试器对象模型表达式)

有关条件断点的详细信息,请参阅设置条件断点

地址
指定设置断点的指令的第一个字节。 如果省略 Address,则使用当前指令指针。 有关语法的详细信息,请参阅地址和地址范围语法

通过
指定激活断点的执行传递数。 调试器将跳过断点位置,直到到达指定的传递。 传递的值可以是任何 16 位或 32 位值。

默认情况下,在应用程序首次执行包含断点位置的代码时,断点处于活动状态。 此默认情况等效于 Passes 的值为 1。 若要仅在应用程序至少执行一次代码后激活断点,请输入一个值 2 或更多。 例如,值 2 会在第二次执行代码时激活断点。

此参数创建一个计数器,该计数器会在每次传递代码时递减。 若要查看传递 计数器的初始值和当前值,请使用 bl(断点列表)

仅当应用程序执行断点以响应 g (Go) 命令时,传递计数器才会递减。 如果单步执行代码或跟踪代码,计数器不会递减。 当传递计数器达到 1 时,只能通过清除和重置断点来重置它。

CommandString
指定每次遇到指定次数的断点时要执行的命令列表。 必须将 CommandString 参数括在引号中。 使用分号分隔多个命令。

CommandString 中的调试器命令可以包含参数。 可以使用标准 C- 控制字符(如 \n\")。 包含在二级引号 (\") 中的分号会被解释为内嵌引号字符串的一部分。

仅当应用程序正在执行以响应 g (Go) 命令时到达断点的情况下,才执行 CommandString 命令。 如果在单步执行代码或跟踪超过此点,则不会执行这些命令。

在断点(如 gt)结束命令列表的执行后恢复程序执行的任何命令。

SymbolPattern
指定模式。 调试器尝试将此模式与现有符号匹配,并在所有模式匹配项上设置断点。 SymbolPattern 可以包含各种通配符字符和说明符。 有关此语法的详细信息,请参阅字符串通配符语法。 由于这些字符与符号匹配,因此匹配不区分大小写,并且单个前导下划线 (_) 表示任意数量的前导下划线。

环境

说明
模式 用户模式、内核模式
目标 仅限实时调试
平台 全部

其他信息

有关如何使用断点及其示例、控制断点的其他断点命令和方法以及如何从内核调试器在用户空间中设置断点的详细信息,请参阅使用断点。 有关条件断点的详细信息,请参阅设置条件断点

注解

bpbubm 命令设置新的断点,但它们具有不同的特征:

  • bp (设置断点) 命令在命令中指定的断点位置的地址处设置一个新的断点。 如果调试器在设置断点时无法解析断点位置的地址表达式,则 bp 断点将自动转换为 bu 断点。 使用 bp 命令创建在卸载模块后不再处于活动状态的断点。

  • bu(设置未解析的断点)命令设置延迟未解析的断点。 bu 断点是在对命令(不在地址上)中指定的断点位置的符号引用上设置的,并在解析具有引用的模块时激活。 有关这些断点的详细信息,请参阅未解析的断点(bu 断点)

  • bm(设置符号断点)命令在与指定模式匹配的符号上设置新断点。 此命令可以创建多个断点。 默认情况下,匹配模式后,bm 断点与 bu 断点相同。 也就是说,bm 断点是在符号引用上设置的延迟断点。 但是,bm /d 命令创建一个或多个 bp 断点。 每个断点都设置在匹配位置的地址上,并且不跟踪模块状态。

如果不确定设置现有断点时使用的命令,请使用 .bpcmds (显示断点命令) 列出所有断点以及用于创建断点的命令。

bp 断点和 bu 断点之间存在三个主要差异:

  • bp 断点位置始终转换为地址。 如果模块更改移动了设置 bp 断点的代码,断点仍会保留在相同地址。 另一方面,bu 断点仍与使用的符号值(通常是符号加偏移量)相关联,即使符号的地址发生更改,也会跟踪此符号位置。

  • 如果在加载的模块中找到 bp 断点地址,并且该模块稍后卸载,则会从断点列表中删除断点。 另一方面,bu 断点在重复卸载和加载后仍然存在。

  • 使用 bp 设置的断点不会保存在 WinDbg 工作区中。 使用 bu 设置的断点保存在工作区中。

如果要在断点的符号模式中使用通配符,则 bm 命令非常有用。 bm SymbolPattern 语法等效于使用 x SymbolPattern,然后在每个结果上使用 bu 例如,若要在以字符串“mem”开头的 Myprogram 模块中的所有符号上设置断点,请使用以下命令。

示例

0:000> bm myprogram!mem* 
  4: 0040d070 MyProgram!memcpy
 5: 0040c560 MyProgram!memmove
  6: 00408960 MyProgram!memset

由于 bm 命令设置软件断点(而不是处理器断点),因此它在设置断点时自动排除数据位置以避免损坏数据。

使用 bp 或 bm /a 命令时,可以指定数据地址而不是程序地址。 但是,即使指定了数据位置,这些命令创建的也是软件断点,而不是处理器断点。 如果软件断点放置在程序数据而不是可执行代码中,则可能会导致数据损坏。 因此,只有在确定存储在数据位置的内存将用作可执行代码而不是程序数据时,才可在该位置使用这些命令。 否则,应改用 ba (访问时中断) 命令。 有关详细信息,请参阅处理器断点(ba 断点)

有关如何在更复杂的语法指定的位置上(如 C++ 公共类的成员或包含其他受限字符的任意文本字符串上)设置断点的详细信息,请参阅断点语法

如果单个逻辑源行跨越多个物理行,则断点设置在语句或调用的最后一个物理行上。 如果调试器无法在请求的位置设置断点,就会将断点置于下一个允许的位置。

如果指定 Thread,断点则会设置在指定的线程上。 例如,~*bp 命令在所有线程上设置断点,~#bp 在导致当前异常的线程上设置断点,~123bp 在线程 123 上设置断点。 ~bp ~.bp 命令都在当前线程上设置断点。

在内核模式下调试多处理器系统时,通过使用 bp ba(访问时中断)设置的断点适用于所有处理器。 例如,如果当前处理器为 3,并且键入 bp MemoryAddress 将断点置于 MemoryAddress。 在该地址执行的任何处理器(不仅仅是处理器 3)都会导致断点陷阱。

bpbubm 命令通过将处理器指令替换为中断指令来设置软件断点。 若要调试无法更改的只读代码或代码,请使用 ba e 命令,其中 e 表示仅执行访问权限。

以下命令将在函数 MyTest 开始后的 12 字节处设置断点。 此断点在前六次通过代码时会被忽略,但在第七次通过代码时停止执行。

0:000> bp MyTest+0xb 7 

以下命令在 RtlRaiseException 处设置断点,显示 eax 寄存器,显示符号 MyVar 的值,并继续。

kd> bp ntdll!RtlRaiseException "r eax; dt MyVar; g"

以下两个 bm 命令设置三个断点。 执行命令时,显示的结果不会区分使用 /d 开关创建的断点以及不使用 /d 开关创建的断点。 .bpcmds (显示断点命令)可用于区分这两种类型。 如果断点是由不使用 /d 开关的 bm 创建的,则.bpcmds 显示将断点类型指示为 bu,后跟 @!"" 令牌中括起来的已计算符号(指示它是文字符号,而不是数值表达式或寄存器)。 如果断点是由使用 /d 开关的 bm 创建的,则 .bpcmds 显示指示断点类型为 bp

0:000> bm myprog!openf* 
  0: 00421200 @!"myprog!openFile"
  1: 00427800 @!"myprog!openFilter"

0:000> bm /d myprog!closef* 
  2: 00421600 @!"myprog!closeFile"

0:000> .bpcmds
bu0 @!"myprog!openFile";
bu1 @!"myprog!openFilter";
bp2 0x00421600 ;