perf: kvm live mode
cover perf: kvm live mode
Hi Arnaldo:
There were no comments from the last round, so this is the same patches rebased to your latest perf/core branch.
David Ahern (11):
perf evlist: restore methods removed in earlier cleanup perf evlist: move tracepoint processing code to evlist.c perf evlist: add initialzation function for tracepoints perf session: export a few functions for event processing perf top: move CONSOLE_CLEAR to header file
perf kvm: split out tracepoints from record args perf stats: add max and min stats
perf kvm: add live mode
perf kvm: add min and max stats to display
perf kvm: option to print events that exceed a threshold perf kvm: debug for missing vmexit/vmentry event
tools/perf/builtin-kvm.c | 685 ++++++++++++++++++++++++++++++++++++++++++--- tools/perf/builtin-top.c | 2 - tools/perf/util/evlist.c | 128 +++++++++ tools/perf/util/evlist.h | 10 + tools/perf/util/header.c | 44 --- tools/perf/util/session.c | 12 +- tools/perf/util/session.h | 7 + tools/perf/util/stat.c | 6 + tools/perf/util/stat.h | 9 + tools/perf/util/top.h | 2 + 10 files changed, 811 insertions(+), 94 deletions(-)
Signature · 1 line
1.7.10.1
PATCH 1/11 perf evlist: restore methods removed in earlier cleanup
e60fc847 removed the perf_evlist__add_tracepoints and helpers, but they are useful for kvm's upcoming live mode.
--- tools/perf/util/evlist.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/evlist.h | 3 +++ 2 files changed, 57 insertions(+)
tools/perf/util/evlist.c +54 -0
@@ -195,6 +195,60 @@ int __perf_evlist__add_default_attrs(struct perf_evlist *evlist,return perf_evlist__add_attrs (evlist, attrs, nr_attrs); } +static int trace_event__id (const char *evname) +{ +char *filename, *colon; +int err =-1 , fd; + +if (asprintf (&filename,"%s/%s/id" , tracing_events_path, evname) <0 ) +return -1 ; + + colon =strrchr (filename,':' ); +if (colon !=NULL ) + *colon ='/' ; + + fd =open (filename,O_RDONLY ); +if (fd >=0 ) { +char id[16 ]; +if (read (fd, id,sizeof (id)) >0 ) + err =atoi (id); +close (fd); + } + +free (filename); +return err; +} + +int perf_evlist__add_tracepoints (struct perf_evlist *evlist, +const char *const tracepoints[], +size_t nr_tracepoints) +{ +int err; +size_t i; +struct perf_event_attr *attrs =zalloc (nr_tracepoints *sizeof (*attrs)); + +if (attrs ==NULL ) +return -1 ; + +for (i =0 ; i < nr_tracepoints; i++) { + err =trace_event__id (tracepoints[i]); + +if (err <0 ) + goto out_free_attrs; + + attrs[i].type =PERF_TYPE_TRACEPOINT ; + attrs[i].config = err; + attrs[i].sample_type = (PERF_SAMPLE_RAW |PERF_SAMPLE_TIME | +PERF_SAMPLE_CPU |PERF_SAMPLE_PERIOD ); + attrs[i].sample_period =1 ; + } + + err =perf_evlist__add_attrs (evlist, attrs, nr_tracepoints); +out_free_attrs: +free (attrs); +return err; +} +struct perf_evsel *perf_evlist__find_tracepoint_by_id (struct perf_evlist *evlist,int id) {
tools/perf/util/evlist.h +3 -0
@@ -59,6 +59,9 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry);int perf_evlist__add_default (struct perf_evlist *evlist);int __perf_evlist__add_default_attrs (struct perf_evlist *evlist,struct perf_event_attr *attrs,size_t nr_attrs); +int perf_evlist__add_tracepoints (struct perf_evlist *evlist, +const char *const tracepoints[], +size_t nr_tracepoints);#define perf_evlist__add_default_attrs (evlist, array) \ __perf_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array))
Signature · 1 line
1.7.10.1
PATCH 2/11 perf evlist: move tracepoint processing code to evlist.c
In reply to David AhernPer function names they are more aligned with the evlist code than the header code. Export perf_evlist__prepare_tracepoint_events in the process.
Code move only; no functional changes.
--- tools/perf/util/evlist.c | 39 +++++++++++++++++++++++++++++++++++++++ tools/perf/util/evlist.h | 4 ++++ tools/perf/util/header.c | 44 -------------------------------------------- 3 files changed, 43 insertions(+), 44 deletions(-)
tools/perf/util/evlist.c +39 -0
@@ -195,6 +195,45 @@ int __perf_evlist__add_default_attrs(struct perf_evlist *evlist,return perf_evlist__add_attrs (evlist, attrs, nr_attrs); } +static int perf_evsel__prepare_tracepoint_event (struct perf_evsel *evsel, +struct pevent *pevent) +{ +struct event_format *event; +char bf[128 ]; + +/* already prepared */ +if (evsel->tp_format) +return 0 ; + + event =pevent_find_event (pevent, evsel->attr.config); +if (event ==NULL ) +return -1 ; + +if (!evsel->name) { +snprintf (bf,sizeof (bf),"%s:%s" , event->system, event->name); + evsel->name =strdup (bf); +if (evsel->name ==NULL ) +return -1 ; + } + + evsel->tp_format = event; +return 0 ; +} + +int perf_evlist__prepare_tracepoint_events (struct perf_evlist *evlist, +struct pevent *pevent) +{ +struct perf_evsel *pos; + +list_for_each_entry (pos, &evlist->entries, node) { +if (pos->attr.type ==PERF_TYPE_TRACEPOINT && +perf_evsel__prepare_tracepoint_event (pos, pevent)) +return -1 ; + } + +return 0 ; +} +static int trace_event__id (const char *evname) {char *filename, *colon;
tools/perf/util/evlist.h +4 -0
@@ -5,6 +5,7 @@#include <stdio.h> #include "../perf.h" #include "event.h" +#include "trace-event.h" #include "evsel.h" #include "util.h" #include <unistd.h>
@@ -59,6 +60,9 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry);int perf_evlist__add_default (struct perf_evlist *evlist);int __perf_evlist__add_default_attrs (struct perf_evlist *evlist,struct perf_event_attr *attrs,size_t nr_attrs); +int perf_evlist__prepare_tracepoint_events (struct perf_evlist *evlist, +struct pevent *pevent); +int perf_evlist__add_tracepoints (struct perf_evlist *evlist,const char *const tracepoints[],size_t nr_tracepoints);
tools/perf/util/header.c +0 -44
50 lines not on this page · raw message
Signature · 1 line
1.7.10.1
PATCH 3/11 perf evlist: add initialzation function for tracepoints
In reply to David AhernHandles initializations typically done as part of processing the file header and HEADER_TRACING_DATA event.
v2: close fd as noticed by Namhyung.
--- tools/perf/util/evlist.c | 35 +++++++++++++++++++++++++++++++++++ tools/perf/util/evlist.h | 3 +++ 2 files changed, 38 insertions(+)
tools/perf/util/evlist.c +35 -0
@@ -14,6 +14,7 @@#include "target.h" #include "evlist.h" #include "evsel.h" +#include "trace-event.h" #include <unistd.h> #include "parse-events.h"
@@ -234,6 +235,40 @@ int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist,return 0 ; } +int perf_evlist__trace_init (struct perf_evlist *evlist, +struct perf_session *session) +{ +struct tracing_data *tdata; +char temp_file[] ="/tmp/perf-XXXXXXXX" ; +int fd; +int rc =0 ; + + fd =mkstemp (temp_file); +if (fd <0 ) { +pr_err ("mkstemp failed\n" ); +return -1 ; + } +unlink (temp_file); + + tdata =tracing_data_get (&evlist->entries, fd, false); +if (!tdata) { + rc =-1 ; + goto out; + } + +lseek (fd,0 ,SEEK_SET ); + (void )trace_report (fd, &session->pevent, false); +tracing_data_put (tdata); + + rc =perf_evlist__prepare_tracepoint_events (evlist, session->pevent); + +out: +close (fd); + +return rc; +} + +static int trace_event__id (const char *evname) {char *filename, *colon;
tools/perf/util/evlist.h +3 -0
@@ -8,6 +8,7 @@#include "trace-event.h" #include "evsel.h" #include "util.h" +#include "session.h" #include <unistd.h> struct pollfd ;
@@ -66,6 +67,8 @@ int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist,int perf_evlist__add_tracepoints (struct perf_evlist *evlist,const char *const tracepoints[],size_t nr_tracepoints); +int perf_evlist__trace_init (struct perf_evlist *evlist, +struct perf_session *session);#define perf_evlist__add_default_attrs (evlist, array) \ __perf_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array))
Signature · 1 line
1.7.10.1
PATCH 4/11 perf session: export a few functions for event processing
In reply to David AhernAllows kvm live mode to reuse the event processing and ordered samples processing used by the perf-report path.
v2: removed flush_sample_queue as noticed by Jiri
--- tools/perf/util/session.c | 12 ++++++------ tools/perf/util/session.h | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-)
tools/perf/util/session.c +6 -6
@@ -252,7 +252,7 @@ static int process_finished_round(struct perf_tool *tool,union perf_event *event, struct perf_session *session); -static void perf_tool__fill_defaults (struct perf_tool *tool) +void perf_tool__fill_defaults (struct perf_tool *tool) {if (tool->sample ==NULL ) tool->sample = process_event_sample_stub;
@@ -499,7 +499,7 @@ static int perf_session_deliver_event(struct perf_session *session, u64 file_offset);static int flush_sample_queue (struct perf_session *s, -struct perf_tool *tool) +struct perf_tool *tool) {struct ordered_samples *os = &s->ordered_samples;struct list_head *head = &os->samples;
@@ -952,10 +952,10 @@ static void event_swap(union perf_event *event, bool sample_id_all)swap (event, sample_id_all); } -static int perf_session__process_event (struct perf_session *session, -union perf_event *event, -struct perf_tool *tool, -u64 file_offset) +int perf_session__process_event (struct perf_session *session, +union perf_event *event, +struct perf_tool *tool, +u64 file_offset) {struct perf_sample sample;int ret;
tools/perf/util/session.h +7 -0
@@ -56,6 +56,13 @@ int __perf_session__process_events(struct perf_session *self,int perf_session__process_events (struct perf_session *self,struct perf_tool *tool); +int perf_session__process_event (struct perf_session *session, +union perf_event *event, +struct perf_tool *tool, +u64 file_offset); + +void perf_tool__fill_defaults (struct perf_tool *tool); + int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel *evsel, struct thread *thread, struct ip_callchain *chain,
Signature · 1 line
1.7.10.1
PATCH 5/11 perf top: move CONSOLE_CLEAR to header file
In reply to David AhernFor use with kvm-live mode.
--- tools/perf/builtin-top.c | 2 -- tools/perf/util/top.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-)
tools/perf/builtin-top.c +0 -2
@@ -238,8 +238,6 @@ out_unlock:pthread_mutex_unlock (¬es->lock); } -static const char CONSOLE_CLEAR [] ="[H[2J" ; -static struct hist_entry *perf_evsel__add_hist_entry (struct perf_evsel *evsel,struct addr_location *al,struct perf_sample *sample)
tools/perf/util/top.h +2 -0
@@ -39,6 +39,8 @@ struct perf_top { float min_percent; }; +#define CONSOLE_CLEAR "[H[2J" +size_t perf_top__header_snprintf (struct perf_top *top,char *bf,size_t size);void perf_top__reset_sample_counters (struct perf_top *top);#endif /* __PERF_TOP_H */
Signature · 1 line
1.7.10.1
PATCH 6/11 perf kvm: split out tracepoints from record args
In reply to David AhernNeeded by kvm live command. Make record_args a local while we are messing with the args.
--- tools/perf/builtin-kvm.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-)
tools/perf/builtin-kvm.c +19 -11
@@ -801,16 +801,11 @@ exit:return ret; } -static const char *const record_args[] = { -"record" , -"-R" , -"-f" , -"-m" ,"1024" , -"-c" ,"1" , -"-e" ,"kvm:kvm_entry" , -"-e" ,"kvm:kvm_exit" , -"-e" ,"kvm:kvm_mmio" , -"-e" ,"kvm:kvm_pio" , +static const char *const kvm_events_tp[] = { +"kvm:kvm_entry" , +"kvm:kvm_exit" , +"kvm:kvm_mmio" , +"kvm:kvm_pio" , };#define STRDUP_FAIL_EXIT (s) \
@@ -826,8 +821,16 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) {unsigned int rec_argc, i, j;const char **rec_argv; +const char *const record_args[] = { +"record" , +"-R" , +"-f" , +"-m" ,"1024" , +"-c" ,"1" , + }; - rec_argc =ARRAY_SIZE (record_args) + argc +2 ; + rec_argc =ARRAY_SIZE (record_args) + argc +2 + +2 *ARRAY_SIZE (kvm_events_tp); rec_argv =calloc (rec_argc +1 ,sizeof (char *));if (rec_argv ==NULL )
@@ -836,6 +839,11 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)for (i =0 ; i <ARRAY_SIZE (record_args); i++) rec_argv[i] =STRDUP_FAIL_EXIT (record_args[i]); +for (j =0 ; j <ARRAY_SIZE (kvm_events_tp); j++) { + rec_argv[i++] ="-e" ; + rec_argv[i++] =STRDUP_FAIL_EXIT (kvm_events_tp[j]); + } + rec_argv[i++] =STRDUP_FAIL_EXIT ("-o" ); rec_argv[i++] =STRDUP_FAIL_EXIT (kvm->file_name);
Signature · 1 line
1.7.10.1
PATCH 7/11 perf stats: add max and min stats
In reply to David AhernNeed an initialization function to set min to -1 to differentiate from an actual min of 0.
--- tools/perf/util/stat.c | 6 ++++++ tools/perf/util/stat.h | 9 +++++++++ 2 files changed, 15 insertions(+)
tools/perf/util/stat.c +6 -0
@@ -10,6 +10,12 @@ void update_stats(struct stats *stats, u64 val) delta = val - stats->mean; stats->mean += delta / stats->n; stats->M2 += delta*(val - stats->mean); + +if (val > stats->max) + stats->max = val; + +if (val < stats->min) + stats->min = val; }double avg_stats (struct stats *stats)
tools/perf/util/stat.h +9 -0
@@ -6,6 +6,7 @@struct stats {double n, mean, M2; +u64 max, min; };void update_stats (struct stats *stats,u64 val);
@@ -13,4 +14,12 @@ double avg_stats(struct stats *stats);double stddev_stats (struct stats *stats);double rel_stddev_stats (double stddev,double avg); +static inline void init_stats (struct stats *stats) +{ + stats->n =0.0 ; + stats->mean =0.0 ; + stats->M2 =0.0 ; + stats->min = (u64) -1 ; + stats->max =0 ; +}#endif
Signature · 1 line
1.7.10.1
PATCH 8/11 perf kvm: add live mode
In reply to David Ahernperf kvm stat currently requires back to back record and report commands to see stats. e.g,.
perf kvm stat record -p $pid -- sleep 1
perf kvm stat report
This is inconvenvient for on box monitoring of a VM. This patch introduces a 'live' mode that in effect combines the record plus report into one command. e.g., to monitor a single VM:
perf kvm stat live -p $pid
or all VMs:
perf kvm stat live
Same stats options for the record+report path work with the live mode. Display rate defaults to 1 second and can be changed using the -d option.
v2:
removed ABSTIME arg from timerfd_settime as mentioned by Namhyung only call perf_kvm__handle_stdin when poll returns activity.
--- tools/perf/builtin-kvm.c | 603 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 577 insertions(+), 26 deletions(-)
tools/perf/builtin-kvm.c +577 -26
@@ -2,6 +2,7 @@#include "perf.h" #include "util/evsel.h" +#include "util/evlist.h" #include "util/util.h" #include "util/cache.h" #include "util/symbol.h"
@@ -15,9 +16,12 @@#include <lk/debugfs.h> #include "util/tool.h" #include "util/stat.h" +#include "util/top.h" #include <sys/prctl.h> +#include <sys/timerfd.h> +#include <termios.h> #include <semaphore.h> #include <pthread.h> #include <math.h>
@@ -82,6 +86,8 @@ struct exit_reasons_table {struct perf_kvm_stat {struct perf_tool tool; +struct perf_record_opts opts; +struct perf_evlist *evlist;struct perf_session *session;const char *file_name;
@@ -96,10 +102,16 @@ struct perf_kvm_stat {struct kvm_events_ops *events_ops;key_cmp_fun compare;struct list_head kvm_events_cache[EVENTS_CACHE_SIZE ]; +u64 total_time;u64 total_count; +u64 lost_events;struct rb_root result; + +int timerfd; +unsigned int display_time; +bool live; };
@@ -320,6 +332,23 @@ static void init_kvm_event_record(struct perf_kvm_stat *kvm)INIT_LIST_HEAD (&kvm->kvm_events_cache[i]); } +static void clear_events_cache_stats (struct list_head *kvm_events_cache) +{ +struct list_head *head; +struct kvm_event *event; +unsigned int i; + +for (i =0 ; i <EVENTS_CACHE_SIZE ; i++) { + head = &kvm_events_cache[i]; +list_for_each_entry (event, head, hash_entry) { +/* reset stats for event */ +memset (&event->total,0 ,sizeof (event->total)); +memset (event->vcpu,0 , + event->max_vcpu *sizeof (*event->vcpu)); + } + } +} +static int kvm_events_hash_fn (u64 key) {return key & (EVENTS_CACHE_SIZE -1 );
@@ -472,7 +501,11 @@ static bool handle_end_event(struct perf_kvm_stat *kvm, vcpu_record->last_event =NULL ; vcpu_record->start_time =0 ; -BUG_ON (timestamp < time_begin); +/* seems to happen once in a while during live mode */ +if (timestamp < time_begin) { +pr_debug ("End time before begin time; skipping event.\n" ); +return true; + } time_diff = timestamp - time_begin;return update_kvm_event (event, vcpu, time_diff);
@@ -639,24 +672,60 @@ static struct kvm_event *pop_from_result(struct rb_root *result) return container_of(node, struct kvm_event, rb); } -static void print_vcpu_info (int vcpu) +static void print_vcpu_info (struct perf_kvm_stat *kvm) { +int vcpu = kvm->trace_vcpu; +pr_info ("Analyze events for " ); +if (kvm->live) { +if (kvm->opts.target.system_wide) +pr_info ("all VMs, " ); +else if (kvm->opts.target.pid) +pr_info ("pid(s) %s, " , kvm->opts.target.pid); +else if (kvm->opts.target.tid) +pr_info ("tid(s) %s, " , kvm->opts.target.tid); +else if (kvm->opts.target.cpu_list) +pr_info ("host cpu(s) %s, " , kvm->opts.target.cpu_list); +else +pr_info ("dazed and confused on what is monitored, " ); + } +if (vcpu ==-1 )pr_info ("all VCPUs:\n\n" );else pr_info ("VCPU %d:\n\n" , vcpu); } +static void show_timeofday (void ) +{ +char date[64 ]; +struct timeval tv; +struct tm ltime; + +gettimeofday (&tv,NULL ); +if (localtime_r (&tv.tv_sec, <ime)) { +strftime (date,sizeof (date),"%H:%M:%S" , <ime); +pr_info ("%s.%06ld" , date, tv.tv_usec); + }else +pr_info ("00:00:00.000000" ); + +return ; +} +static void print_result (struct perf_kvm_stat *kvm) {char decode[20 ];struct kvm_event *event;int vcpu = kvm->trace_vcpu; +if (kvm->live) { +puts (CONSOLE_CLEAR ); +show_timeofday (); + } +pr_info ("\n\n" ); -print_vcpu_info (vcpu); +print_vcpu_info (kvm);pr_info ("%20s " , kvm->events_ops->name);pr_info ("%10s " ,"Samples" );pr_info ("%9s " ,"Samples%" );
@@ -683,6 +752,20 @@ static void print_result(struct perf_kvm_stat *kvm)pr_info ("\nTotal Samples:%" PRIu64", Total events handled time:%.2fus.\n\n" , kvm->total_count, kvm->total_time /1e3 ); + +if (kvm->lost_events) +pr_info ("\nLost events: %" PRIu64"\n\n" , kvm->lost_events); +} + +static int process_lost_event (struct perf_tool *tool, +union perf_event *event__maybe_unused , +struct perf_sample *sample__maybe_unused , +struct machine *machine__maybe_unused ) +{ + struct perf_kvm_stat *kvm = container_of(tool, struct perf_kvm_stat, tool); + + kvm->lost_events++; +return 0 ; } static int process_sample_event(struct perf_tool *tool,
@@ -707,10 +790,20 @@ static int process_sample_event(struct perf_tool *tool,return 0 ; } -static int get_cpu_isa (struct perf_session *session) +static int cpu_isa_config (struct perf_kvm_stat *kvm) { -char *cpuid = session->header.env.cpuid; -int isa; +char buf[64 ], *cpuid; +int err, isa; + +if (kvm->live) { + err =get_cpuid (buf,sizeof (buf)); +if (err !=0 ) { +pr_err ("Failed to look up CPU type (Intel or AMD)\n" ); +return err; + } + cpuid = buf; + }else + cpuid = kvm->session->header.env.cpuid;if (strstr (cpuid,"Intel" )) isa =1 ;
@@ -718,10 +811,339 @@ static int get_cpu_isa(struct perf_session *session) isa =0 ; else {pr_err ("CPU %s is not supported.\n" , cpuid); - isa = -ENOTSUP ; +return -ENOTSUP ; + } + +if (isa ==1 ) { + kvm->exit_reasons = vmx_exit_reasons; + kvm->exit_reasons_size =ARRAY_SIZE (vmx_exit_reasons); + kvm->exit_reasons_isa ="VMX" ; + } + +return 0 ; +} + +static bool verify_vcpu (int vcpu) +{ +int nr_cpus; + +if (vcpu !=-1 && vcpu <0 ) { +pr_err ("Invalid vcpu:%d.\n" , vcpu); +return false; + } + + nr_cpus =sysconf (_SC_NPROCESSORS_ONLN); +if ((nr_cpus >0 ) && (vcpu > nr_cpus -1 )) { +pr_err ("Invalid vcpu:%d.\n" , vcpu); +return false; + } + +return true; +} + +#define PERF_KVM__MAX_EVENTS_PER_MMAP 1000 + +static s64 perf_kvm__mmap_read_idx (struct perf_kvm_stat *kvm,int idx) +{ +union perf_event *event; +s64 n =0 ; +int err; + +while ((event =perf_evlist__mmap_read (kvm->evlist, idx)) !=NULL ) { + err =perf_session__process_event (kvm->session, event, + &kvm->tool,0 ); +if (err) { +pr_err ("Failed to process event\n" ); +return err; + } + n++; + +/* limit events per mmap handled all at once */ +if (n ==PERF_KVM__MAX_EVENTS_PER_MMAP ) +break ; + } + +return n; +} + +static int perf_kvm__mmap_read (struct perf_kvm_stat *kvm) +{ +int i, err, throttled =0 ; +s64 n, ntotal =0 ; + +for (i =0 ; i < kvm->evlist->nr_mmaps; i++) { + n =perf_kvm__mmap_read_idx (kvm, i); +if (n <0 ) +return -1 ; + ntotal += n; +if (n ==PERF_KVM__MAX_EVENTS_PER_MMAP ) + throttled =1 ; + } + +/* flush queue after each round in which we processed events */ +if (ntotal) { + err = kvm->tool.finished_round (&kvm->tool,NULL , kvm->session); +if (err) { +if (kvm->lost_events) +pr_info ("\nLost events: %" PRIu64"\n\n" , + kvm->lost_events); +return err; + } + } + +return throttled; +} + +static volatile int done; + +static void sig_handler (int sig__maybe_unused ) +{ + done =1 ; +} + +static int perf_kvm__timerfd_create (struct perf_kvm_stat *kvm) +{ +struct itimerspec new_value; +int rc =-1 ; + + kvm->timerfd =timerfd_create (CLOCK_MONOTONIC ,TFD_NONBLOCK ); +if (kvm->timerfd <0 ) { +pr_err ("timerfd_create failed\n" ); + goto out; + } + + new_value.it_value.tv_sec = kvm->display_time; + new_value.it_value.tv_nsec =0 ; + new_value.it_interval.tv_sec = kvm->display_time; + new_value.it_interval.tv_nsec =0 ; + +if (timerfd_settime (kvm->timerfd,0 , &new_value,NULL ) !=0 ) { +pr_err ("timerfd_settime failed: %d\n" , errno); +close (kvm->timerfd); + goto out; + } + + rc =0 ; +out: +return rc; +} + +static int perf_kvm__handle_timerfd (struct perf_kvm_stat *kvm) +{ +uint64_t c; +int rc; + + rc =read (kvm->timerfd, &c,sizeof (uint64_t )); +if (rc <0 ) { +if (errno ==EAGAIN ) +return 0 ; + +pr_err ("Failed to read timer fd: %d\n" , errno); +return -1 ; + } + +if (rc !=sizeof (uint64_t )) { +pr_err ("Error reading timer fd - invalid size returned\n" ); +return -1 ; + } + +if (c !=1 ) +pr_debug ("Missed timer beats: %" PRIu64"\n" , c-1 ); + +/* update display */ +sort_result (kvm); +print_result (kvm); + +/* reset counts */ +clear_events_cache_stats (kvm->kvm_events_cache); + kvm->total_count =0 ; + kvm->total_time =0 ; + kvm->lost_events =0 ; + +return 0 ; +} + +static int fd_set_nonblock (int fd) +{ +long arg =0 ; + + arg =fcntl (fd,F_GETFL ); +if (arg <0 ) { +pr_err ("Failed to get current flags for fd %d\n" , fd); +return -1 ; + } + +if (fcntl (fd,F_SETFL , arg |O_NONBLOCK ) <0 ) { +pr_err ("Failed to set non-block option on fd %d\n" , fd); +return -1 ; + } + +return 0 ; +} + +static +int perf_kvm__handle_stdin (struct termios *tc_now,struct termios *tc_save) +{ +int c; + +tcsetattr (0 ,TCSANOW , tc_now); + c =getc (stdin); +tcsetattr (0 ,TCSAFLUSH , tc_save); + +if (c =='q' ) +return 1 ; + +return 0 ; +} + +static int kvm_events_live_report (struct perf_kvm_stat *kvm) +{ +struct pollfd *pollfds =NULL ; +int nr_fds, nr_stdin, ret, err = -EINVAL ; +struct termios tc, save; + +/* live flag must be set first */ + kvm->live = true; + + ret =cpu_isa_config (kvm); +if (ret <0 ) +return ret; + +if (!verify_vcpu (kvm->trace_vcpu) || + !select_key (kvm) || + !register_kvm_events_ops (kvm)) { + goto out; + } + +init_kvm_event_record (kvm); + +tcgetattr (0 , &save); + tc = save; + tc.c_lflag &= ~(ICANON |ECHO ); + tc.c_cc[VMIN ] =0 ; + tc.c_cc[VTIME ] =0 ; + +signal (SIGINT , sig_handler); +signal (SIGTERM , sig_handler); + +/* copy pollfds -- need to add timerfd and stdin */ + nr_fds = kvm->evlist->nr_fds; + pollfds =zalloc (sizeof (struct pollfd ) * (nr_fds +2 )); +if (!pollfds) { + err = -ENOMEM ; + goto out; + } +memcpy (pollfds, kvm->evlist->pollfd, +sizeof (struct pollfd ) * kvm->evlist->nr_fds); + +/* add timer fd */ +if (perf_kvm__timerfd_create (kvm) <0 ) { + err =-1 ; + goto out; + } + + pollfds[nr_fds].fd = kvm->timerfd; + pollfds[nr_fds].events =POLLIN ; + nr_fds++; + + pollfds[nr_fds].fd =fileno (stdin); + pollfds[nr_fds].events =POLLIN ; + nr_stdin = nr_fds; + nr_fds++; +if (fd_set_nonblock (fileno (stdin)) !=0 ) + goto out; + +/* everything is good - enable the events and process */ +perf_evlist__enable (kvm->evlist); + +while (!done) { +int rc; + + rc =perf_kvm__mmap_read (kvm); +if (rc <0 ) +break ; + + err =perf_kvm__handle_timerfd (kvm); +if (err) + goto out; + +if (pollfds[nr_stdin].revents &POLLIN ) + done =perf_kvm__handle_stdin (&tc, &save); + +if (!rc && !done) + err =poll (pollfds, nr_fds,100 ); + } + +perf_evlist__disable (kvm->evlist); + +if (err ==0 ) { +sort_result (kvm); +print_result (kvm); } -return isa; +out: +if (kvm->timerfd >=0 ) +close (kvm->timerfd); + +if (pollfds) +free (pollfds); + +return err; +} + +static int perf_kvm__open_counters (struct perf_kvm_stat *kvm) +{ +struct perf_evsel *pos; +struct perf_evlist *evlist = kvm->evlist; +char msg[512 ]; + +perf_evlist__config (evlist, &kvm->opts); + +/* +* Note: exclude_{guest,host} do not apply here. +* This command processes KVM tracepoints from host only +*/ +list_for_each_entry (pos, &evlist->entries, node) { +struct perf_event_attr *attr = &pos->attr; + + attr->sample_type |=PERF_SAMPLE_TID ; + attr->sample_type |=PERF_SAMPLE_TIME ; + attr->sample_type |=PERF_SAMPLE_CPU ; + attr->sample_type |=PERF_SAMPLE_RAW ; + + attr->sample_period =1 ; + + attr->watermark =0 ; + attr->wakeup_events =1000 ; + +/* will enable all once we are ready */ + attr->disabled =1 ; + +try_again: +if (perf_evsel__open (pos, evlist->cpus, evlist->threads) <0 ) { +if (perf_evsel__fallback (pos, errno, msg,sizeof (msg))) { +if (verbose) +ui__warning ("%s\n" , msg); + goto try_again; + } + +perf_evsel__open_strerror (pos, &kvm->opts.target, + errno, msg,sizeof (msg)); +ui__error ("%s\n" , msg); + goto out_err; + } + } + +if (perf_evlist__mmap (evlist, kvm->opts.mmap_pages, false) <0 ) { +ui__error ("Failed to mmap with %d (%s)\n" , + errno,strerror (errno)); + goto out_err; + } + +return 0 ; + +out_err: +return -1 ; }static int read_events (struct perf_kvm_stat *kvm)
@@ -749,30 +1171,13 @@ static int read_events(struct perf_kvm_stat *kvm)* Do not use 'isa' recorded in kvm_exit tracepoint since it is not * traced in the old kernel. */ - ret =get_cpu_isa (kvm->session); - + ret =cpu_isa_config (kvm);if (ret <0 )return ret; -if (ret ==1 ) { - kvm->exit_reasons = vmx_exit_reasons; - kvm->exit_reasons_size =ARRAY_SIZE (vmx_exit_reasons); - kvm->exit_reasons_isa ="VMX" ; - } -return perf_session__process_events (kvm->session, &kvm->tool); } -static bool verify_vcpu (int vcpu) -{ -if (vcpu !=-1 && vcpu <0 ) { -pr_err ("Invalid vcpu:%d.\n" , vcpu); -return false; - } - -return true; -} -static int kvm_events_report_vcpu (struct perf_kvm_stat *kvm) {int ret = -EINVAL ;
@@ -886,6 +1291,148 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)return kvm_events_report_vcpu (kvm); } +static int kvm_events_live (struct perf_kvm_stat *kvm, +int argc,const char **argv) +{ +char errbuf[BUFSIZ ]; +int err; +const struct option live_options[] = { +OPT_STRING ('p' ,"pid" , &kvm->opts.target.pid,"pid" , +"record events on existing process id" ), +OPT_STRING ('t' ,"tid" , &kvm->opts.target.tid,"tid" , +"record events on existing thread id" ), +OPT_STRING ('C' ,"cpu" , &kvm->opts.target.cpu_list,"cpu" , +"list of host cpus to monitor" ), +OPT_UINTEGER ('m' ,"mmap-pages" , &kvm->opts.mmap_pages, +"number of mmap data pages" ), +OPT_INCR ('v' ,"verbose" , &verbose, +"be more verbose (show counter open errors, etc)" ), +OPT_BOOLEAN ('a' ,"all-cpus" , &kvm->opts.target.system_wide, +"system-wide collection from all CPUs" ), +OPT_UINTEGER ('d' ,"display" , &kvm->display_time, +"time in seconds between display updates" ), +OPT_STRING (0 ,"event" , &kvm->report_event,"report event" , +"event for reporting: vmexit, mmio, ioport" ), +OPT_INTEGER (0 ,"vcpu" , &kvm->trace_vcpu, +"vcpu id to report" ), +OPT_STRING ('k' ,"key" , &kvm->sort_key,"sort-key" , +"key for sorting: sample(sort by samples number)" +" time (sort by avg time)" ), +OPT_END () + }; +const char *const live_usage[] = { +"perf kvm stat live [<options>]" , +NULL + }; + + +/* event handling */ + kvm->tool.sample = process_sample_event; + kvm->tool.comm = perf_event__process_comm; + kvm->tool.exit = perf_event__process_exit; + kvm->tool.fork = perf_event__process_fork; + kvm->tool.lost = process_lost_event; + kvm->tool.ordered_samples = true; +perf_tool__fill_defaults (&kvm->tool); + +/* set defaults */ + kvm->display_time =1 ; + kvm->opts.user_interval =1 ; + kvm->opts.mmap_pages =512 ; + kvm->opts.target.uses_mmap = true; + kvm->opts.target.uid_str =NULL ; + kvm->opts.target.uid =UINT_MAX ; + +symbol__init (); +disable_buildid_cache (); + + use_browser =0 ; +setup_browser (false); + +if (argc) { + argc =parse_options (argc, argv, live_options, + live_usage,0 ); +if (argc) +usage_with_options (live_usage, live_options); + } + +/* +* target related setups +*/ + err =perf_target__validate (&kvm->opts.target); +if (err) { +perf_target__strerror (&kvm->opts.target, err, errbuf,BUFSIZ ); +ui__warning ("%s" , errbuf); + } + +if (perf_target__none (&kvm->opts.target)) + kvm->opts.target.system_wide = true; + + +/* +* generate the event list +*/ + kvm->evlist =perf_evlist__new (); +if (kvm->evlist ==NULL ) +return -ENOMEM ; + + err =perf_evlist__add_tracepoints (kvm->evlist, + kvm_events_tp, +ARRAY_SIZE (kvm_events_tp)); +if (err !=0 ) + goto out; + + symbol_conf.nr_events = kvm->evlist->nr_entries; + +if (perf_evlist__create_maps (kvm->evlist, &kvm->opts.target) <0 ) +usage_with_options (live_usage, live_options); + +/* +* perf session +*/ + kvm->session =perf_session__new (NULL ,O_WRONLY , false, false, &kvm->tool); +if (kvm->session ==NULL ) { + err = -ENOMEM ; + goto out; + } + kvm->session->evlist = kvm->evlist; +perf_session__set_id_hdr_size (kvm->session); + +/* +* need to get pevent initialized +*/ + err =perf_evlist__trace_init (kvm->evlist, kvm->session); +if (err !=0 ) + goto out; + +if (perf_target__has_task (&kvm->opts.target)) +perf_event__synthesize_thread_map (&kvm->tool, + kvm->evlist->threads, + perf_event__process, + &kvm->session->machines.host); +else +perf_event__synthesize_threads (&kvm->tool, perf_event__process, + &kvm->session->machines.host); + + + err =perf_kvm__open_counters (kvm); +if (err) + goto out; + + err =kvm_events_live_report (kvm); + +out: +exit_browser (0 ); + +if (kvm->session) +perf_session__delete (kvm->session); + kvm->session =NULL ; +perf_evlist__delete_maps (kvm->evlist); +perf_evlist__delete (kvm->evlist); + +return err; +} +static void print_kvm_stat_usage (void ) {printf ("Usage: perf kvm stat <command>\n\n" );
@@ -893,6 +1440,7 @@ static void print_kvm_stat_usage(void)printf ("# Available commands:\n" );printf ("\trecord: record kvm events\n" );printf ("\treport: report statistical data of kvm events\n" ); +printf ("\tlive: live reporting of statistical data of kvm events\n" );printf ("\nOtherwise, it is the alias of 'perf stat':\n" ); }
@@ -922,6 +1470,9 @@ static int kvm_cmd_stat(const char *file_name, int argc, const char **argv)if (!strncmp (argv[1 ],"rep" ,3 ))return kvm_events_report (&kvm, argc -1 , argv +1 ); +if (!strncmp (argv[1 ],"live" ,4 )) +return kvm_events_live (&kvm, argc -1 , argv +1 ); + perf_stat:return cmd_stat (argc, argv,NULL ); }
Signature · 1 line
1.7.10.1
PATCH 9/11 perf kvm: add min and max stats to display
In reply to David Ahern--- tools/perf/builtin-kvm.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-)
tools/perf/builtin-kvm.c +18 -3
@@ -337,14 +337,19 @@ static void clear_events_cache_stats(struct list_head *kvm_events_cache)struct list_head *head;struct kvm_event *event;unsigned int i; +int j;for (i =0 ; i <EVENTS_CACHE_SIZE ; i++) { head = &kvm_events_cache[i];list_for_each_entry (event, head, hash_entry) {/* reset stats for event */ -memset (&event->total,0 ,sizeof (event->total)); -memset (event->vcpu,0 , - event->max_vcpu *sizeof (*event->vcpu)); + event->total.time =0 ; +init_stats (&event->total.stats); + +for (j =0 ; j < event->max_vcpu; ++j) { + event->vcpu[j].time =0 ; +init_stats (&event->vcpu[j].stats); + } } } }
@@ -718,6 +723,7 @@ static void print_result(struct perf_kvm_stat *kvm)char decode[20 ];struct kvm_event *event;int vcpu = kvm->trace_vcpu; +struct kvm_event_stats *kvm_stats;if (kvm->live) {puts (CONSOLE_CLEAR );
@@ -731,6 +737,8 @@ static void print_result(struct perf_kvm_stat *kvm)pr_info ("%9s " ,"Samples%" );pr_info ("%9s " ,"Time%" ); +pr_info ("%10s " ,"Min Time" ); +pr_info ("%10s " ,"Max Time" );pr_info ("%16s " ,"Avg time" );pr_info ("\n\n" );
@@ -740,11 +748,18 @@ static void print_result(struct perf_kvm_stat *kvm) ecount =get_event_count (event, vcpu); etime =get_event_time (event, vcpu); +if (vcpu ==-1 ) + kvm_stats = &event->total; +else + kvm_stats = &event->vcpu[vcpu]; + kvm->events_ops->decode_key (kvm, &event->key, decode);pr_info ("%20s " , decode);pr_info ("%10llu " , (unsigned long long )ecount);pr_info ("%8.2f%% " , (double )ecount / kvm->total_count *100 );pr_info ("%8.2f%% " , (double )etime / kvm->total_time *100 ); +pr_info ("%8" PRIu64"us " , kvm_stats->stats.min /1000 ); +pr_info ("%8" PRIu64"us " , kvm_stats->stats.max /1000 );pr_info ("%9.2fus ( +-%7.2f%% )" , (double )etime / ecount/1e3 ,kvm_event_rel_stddev (vcpu, event));pr_info ("\n" );
Signature · 1 line
1.7.10.1
PATCH 10/11 perf kvm: option to print events that exceed a threshold
In reply to David AhernThis is useful to spot high latency blips.
--- tools/perf/builtin-kvm.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-)
tools/perf/builtin-kvm.c +20 -4
@@ -106,6 +106,7 @@ struct perf_kvm_stat {u64 total_time;u64 total_count;u64 lost_events; +u64 threshold;struct rb_root result;
@@ -470,7 +471,7 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id,static bool handle_end_event (struct perf_kvm_stat *kvm,struct vcpu_event_record *vcpu_record,struct event_key *key, -u64 timestamp) +struct perf_sample *sample) {struct kvm_event *event;u64 time_begin, time_diff;
@@ -507,12 +508,24 @@ static bool handle_end_event(struct perf_kvm_stat *kvm, vcpu_record->start_time =0 ;/* seems to happen once in a while during live mode */ -if (timestamp < time_begin) { +if (sample->time < time_begin) {pr_debug ("End time before begin time; skipping event.\n" );return true; } - time_diff = timestamp - time_begin; + time_diff = sample->time - time_begin; + +if (kvm->threshold && time_diff > kvm->threshold) { +char decode[32 ]; + + kvm->events_ops->decode_key (kvm, &event->key, decode); +if (strcmp (decode,"HLT" )) { +pr_info ("%" PRIu64" VM %d, vcpu %d: %s event took %" PRIu64"usec\n" , + sample->time, sample->pid, vcpu_record->vcpu_id, + decode, time_diff/1000 ); + } + } +return update_kvm_event (event, vcpu, time_diff); }
@@ -559,7 +572,7 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm,return handle_begin_event (kvm, vcpu_record, &key, sample->time);if (kvm->events_ops->is_end_event (evsel, sample, &key)) -return handle_end_event (kvm, vcpu_record, &key, sample->time); +return handle_end_event (kvm, vcpu_record, &key, sample);return true; }
@@ -1333,6 +1346,8 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,OPT_STRING ('k' ,"key" , &kvm->sort_key,"sort-key" ,"key for sorting: sample(sort by samples number)" " time (sort by avg time)" ), +OPT_U64 ('T' ,"threshold" , &kvm->threshold, +"show events that take longer than threshold usecs" ),OPT_END () }; const char * const live_usage[] = {
@@ -1363,6 +1378,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, use_browser =0 ;setup_browser (false); + kvm->threshold *=1000 ;/* convert usec to nsec */ if (argc) { argc = parse_options(argc, argv, live_options,
Signature · 1 line
1.7.10.1
PATCH 11/11 perf kvm: debug for missing vmexit/vmentry event
In reply to David AhernExpected to have missing events for each vcpu when perf is started. After that should not have missing events.
--- tools/perf/builtin-kvm.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
tools/perf/builtin-kvm.c +13 -2
@@ -568,11 +568,22 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm, (kvm->trace_vcpu != vcpu_record->vcpu_id))return true; -if (kvm->events_ops->is_begin_event (evsel, sample, &key)) +if (kvm->events_ops->is_begin_event (evsel, sample, &key)) { +if (vcpu_record->start_time) { +pr_debug ("consecutive begin events (%s) for pid %d, vcpu %d\n" , + evsel->name, sample->pid, vcpu_record->vcpu_id); + }return handle_begin_event (kvm, vcpu_record, &key, sample->time); + } + -if (kvm->events_ops->is_end_event (evsel, sample, &key)) +if (kvm->events_ops->is_end_event (evsel, sample, &key)) { +if (vcpu_record->start_time ==0 ) { +pr_debug ("consecutive end events (%s) for pid %d, vcpu %d\n" , + evsel->name, sample->pid, vcpu_record->vcpu_id); + }return handle_end_event (kvm, vcpu_record, &key, sample); + }return true; }
Signature · 1 line
1.7.10.1