Skip to content

Instantly share code, notes, and snippets.

@elazarl
Created March 27, 2015 11:29
Show Gist options
  • Save elazarl/c8404686e71ef0b36cc7 to your computer and use it in GitHub Desktop.
Save elazarl/c8404686e71ef0b36cc7 to your computer and use it in GitHub Desktop.
A small C code sample, demonstrating the complexity of parsing the perf_event_open(2) buffer format
int print_records_read(void *_this, void *buf, int size) {
int nread = 0;
struct perf_record_mmap *mmap_r;
struct perf_record_lost *lost_r;
struct perf_record_comm *comm_r;
struct perf_record_exit *exit_r;
struct perf_record_throttle *throttle_r;
struct perf_record_fork *fork_r;
struct perf_record_read *read_r;
struct print_records *pr = _this;
struct json_printer *p = &pr->printer;
int sample_max_size = 10 * 1024 + pr->attr.sample_stack_user * 4;
char *sample_buf = alloca(sample_max_size);
while (nread < size) {
/* representing X bytes as JSON array takes at most 4 bytes per byte (FF is
* 256,) */
int sample_max_size = 10 * 1024 + pr->attr.sample_stack_user * 4;
FILE *sample_fp;
struct perf_event_header *header = buf;
void *sample = (void *)(header + 1);
if (nread + header->size > size) {
break;
}
sample_fp = fmemopen(sample_buf, sample_max_size, "w");
pr->counters.events++;
json_printer_start_object(p, sample_fp);
json_print_event_header_inner(p, sample_fp, header);
json_printer_string(p, sample_fp, "sample");
json_printer_start_object(p, sample_fp);
switch (header->type) {
case PERF_RECORD_MMAP:
mmap_r = sample;
json_printer_string(p, sample_fp, "pid");
json_printer_unsigned(p, sample_fp, mmap_r->pid);
json_printer_string(p, sample_fp, "tid");
json_printer_unsigned(p, sample_fp, mmap_r->tid);
json_printer_string(p, sample_fp, "addr");
json_printer_unsigned(p, sample_fp, mmap_r->addr);
json_printer_string(p, sample_fp, "len");
json_printer_unsigned(p, sample_fp, mmap_r->len);
json_printer_string(p, sample_fp, "pgoff");
json_printer_unsigned(p, sample_fp, mmap_r->pgoff);
json_printer_string(p, sample_fp, "filename");
json_printer_string(p, sample_fp, mmap_r->filename);
break;
case PERF_RECORD_LOST:
lost_r = sample;
json_printer_string(p, sample_fp, "id");
json_printer_unsigned(p, sample_fp, lost_r->id);
json_printer_string(p, sample_fp, "lost");
json_printer_unsigned(p, sample_fp, lost_r->lost);
break;
case PERF_RECORD_COMM:
comm_r = sample;
json_printer_string(p, sample_fp, "pid");
json_printer_unsigned(p, sample_fp, comm_r->pid);
json_printer_string(p, sample_fp, "tid");
json_printer_unsigned(p, sample_fp, comm_r->tid);
json_printer_string(p, sample_fp, "comm");
json_printer_string(p, sample_fp, comm_r->comm);
break;
case PERF_RECORD_EXIT:
exit_r = sample;
json_printer_string(p, sample_fp, "pid");
json_printer_unsigned(p, sample_fp, exit_r->pid);
json_printer_string(p, sample_fp, "ppid");
json_printer_unsigned(p, sample_fp, exit_r->ppid);
json_printer_string(p, sample_fp, "tid");
json_printer_unsigned(p, sample_fp, exit_r->tid);
json_printer_string(p, sample_fp, "ptid");
json_printer_unsigned(p, sample_fp, exit_r->ptid);
json_printer_string(p, sample_fp, "time");
json_printer_unsigned(p, sample_fp, exit_r->time);
break;
case PERF_RECORD_THROTTLE:
case PERF_RECORD_UNTHROTTLE:
throttle_r = sample;
json_printer_string(p, sample_fp, "time");
json_printer_unsigned(p, sample_fp, throttle_r->time);
json_printer_string(p, sample_fp, "id");
json_printer_unsigned(p, sample_fp, throttle_r->id);
json_printer_string(p, sample_fp, "stream_id");
json_printer_unsigned(p, sample_fp, throttle_r->stream_id);
break;
case PERF_RECORD_FORK:
fork_r = sample;
json_printer_string(p, sample_fp, "pid");
json_printer_unsigned(p, sample_fp, fork_r->pid);
json_printer_string(p, sample_fp, "ppid");
json_printer_unsigned(p, sample_fp, fork_r->ppid);
json_printer_string(p, sample_fp, "tid");
json_printer_unsigned(p, sample_fp, fork_r->tid);
json_printer_string(p, sample_fp, "ptid");
json_printer_unsigned(p, sample_fp, fork_r->ptid);
json_printer_string(p, sample_fp, "time");
json_printer_unsigned(p, sample_fp, fork_r->time);
break;
case PERF_RECORD_READ:
read_r = sample;
puts("got PERF_RECORD_READ");
break;
case PERF_RECORD_SAMPLE:
sample = (void *)(header + 1);
if (pr->attr.sample_type & PERF_SAMPLE_IP) {
struct perf_record_sample_ip *ip = sample;
json_printer_string(p, sample_fp, "ip");
json_printer_printf(p, sample_fp, "%llx", ip->ip);
sample += sizeof(*ip);
}
if (pr->attr.sample_type & PERF_SAMPLE_TID) {
struct perf_record_sample_tid *tid = sample;
json_printer_string(p, sample_fp, "pid");
json_printer_decimal(p, sample_fp, tid->pid);
json_printer_string(p, sample_fp, "tid");
json_printer_decimal(p, sample_fp, tid->tid);
sample += sizeof(*tid);
}
if (pr->attr.sample_type & PERF_SAMPLE_TIME) {
struct perf_record_sample_time *time = sample;
json_printer_string(p, sample_fp, "time");
json_printer_unsigned(p, sample_fp, time->time);
sample += sizeof(*time);
}
if (pr->attr.sample_type & PERF_SAMPLE_ADDR) {
struct perf_record_sample_addr *addr = sample;
json_printer_string(p, sample_fp, "time");
json_printer_printf(p, sample_fp, "%llx", addr->addr);
sample += sizeof(*addr);
}
if (pr->attr.sample_type & PERF_SAMPLE_ID) {
struct perf_record_sample_id *id = sample;
json_printer_string(p, sample_fp, "id");
json_printer_unsigned(p, sample_fp, id->id);
sample += sizeof(*id);
}
if (pr->attr.sample_type & PERF_SAMPLE_STREAM_ID) {
struct perf_record_sample_stream_id *id = sample;
json_printer_string(p, sample_fp, "stream_id");
json_printer_unsigned(p, sample_fp, id->stream_id);
sample += sizeof(*id);
}
if (pr->attr.sample_type & PERF_SAMPLE_CPU) {
struct perf_record_sample_cpu *cpu = sample;
json_printer_string(p, sample_fp, "cpu");
json_printer_unsigned(p, sample_fp, cpu->cpu);
if (cpu->res != 0) {
json_printer_string(p, sample_fp, "cpu_res");
json_printer_unsigned(p, sample_fp, cpu->res);
}
sample += sizeof(*cpu);
}
if (pr->attr.sample_type & PERF_SAMPLE_PERIOD) {
struct perf_record_sample_period *period = sample;
json_printer_string(p, sample_fp, "period");
json_printer_unsigned(p, sample_fp, period->period);
sample += sizeof(*period);
}
if (pr->attr.sample_type & PERF_SAMPLE_READ) {
struct perf_record_sample_read *read = read;
fprintf(stderr, "we do not support PERF_SAMPLE_READ yet\n");
exit(1);
sample += sizeof(*read);
}
if (pr->attr.sample_type & PERF_SAMPLE_CALLCHAIN) {
__u64 i = 0;
struct perf_record_sample_callchain *chain = sample;
json_printer_string(p, sample_fp, "ips");
json_printer_start_array(p, sample_fp);
for (; i < chain->nr; i++) {
json_printer_printf(p, sample_fp, "%llx", chain->ips[i]);
}
json_printer_end_array(p, sample_fp);
sample += sizeof(chain->nr) + sizeof(chain->ips[0]) * chain->nr;
}
if (pr->attr.sample_type & PERF_SAMPLE_RAW) {
__u32 i = 0;
struct perf_record_sample_raw *raw = sample;
json_printer_string(p, sample_fp, "data");
json_printer_start_array(p, sample_fp);
for (; i < raw->size; i++) {
json_printer_decimal(p, sample_fp, raw->data[i]);
}
json_printer_end_array(p, sample_fp);
sample += sizeof(raw->size) + sizeof(raw->data[0]) * raw->size;
}
if (pr->attr.sample_type & PERF_SAMPLE_BRANCH_STACK) {
__u64 i = 0;
struct perf_record_sample_branch_stack *b = sample;
json_printer_string(p, sample_fp, "data");
json_printer_start_array(p, sample_fp);
for (; i < b->bnr; i++) {
#define JSON_PRINTER_FIELD_HEX(obj, field) \
json_printer_string(p, sample_fp, #field); \
json_printer_printf(p, sample_fp, "%llx", obj.field)
json_printer_start_object(p, sample_fp);
JSON_PRINTER_FIELD_HEX(b->lbr[i], from);
JSON_PRINTER_FIELD_HEX(b->lbr[i], to);
JSON_PRINTER_FIELD_HEX(b->lbr[i], mispred);
JSON_PRINTER_FIELD_HEX(b->lbr[i], in_tx);
JSON_PRINTER_FIELD_HEX(b->lbr[i], abort);
json_printer_end_object(p, sample_fp);
}
json_printer_end_array(p, sample_fp);
sample += sizeof(b->bnr) + sizeof(b->lbr[0]) * b->bnr;
}
if (pr->attr.sample_type & PERF_SAMPLE_REGS_USER) {
int i = 0;
int nregs = __builtin_popcount(pr->attr.sample_regs_user);
struct perf_record_sample_regs_user *regs = sample;
JSON_PRINTER_FIELD_ENUM(p, sample_fp, regs, abi,
JSMN_SYM(PERF_SAMPLE_REGS_ABI_NONE),
JSMN_SYM(PERF_SAMPLE_REGS_ABI_32),
JSMN_SYM(PERF_SAMPLE_REGS_ABI_64));
json_printer_string(p, sample_fp, "regs");
json_printer_start_array(p, sample_fp);
for (; i < nregs; i++) {
json_printer_printf(p, sample_fp, "%llx", regs->regs[i]);
}
json_printer_end_array(p, sample_fp);
sample += sizeof(regs->abi) + sizeof(regs->regs[0]) * nregs;
}
if (pr->attr.sample_type & PERF_SAMPLE_STACK_USER) {
struct perf_record_sample_stack_user *stack = sample;
if (stack->size > 0) {
__u64 i = 0;
__u32 dyn_size = *((__u64 *)(sample + sizeof(*stack) + stack->size));
json_printer_string(p, sample_fp, "dyn_size");
json_printer_unsigned(p, sample_fp, dyn_size);
json_printer_string(p, sample_fp, "stack_user");
json_printer_start_array(p, sample_fp);
for (; i < dyn_size; i++) {
json_printer_unsigned(p, sample_fp,
stack->data_and_u64_dyn_size[i]);
}
json_printer_end_array(p, sample_fp);
sample += sizeof(stack->size) + stack->size;
sample += sizeof(__u64);
} else {
/* See
* http://lxr.missinglinkelectronics.com/linux+v3.10/kernel/events/core.c#L3991
* in kernel stack, kernel output a single zero, and that's it.
*/
sample += sizeof(stack->size);
}
}
break;
default:
printf("unrecognized sample {\"type\":\"%x\", \"size\":%d, "
"\"misc\": \"%x\"\n",
header->misc, header->size, header->type);
return -1;
}
json_printer_end_object(p, sample_fp);
json_printer_end_object(p, sample_fp); /* perf_event_header */
buf += header->size;
nread += header->size;
fputc('\n', sample_fp);
fclose(sample_fp);
fputs(sample_buf, pr->out);
}
return nread;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment