编程接口指南

CPU 性能计数器

本节介绍了使用 CPU 性能计数器 (CPU Performance counters, CPC) 时用到的开发者接口。Solaris 应用程序可以使用与底层计数器体系结构无关的 CPC。

libcpc 的 API 附加功能

本节介绍 libcpc(3LIB) 库的最新附加功能。有关旧接口的信息,请参见 libcpc 手册页。

初始化接口

准备使用 CPC 工具的应用程序会通过调用 cpc_open() 函数来初始化该库。此函数会返回一个供其他接口使用的 cpc_t * 参数。cpc_open() 函数的语法如下:

cpc_t*cpc_open(intver);

ver 参数的值可标识应用程序所使用的接口的版本。如果底层计数器无法访问或不可用,则 cpc_open() 函数将失败。

硬件查询接口

uint_t cpc_npic(cpc_t *cpc);

uint_t cpc_caps(cpc_t *cpc);

void cpc_walk_events_all(cpc_t *cpc, void *arg,

          void (*action)(void *arg, const char *event));

void cpc_walk_events_pic(cpc_t *cpc, uint_t picno, void *arg, 

          void(*action)(void *arg, uint_t picno, const char *event));

void cpc_walk-attrs(cpc_t *cpc, void *arg,

          void (*action)(void *arg, const char *attr));

cpc_npic() 函数可返回底层处理器上的物理计数器的数量。

cpc_caps() 函数可返回 uint_t 参数,此参数的值是基于底层处理器支持的功能执行按位或运算的结果。共有两项功能。CPC_CAP_OVERFLOW_INTERRUPT 功能,允许处理器在计数器溢出时产生中断;CPC_CAP_OVERFLOW_PRECISE 功能,允许处理器确定哪一个计数器产生溢出中断。

内核可维护底层处理器支持的事件的列表。单个芯片上的不同物理计数器不必使用相同的事件列表。cpc_walk_events_all() 函数可针对每个处理器支持的事件调用 action() 例程,而不用考虑物理计数器。cpc_walk_events_pic() 函数可针对特定物理计数器上每个处理器支持的事件调用 action() 例程。这两个函数都会将未解释的 arg 参数从调用方传递到每个 action() 函数调用。

平台可维护底层处理器支持的属性的列表。利用这些属性,可以访问特定于处理器的高级性能计数器功能。cpc_walk_attrs() 函数可针对每个属性名称调用操作例程。

配置接口

cpc_set_t *cpc_set_create(cpc_t *cpc);

int cpc_set_destroy(cpc_t *cpc, cpc_set_t *set);

int cpc_set_add_request(cpc_t *cpc, cpc_set_t *set, const char *event,

          uint64_t preset, uint_t flags, uint_t nattrs,

          const cpc_attr_t *attrs);

int cpc_set_request_preset(cpc_t *cpc, cpc_set_t *set, int index,

          uint64_t preset);

不透明数据类型 cpc_set_t 表示请求的集合。这些集合称为集。cpc_set_create() 函数可用于创建空集。cpc_set_destroy() 函数可销毁一个集,并释放该集使用的所有内存。销毁集将释放该集使用的硬件资源。

cpc_set_add_request() 函数可用于向集中添加请求。以下列出了请求的参数。

event

用于指定要进行计数的事件的名称的字符串。

preset

用作计数器初始值的 64 位无符号整数。

flags

应用于一组请求标志的逻辑或运算的结果。

nattrs

attrs 指向的数组中的属性个数。

attrs

指向 cpc_attr_t 结构数组的指针。

以下列出了有效的请求标志。

CPC_COUNT_USER

利用此标志,当 CPU 在用户模式下执行时可对出现的事件进行计数。

CPC_COUNT_SYSTEM

利用此标志,当 CPU 在特权模式下执行时可对出现的事件进行计数。

CPC_OVF_NOTIFY_EMT

