Created
March 27, 2015 11:29
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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