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



在环形缓冲区中,我们可以只检索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
}

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

调用链仅适用于PERF_RECORD_SAMPLE事件。

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

struct perf_record_switch {
struct perf_event_header header;
struct sample_id sample_id;
};

然后把整个事件投reinterpret_cast<struct perf_record_switch*>(header)

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

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

最新更新