return -1;
}
-static void perf_session__id_header_size(struct perf_session *session)
-{
- struct perf_sample *data;
- u64 sample_type = session->sample_type;
- u16 size = 0;
-
- if (!session->sample_id_all)
- goto out;
-
- if (sample_type & PERF_SAMPLE_TID)
- size += sizeof(data->tid) * 2;
-
- if (sample_type & PERF_SAMPLE_TIME)
- size += sizeof(data->time);
-
- if (sample_type & PERF_SAMPLE_ID)
- size += sizeof(data->id);
-
- if (sample_type & PERF_SAMPLE_STREAM_ID)
- size += sizeof(data->stream_id);
-
- if (sample_type & PERF_SAMPLE_CPU)
- size += sizeof(data->cpu) * 2;
-out:
- session->id_hdr_size = size;
-}
-
void perf_session__update_sample_type(struct perf_session *self)
{
self->sample_type = perf_evlist__sample_type(self->evlist);
self->sample_size = __perf_evsel__sample_size(self->sample_type);
self->sample_id_all = perf_evlist__sample_id_all(self->evlist);
- perf_session__id_header_size(self);
+ self->id_hdr_size = perf_evlist__id_hdr_size(self->evlist);
}
int perf_session__create_kernel_maps(struct perf_session *self)
goto out;
memcpy(self->filename, filename, len);
- self->threads = RB_ROOT;
- INIT_LIST_HEAD(&self->dead_threads);
- self->last_match = NULL;
/*
* On 64bit we can mmap the data file in one go. No need for tiny mmap
* slices. On 32bit we use 32MB.
return NULL;
}
-static void perf_session__delete_dead_threads(struct perf_session *self)
+static void machine__delete_dead_threads(struct machine *machine)
{
struct thread *n, *t;
- list_for_each_entry_safe(t, n, &self->dead_threads, node) {
+ list_for_each_entry_safe(t, n, &machine->dead_threads, node) {
list_del(&t->node);
thread__delete(t);
}
}
-static void perf_session__delete_threads(struct perf_session *self)
+static void perf_session__delete_dead_threads(struct perf_session *session)
+{
+ machine__delete_dead_threads(&session->host_machine);
+}
+
+static void machine__delete_threads(struct machine *self)
{
struct rb_node *nd = rb_first(&self->threads);
}
}
+static void perf_session__delete_threads(struct perf_session *session)
+{
+ machine__delete_threads(&session->host_machine);
+}
+
void perf_session__delete(struct perf_session *self)
{
perf_session__destroy_kernel_maps(self);
free(self);
}
-void perf_session__remove_thread(struct perf_session *self, struct thread *th)
+void machine__remove_thread(struct machine *self, struct thread *th)
{
self->last_match = NULL;
rb_erase(&th->rb_node, &self->threads);
return 0;
}
-int perf_session__resolve_callchain(struct perf_session *self,
+int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel *evsel,
struct thread *thread,
struct ip_callchain *chain,
struct symbol **parent)
unsigned int i;
int err;
- callchain_cursor_reset(&self->callchain_cursor);
+ callchain_cursor_reset(&evsel->hists.callchain_cursor);
for (i = 0; i < chain->nr; i++) {
u64 ip;
break;
}
- err = callchain_cursor_append(&self->callchain_cursor,
+ err = callchain_cursor_append(&evsel->hists.callchain_cursor,
ip, al.map, al.sym);
if (err)
return err;
return 0;
}
-static int process_event_synth_stub(union perf_event *event __used,
+static int process_event_synth_stub(struct perf_event_ops *ops __used,
+ union perf_event *event __used,
struct perf_session *session __used)
{
dump_printf(": unhandled!\n");
return 0;
}
-static int process_event_sample_stub(union perf_event *event __used,
+static int process_event_synth_tracing_data_stub(union perf_event *event __used,
+ struct perf_session *session __used)
+{
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
+static int process_event_synth_attr_stub(union perf_event *event __used,
+ struct perf_evlist **pevlist __used)
+{
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
+static int process_event_sample_stub(struct perf_event_ops *ops __used,
+ union perf_event *event __used,
struct perf_sample *sample __used,
struct perf_evsel *evsel __used,
struct perf_session *session __used)
return 0;
}
-static int process_event_stub(union perf_event *event __used,
+static int process_event_stub(struct perf_event_ops *ops __used,
+ union perf_event *event __used,
struct perf_sample *sample __used,
struct perf_session *session __used)
{
return 0;
}
-static int process_finished_round_stub(union perf_event *event __used,
- struct perf_session *session __used,
- struct perf_event_ops *ops __used)
+static int process_finished_round_stub(struct perf_event_ops *ops __used,
+ union perf_event *event __used,
+ struct perf_session *session __used)
{
dump_printf(": unhandled!\n");
return 0;
}
-static int process_finished_round(union perf_event *event,
- struct perf_session *session,
- struct perf_event_ops *ops);
+static int process_finished_round(struct perf_event_ops *ops,
+ union perf_event *event,
+ struct perf_session *session);
static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
{
if (handler->unthrottle == NULL)
handler->unthrottle = process_event_stub;
if (handler->attr == NULL)
- handler->attr = process_event_synth_stub;
+ handler->attr = process_event_synth_attr_stub;
if (handler->event_type == NULL)
handler->event_type = process_event_synth_stub;
if (handler->tracing_data == NULL)
- handler->tracing_data = process_event_synth_stub;
+ handler->tracing_data = process_event_synth_tracing_data_stub;
if (handler->build_id == NULL)
handler->build_id = process_event_synth_stub;
if (handler->finished_round == NULL) {
struct perf_sample sample;
u64 limit = os->next_flush;
u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL;
+ unsigned idx = 0, progress_next = os->nr_samples / 16;
int ret;
if (!ops->ordered_samples || !limit)
os->last_flush = iter->timestamp;
list_del(&iter->list);
list_add(&iter->list, &os->sample_cache);
+ if (++idx >= progress_next) {
+ progress_next += os->nr_samples / 16;
+ ui_progress__update(idx, os->nr_samples,
+ "Processing time ordered events...");
+ }
}
if (list_empty(head)) {
os->last_sample =
list_entry(head->prev, struct sample_queue, list);
}
+
+ os->nr_samples = 0;
}
/*
* Flush every events below timestamp 7
* etc...
*/
-static int process_finished_round(union perf_event *event __used,
- struct perf_session *session,
- struct perf_event_ops *ops)
+static int process_finished_round(struct perf_event_ops *ops,
+ union perf_event *event __used,
+ struct perf_session *session)
{
flush_sample_queue(session, ops);
session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
u64 timestamp = new->timestamp;
struct list_head *p;
+ ++os->nr_samples;
os->last_sample = new;
if (!sample) {
dump_event(session, event, file_offset, sample);
+ evsel = perf_evlist__id2evsel(session->evlist, sample->id);
+ if (evsel != NULL && event->header.type != PERF_RECORD_SAMPLE) {
+ /*
+ * XXX We're leaving PERF_RECORD_SAMPLE unnacounted here
+ * because the tools right now may apply filters, discarding
+ * some of the samples. For consistency, in the future we
+ * should have something like nr_filtered_samples and remove
+ * the sample->period from total_sample_period, etc, KISS for
+ * now tho.
+ *
+ * Also testing against NULL allows us to handle files without
+ * attr.sample_id_all and/or without PERF_SAMPLE_ID. In the
+ * future probably it'll be a good idea to restrict event
+ * processing via perf_session to files with both set.
+ */
+ hists__inc_nr_events(&evsel->hists, event->header.type);
+ }
+
switch (event->header.type) {
case PERF_RECORD_SAMPLE:
dump_sample(session, event, sample);
- evsel = perf_evlist__id2evsel(session->evlist, sample->id);
if (evsel == NULL) {
++session->hists.stats.nr_unknown_id;
return -1;
}
- return ops->sample(event, sample, evsel, session);
+ return ops->sample(ops, event, sample, evsel, session);
case PERF_RECORD_MMAP:
- return ops->mmap(event, sample, session);
+ return ops->mmap(ops, event, sample, session);
case PERF_RECORD_COMM:
- return ops->comm(event, sample, session);
+ return ops->comm(ops, event, sample, session);
case PERF_RECORD_FORK:
- return ops->fork(event, sample, session);
+ return ops->fork(ops, event, sample, session);
case PERF_RECORD_EXIT:
- return ops->exit(event, sample, session);
+ return ops->exit(ops, event, sample, session);
case PERF_RECORD_LOST:
- return ops->lost(event, sample, session);
+ return ops->lost(ops, event, sample, session);
case PERF_RECORD_READ:
- return ops->read(event, sample, session);
+ return ops->read(ops, event, sample, session);
case PERF_RECORD_THROTTLE:
- return ops->throttle(event, sample, session);
+ return ops->throttle(ops, event, sample, session);
case PERF_RECORD_UNTHROTTLE:
- return ops->unthrottle(event, sample, session);
+ return ops->unthrottle(ops, event, sample, session);
default:
++session->hists.stats.nr_unknown_events;
return -1;
static int perf_session__process_user_event(struct perf_session *session, union perf_event *event,
struct perf_event_ops *ops, u64 file_offset)
{
+ int err;
+
dump_event(session, event, file_offset, NULL);
/* These events are processed right away */
switch (event->header.type) {
case PERF_RECORD_HEADER_ATTR:
- return ops->attr(event, session);
+ err = ops->attr(event, &session->evlist);
+ if (err == 0)
+ perf_session__update_sample_type(session);
+ return err;
case PERF_RECORD_HEADER_EVENT_TYPE:
- return ops->event_type(event, session);
+ return ops->event_type(ops, event, session);
case PERF_RECORD_HEADER_TRACING_DATA:
/* setup for reading amidst mmap */
lseek(session->fd, file_offset, SEEK_SET);
return ops->tracing_data(event, session);
case PERF_RECORD_HEADER_BUILD_ID:
- return ops->build_id(event, session);
+ return ops->build_id(ops, event, session);
case PERF_RECORD_FINISHED_ROUND:
- return ops->finished_round(event, session, ops);
+ return ops->finished_round(ops, event, session);
default:
return -EINVAL;
}
self->size = bswap_16(self->size);
}
+struct thread *perf_session__findnew(struct perf_session *session, pid_t pid)
+{
+ return machine__findnew_thread(&session->host_machine, pid);
+}
+
static struct thread *perf_session__register_idle_thread(struct perf_session *self)
{
struct thread *thread = perf_session__findnew(self, 0);
const struct perf_event_ops *ops)
{
if (ops->lost == perf_event__process_lost &&
- session->hists.stats.total_lost != 0) {
- ui__warning("Processed %" PRIu64 " events and LOST %" PRIu64
- "!\n\nCheck IO/CPU overload!\n\n",
- session->hists.stats.total_period,
- session->hists.stats.total_lost);
+ session->hists.stats.nr_events[PERF_RECORD_LOST] != 0) {
+ ui__warning("Processed %d events and lost %d chunks!\n\n"
+ "Check IO/CPU overload!\n\n",
+ session->hists.stats.nr_events[0],
+ session->hists.stats.nr_events[PERF_RECORD_LOST]);
}
if (session->hists.stats.nr_unknown_events != 0) {
{
u64 head, page_offset, file_offset, file_pos, progress_next;
int err, mmap_prot, mmap_flags, map_idx = 0;
- struct ui_progress *progress;
size_t page_size, mmap_size;
char *buf, *mmaps[8];
union perf_event *event;
file_size = data_offset + data_size;
progress_next = file_size / 16;
- progress = ui_progress__new("Processing events...", file_size);
- if (progress == NULL)
- return -1;
mmap_size = session->mmap_window;
if (mmap_size > file_size)
if (file_pos >= progress_next) {
progress_next += file_size / 16;
- ui_progress__update(progress, file_pos);
+ ui_progress__update(file_pos, file_size,
+ "Processing events...");
}
if (file_pos < file_size)
session->ordered_samples.next_flush = ULLONG_MAX;
flush_sample_queue(session, ops);
out_err:
- ui_progress__delete(progress);
perf_session__warn_about_errors(session, ops);
perf_session_free_sample_buffers(session);
return err;
return ret;
}
+size_t perf_session__fprintf(struct perf_session *session, FILE *fp)
+{
+ /*
+ * FIXME: Here we have to actually print all the machines in this
+ * session, not just the host...
+ */
+ return machine__fprintf(&session->host_machine, fp);
+}
+
+void perf_session__remove_thread(struct perf_session *session,
+ struct thread *th)
+{
+ /*
+ * FIXME: This one makes no sense, we need to remove the thread from
+ * the machine it belongs to, perf_session can have many machines, so
+ * doing it always on ->host_machine is wrong. Fix when auditing all
+ * the 'perf kvm' code.
+ */
+ machine__remove_thread(&session->host_machine, th);
+}
+
struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
unsigned int type)
{
return NULL;
}
-void perf_session__print_ip(union perf_event *event,
+void perf_session__print_ip(union perf_event *event, struct perf_evsel *evsel,
struct perf_sample *sample,
struct perf_session *session,
int print_sym, int print_dso)
{
struct addr_location al;
const char *symname, *dsoname;
- struct callchain_cursor *cursor = &session->callchain_cursor;
+ struct callchain_cursor *cursor = &evsel->hists.callchain_cursor;
struct callchain_cursor_node *node;
if (perf_event__preprocess_sample(event, session, &al, sample,
if (symbol_conf.use_callchain && sample->callchain) {
- if (perf_session__resolve_callchain(session, al.thread,
+ if (perf_session__resolve_callchain(session, evsel, al.thread,
sample->callchain, NULL) != 0) {
if (verbose)
error("Failed to resolve callchain. Skipping\n");