static void process_smpl_buf(perf_event_desc_t *hw) { struct perf_event_header ehdr; int ret; for(;;) { ret = perf_read_buffer(hw, &ehdr, sizeof(ehdr)); if (ret) return; /* nothing to read */ if (options.opt_no_show) { perf_skip_buffer(hw, ehdr.size - sizeof(ehdr)); continue; } switch(ehdr.type) { case PERF_RECORD_SAMPLE: collected_samples++; ret = perf_display_sample(fds, num_fds, hw - fds, &ehdr, options.output_file); if (ret) errx(1, "cannot parse sample"); break; case PERF_RECORD_EXIT: display_exit(hw, options.output_file); break; case PERF_RECORD_LOST: lost_samples += display_lost(hw, fds, num_fds, options.output_file); break; case PERF_RECORD_THROTTLE: display_freq(1, hw, options.output_file); break; case PERF_RECORD_UNTHROTTLE: display_freq(0, hw, options.output_file); break; default: printf("unknown sample type %d\n", ehdr.type); perf_skip_buffer(hw, ehdr.size - sizeof(ehdr)); } } }
static void sigio_handler(int n, siginfo_t *info, void *uc) { struct perf_event_header ehdr; int ret, id; /* * positive si_code indicate kernel generated signal * which is normal for SIGIO */ if (info->si_code < 0) errx(1, "signal not generated by kernel"); /* * SIGPOLL = SIGIO * expect POLL_HUP instead of POLL_IN because we are * in one-shot mode (IOC_REFRESH) */ if (info->si_code != POLL_HUP) errx(1, "signal not generated by SIGIO"); id = perf_fd2event(fds, num_fds, info->si_fd); if (id == -1) errx(1, "no event associated with fd=%d", info->si_fd); ret = perf_read_buffer(fds+id, &ehdr, sizeof(ehdr)); if (ret) errx(1, "cannot read event header"); if (ehdr.type != PERF_RECORD_SAMPLE) { warnx("unexpected sample type=%d, skipping\n", ehdr.type); perf_skip_buffer(fds+id, ehdr.size); goto skip; } printf("Notification:%lu ", notification_received); ret = perf_display_sample(fds, num_fds, 0, &ehdr, stdout); /* * increment our notification counter */ notification_received++; skip: /* * rearm the counter for one more shot */ ret = ioctl(info->si_fd, PERF_EVENT_IOC_REFRESH, 1); if (ret == -1) err(1, "cannot refresh"); }
static void process_smpl_buf(perf_event_desc_t *hw) { struct perf_event_header ehdr; int ret; for(;;) { ret = perf_read_buffer(hw->buf, hw->pgmsk, &ehdr, sizeof(ehdr)); if (ret) return; /* nothing to read */ switch(ehdr.type) { case PERF_RECORD_SAMPLE: ret = perf_display_sample(fds, num_fds, hw - fds, &ehdr, stdout); if (ret) errx(1, "cannot parse sample"); collected_samples++; break; case PERF_RECORD_EXIT: display_exit(hw); break; case PERF_RECORD_LOST: display_lost(hw); break; case PERF_RECORD_THROTTLE: display_freq(1, hw); break; case PERF_RECORD_UNTHROTTLE: display_freq(0, hw); break; default: printf("unknown sample type %d\n", ehdr.type); perf_skip_buffer(hw->buf, ehdr.size - sizeof(ehdr)); } } }
int perf_display_sample(perf_event_desc_t *fds, int num_fds, int idx, struct perf_event_header *ehdr, FILE *fp) { perf_event_desc_t *hw; struct { uint32_t pid, tid; } pid; struct { uint64_t value, id; } grp; uint64_t time_enabled, time_running; size_t sz; uint64_t type, fmt; uint64_t val64; const char *str; int ret, e; if (!fds || !fp || !ehdr || num_fds < 0 || idx < 0 || idx >= num_fds) return -1; sz = ehdr->size - sizeof(*ehdr); hw = fds+idx; type = hw->hw.sample_type; fmt = hw->hw.read_format; /* * the sample_type information is laid down * based on the PERF_RECORD_SAMPLE format specified * in the perf_event.h header file. * That order is different from the enum perf_event_sample_format */ if (type & PERF_SAMPLE_IP) { const char *xtra = " "; ret = perf_read_buffer_64(hw, &val64); if (ret) { warnx("cannot read IP"); return -1; } /* * MISC_EXACT_IP indicates that kernel is returning * th IIP of an instruction which caused the event, i.e., * no skid */ if (hw->hw.precise_ip && (ehdr->misc & PERF_RECORD_MISC_EXACT_IP)) xtra = " (exact) "; fprintf(fp, "IIP:%#016"PRIx64"%s", val64, xtra); sz -= sizeof(val64); } if (type & PERF_SAMPLE_TID) { ret = perf_read_buffer(hw, &pid, sizeof(pid)); if (ret) { warnx( "cannot read PID"); return -1; } fprintf(fp, "PID:%d TID:%d ", pid.pid, pid.tid); sz -= sizeof(pid); } if (type & PERF_SAMPLE_TIME) { ret = perf_read_buffer_64(hw, &val64); if (ret) { warnx( "cannot read time"); return -1; } fprintf(fp, "TIME:%'"PRIu64" ", val64); sz -= sizeof(val64); } if (type & PERF_SAMPLE_ADDR) { ret = perf_read_buffer_64(hw, &val64); if (ret) { warnx( "cannot read addr"); return -1; } fprintf(fp, "ADDR:%#016"PRIx64" ", val64); sz -= sizeof(val64); } if (type & PERF_SAMPLE_ID) { ret = perf_read_buffer_64(hw, &val64); if (ret) { warnx( "cannot read id"); return -1; } fprintf(fp, "ID:%"PRIu64" ", val64); sz -= sizeof(val64); } if (type & PERF_SAMPLE_STREAM_ID) { ret = perf_read_buffer_64(hw, &val64); if (ret) { warnx( "cannot read stream_id"); return -1; } fprintf(fp, "STREAM_ID:%"PRIu64" ", val64); sz -= sizeof(val64); } if (type & PERF_SAMPLE_CPU) { struct { uint32_t cpu, reserved; } cpu; ret = perf_read_buffer(hw, &cpu, sizeof(cpu)); if (ret) { warnx( "cannot read cpu"); return -1; } fprintf(fp, "CPU:%u ", cpu.cpu); sz -= sizeof(cpu); } if (type & PERF_SAMPLE_PERIOD) { ret = perf_read_buffer_64(hw, &val64); if (ret) { warnx( "cannot read period"); return -1; } fprintf(fp, "PERIOD:%'"PRIu64" ", val64); sz -= sizeof(val64); } /* struct read_format { * { u64 value; * { u64 time_enabled; } && PERF_FORMAT_ENABLED * { u64 time_running; } && PERF_FORMAT_RUNNING * { u64 id; } && PERF_FORMAT_ID * } && !PERF_FORMAT_GROUP * * { u64 nr; * { u64 time_enabled; } && PERF_FORMAT_ENABLED * { u64 time_running; } && PERF_FORMAT_RUNNING * { u64 value; * { u64 id; } && PERF_FORMAT_ID * } cntr[nr]; * } && PERF_FORMAT_GROUP * }; */ if (type & PERF_SAMPLE_READ) { uint64_t values[3]; uint64_t nr; if (fmt & PERF_FORMAT_GROUP) { ret = perf_read_buffer_64(hw, &nr); if (ret) { warnx( "cannot read nr"); return -1; } sz -= sizeof(nr); time_enabled = time_running = 1; if (fmt & PERF_FORMAT_TOTAL_TIME_ENABLED) { ret = perf_read_buffer_64(hw, &time_enabled); if (ret) { warnx( "cannot read timing info"); return -1; } sz -= sizeof(time_enabled); } if (fmt & PERF_FORMAT_TOTAL_TIME_RUNNING) { ret = perf_read_buffer_64(hw, &time_running); if (ret) { warnx( "cannot read timing info"); return -1; } sz -= sizeof(time_running); } fprintf(fp, "ENA=%'"PRIu64" RUN=%'"PRIu64" NR=%"PRIu64"\n", time_enabled, time_running, nr); values[1] = time_enabled; values[2] = time_running; while(nr--) { grp.id = -1; ret = perf_read_buffer_64(hw, &grp.value); if (ret) { warnx( "cannot read group value"); return -1; } sz -= sizeof(grp.value); if (fmt & PERF_FORMAT_ID) { ret = perf_read_buffer_64(hw, &grp.id); if (ret) { warnx( "cannot read leader id"); return -1; } sz -= sizeof(grp.id); } e = perf_id2event(fds, num_fds, grp.id); if (e == -1) str = "unknown sample event"; else str = fds[e].name; values[0] = grp.value; grp.value = perf_scale(values); fprintf(fp, "\t%'"PRIu64" %s (%"PRIu64"%s)\n", grp.value, str, grp.id, time_running != time_enabled ? ", scaled":""); } } else { time_enabled = time_running = 0; /* * this program does not use FORMAT_GROUP when there is only one event */ ret = perf_read_buffer_64(hw, &val64); if (ret) { warnx( "cannot read value"); return -1; } sz -= sizeof(val64); if (fmt & PERF_FORMAT_TOTAL_TIME_ENABLED) { ret = perf_read_buffer_64(hw, &time_enabled); if (ret) { warnx( "cannot read timing info"); return -1; } sz -= sizeof(time_enabled); } if (fmt & PERF_FORMAT_TOTAL_TIME_RUNNING) { ret = perf_read_buffer_64(hw, &time_running); if (ret) { warnx( "cannot read timing info"); return -1; } sz -= sizeof(time_running); } if (fmt & PERF_FORMAT_ID) { ret = perf_read_buffer_64(hw, &val64); if (ret) { warnx( "cannot read leader id"); return -1; } sz -= sizeof(val64); } fprintf(fp, "ENA=%'"PRIu64" RUN=%'"PRIu64"\n", time_enabled, time_running); values[0] = val64; values[1] = time_enabled; values[2] = time_running; val64 = perf_scale(values); fprintf(fp, "\t%'"PRIu64" %s %s\n", val64, fds[0].name, time_running != time_enabled ? ", scaled":""); } } if (type & PERF_SAMPLE_CALLCHAIN) { uint64_t nr, ip; ret = perf_read_buffer_64(hw, &nr); if (ret) { warnx( "cannot read callchain nr"); return -1; } sz -= sizeof(nr); while(nr--) { ret = perf_read_buffer_64(hw, &ip); if (ret) { warnx( "cannot read ip"); return -1; } sz -= sizeof(ip); fprintf(fp, "\t0x%"PRIx64"\n", ip); } } if (type & PERF_SAMPLE_RAW) { ret = __perf_handle_raw(hw); if (ret == -1) return -1; sz -= ret; } if (type & PERF_SAMPLE_BRANCH_STACK) { ret = perf_display_branch_stack(hw, fp); sz -= ret; } if (type & PERF_SAMPLE_REGS_USER) { ret = perf_display_regs_user(hw, fp); sz -= ret; } if (type & PERF_SAMPLE_STACK_USER) { ret = perf_display_stack_user(hw, fp); sz -= ret; } if (type & PERF_SAMPLE_WEIGHT) { ret = perf_read_buffer_64(hw, &val64); if (ret) { warnx( "cannot read weight"); return -1; } fprintf(fp, "WEIGHT:%'"PRIu64" ", val64); sz -= sizeof(val64); } if (type & PERF_SAMPLE_DATA_SRC) { ret = perf_read_buffer_64(hw, &val64); if (ret) { warnx( "cannot read data src"); return -1; } fprintf(fp, "DATA_SRC:%'"PRIu64" ", val64); sz -= sizeof(val64); } /* * if we have some data left, it is because there is more * than what we know about. In fact, it is more complicated * because we may have the right size but wrong layout. But * that's the best we can do. */ if (sz) { warnx("did not correctly parse sample leftover=%zu", sz); perf_skip_buffer(hw, sz); } fputc('\n',fp); return 0; }