Intel 64 和 IA-32 架构提供了调试代码和监视性能的调试工具 , 对于调试应用程序 , 系统软件和多任务 OS 十分重要 . 调试支持通过调试寄存器和 MSR 获取 :

  • 调试寄存器保存内存和 IO 的断点地址 , 程序员或系统设计者想要停止程序的执行 , 获取处理器的状态时 , 就设置断点 . 内存或 IO 访问涉及到断点地址时 , 就会产生 #DB 异常 .
  • MSR 监测分支 , 中断和异常 , 记录上一条分支 , 中断或者异常的地址 , 也记录中断或异常前执行的分支地址 .
  • 时间戳计数器 .
  • 监测共享的平台资源的特性 , 即 Intel Resource Director Technology .

1. 简介

下列 CPU 组件支持调试和性能监视 :

  • 调试异常 #DB , 发生调试事件时 , 切换程序控制到一个调试程序或任务 .
  • 断点异常 #BP , INT3 指令.
  • 断点地址寄存器 , DR0-DR3 .
  • DR6 , 产生调试或断点异常时报告生效的条件 .
  • DR7 , 指明导致断点的内存或 IO 访问形式 .
  • TSS 的 T 标志 , 尝试切换到设置了 TSS 的 T 标志的任务会产生一个 #DB .
  • EFLAGS 的 RF 标志 , 压缩多个异常到一条指令 .
  • EFLAGS 的 TF 标志 , 每执行一条指令 , 产生一个 #DB .
  • 断点指令 INT3 , 产生一个 #BP , 切换程序控制到调试器任务或进程 .
  • 上一个分支的记录工具 , 记录最近一次的分支记录到上一次分支记录 ( last branch record , LBR ) 栈 MSR . 一条分支记录包括一个分支源和目的指令地址 , 将分支记录作为 branch trace message ( BTM ) 发送到系统总线 .

这些组件允许在当前的程序或任务的上下文将调试器作为一个单独的任务或程序调用 , 下列条件可以用于调用调试器 :

  • 切换到一个特定的任务
  • 执行断点指令
  • 执行任何指令
  • 执行特定地址的指令
  • 读写特定内存地址 / 范围
  • 写入特定的内存地址 / 范围
  • 从特定 IO 地址 / 范围输入
  • 输出到特定 IO 地址 / 范围
  • 实处改变调试寄存器的内容

2. DR

DR 时特权级资源 , 实地址模式 , SMM 或者 CPL = 0 的保护模式可以通过 MOV 指令访问寄存器 .

DR 的基本功能时设置并监测最多 4 个断点 , 指明每个断点的下列信息 :

  • 产生断点的线性地址
  • 断点位置的长度 : 1 , 2 , 4 或 8 字节
  • 产生调试异常的地址必须执行的操作
  • 断点是否开启
  • 产生调试异常时 , 断点的条件是否存在

DR 寄存器介绍如下 :

  • DR0-DR3 , 调试地址寄存器 , 保存断点的 32 bit 线性地址 , 物理地址转换前进行断点比较 .

  • DR4 和 DR5 , 开启调试扩展时保留 .

  • DR6 , 调试状态寄存器 , 报告上一次调试异常产生时的调试条件 , 只有产生异常时才会更新这个寄存器 . 寄存器中的标志包含以下信息 :

    • B0-B3 标志 , 指明 #DB 产生时 , 关联的断点条件是否满足 .
    • BD ( debug register access detected ) 标志 , 指明指令流中的下一条指令访问了一个调试寄存器 .
    • BS ( single step ) 标志 , 指令调试异常由单步执行模式触发 , 单步模式是特权级最高的 #DB 异常 .
    • BT ( task switch ) 标志 , 表明 #DB 异常由切换到设置了 T 标志的任务产生 .
    • RTM ( restricted transactional memory ) 标志 , 清除时表示开启 RTM 会话区域的高级调试时 , RTM 区域发生了 #DB 或者 #BP .
  • DR7 , 开启或关闭断点 , 设置断点条件 , 寄存器中的标志和域控制下列设置 :

    • L0 - L3 ( local breakpoint enable ) 标志 , 设置时开启当前任务的断点的断点条件 . 检测到一个断点条件 , 并且相关的 Ln 标志设置 , 产生一个 #DB .
      每次任务切换处理器都会自动清除这些标志 , 避免新任务的断点条件 .
    • G0 - G3 ( global breakpoint enable ) 标志 , 设置时开启所有任务的断点的断点条件 . 检测到一个断点条件 , 并且相关的 Gn 标志设置 , 产生一个 #DB . 任务切换时处理器不会清除这些标志 , 允许所有任务开启一个断点 .
    • LE 和 GE ,( local and global exact breakpoint enable ) 标志 , 较新的处理器不支持 , 设置为 1 .
    • RTM ( restricted transactional memory ) 标志 , 设置时开启 RTM 事务区域的高级调试 .
    • GD ( general detect enable ) 标志 , 设置时开启 DR 保护 , 任何访问 DR 的 MOV 指令前都会产生一个 #DB 异常 . 检测到这种条件时 , 产生异常前设置 DR6 的 BD 标志 .
    • R / W0 - R / W3 , 指明断点的断点条件 , 含义受 CR4.DE 的影响 .
    • LEN0 - LEN3 , 指明对应断点地址寄存器中内存地址的大小 .

如果 RWn 是 00 ( 指令执行 ) , LENn 也应该是 00 , 使用其他的大小未定义 .

如果一次访问中的任何字节在断点地址寄存器和 LENn 域指明的范围内 , 就会触发数据断点 .

3. 调试异常