此标志可请求在硬件计数器溢出时发出通知。

CPC 接口可将属性作为 cpc_attr_t 结构数组来进行传递。

cpc_set_add_request() 函数成功返回时,它将返回一个索引。此索引会引用通过调用 cpc_set_add_request() 函数时添加的请求所生成的数据。

cpc_set_request_preset() 函数可更改请求的预设值。通过更改可将新的预设值与溢出集重新绑定在一起。

cpc_walk_requests() 函数可针对 cpc_set_t 中的每个请求调用用户提供的 action() 例程。arg 参数的值会在未进行解释的情况下传递给用户例程。应用程序使用 cpc_walk_requests() 函数可列显集中每个请求的配置。cpc_walk_requests() 函数的语法如下:

void cpc_walk_requests(cpc_t *cpc, cpc_set_t *set, void *arg,

void (*action)(void *arg, int index, const char *event,

uint64_t preset, uint_t flags, int nattrs,

            const cpc_attr_t *attrs));

绑定

本节中的接口可将集中的请求绑定到物理硬件,并将计数器设置到起始位置。

int cpc_bind_curlwp(cpc_t *cpc, cpc_set_t *set, uint_t flags);

int cpc_bind_pctx(cpc_t *cpc, pctx_t *pctx, id_t id, cpc_set_t *set,

          uint_t flags);

int cpc_bind_cpu(cpc_t *cpc, processorid_t id, cpc_set_t *set, 

          uint_t flags);

int cpc_unbind(cpc_t *cpc, cpc_set_t *set);

cpc_bind_curlwp() 函数可将集绑定到调用者 LWP。该集的计数器将虚拟化为到此 LWP 上,并计算调用者 LWP 运行时 CPU 上发生的事件的数量。cpc_bind_curlwp() 例程唯一的有效标志为 CPC_BIND_LWP_INHERIT

cpc_bind_pctx() 函数可将集绑定到使用 libpctx(3LIB) 捕获的进程中的 LWP。此函数没有任何有效标志。

cpc_bind_cpu() 函数可将集绑定到在 id 参数中指定的处理器。将集绑定到 CPU 会使系统中的现有性能计数器上下文无效。此函数没有任何有效标志。

cpc_unbind() 函数可停止性能计数器并释放与绑定集关联的硬件。如果将集绑定到 CPU,则 cpc_unbind() 函数将从该 CPU 解除绑定 LWP 并释放 CPC 伪设备。

抽样

利用本节中介绍的接口,可以将数据从计数器返回到应用程序。计数器数据驻留在名为 cpc_buf_t 的不透明数据结构中。此数据结构可用于捕获绑定集正在使用的计数器的状态快照,并且包括以下信息:

cpc_buf_t *cpc_buf_create(cpc_t *cpc, cpc_set_t *set);

int cpc_buf_destroy(cpc_t *cpc, cpc_buf_t *buf);

int cpc_set_sample(cpc_t *cpc, cpc_set_t *set, cpc_buf_t *buf);

cpc_buf_create() 函数可创建一个缓冲区,用于存储在 cpc_set_t 中指定的集中的数据。cpc_buf_destroy() 函数可释放与给定的 cpc_buf_t 关联的内存。cpc_buf_sample() 函数可捕获正在代表指定集进行计数的计数器的快照。指定集必须已绑定并且创建了缓冲区,然后才能调用 cpc_buf_sample() 函数。

在缓冲区中抽样不会更新与该集关联的请求的预设值。使用 cpc_buf_sample() 函数对某个缓冲区进行抽样,然后解除绑定并再次绑定时,计数将从请求的预设值开始,该预设值与初始 cpc_set_add_request() 函数调用中的值相同。

缓冲区操作

利用以下例程,可以访问 cpc_buf_t 结构中的数据。

int cpc_buf_get(cpc_t *cpc, cpc_buf_t *buf, int index, uint64_t *val);

