性能事件管道循环
int evpipe_loop(evpipe_t *evp, int *sig, int strict) {
int cpu, err, ready;
//性能事件循环
for (;!(*sig);) {
//监测性能事件
ready = poll(evp->poll, evp->ncpus, -1);
if (ready <= 0) return ready ? : 0;
//如果准备好了, 我们直接读取对应的性能事件
for (cpu = 0; ready && (cpu < evp->ncpus); cpu++) {
if (!(evp->poll[cpu].revents & POLLIN))
continue;
//不断的提取
err = evqueue_drain(&evp->q[cpu], strict);
ready--;
}
}
return 0;
}
下面是核心的实现:
int evqueue_drain(struct evqueue *q, int strict) {
struct lost_event *lost;
uint64_t size, offs, head, tail;
uint8_t *base, *this, *next;
event_t *ev;
int err = 0;
size = q->mem->data_size;
offs = q->mem->data_offset;
base = (uint8_t *)q->mem + offs;
for (head = __get_head(q->mem); q->mem->data_tail != head;
__set_tail(q->mem, q->mem->data_tail + ev->hdr.size)) {
tail = q->mem->data_tail;
this = base + (tail % size);
ev = (void *)this;
next = base + ((tail + ev->hdr.size) % size);
if (next < this) {
size_t left = (base + size) - this;
q->buf = realloc(q->buf, ev->hdr.size);
memcpy(q->buf, this, left);
memcpy(q->buf + left, base, ev->hdr.size - left);
ev = q->buf;
}
switch (ev->hdr.type) {
//如果是普通的事件直接返回
case PERF_RECORD_SAMPLE:
err = event_handle(ev, ev->hdr.size);
break;
case PERF_RECORD_LOST:
lost = (void *)ev;
if (strict) {
_e("lost %"PRId64" events", lost->lost);
err = -EOVERFLOW;
} else {
_w("lost %"PRId64" events", lost->lost);
}
break;
default:
_e("unknown perf event %#"PRIx32, ev->hdr.type);
err = -EINVAL;
break;
}
if (err)
break;
}
return err;
}
在这个流程图中,我使用了Mermaid语法来描述evqueue_drain函数的主要步骤:
- 开始执行函数。
- 初始化所有必要的变量。
- 获取内存中事件队列的头部指针。
- 进入循环,条件是队列的尾部指针不等于头部指针。
- 获取内存中事件队列的尾部指针。
- 计算当前事件的内存地址。
- 计算下一个事件的内存地址。
- 检查当前事件是否跨越了环形缓冲区的边界。
- 如果事件跨越边界,则重新分配缓冲区并复制事件内容。
- 根据事件类型处理事件:
- 如果是采样事件(PERF_RECORD_SAMPLE),调用event_handle函数。
- 如果是丢失事件(PERF_RECORD_LOST),根据strict标志打印错误或警告。
- 对于未知事件类型,打印错误信息。
- 检查是否有错误发生。
- 如果没有错误,继续循环。
- 如果有错误,结束循环。
- 结束函数。