Intel 64 和 IA-32 架构有两个中断向量专用于处理调试异常 : vector 1 ( #DB ) 和 vector 3 ( #BP ) .

3.1. #DB , 中断向量 1

调试异常的处理函数通常是一个调试器程序 , 或一个大的软件系统的一部分 .

指令断点和一般检测条件导致 fault , 其他的调试异常条件导致 trap , 调试异常可能一次报告一个或者两个 .

INT1 指令产生一个 trap 类型的调试异常 , 硬件厂商可能使用这个指令进行硬件调试 , 因此 Intel 推荐软件厂商使用 INT3 指令产生软件断点 .

picture 1

3.1.1. 指令断点异常条件

处理器试图执行的指令位于用于检测指令执行的断点地址寄存器指明的地址时 , 报告一个指令断点 , 执行断点处的目标指令前产生一个 fault 类型的 #DB .

指令断点是优先级最高的调试异常 , 优先指令的译码或执行阶段检测到的任何其他异常处理 . 但是 , 如果一个指令断点紧跟在 POP SS / MOV SS 指令后 , 断点会被压缩 , 就像 EFLAGS.RF = 1 .

由于指令断点的调试异常在指令执行前产生 , 如果异常处理函数没有移除指令断点 , 处理器恢复执行指令后 , 会再次检测到指令断点 , 产生另一个 #DB . 为了防止在一个指令断点循环 , Intel 64 和 IA-32 架构提供了 EFLAGS 的 RF 标志 , 设置时 , 处理器忽略指令断点 .

所有的处理器按照下列防止管理 RF 标志 : 检查指令断点 , CS 限制 , FP 异常后 , 开始执行指令时清除 RF 标志 . 任务切换和 IRETD / IRETQ 指令会传递 TSS / stack 中的 RF 镜像到 EFLAGS 寄存器 .

处理器调用一个处理函数时 , 建立压入栈中的 EFLAGS 镜像的 RF 标志 :

  • 对于指令断点产生的 #DB 外的任何 fault 类型的异常 , 压入栈中的 RF 值为 1 .
  • 对于任何重复的字符串指令的迭代后到达的中断 , 除了最后一次迭代 , 压入的 RF 值为 1 .
  • 对于任何重复的字符串指令的迭代产生的 trap 类型的异常 , 除了最后一次迭代 , 压入的 RF 值为 1 .
  • 对于其他情况 , 压入的 RF 值是调用处理函数时 EFLAGS.RF 的值 , 这些情况包括 :
    • 指令断点产生的 #DB .
    • 指令间硬件产生的中断 .
    • 指令完成后产生的陷入类型的异常 .
    • 软件产生的中断 .

如上所述 , 处理器调用指令断点产生的调试异常的处理函数前不会设置 RF 标志 , 异常处理函数通过设置栈中 EFLAGS 镜像的 RF 标志防止指令断点的重复发生 . 处理器从异常处理函数返回时 , 导致返回的 IRETD / IRETQ 或任务切换拷贝 EFLAGS 镜像到 EFLAGS 的 RF 标志 , 处理器就会在执行下一条指令时忽略指令断点 .
设置 RF 标志不会阻止检测其他类型的调试异常条件 , 也不会阻止非调试异常的产生 .

对于 Pentium 处理器 , 一个指令断点和另一个 fault 类型的异常同时产生时 , 第二个异常处理后 , 处理器可能产生一个假的调试异常 , 即使调试异常处理函数设置了 EFLAGS 镜像的 RF 标志 . 为了防止产生假的异常 , 所有的 fault 类型的异常处理函数需要设置 EFLAGS 镜像的 RF 标志 .

3.1.2. 数据内存和 IO 断点异常条件

处理器试图访问断点地址寄存器中指明的内存或 IO 地址时 , 就会报告数据内存和 IO 断点 . 处理器执行进行访问的指令后产生异常 , 因此这些断点条件会产生陷入类型的异常 .

由于数据断点是陷入 , 写内存的指令会在异常产生前覆盖原始数据 , 如果一个调试器需要保存写断点的内容 , 需要在设置断点前保存原始内容 . 处理函数可以在触发断点后报告保存的值 , 调试寄存器中的地址可以用于定位触发断点的指令保存的新值 .

如果快速字符串操作的字符串指令迭代期间检测到一个数据断点 , 调试异常的传递可能推迟到对应的迭代组完成后 .

对于产生 IO 断点调试异常的重复的 INSOUTS 指令 , 处理器在第一次迭代完成后产生异常 . 重复的 INSOUTS 指令在访问内存地址断点位置的迭代后产生数据断点调试异常 .

如果 MOVPOP 指令加载了 SS 寄存器 , 遇到一个数据断点 , 产生的调试异常在下一条指令完成后传递 .

任何挂起的数据或 IO 断点在传递异常时都会丢失 , 例如 , 如果一个 #MC 异常在包含数据断点的指令后发生 ( 在传递调试异常前 ) , 数据断点就会丢失 . 如果加载 SS 寄存器的 MOVPOP 指令遇到一个数据断点 , 如果下一条指令导致一个 fault , 数据断点就会丢失 .

INT n , INT3INTO 产生的事件不会导致数据断点的丢失 . 如果一个加载 SS 寄存器的 MOVPOP 指令遇到一个数据断点 , 并且下一条指令是软件中断 ( INT n , INT3 , 或 INTO ) , 数据断点导致的 #DB 异常会在切换到软件中断处理函数后传递 . #DB 处理函数需要考虑 #DB 可能在调用软件中断处理函数后传递的事实 , 尤其是识别数据断点和传递 #DB 之间 , CPL 可能已经改变 .

3.1.3. 一般检测的异常条件

设置 DR7 的 GD 标志时 , 如果 DR 正在被另一个应用 , 比如模拟器或调试器 , 使用 , 处理器试图访问任何 DR 都会产生一般检测的调试异常 . 这个保护特性保证在需要时能够完全控制 DR , 调试异常处理函数可以检查 DR6 的 BD 标志检测这个条件 . 处理器在执行访问 DR 的 MOV 指令前产生一个 fault 类型的异常 .

3.1.4. 单步异常条件

如果处理器在执行指令时检测到设置了 EFLAGS 寄存器的 TF 标志 , 就会产生一个陷入类型的单步调试异常 , 因为异常在指令执行后产生 . 处理器不会在设置 TF 标志的指令后产生异常 .

处理器调用异常处理函数前清除 TF 标志 , 如果任务切换时 TSS 设置了 TF 标志 , 新任务执行第一条指令后产生异常 .

任务内的特权级改变不会清除 TF 标志 , INT n , INT3INTO 指令会清除这个标志 . 为了保证安全 , OS 必须在每一个单步陷入后检查 CPL , 判断当前的特权级是否应该继续单步 .

中断特权级保证发生外部中断时 , 单步停止 . 外部中断和单步中断同时一起时 , 单步异常优先处理 . 这个操作清除 TF 标志 .
保存返回地址或者切换任务后 , 执行单步处理函数的第一条指令前检查外部的中断输入 , 如果外部中断仍然挂起 , 进行处理 . 外部中断处理函数不会运行在单步模式 , 要单步执行一个中断处理函数 , 单步执行调用中断处理函数的 INT n 指令 .

如果 EFLAGS.TF = 1 时 MOVPOP 指令加载 SS 寄存器 , MOVPOP 指令后不会产生单步调试异常 .

3.1.5. 任务切换异常条件

如果设置了新任务的 TSS 的 T 标志 , 任务切换后处理器产生一个调试异常 . 异常在程序控制传给新的任务后 , 执行新任务的第一条指令前产生 . 异常处理函数可以检查 DR6.BT 标志检测这个条件 .

如果 IDT 的第一项 #DB 是一个人物们 , 对应的 TSS 不能设置 T 标志 , 否则会导致处理器处于循环状态 .

3.2. #BP , 中断向量 3

#BP 由 INT3 指令产生 , 是调试器挂起程序执行 , 检查寄存器和内存地址的机制 , 对于断点调试器十分有用 , 因为断点异常可以调用单独的异常处理函数 .
需要设置的断点多于调试寄存器时 , 或者开发时需要放置断点在程序的源码时 , 断点异常也十分有用 .

4.1. 调试异常 , 断点异常和受限事务内存 ( RTM )

RTM 是一个指令集接口 , 允许软件使用 XBEGINXEND 指令标识事务区域 ( 或者临界区域 ) .

一个 RTM 事务区域的执行以 XBEGIN 指令开始 , 如果区域的执行成功到达一个 XEND 指令 , 处理器保证区域内发生的所有内存操作就像是一瞬间发生的 , 从其他的逻辑处理器看时 .
如果处理器无法原子的提交所做更改 , RTM 事务区域的执行无法成功 . 这种情况发生时 , 处理器回退执行 , 即事务终止 , 处理器丢弃区域内执行的所有更改 , 恢复状态到执行没有发生时 , 恢复执行 XBEGIN 指令指明的 fallback 指令地址 .

如果 RTM 事务区发生了 #DB 或 #BP , 事务终止 , 处理器设置 EAX[4] , 不会传递异常 .

软件可以开启 RTM 事务区的高级调试 , RTM 事务区域内 #DB 或 #BP 导致的事务终止不会从 XBEGIN 指令指明的 fallback 地址恢复执行 , 而是在 XBEGIN 指令恢复执行 , 并传递一个 #DB , 这种 #DB 会清除 DR6.RTM[bit 16] .

5. 上一个分支 , 中断 , 和异常记录

P6 处理器引入了在执行的分支 , 中断 , 异常设置断点 , 从一个分支单步执行到下一个的能力 ; 更新的处理器允许在内存中的分支轨迹存储缓冲区 ( branch trace store , BTS ) 记录分支信息 .

17 章有很多部分介绍了这种功能 , 不同的处理器架构在不同的部分介绍 , 这个文档只介绍 Skylake . 这一部分介绍共同的特性 , 通常由 IA32_DEBUGCTL MSR 控制 .

5.1. IA32_DEBUGCTL MSR

IA32_DEBUGCTL MSR 位于地址 01D9H , 包括以下标志 :

  • LBR ( last branch / interrupt / exception ) 标志 , 设置时处理器记录最近分支 , 中断 , 或 / 和异常的执行轨迹到上一个分支记录 ( LBR ) 栈中 .
  • BTF ( single-step on branches ) 标志 , 设置时 , 处理器将 EFLAGS 寄存器的 TF 标志作为 “单步分支” , 而不是 “单步指令” , 允许处理器单步执行分支 .
  • TR ( trace message enable ) 标志 , 设置时 , 开启分支轨迹消息 , 处理器检测到分支 , 中断或异常时 , 将分支记录作为一个分支轨迹消息 ( BTM ) 发送到系统总线 .
  • BTS ( branch trace store ) 标志 , 设置时 , BTS 组件记录 BTM 到内存驻留 BTS 缓冲区 , 作为 DS 保存区域的一部分 .
  • BTINT ( branch trace interrupt ) 标志 , 设置时 , BTS 组件在 BTS 缓冲区满时产生一个中断 ; 清除时 , BTM 以循环的方式记录 BTM 到缓冲区 .
  • BTS_OFF_OS ( branch trace off in privileged code ) 标志 , 设置时 , CPL = 0 时忽略 BTS 或 BTM .
  • BTS_OFF_USR ( branch trace off in user code ) 标志 , 设置时 , CPL > 0 时忽略 BTS 或 BTM .
  • FREEZE_LBRS_ON_PMI 标志 , 设置时 , 硬件 PMI 请求会冻结 LBR 栈 .
  • FREEZE_PERFMON_ON_PMI 标志 , 设置时 , PMI 请求冻结性能计数器 .
  • FREEZE_WHILE_SMM , 设置时 , 传递 SMI 时 , 处理器切换控制到 SMI 处理函数前清除 IA32_PERF_GLOBAL_CTRL 的所有使能位 , 保存 IA32_DEBUGCTL 的内容 , 关闭 LBT , BTF , TR , BTS 域 .
    随后 , SMI 处理函数发出 RSM 完成服务后 , 设置 IA32_PERF_GLOBAL_CTRL 的使能位为 1 , 恢复 SMI 切换前保存的 IA32_DEBUGCTL 副本 .
  • RTM , 设置时 , 如果同时设置了 DR7.RTM , 开启 RTM 事务区域的高级调试 .

5.2. 分支 , 异常 , 和中断的监测

IA32_DEBUGCTL MSR 的 LBR 标志设置时 , 处理器自动记录分支 , 中断和异常 ( 除了调试异常 ) 的分支记录到 LBR 栈 MSR .

处理器产生一个 #DB , 执行异常处理函数前自动清除 LBR 标志 , 不会清除之前保存的 LBR 栈 MSR .

调试器可以使用 LBR 栈中的线性地址重新设置断点地址寄存器中的地址 , 允许从特定 bug 反向追踪到源码 .

某些处理器如果清除了 LBR 标志 , 设置 TR 标志 , 处理器会继续更新 LBR 栈 MSR , 这是因为这些处理器产生 BTM / BTS 记录过程中使用 LBR 栈中的项 , #DB 不会自动清除 TR 标志 .

5.3. 分支的单步执行

软件同时设置 IA32_DEBUGCTL MSR 的 BTF 标志和 EFLAGS 的 TF 标志时 , 处理器只会在导致分支的指令后产生一个单步调试异常 , 允许调试器单步执行分支导致的控制转换 , 隔离一个 bug 到特定的代码块 .

处理器产生调试异常时自动清除 BTF 标志 , 调试器恢复程序执行时 , 要继续单步执行分支 , 必须设置 BTF 标志 .

5.4. 分支轨迹消息

设置 IA32_DEBUGCTL MSR 的 TR 标志开启 BTM , 处理器检测到一个分支 , 异常或中断时 , 将一个分支记录作为一个 BTM 发送到系统总线 . 监视系统总线的调试设备可以读取这些信息 , 和分支 , 中断 , 异常事件的操作同步 .

5.5. 分支轨迹存储

分支 , 中断和异常的轨迹提供了确定达到特定代码位置执行的代码路径 , 对于调试代码十分有用 . IA32_DEBUGCTL MSR 的 LBR 标志提供了记录分支 , 中断和异常的机制 , 将其保存到 LBR 栈 MSR , 并且通过 BTM 发送到系统总线 .
BTS 机制提供了保存分支记录到内存驻留 BTS 缓冲区的机制 , 作为 DS 保存区域的一部分 . BTS 缓冲区可以配置为循环的 , 也可以配置为缓冲区满时产生一个中断 .

单独设置 BTS 可能严重降低处理器的性能 , CPL 限定的分支轨迹保存机制可以减轻发送 / 记录 BTM 导致的性能下降 .

5.6. CPL 限定的分支轨迹机制

CPL 限定的分支轨迹机制只有部分处理器可用 , 系统软件可以选择性的指明发送 / 保存 BTM 需要的特权级 .

5.7. 收到 PMI 时冻结 LBR 和性能计数器

很多情况都会产生性能监视中断 ( PMI ) , 处理函数需要确定原因 . IA21_DEBUGCTL 中的接口可以降低服务 PMI 的时间开销 :

  • 收到 PMI 时冻结 LBR , 允许 PMI 服务例程确保 LBR 栈中的内容和目标负载相关 , 不会被处理 PMI 的分支更改 .
  • 收到 PMI 时冻结 PMC , 允许 PMI 服务例程确保性能计数器中的内容和目标负载相关 , 不会被 PMI 服务例程内的活动修改 .

满足下列条件时 , 收到 PMI 时冻结 LBR 和 PMC :

  • 一个性能计数器溢出 , 并且编程设定溢出时发送 PMI .
  • PEBS 缓冲区快满 , 到达了中断的阈值 .
  • BTS 缓冲区快满 , 达到了中断的阈值 .

5.8. LBR 栈

Intel 处理器支持 LBR 栈和 top-of-stack ( TOS ) 指针 MSR , 但是 LBR 栈中 MSR 的数量和 TOS 指针值的有效范围可能不同 .

上一个分支记录机制不但追踪分支指令 ( JMP , Jcc , LOOP , CALL ) , 还会记录改变指令指针的操作 ( 外部中断 , 陷入和 fault ) .
分支记录机制通常使用一组 MSR , 称作上一个分支记录 ( LBR ) 栈 .

  • LBR 栈 , 由 N 对 MSR 组成 , 保存最近分支的源和目的地址 :
    • MSR_LASTBRANCH_0_FROM_IP 到连续的 N-1 个 MSR 地址保存源地址 .
    • MSR_LASTBRANCH_0_TO_IP 到连续的 N-1 个 MSR 地址保存目的地址 .
  • 上一个分支记录 TOS 指针 , TOS 指针 MSR 的最低 M 位包含一个指向包含最近记录的分支 , 中断或异常的 M 位指针 .

5.8.1. LBR 栈和 Intel 64 处理器

LBR MSR 都是 64 位的 , 64 位模式保存完整的地址 ; 64 位模式外 , 高 32 位为 0 .

软件需要查询 IA32_PERF_CAPABILITIES[5:0] 查询 LBR 栈中保存的地址格式 , 定义了 4 种格式 :

  • 000000B , 32 位记录格式 , 保存源 / 目的地址的 32 位偏移到当前的 CS .
  • 000001B , 64 位 LIP 记录格式 , 保存源 / 目的地址的 64 位线性地址 .
  • 000010B , 64 位 EIP 记录格式 , 保存源 / 目的地址的 64 位偏移 ( 有效地址 ) .
  • 000011B , 64 位 EIP 记录格式和标志 , 保存源 / 目的地址的 64 位偏移 ( 有效地址 ) , 错误预测信息报告在 LBR 栈中 FROM 寄存器的高位 .

其余的格式和 000011B 类似 , 只是保存更多的额外信息 , 包括 :

  • TSX 信息
  • 报告上次 LBR 更新后逝去的 cycle 数
  • 保存信息到 LBR_INFO MSR 中

5.8.2. 上一个异常记录和 Intel 64 架构

Intel 64 和 IA-32 架构还提供了保存异常或中断前的最后一次分支的分支记录的 MSR , 64 位模式记录是 64 位的 , 兼容模式下高 32 位为 0 .

5.9. BTS 和 DS 保存区域

调试存储 ( debug store , DS ) 特性标志指明处理器是否提供了 DS 机制 , DS 机制允许 :

  • 保存 BTM 到一个内存驻留 BTS 缓冲区 .
  • 处理器基于事件取样 ( processor event-based sampling , PEBS ) 也使用 DS 保存区域 .

DS 保存区域时软件设计的内存区域 , 用于收集下列两类信息 :

  • 分支记录 , 任何时候检测到分支 , 中断或异常 , 都会保存一个分支记录到 DS 保存区域的 BTS 缓冲区 .
  • PEBS 记录 , 配置一个性能计数器给 PEBS , 计数器溢出时 , 保存一个 PEBS 记录到 DS 保存区域 . 记录包含导致计数器溢出的下一个 PEBS 事件发生时处理器的状态 ( 8 个通用寄存器 , EIP 寄存器 , EFLAGS 寄存器 ) .
    状态信息记录后 , 计数器自动重置为特定值 , 事件计数再次开始 .

DS 保存区域分为三部分 : 缓冲区管理区域 , BTS 缓冲区 , PEBS 缓冲区 .
缓冲区管理区域用于定义 BTS 和 PEBS 缓冲区的位置和大小 , 处理器使用缓冲区管理区追踪分支和 / 或对应缓冲区中的 PEBS 记录 , 并且记录性能计数器的重置值 . DS 缓冲区管理区域的第一个字节的线性地址通过 IA32_DS_AREA MSR 指明 .


缓冲区管理区域包括以下域:

  • BTS 缓冲区基地址 , BTS 缓冲区第一个字节的线性地址
  • BTS 索引 , 下一条 BTS 记录要写入的线性地址
  • BTS 绝对最大值 , BTS 缓冲区外的第一个字节的线性地址
  • BTS 中断阈值 , 产生中断的 BTS 记录的线性地址
  • PEBS 缓冲区基地址
  • PEBS 索引
  • PEBS 绝对最大值
  • PEBS 中断阈值
  • PEBS 计数器重置值

BTS 缓冲区中的记录长度为 12 byte , 包括以下域 :

  • last branch from , 分支 , 中断或异常产生分支的指令的线性地址 .
  • last branch to , 分支目标 , 异常或中断处理函数服务例程的第一条指令的线性地址 .
  • branch predicted , bit 4 指明分支是否被预测到 .

5.9.1. 建立 DS 保存区域

DS 保存区域必须在内存中按照下列方式建立 :

  1. 在内存中创建 DS 缓冲区管理信息 .
  2. 写入 DS 缓冲区管理区域的基线性地址到 IA32_DS_AREA MSR .
  3. 在 xAPIC LVT 为 fixed delivery and edge sensitive 创建性能计数器项 .
  4. 在 IDT 创建和 xAPIC LVT 中性能计数器项相关的 vector 的中断处理函数 .
  5. 编写处理中断的中断服务例程 .

DS 保存区域需要满足以下限制 :

  • 三个 DS 保存区域需要从一个 non-paged pool 分配 , 标记为 accessed 和 dirty , OS 需要负责保留包含缓冲区的内存页 present , 并且标记为 accessed 和 dirty .
  • DS 保存区域可以大于一个页 , 但是页面必须映射到连续的线性地址 .
  • 推荐 BTS 和 PEBS 缓冲区的大小是对应记录大小的整数倍 .
  • 精确的事件记录缓冲区应该足以保存等待服务中断时可能发生的精确的事件记录 .
  • DS 保存区应该位于内核空间 , 不能和代码位于同一个页 , 避免触发自修改代码的行为 .
  • 缓冲区没有内存类型的限制 , 处于性能考虑 , 推荐使用 WB 内存类型 .
  • DS 保存区域活跃时 , 必须防止系统进入 A20M 模式 , 缓冲区内的所有地址的 bit 20 必须为 0 .
  • 所有的进程必须映射包含缓冲区的页到相同的物理地址 , 保证对于 CR3 的更改不会改变 DS 地址 .
  • DS 保存区域预期只在开启 APIC 的系统上使用 , APIC 中的 LVT 性能计数器项必须初始化使用中断门而不是陷入门 .

5.9.2. 建立 BTS 缓冲区

三个标志 TR , BTS , BTINT 控制分支记录的生成和保存到 BTS 缓冲区 .
TR 开启 BTM 的生成 , BTS 决定是发送 BTM 到系统总线 ( 清除 ) 还是保存到 BTS 缓冲区 ( 设置 ) .
BTM 无法在发送到系统总线的同时保存到 BTS 缓冲区 .
BTINT 标志开启 BTS 缓冲区满时产生中断 , 清除时 , 缓冲区是一个循环缓冲区 .

建立一个 DS 保存区 , 收集分支记录到 BTS 缓冲区的操作如下 :

  1. 设置 DS 缓冲区管理区域的 BTS 缓冲区基地址 , BTS 索引 , BTS 绝对最大值 , BTS 中断阈值 , 在内存建立 BTS 缓冲区 .
  2. 设置较新的处理器的 IA32_DEBUGCTL 的 TR 和 BTS 标志 .
  3. 如果使用循环缓冲区 , 清除 IA32_DEBUGCTL 的 BTINT 标志 .

5.9.3. 编写 DS 中断服务例程 ( ISR )

BTS , 非精确的基于事件取样 ( non-precise event-based sampling ) 和 PEBS 组件共享同样的中断向量和 ISR . 要处理三种中断 , DS ISR 必须包含分开的处理函数例程 . 通过下列方式编写一个 DS ISR :

  • ISR 必须是内核驱动的一部分 , 运行在特权级 0 , 保证缓冲区存储区域的安全 .
  • 三种组件共享中断向量 , DS ISR 必须检查所有可能的中断原因 , 传递控制给合适的处理函数 .
  • MP 系统中的每个处理器都必须有单独的保存区域 , 缓冲区和状态 .
  • 一进入 ISR , 应该关闭分支踪迹信息和 PEBS , 防止访问 DS 保存区域的竞争条件 . 退出 ISR 时需要恢复到原来的值 .
  • 缓冲区满时 , 处理器不会关闭 DS 保存区域 .
  • 读取合适缓冲区的内容后 , 直到当前索引 , 不包括缓冲区的当前索引 , ISR 必须重置缓冲区的索引为缓冲区的其实地址 .
  • ISR 必须清除性能计数器 LVT 项中的掩码 .
  • 如果 ISR 正在处理 PEBS 导致的 PMI 溢出 , 必须通过 IA32_PERF_GLOBAL_CTRL / IA32_PERF_GLOBAL_OVF_CTRL 重新开启计数器 .
  • Pentium 4 和 Xeon 处理器收到中断会屏蔽 PMI , 退出中断处理函数时需要清除 .

6. Skylake 架构的上一次分支 , 调用栈 , 中断和异常记录

基于 Skylake 微架构的处理器提供了一些保存上一次分支记录的扩展 :

  • 新 LBR 格式 : IA32_PERF_CAPABILITIES[5:0] = 00101b .
  • 每个 LBR 栈包括一个 MSR 三元组 :
    • MSR_LASTBRACH_x_FROM_IP
    • MSR_LASTBRANCH_x_TO_IP
    • MSR_LBR_INFO_x , 保存分支预测标志 , TSX 信息 , 逝去的 cycle 数据 .
  • LBR 栈的大小增加到 32 .

7. TSC

Intel 处理器定义了一个时间戳计数器机制 , 可以用于监测和标识处理器事件发生的相对时间 . 计数器的架构包含下列组件 :

  • TSC 标志 , 指示 TSC 是否可用 .
  • IA32_TIME_STAMP_COUNTER MSR , 作为计数器使用的 MSR .
  • RDTSC 指令 , 读取 TSC 的指令 .
  • TSD 标志 , 控制寄存器的标志位 , 用于开启或关闭 TSC .

TSC 是一个 64-bit 计数器 , RESET 后重置为 0 , 即使处理器由 HLT 指令或者外部引脚 STPCLK# 停止 , 计数依然增加 . 外部的 DPSLP# 引脚可能停止 TSC .

不同的处理器以不同的方式增加 TSC :

  • 较老的处理器 , TSC 随着每个内部处理器时钟周期增加 , 后者通过当前的核心时钟和总线时钟的比率确定 , Intel Speedstep 计数可能影响处理器时钟 .
  • 较新的处理器 , TSC 以固定的比率增加 . 比率可以是处理器的最大核心时钟和总线时钟的比率 , 或者是处理器启动时的最大解析频率 . 某些处理器的 TSC 频率可能和品牌字符串中的频率不同 .
    特定的处理器配置决定了具体行为 , 不变的 TSC 行为保证每一个时钟周期是相同的 , 支持将 TSC 作为一个 wall clock 使用 , 即使处理器核心改变了频率 .

RDTSC 指令返回的值至少在 10 年内不会重复 , 可以由运行在任何特权级的软件使用 , 也可以在 virtual-8086 模式下执行 . TSD 标志可以限制指令只能由运行在特权级 0 的应用执行 .

RDMSRWRMSR 也可以用于读写 TSC , 某些处理器只能写低 32 位 , 有些寄存器可以写全部 64 位 .

7.1. invariant TSC

较新的处理器可能支持不变的 TSC 特性 , 在所有的 ACPI P- , C- 和 T-State 下以固定的比率运行 .

7.2. IA32_TSC_AUX 寄存器和 RDTSCP 支持

Nehalem 架构提供了一个辅助 TSC 寄存器 , IA32_TSC_AUX , 和 IA32_TSC 一起使用 , 由特权软件初始化为一个签名值 ( 比如逻辑处理器的 ID ) .

RDTSCP 可以在一个原子操作内读取 64-bit TS 和签名值 , 前者保存在 EDX:EAX , 后者保存到 ECX . 原子性确保读取 TSC 和 TSC_AUX 之间不会发生上下文的切换 .

用户模式软件可以使用 RDTSCP 检测连续读取 TSC 之间是否发生了 CPU 迁移 , 也可以用于调整 NUMA 系统内的 per-CPU 差异 .

7.3. TSC 调整

软件可以使用 WRMSR 指令写入 IA32_TIME_STAMP_COUNTER MSR 修改逻辑处理器的 TSC 值 , 要同步多个逻辑处理器的 TSC 值 , 软件必须写入每个逻辑处理器 . 软件可能很难保证某个时刻所有的逻辑处理器都有相同的值 .

IA32_TSC_ADJUST MSR 可以简化这个同步调整操作 , 每个逻辑处理器都维护自己的 IA32_TSC_ADJUST , 并且按照以下方式使用 :

  • 重置后 , IA32_TSC_ADJUST MSR 为 0 .
  • 如果写入 IA32_TIME_STAMP_COUNTER 的操作和 TSC 相比增加了值 X , 逻辑处理器也增加值 X 到 IA32_TSC_ADJUST .
  • 如果写入 IA32_TSC_ADJUST 的操作向 MSR 增加了值 X , 逻辑处理器也增加值 X 到 TSC .

和 TSC 不同 , IA32_TSC_ADJUST MSR 的值只会随着 WRMSR 改变 , 不会随着时间的消逝改变 , 因此软件可以向 IA32_TSC_ADJUST 写入相同的值调整 TSC .

7.4. invariant time-keeping

不变的 TSC 基于不变的时间保持硬件 ( 称作 always running timer , ART ) , 运行在核心水晶时钟频率 .

8. Intel 资源主管技术监测特性

Intel Resource Director Technology ( Intel RDT ) 特性提供了一组监测技术 , 包括 cache 监测技术 ( CMT ) 和内存带宽监测 ( MBM ) . Xeon E5 v3 引入了每个逻辑处理器的资源监测能力 , 测量共享资源 .

CMT 允许 OS , 管理员或类似的系统管理代理确定平台上运行的应用对 cache 的使用情况 .
MBM 基于 CMT 架构 , 允许监测某一级到下一级 cache 的带宽 , 比如 L3 到系统内存 .

上述的监测机制提供了下列主要共享的基本架构特性 :

  • 枚举平台可用的监测机制 .
  • 枚举每个一个子特性的细节的框架 .
  • OS 或管理程序为每一个要调度执行在逻辑处理器的软件线程指明一个软件定义的 ID 的机制 , 这些标识符称作资源监视 ID ( RMID ) .
  • 硬件内监视 cache 占有率和带宽数据的机制 .
  • 软件执行期间的任意时刻 , OS 或管理程序读取收集的某个给定软件 ID 的度量信息比如 L3 占有率或内存带宽的机制 .

8.1. CMT 和 MBM 简介

共享资源的监测机制通过资源监视 ID ( RMID ) 提供了应用和逻辑处理器之间的一层抽象 , 系统中的每个逻辑处理器可以独立分配一个 RMID , 或者多个逻辑处理器可以分配相同的 RMID . 对于每个逻辑处理器 , 某个时间只有一个 RMID 活跃 , IA32_PQR_ASSOC MSR 指明逻辑处理器的活跃 RMID . 软件可以修改这个 MSR , 改变活跃的 RMID .

底层的平台共享资源监视硬件根据 RMID 记录 cache 数据 , 比如使用率 , 内存访问导致的 miss , 并通过一个计数器寄存器 ( IA32_QM_CTR ) 报告监视到的数据 .
读取数据前 , 软件必须通过 IA32_QM_EVTSEL 选择要报告的数据类型和 RMID .

处理器对于 CMT 和 BMB 的支持 , 对于监测资源类型和能力的支持都需要通过 CPUID 指令获取 .

8.2. 监测资源 RMID 的关联

软件使用监测特性的第一步是关联一个 RMID 到一个软件线程 ( 或某个应用的一组线程 ) , 这个过程对于所有的监测特性 ( CMT , MBM ) 都相同 ; 从 package 内的任何逻辑处理器的角度看 , 一个给定的 RMID 含义相同 . 也就是说 , 线程和 RMID 是一一对应的关系 , 从而通过 RMID 获取 cache 占用率 , 内存带宽或者其他的监测信息 .

应用线程和 RMID 的关联要求 OS 在上下文切换时编程 per-logical-processor MSR IA32_PQR_ASSOC , MSR 指明监测硬件用于标记内部操作的活跃 RMID . RMID 的宽度和处理器的型号相关 , 在物理封装内全局有效 .

8.3. 监测资源的选择和报告架构

CMT 和其他相关的特性的报告机制通过一对 MSR 导出 , 可以用于读取不同的测量数据 , 数据基于 per-RMID 报告 . 这些事件不会触发 APIC 中断 , 只是显式用于取样计数 .

共享资源监视特性的 MSR 对和性能监视计数器独立 , 软件可以同时使用监视特性和性能监视计数器 .

访问汇总的监测信息通过下列可编程的监测 MSR 完成 :

  • IA32_QM_EVTSEL , 和可编程的性能监视的事件选择 MSR 类似 , IA32_QM_EVTSEL.EvtID 指明支持的资源类型的事件代码 , 供硬件报告和 IA32_QM_EVTSEL.RMID 相关的监测数据 . 支持的事件编码包括 :

    picture 1

  • IA32_QM_CTR , 可用时报告监控数据 , IA32_QM_CTR.Error 表明不支持软件配置的 RMID 或事件类型 ; IA32_QM_CTR.Unavailable 表明 RMID 的监测数据不可用 ; IA32_QM_CTR.data 只有 error 和 unavailable 清除时有效 , CMT 可以转化为 cache 占用或带宽数据 .

8.4. 监测编程需要考虑的事

系统软件编程 IA32_QM_EVTSEL , 然后读取 IA32_QM_CTR 获取返回的数据 . 具体使用时 , 需要考虑下列情况 :

  • 监测动态配置 , IA32_QM_EVTSEL 和 IA32_PQR_ASSOC 都能通过 RDMSR / WRMSR 读写 , 写入保留位或者 RMID 超过 maxRMID 时会产生 #GP .
  • 监测节电特性的操作 , 一些高级电源管理特性可能收缩 L3 cache , 减少 CMT 占用计数 , 也可能刷出 L3 数据影响 MBM 带宽 .
  • 监测其他运行模式的操作 , 传递 SMI 的过程中 IA32_PQR_ASSOC 和监测计数器的状态保持不变 , 因此 SMM 处理函数和 SMM 处理函数的数据可能对监测数据表现出虚假的贡献 .
    SMM 可以在进入 SMM 时保存配置的 QOS 监测状态 , 退出时恢复 , 减少对于监测数据的影响 .
  • 监测 RAS 特性的操作 , 总体而言 , Intel 平台的 Reliability , Availability , Serviceability ( RAS ) 特性不会影响共享资源的监测计数 . 软件 RAS 特性导致的内存拷贝 , cache 访问可能影响共享资源监视计数器 .

9. Intel RDT 分配特性

RDT 特性提供了一组分配功能 , 包括 cache 分配技术 ( CAT ) 和代码数据分级 ( CDP ) .

9.1. CAT 简介

OS , 管理程序 / 虚拟机管理者 ( VMM ) 可以通过 CAT 指明应用可以填充的 cache 空间 ( 作为硬件的提示信息 – 一些特性如电源管理可能覆盖 CAT 设置 ) .

CAT 包含以下主要特性 :

  • 一种枚举平台的 CAT 功能和可用的提供 CAT 控制的资源类型的机制 .
  • 一种 OS 或管理程序可以配置可用资源的机制 .
  • 控制 LLC 填充策略的硬件机制 .

9.2. CAT 架构

CAT 的基本目标是基于应用的优先级或服务类型 ( COS 或 CLOS ) 分配资源 , 处理器提供一组可以分配给应用的 COS .
资源间的 COS 是一致的 , 一个 COS 可能包含多个资源控制参数 , 减少上下文切换时的软件开销 .

CAT 的基本组件包括 :

  • 使用 CPUID 指明是否支持 CAT , 可以控制的资源类型
  • 对于每种可用的资源类型 , CPUID 可以枚举 COS 的数量和可以用于控制资源分配的位掩码 ( CBM )
  • 执行环境 ( OS / VMM ) 可以通过位掩码配置不同 COS 的行为
  • 执行环境 ( OS / VMM ) 可以分配一个 COS 给软件线程
  • 指明和内存访问关联的 COS , 基于 COS 控制 cache 分配

需要说明的是 , capacity bitmask ( CBM ) 中的 1 必须连续 .

9.3. 代码和数据分级 ( CDP ) 技术

CDP 技术是 CAT 的扩展 , CDP 以软件可以配置的方式隔离和分级取回到 L2 和 L3 cache 的代码和数据 , 依赖硬件支持 , 根据工作负载的特点进行分级和调整 cache .
CDP 通过提供单独的基于 COS 的代码和数据掩码扩展了 CAT , L2 CDP 和 L3 CDP 的支持单独控制 .

处理器默认关闭 CDP , 开启时 , CAT 掩码 MSR 重映射到取回的代码或数据的 interleaved 掩码 MSR 对 ; CAT 的 COS 范围重新索引 , COS 范围的低一半对于 CDP 可用 .

和 CAT 类似 , CDP 可以由特权软件动态配置 .

9.4. 开启 CAT 的流程

开启处理器的 CAT , 需要执行以下流程 :

  1. 枚举和监测处理器对于 CAT 的支持 , 通过 CPUID .
  2. CAT : 枚举资源类型和功能 .
  3. CAT : cache 掩码配置 .
  4. cache 掩码关联的 COS .

9.5. CDP : 枚举和开启 L3 CDP 技术

支持 CDP 的处理器提供了一个新的 MSR IA32_L3_QOS_CFG , 设置 bit 0 开启 CDP , 映射 CAT 掩码 MSR 到数据掩码和代码掩码 MSR 对 ; 其余的 bit 保留 .

开启 CDP 时 , 重映射已有的 CAT 掩码 MSR 空间 , 给每个 COS 提供一个代码掩码和一个数据掩码 . 每个 COS 对应两个掩码 MSR , 一个掩码开启数据填充位置的控制 , 一个掩码开启代码位置的控制 .

掩码 MSR 的定义相同 , 必须由连续的 1 组成 , 长度不能超过掩码长度的最大值 .

9.6. CDP : 枚举和开启 L2 CDP 技术

L2 CDP 是 L2 CAT 的扩展 , 支持 L2 CDP 的处理器提供了新的 MSR IA32_l2_QOS_CFG , 设置 bit 0 开启 CDP , 映射 CAT 掩码 MSR 到数据掩码和代码掩码 MSR 对 ; 其余位保留 .

L2 CDP 掩码和 L2 CAT 掩码之间的映射关系和 L3 相同 , 掩码 MSR 的定义也相同 .

9.6.1. 节电特性下的 CAT 操作

CAT 无法用于保持 cache 一致性 , 一些高级电源管理的特性 , 比如 C-state 可能压缩或关闭某些 cache , 妨碍 CAT 线索 – 这种情况下 CAT 掩码会被忽略 , 其他的特性优先生效 .

9.6.2. 其他操作模式的 CAT 操作

SMI 传递过程中 IA32_PQR_ASSOC 和掩码寄存器的状态保持不变 , 因此 SMM 处理函数的执行可以和 CAT 资源交互 , 对 non-SMM 软件栈表现出某种程度上的非确定性 . SMM 处理函数还可以执行一些影响 CAT 操作的系统层或电源管理操作 .

9.6.3. 关联线程和 CAT / CDP COS

线程通过每个逻辑处理器的 IA32_PQR_ASSOC MSR 和 COS 关联 , 相同 COS 适用于 CAT 和 CDP , 开启 CDP 时改变 COS 和掩码 MSR 的映射 .

9.7. 内存带宽分配简介

内存带宽分配 ( MBA ) 特性间接大约控制每个核心的可用内存带宽 , 可以控制相对于其优先级多度使用内存带宽的应用 .

MBA 使用 RDT 特性集的限制 , 包括 COS , 即 MBA 的 COS 和 L3 CAT 的含义相同 , 共享一些基本框架比如关联到 COS 的 MSR 和 CPUID 枚举的元素 .

9.7.1. MBA 枚举

和其他 RDT 特性类似 , MBA 的支持和特性通过 CPUID 枚举 , 主要组件包括 :

  • 处理器支持的 COS 数
  • 支持的最大 MBA 延时值
  • 可以编程的延时值是否是线性的

9.7.2. MBA 配置

枚举过后 , MBA 的配置由两个步骤组成 :

  • 关联线程到 COS , 软件可能在上下文切换时更新 PQR MSR 的 COS 域 , 维护软件线程和硬件 COS 的关联 .
  • 配置 per-COS 延时值 , 通过 IA32_L2_QoS_Ext_BW_Thrtl_n MSR .

延时值可以是 0 到 MBA_MAX 之间的值 , throttling 值是约数 , 所有 COS 相加可能不到 100% .