bp、bu、bm(设置断点)

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

User-Mode

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

Kernel-Mode

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
创建“一次性”断点。 触发此断点后,将从断点列表中将其删除。

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

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

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

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

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

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

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

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

/w dx 对象表达式 根据 dx 对象表达式返回的布尔值设置条件断点。 参数是 dx) 表达式 (数据模型,该表达式的计算结果为 true, (匹配条件 ( break) 或 false (不匹配条件 ) 不中断) 。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

环境

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

其他信息

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

注解

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 命令非常有用。 bmSymbolPattern 语法等效于使用 x SymbolPattern,然后在每个结果上使用 bu。 例如,若要在 Myprogram 模块中以字符串“mem”开头的所有符号上设置断点,请使用以下命令。

示例

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

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

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

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

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

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

在内核模式下调试多处理器系统时,使用 bpba (Access 中断 设置的断点) 适用于所有处理器。 例如,如果当前处理器为 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 ;