如何从 perf_event_open 中的环形缓冲区中检索调用链以获取 PERF_RECORD_SWITCH?

Posted

技术标签:

【中文标题】如何从 perf_event_open 中的环形缓冲区中检索调用链以获取 PERF_RECORD_SWITCH?【英文标题】:How to retrieve callchain from ring buffer in perf_event_open for PERF_RECORD_SWITCH? 【发布时间】:2019-09-05 06:33:36 【问题描述】:

在环形缓冲区中,我们可以只检索PERF_RECORD_SAMPLE 的调用链,还是也可以检索其他记录类型?

perf_event_open 的手册页仅明确说明调用链可用于PERF_RECORD_SAMPLE。我对获取PERF_RECORD_SWITCH 的调用链特别感兴趣,以获取我的程序在上下文切换进出时的堆栈跟踪。我尝试了一种从缓冲区读取调用链的方法,但是看到返回的地址,它看起来不正确。

size_t index = mapping->data_tail; //mapping is the pointer to the ring buffer

uintptr_t base = reinterpret_cast<uintptr_t>(mapping) + PageSize;
size_t start_index = index % DataSize;
size_t end_index = start_index + sizeof(struct perf_event_header);

memcpy(buf, reinterpret_cast<void*>(base + start_index), sizeof(struct perf_event_header));

struct perf_event_header* header = reinterpret_cast<struct perf_event_header*>(buf);

uintptr_t p = reinterpret_cast<uintptr_t>(header) + sizeof(struct perf_event_header)

// Only sampling PERF_SAMPLE_CALLCHAIN
uint64_t* base = reinterpret_cast<uint64_t*>(p);
uint64_t size = *base; // Should be callchain size
base++;

for (int i = 0; i < size; i++) 
    cout << *base << endl; // prints the addresses in the callchain stack


我使用这个 sn-p 得到的输出的两个主要问题是: 1.所有PERF_RECORD_SWITCH都有相同的调用链。这应该是极不可能的。 2. 多次运行的输出不一致。调用链大小从 0(大部分)到 4,6、16 不等,有时甚至是一个非常大(未定义)的数字。

【问题讨论】:

【参考方案1】:

调用链仅适用于PERF_RECORD_SAMPLE 事件。

在读取不同的记录类型时,您应该遵循 perf_event_open 中的 struct 定义,而不是仅仅尝试通过指针访问单个字段,即,

struct perf_record_switch 
    struct perf_event_header header;
    struct sample_id sample_id;
;

然后投射整个事件reinterpret_cast&lt;struct perf_record_switch*&gt;(header)

具体来说,样本类型的布局高度依赖于配置,可能包含多个动态大小的数组,这会阻止使用静态结构。

从技术上讲,您可以使用sched:sched_switch 跟踪点采样事件从切换事件中收集调用链。这会导致PERF_RECORD_SAMPLE 事件。但是,您可能并不总是看到有用的信息,而主要是内核中的调度详细信息。

【讨论】:

性能记录如何为上下文切换创建调用图? perf record -g -e cs ./a.out 然后 perf report 给出一个调用图百分比明智。或者是别的什么? cs 事件使用type=PERF_TYPE_SOFTWARE, config=PERF_COUNT_SW_CONTEXT_SWITCHES。这也适用于PERF_RECORD_SAMPLE 事件。 sched:sched_switch 跟踪点的优势在于包括切换到和切换到的 pid,PERF_RECORD_SWITCH_CPU_WIDE 事件也是如此。 好的。但是我如何模拟使用perf_event_open 获取调用图以获取sched:sched_switchcs,如perf record -g?因为PERF_RECORD_SWITCH 样本是在切换事件期间创建的。 PERF_RECORD_SAMPLE 和上下文切换有关系吗? 调用perf_event_open时,您使用struct perf_event_attr,其中包括type=PERF_TYPE_SOFTWARE, config=PERF_COUNT_SW_CONTEXT_SWITCHEStype=PERF_TYPE_TRACEPOINT, config=(/sys/kernel/debug/tracing/events/sched/sched_switch/id)

以上是关于如何从 perf_event_open 中的环形缓冲区中检索调用链以获取 PERF_RECORD_SWITCH?的主要内容,如果未能解决你的问题,请参考以下文章

perf_event_open:包括在采样时执行子进程

使用无锁队列(环形缓冲区)注意事项

破坏者模式 - 主节点和从节点如何保持同步?

perf_event_open 系统调用

Linux内核 eBPF基础:perf基础perf_event_open系统调用内核源码分析

一致性哈希算法