不明确的断点解析
在调试器引擎的 10.0.25310.1001 版本及更高版本中,目前支持不明确的断点解析。
不明确的断点允许调试器在某些断点表达式解析为多个位置的特定情况下设置断点。 例如,在以下情况时可能会发生这种问题:
- 函数的多个重载。
- 有多个符号与断点表达式匹配。
- 同一个符号名称用于多个位置。
- 符号已内联。
- 在源窗口中具有多个实例的模板函数中设置断点。
启用后,调试器将在给定断点表达式的每个符号匹配上设置断点。 如果满足某些条件,调试器还将过滤符号匹配项。
有关使用断点的一般信息,请参阅使用断点。
启用不明确的断点解析
默认情况下,禁用不明确的断点。 要在调试器会话中启用,请在 WinDbg 控制台中运行以下命令:
dx @$debuggerRootNamespace.Debugger.Settings.EngineInitialization.ResolveAmbiguousBreakpoints = true;
要确认不明确的断点设置是否已处于活动状态:
0:010> dx @$debuggerRootNamespace.Debugger.Settings.EngineInitialization.ResolveAmbiguousBreakpoints
@$debuggerRootNamespace.Debugger.Settings.EngineInitialization.ResolveAmbiguousBreakpoints : true
有关使用 dx 命令的详细信息,请参阅 dx(显示调试器对象模型表达式)。
要禁用该功能,请将上述值设置为 false
。 要确保设置在会话之间保留,请确保单击File -> Settings -> Debugger Settings
,然后检查标记为Persist engine settings across debugger sessions
的框。
用法适用于单个断点
解析不明确的断点表达式仅适用于运行断点命令以在调试器中设置单个断点。 换句话说,使用 bm
命令设置多个断点将继续像往常一样工作。 运行启用了此功能的命令将导致单个断点的新断点行为。
有关断点命令的常规信息,请参阅 bp、bu、bm(设置断点)。
分层断点
分层断点表示将不明确的断点表达式解析为多个断点的结果。 如果表达式生成两个或多个将用于设置断点的匹配项,则会创建另一个断点来控制断点设置。 此重写断点(分层断点)可以像正常断点一样启用/禁用/清除和列出,并添加了对它拥有的断点执行相同操作的功能。
例如,如果运行命令 bp foo!bar
,导致两个匹配项与符号 bar
匹配,则将创建一个分层断点来控制这两个匹配项。 如果分层已启用/禁用/清除,则匹配的断点也将如此。
.bpcmds(显示断点命令)将列出可以运行以设置每个断点的断点命令。 分层断点拥有的断点仍将列出一个有效的 bp 命令,该命令将在其地址上设置断点。 分层断点也将在输出中列出,并显示可用于重新创建整组断点而非单个断点的命令。
不明确的符号
如果符号如下,在符号名称上设置断点应会导致以下行为:
重载:与符号匹配的每个重载都应具有断点。
模板函数:
如果表达式指定了所有模板参数(例如
bp foo!bar<int>
),则将在模板函数的特定实现上设置断点。如果表达式未指定类型实现(例如
bp foo!bar
),则不会设置断点。 在这种情况下,bm
应用于在模板函数上设置断点。调试器不支持部分模板规范,在这种情况下不会设置断点。
内联函数:每个内联位置都有一个断点
请注意,当符号表达式包含需要调试器进行更多计算的运算符或偏移量时,将不会设置多个断点。 例如,如果符号 foo
解析到多个位置,但表达式 foo+5
已计算,调试器将不会尝试解析要设置断点的所有位置。
断点代码示例
给出以下代码片段:
class BikeCatalog
{
public:
void GetNumberOfBikes()
{
std::cout << "There are 42 bikes." << std::endl;
}
int GetNumberOfBikes(int num)
{
std::cout << "There are " << num << " bikes." << std::endl;
return num;
}
};
调用命令 bu BikeCatalog::GetNumberOfBikes
将导致创建两个断点,每个重载一个断点。 列出断点将导致以下输出:
0:000> bl
2 e Disable Clear <hierarchical breakpoint> 0001 (0001) 0:**** {BikeCatalog!BikeCatalog::GetNumberOfBikes}
0 e Disable Clear 00007ff6`c6f52200 [C:\BikeCatalog\BikeCatalog.cpp @ 13] 0001 (0001) 0:**** BikeCatalog!BikeCatalog::GetNumberOfBikes
1 e Disable Clear 00007ff6`c6f522a0 [C:\BikeCatalog\BikeCatalog.cpp @ 9] 0001 (0001) 0:**** BikeCatalog!BikeCatalog::GetNumberOfBikes
不明确的源行
如果源行为如下,在源行上设置断点应会导致以下行为:
- 编译器优化函数:如果由于编译器优化导致行被拆分在多个位置,则断点将设置在函数中与指定行对应的最低位置上。
- 内联函数:为每个调用站点设置断点,除非指定的行已作为内联的一部分进行优化。
- 解析为多个位置:如果未满足上述条件,则将为每个地址设置一个断点,条件如下:
- 如果表达式中有一组与源行匹配的 N 地址,并且这些 N 地址的子集 M 在表达式的源行中没有源行位移,则只有 M 地址将具有断点。
- 如果 N 地址集中没有表达式中源行的源行位移为零的地址,则所有 N 地址都将具有断点。
基于符号索引进行筛选
每个符号都应具有唯一的符号索引。 有关符号结构的详细信息,请参阅 SYMBOL_INFO 结构。
如果多个地址的源行位移为零,调试器将使用符号索引来确保过滤重复匹配项。
模板和重载函数的示例
模板函数
在模板函数定义的源行上设置断点后,将导致模板函数的每个实现都产生一个断点。 给定第 19 行BikeCatalog.cpp
的以下模板函数:
template <class T>
void RegisterBike(T id)
{
std::cout << "Registered bike " << id << std::endl;
}
及其用法:
catalog.RegisterBike("gravel bike");
catalog.RegisterBike(1234);
调用命令 bp `BikeCatalog.cpp:19`
将设置两个断点,这些断点解析为稍后在文件中使用的模板函数的实现。 如果用户想要在函数上设置单个断点,则必须在模板函数实现的特定源行上设置断点,或者在具有适当类型信息(例如 bp BikeCatalog::RegisterBike<int>
)的模板函数符号上设置断点。
列出断点会导致以下输出:
0:000> bl
2 e Disable Clear <hierarchical breakpoint> 0001 (0001) 0:**** {BikeCatalog!BikeCatalog::RegisterBike<int>}
0 e Disable Clear 00007ff7`6b691dd0 [C:\BikeCatalog\BikeCatalog.cpp @ 20] 0001 (0001) 0:**** BikeCatalog!BikeCatalog::RegisterBike<int>
1 e Disable Clear 00007ff7`6b691e60 [C:\BikeCatalog\BikeCatalog.cpp @ 20] 0001 (0001) 0:**** BikeCatalog!BikeCatalog::RegisterBike<char const *>
重载函数
在源行上为重载函数的定义设置断点将导致重载函数的该定义上仅产生一个断点。 重用上述代码片段,第一行从第 5 行开始:
class BikeCatalog
{
public:
void GetNumberOfBikes()
{
std::cout << "There are 42 bikes." << std::endl;
}
int GetNumberOfBikes(int num)
{
std::cout << "There are " << num << " bikes." << std::endl;
return num;
}
};
调用命令 bp `BikeCatalog.cpp:9`
将在行上设置一个断点,以便 void
实现 GetNumberOfBikes
。 列出断点会导致以下输出:
0:000> bl
0 e Disable Clear 00007ff7`6b691ec0 [C:\BikeCatalog\BikeCatalog.cpp @ 9] 0001 (0001) 0:**** BikeCatalog!BikeCatalog::GetNumberOfBikes
内联函数
在内联函数的调用站点的源行上设置断点只会在该特定调用站点上生成一个断点,即使同一函数中存在另一个调用站点也是如此。
多个分层断点
分层断点将拥有其集中的每个断点,除非:
其集中的断点已清除
- 分层断点已清除。
- 创建另一个分层断点,其中包含此分层断点集中的一个断点。
另一种思路是,断点可能只有一个分层断点所有者,最近的断点命令将确定断点列表的状态。
此外,一个分层断点不能拥有另一个分层断点。
包含预先存在的断点
如果断点 A 本身存在,然后解析不明确的断点表达式以创建断点 A、B,则 A 将包含在带有 B 的新断点集中。
包含分层断点集交集
如果分层断点 A 拥有断点 B,则 C 和不明确的断点表达式被解析为创建断点:
B、C、D:断点 B、C 将联接具有断点 D 的新分层断点组,并且分层断点 A 被清除。
C、D 或 B、D:其中一个断点将联接具有断点 D 的新分层断点组,而分层断点 A 将继续存在,其余一个断点未加入新组。