int cpc_buf_set(cpc_t *cpc, cpc_buf_t *buf, int index, uint64_t *val);

hrtime_t cpc_buf_hrtime(cpc_t *cpc, cpc_buf_t *buf);

uint64_t cpc_buf_tick(cpc_t *cpc, cpc_buf_t *buf);

int cpc_buf_sub(cpc_t *cpc, cpc_buf_t *result, cpc_buf_t *left

      cpc_buf_t *right);

int cpc_buf_add(cpc_t *cpc, cpc_buf_t *result, cpc_buf_t *left,

      cpc_buf_t *right);

int cpc_buf_copy(cpc_t *cpc, cpc_buf_t *dest, cpc_buf_t *src);

void cpc_buf_zero(cpc_t *cpc, cpc_buf_t *buf);

cpc_buf_get() 函数可检索 index 参数所标识的计数器的值。index 参数是指在绑定集之前由 cpc_set_add_request() 函数返回的值。cpc_buf_get() 函数可将计数器的值存储在 val 参数所指示的位置。

cpc_buf_set() 函数可设置 index 参数所标识的计数器的值。index 参数是指在绑定集之前由 cpc_set_add_request() 函数返回的值。cpc_buf_set() 函数可将计数器的值设置为位于 val 参数所指示的位置的值。cpc_buf_get() 函数和 cpc_buf_set() 函数均不会更改对应的 CPC 请求的预设值。

cpc_buf_hrtime() 函数可返回高精度时间标记,用于指示对硬件进行抽样的时间。cpc_buf_tick() 函数可返回 LWP 运行时已经过的 CPU 时钟周期的数量。

cpc_buf_sub() 函数可计算在 leftright 参数中指定的计数器与周期值之间的差值。cpc_buf_sub() 函数可将结果存储在 result 中。cpc_buf_sub() 函数的给定调用必须确保所有 cpc_buf_t 值都源自同一 cpc_set_t 结构。result 索引包含缓冲区中每个请求索引的 left - right 计算的结果。结果索引还包含 tick 的差。cpc_buf_sub() 函数可用于将目标缓冲区的高精度时间标记设置为 leftright 缓冲区的最新时间。

cpc_buf_add() 函数可计算在 leftright 参数中指定的计数器和周期值的总和。cpc_buf_add() 函数可将结果存储在 result 中。cpc_buf_add() 函数的给定调用必须确保所有 cpc_buf_t 值都源自同一 cpc_set_t 结构。result 索引包含缓冲区中每个请求索引的 left + right 计算的结果。结果索引还包含 tick 的总和。cpc_buf_add() 函数可用于将目标缓冲区的高精度时间标记设置为 leftright 缓冲区的最新时间。

使用 cpc_buf_copy() 函数时 destsrc 相同。

cpc_buf_zero() 函数可将 buf 中的所有内容都设置为零。

激活接口

本节介绍 CPC 的激活接口。

int cpc_enable(cpc_t *cpc);

int cpc_disable(cpc_t *cpc);

这两个接口可分别启用和禁用绑定到正在执行的 LWP 的任意集的计数器。应用程序使用这些接口可以指定所需的代码,同时使用 libpctx 将计数器配置延迟到某个控制进程。

错误处理接口

本节介绍 CPC 的错误处理接口。

typedef void (cpc_errhndlr_t)(const char *fn, int subcode, const char *fmt,

          va_list ap);

void cpc_seterrhndlr(cpc_t *cpc, cpc_errhndlr_t *errhndlr);

这两个接口允许传递 cpc_t 句柄。除了一个字符串之外,cpc_errhndlr_t 句柄还采用一个整型子代码。整型 subcode 用于描述 fn 参数所引用的函数所遇到的特定错误。通过整型 subcode,应用程序可轻易识别各种错误情况。fmt 参数的字符串值包含对错误子代码的国际化说明,并且适用于列显。