Skip to contentSkip to conversation

perf kvm: Add live mode for analyzing events

PATCH In review David Ahern 3 participants 26 messages Oct 8, 2012 – Oct 17, 2012 perf events

lore
Share & tools
lore
#0 lore raw

cover perf kvm: Add live mode for analyzing events

perf 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 rather 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

Same stats options for the record+report path work with the live mode. Display rate defaults to 1 and can be changed using the -d option.

The command works fine for low event rates, but as the number of events increases (e.g., building a kernel in a 4-vcpu VM or nested virtualization) the ordered samples code is failing. e.g,

...
Total Samples:76309, Total events handled time:1121314.04us.

Warning: Timestamp below last timeslice flush
Failed to process event

This is on a 16-cpu server. For the kernel build in a 4 vcpu VM I am seeing on the order of 50,000+ events per second and in the case of nested virtualization (at least this one use case) I am seeing on the order of 100,000+ events per second. Not surprising it's having problems keeping up.

I'll continue poking around the ordered samples code, but wanted to kick this out in case someone has ideas or objections to the approach.

David Ahern (12):
perf evlist: restore some methods removed in an 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: remove typecast in init_kvm_event_record perf kvm: handle realloc failures
perf kvm: add braces around multi-line statements perf kvm: split out tracepoints from record args
perf kvm: total count is a u64, print as so
perf kvm: only process events for vcpus of interest perf kvm: add live mode

 tools/perf/builtin-kvm.c  |  717 ++++++++++++++++++++++++++++++++++++++++++---
 tools/perf/builtin-top.c  |    2 -
 tools/perf/util/evlist.c  |  120 ++++++++
 tools/perf/util/evlist.h  |   10 +
 tools/perf/util/header.c  |   39 ---
 tools/perf/util/session.c |   15 +-
 tools/perf/util/session.h |    9 +
 tools/perf/util/top.h     |    2 +
 8 files changed, 819 insertions(+), 95 deletions(-)
Signature · 1 line
1.7.10.1
#1 lore raw

PATCH 1/12 perf evlist: restore some methods removed in an earlier cleanup

e60fc847 removed the perf_evlist__add_tracepoints and helpers, but they are useful for kvm's upcoming live mode.

Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Peter Zijlstra <[email protected]>
---
 tools/perf/util/evlist.c |   54 ++++++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/evlist.h |    3 +++
 2 files changed, 57 insertions(+)
tools/perf/util/evlist.c +54 -0
index a41dc4a..a409b25 100644
@@ -189,6 +189,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
index 56003f7..be85147 100644
@@ -53,6 +53,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
#2 lore raw

PATCH 2/12 perf evlist: move tracepoint processing code to evlist.c

In reply to David Ahern

Code move only; no functional changes.

Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Peter Zijlstra <[email protected]>
---
 tools/perf/util/evlist.c  |   39 +++++++++++++++++++++++++++++++++++++++
 tools/perf/util/evlist.h  |    4 ++++
 tools/perf/util/header.c  |   39 ---------------------------------------
 tools/perf/util/session.c |    1 -
 4 files changed, 43 insertions(+), 40 deletions(-)
tools/perf/util/evlist.c +39 -0
index a409b25..7fff06f 100644
@@ -189,6 +189,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
index be85147..9959954 100644
@@ -5,6 +5,7 @@
 #include <stdio.h>
 #include "../perf.h"
 #include "event.h"
+#include "event-parse.h"
 #include "evsel.h"
 #include "util.h"
 #include <unistd.h>
@@ -53,6 +54,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 -39
index 7daad23..42f8ec9 100644
@@ -2553,45 +2553,6 @@ static int read_attr(int fd, struct perf_header *ph,
 	return ret <= 0 ? -1 : 0;
 }
 
-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;
-}
-
-static 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;
-}
-
 int perf_session__read_header(struct perf_session *session, int fd)
 {
 	struct perf_header *header = &session->header;
tools/perf/util/session.c +0 -1
index 15abe40..23d2ea3 100644
@@ -14,7 +14,6 @@
 #include "sort.h"
 #include "util.h"
 #include "cpumap.h"
-#include "event-parse.h"
 #include "perf_regs.h"
 #include "unwind.h"
 #include "vdso.h"
Signature · 1 line
1.7.10.1
#3 lore raw

PATCH 3/12 perf evlist: add initialzation function for tracepoints

In reply to David Ahern

Handles initializations typically done as part of processing the file header and HEADER_TRACING_DATA event.

Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Peter Zijlstra <[email protected]>
---
 tools/perf/util/evlist.c |   27 +++++++++++++++++++++++++++
 tools/perf/util/evlist.h |    3 +++
 2 files changed, 30 insertions(+)
tools/perf/util/evlist.c +27 -0
index 7fff06f..d77135b 100644
@@ -14,6 +14,7 @@
 #include "target.h"
 #include "evlist.h"
 #include "evsel.h"
+#include "trace-event.h"
 #include <unistd.h>
 
 #include "parse-events.h"
@@ -228,6 +229,32 @@ 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;
+
+	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)
+		return -1;
+
+	lseek(fd, 0, SEEK_SET);
+	(void) trace_report(fd, &session->pevent, false);
+	tracing_data_put(tdata);
+
+	return perf_evlist__prepare_tracepoint_events(evlist, session->pevent);
+}
+
+
 static int trace_event__id(const char *evname)
 {
 	char *filename, *colon;
tools/perf/util/evlist.h +3 -0
index 9959954..e375a18 100644
@@ -8,6 +8,7 @@
 #include "event-parse.h"
 #include "evsel.h"
 #include "util.h"
+#include "session.h"
 #include <unistd.h>
 
 struct pollfd;
@@ -60,6 +61,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
#4 lore raw

PATCH 4/12 perf session: export a few functions for event processing

In reply to David Ahern

Allows kvm live mode to reuse the event processing and ordered samples processing used by the perf-report path.

Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Peter Zijlstra <[email protected]>
---
 tools/perf/util/session.c |   14 +++++++-------
 tools/perf/util/session.h |    9 +++++++++
 2 files changed, 16 insertions(+), 7 deletions(-)
tools/perf/util/session.c +7 -7
index 23d2ea3..576ab8b 100644
@@ -456,7 +456,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;
@@ -702,8 +702,8 @@ static int perf_session_deliver_event(struct perf_session *session,
 				      struct perf_tool *tool,
 				      u64 file_offset);
 
-static int flush_sample_queue(struct perf_session *s,
-			       struct perf_tool *tool)
+int flush_sample_queue(struct perf_session *s,
+		       struct perf_tool *tool)
 {
 	struct ordered_samples *os = &s->ordered_samples;
 	struct list_head *head = &os->samples;
@@ -1150,10 +1150,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 +9 -0
index aab414f..3fb5049 100644
@@ -63,6 +63,15 @@ 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);
+
+int flush_sample_queue(struct perf_session *s, struct perf_tool *tool);
+
+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
#5 lore raw

PATCH 5/12 perf top: move CONSOLE_CLEAR to header file

In reply to David Ahern

For use with kvm-live mode.

Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Peter Zijlstra <[email protected]>
---
 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
index fb9da71..a053812 100644
@@ -263,8 +263,6 @@ out_unlock:
 	pthread_mutex_unlock(&notes->lock);
 }
 
-static const char		CONSOLE_CLEAR[] = "";
-
 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
index 86ff1b1..8630570 100644
@@ -47,6 +47,8 @@ struct perf_top {
 	const char	   *sym_filter;
 };
 
+#define CONSOLE_CLEAR ""
+
 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
#6 lore raw

PATCH 6/12 perf kvm: remove typecast in init_kvm_event_record

In reply to David Ahern

Not needed after changing i to unsigned int.

Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Xiao Guangrong <[email protected]>
Cc Dong Hao <[email protected]>
---
 tools/perf/builtin-kvm.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
tools/perf/builtin-kvm.c +2 -2
index 260abc5..a23db89 100644
@@ -313,9 +313,9 @@ struct vcpu_event_record {
 
 static void init_kvm_event_record(struct perf_kvm *kvm)
 {
-	int i;
+	unsigned int i;
 
-	for (i = 0; i < (int)EVENTS_CACHE_SIZE; i++)
+	for (i = 0; i < EVENTS_CACHE_SIZE; i++)
 		INIT_LIST_HEAD(&kvm->kvm_events_cache[i]);
 }
 
Signature · 1 line
1.7.10.1
#7 lore raw

[tip:perf/core] perf kvm: Remove typecast in init_kvm_event_record

Commit-ID:  b880deeaffd9df0cec9a4496447d0d7fd78086f2
Gitweb:     http://git.kernel.org/tip/b880deeaffd9df0cec9a4496447d0d7fd78086f2
Author:     David Ahern <[email protected]>
AuthorDate: Mon, 8 Oct 2012 11:17:30 -0600
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Mon, 8 Oct 2012 17:14:57 -0300

perf kvm: Remove typecast in init_kvm_event_record

Not needed after changing i to unsigned int.

---
 tools/perf/builtin-kvm.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
tools/perf/builtin-kvm.c +2 -2
index b98095e..8416754 100644
@@ -313,9 +313,9 @@ struct vcpu_event_record {
 
 static void init_kvm_event_record(struct perf_kvm *kvm)
 {
-	int i;
+	unsigned int i;
 
-	for (i = 0; i < (int)EVENTS_CACHE_SIZE; i++)
+	for (i = 0; i < EVENTS_CACHE_SIZE; i++)
 		INIT_LIST_HEAD(&kvm->kvm_events_cache[i]);
 }
 
#8 lore raw

PATCH 7/12 perf kvm: handle realloc failures

In reply to David Ahern

Save previous pointer and free on failure.

Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Xiao Guangrong <[email protected]>
Cc Dong Hao <[email protected]>
---
 tools/perf/builtin-kvm.c |    3 +++
 1 file changed, 3 insertions(+)
tools/perf/builtin-kvm.c +3 -0
index a23db89..af74a97 100644
@@ -327,6 +327,7 @@ static int kvm_events_hash_fn(u64 key)
 static bool kvm_event_expand(struct kvm_event *event, int vcpu_id)
 {
 	int old_max_vcpu = event->max_vcpu;
+	void *prev;
 
 	if (vcpu_id < event->max_vcpu)
 		return true;
@@ -334,9 +335,11 @@ static bool kvm_event_expand(struct kvm_event *event, int vcpu_id)
 	while (event->max_vcpu <= vcpu_id)
 		event->max_vcpu += DEFAULT_VCPU_NUM;
 
+	prev = event->vcpu;
 	event->vcpu = realloc(event->vcpu,
 			      event->max_vcpu * sizeof(*event->vcpu));
 	if (!event->vcpu) {
+		free(prev);
 		pr_err("Not enough memory\n");
 		return false;
 	}
Signature · 1 line
1.7.10.1
#9 lore raw

Re: [PATCH 07/12] perf kvm: handle realloc failures

Em Mon, Oct 08, 2012 at 11:17:31AM -0600, David Ahern escreveu:

Quoted 1 line

Save previous pointer and free on failure.

<SNIP>

tools/perf/builtin-kvm.c static bool kvm_event_expand(struct kvm_event *event, int vcpu_id) · line 343

Quoted 6 lines
tools/perf/builtin-kvm.c +2 -0
+	prev = event->vcpu;
 	event->vcpu = realloc(event->vcpu,
 			      event->max_vcpu * sizeof(*event->vcpu));
 	if (!event->vcpu) {
+		free(prev);
 		pr_err("Not enough memory\n");

Unfamiliar way of doing it, usually we do it like:

  	vcpu = realloc(event->vcpu, event->max_vcpu * sizeof(*event->vcpu));
	if (!vcpu) {
  		pr_err("Not enough memory\n");
		return -1;
	}

	event->vcpu = vcpu;

I.e. we don't trow away the old value of event->vcpu.

- Arnaldo

#10 lore raw

Re: [PATCH 07/12] perf kvm: handle realloc failures

Quoted 16 lines · Arnaldo Carvalho de Melo

Em Mon, Oct 08, 2012 at 11:17:31AM -0600, David Ahern escreveu:

Quoted 1 line

Save previous pointer and free on failure.

<SNIP>

tools/perf/builtin-kvm.c static bool kvm_event_expand(struct kvm_event *event, int vcpu_id) · line 343

Quoted 6 lines
tools/perf/builtin-kvm.c +2 -0
+	prev = event->vcpu;
  	event->vcpu = realloc(event->vcpu,
  			      event->max_vcpu * sizeof(*event->vcpu));
  	if (!event->vcpu) {
+		free(prev);
  		pr_err("Not enough memory\n");

Unfamiliar way of doing it, usually we do it like:

   	vcpu = realloc(event->vcpu, event->max_vcpu * sizeof(*event->vcpu));
Quoted 8 lines
	if (!vcpu) {
   		pr_err("Not enough memory\n");
		return -1;
	}

	event->vcpu = vcpu;

I.e. we don't trow away the old value of event->vcpu.

right. Command is going to exit on a failure, so leaving the old value does not have any impact. I can leave the old if you want it for consistency.

David

#11 lore raw

Re: [PATCH 07/12] perf kvm: handle realloc failures

Em Mon, Oct 08, 2012 at 02:34:55PM -0600, David Ahern escreveu:

Quoted 17 lines · Arnaldo Carvalho de Melo
Quoted 15 lines

tools/perf/builtin-kvm.c static bool kvm_event_expand(struct kvm_event *event, int vcpu_id) · line 343

Quoted 6 lines
tools/perf/builtin-kvm.c +2 -0
+	prev = event->vcpu;
 	event->vcpu = realloc(event->vcpu,
 			      event->max_vcpu * sizeof(*event->vcpu));
 	if (!event->vcpu) {
+		free(prev);
 		pr_err("Not enough memory\n");

Unfamiliar way of doing it, usually we do it like:

  	vcpu = realloc(event->vcpu, event->max_vcpu * sizeof(*event->vcpu));
	if (!vcpu) {
  		pr_err("Not enough memory\n");
		return -1;
	}
Quoted 7 lines
Quoted 3 lines
	event->vcpu = vcpu;

I.e. we don't trow away the old value of event->vcpu.

right. Command is going to exit on a failure, so leaving the old value does not have any impact. I can leave the old if you want it for consistency.

I think do it in the familiar way for consistency.

- Arnaldo

#12 lore raw

PATCH 8/12 perf kvm: add braces around multi-line statements

In reply to David Ahern

Multi-line statements should have braces. Improves readability.

Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Xiao Guangrong <[email protected]>
Cc Dong Hao <[email protected]>
---
 tools/perf/builtin-kvm.c |    9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
tools/perf/builtin-kvm.c +6 -3
index af74a97..43fd71c 100644
@@ -372,9 +372,10 @@ static struct kvm_event *find_create_kvm_event(struct perf_kvm *kvm,
 	BUG_ON(key->key == INVALID_KEY);
 
 	head = &kvm->kvm_events_cache[kvm_events_hash_fn(key->key)];
-	list_for_each_entry(event, head, hash_entry)
+	list_for_each_entry(event, head, hash_entry) {
 		if (event->key.key == key->key && event->key.info == key->info)
 			return event;
+	}
 
 	event = kvm_alloc_init_event(key);
 	if (!event)
@@ -599,13 +600,15 @@ static void sort_result(struct perf_kvm *kvm)
 	int vcpu = kvm->trace_vcpu;
 	struct kvm_event *event;
 
-	for (i = 0; i < EVENTS_CACHE_SIZE; i++)
-		list_for_each_entry(event, &kvm->kvm_events_cache[i], hash_entry)
+	for (i = 0; i < EVENTS_CACHE_SIZE; i++) {
+		list_for_each_entry(event, &kvm->kvm_events_cache[i], hash_entry) {
 			if (event_is_valid(event, vcpu)) {
 				update_total_count(kvm, event);
 				insert_to_result(&kvm->result, event,
 						 kvm->compare, vcpu);
 			}
+		}
+	}
 }
 
 /* returns left most element of result, and erase it */
Signature · 1 line
1.7.10.1
#13 lore raw

[tip:perf/core] perf kvm: Add braces around multi-line statements

Commit-ID:  355afe816312faf20d81fdcade29e0361d72a7b4
Gitweb:     http://git.kernel.org/tip/355afe816312faf20d81fdcade29e0361d72a7b4
Author:     David Ahern <[email protected]>
AuthorDate: Mon, 8 Oct 2012 11:17:32 -0600
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Mon, 8 Oct 2012 17:15:54 -0300

perf kvm: Add braces around multi-line statements

Multi-line statements should have braces. Improves readability.

---
 tools/perf/builtin-kvm.c |    9 ++++++---
 1 files changed, 6 insertions(+), 3 deletions(-)
tools/perf/builtin-kvm.c +6 -3
index 72a302e..836c82f 100644
@@ -369,9 +369,10 @@ static struct kvm_event *find_create_kvm_event(struct perf_kvm *kvm,
 	BUG_ON(key->key == INVALID_KEY);
 
 	head = &kvm->kvm_events_cache[kvm_events_hash_fn(key->key)];
-	list_for_each_entry(event, head, hash_entry)
+	list_for_each_entry(event, head, hash_entry) {
 		if (event->key.key == key->key && event->key.info == key->info)
 			return event;
+	}
 
 	event = kvm_alloc_init_event(key);
 	if (!event)
@@ -610,13 +611,15 @@ static void sort_result(struct perf_kvm *kvm)
 	int vcpu = kvm->trace_vcpu;
 	struct kvm_event *event;
 
-	for (i = 0; i < EVENTS_CACHE_SIZE; i++)
-		list_for_each_entry(event, &kvm->kvm_events_cache[i], hash_entry)
+	for (i = 0; i < EVENTS_CACHE_SIZE; i++) {
+		list_for_each_entry(event, &kvm->kvm_events_cache[i], hash_entry) {
 			if (event_is_valid(event, vcpu)) {
 				update_total_count(kvm, event);
 				insert_to_result(&kvm->result, event,
 						 kvm->compare, vcpu);
 			}
+		}
+	}
 }
 
 /* returns left most element of result, and erase it */
#14 lore raw

PATCH 9/12 perf kvm: split out tracepoints from record args

In reply to David Ahern

Needed by kvm live command. Make record_args a local while we are messing with the args.

Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Xiao Guangrong <[email protected]>
Cc Dong Hao <[email protected]>
---
 tools/perf/builtin-kvm.c |   30 +++++++++++++++++++-----------
 1 file changed, 19 insertions(+), 11 deletions(-)
tools/perf/builtin-kvm.c +19 -11
index 43fd71c..f6091d2 100644
@@ -784,16 +784,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)		\
@@ -808,8 +803,16 @@ static int kvm_events_record(struct perf_kvm *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 +
+		   ARRAY_SIZE(kvm_events_tp);
 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
 
 	if (rec_argv == NULL)
@@ -818,6 +821,11 @@ static int kvm_events_record(struct perf_kvm *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
#15 lore raw

Re: [PATCH 09/12] perf kvm: split out tracepoints from record args

tools/perf/builtin-kvm.c static int kvm_events_record(struct perf_kvm *kvm, int argc, const char **argv) · line 815

Quoted 13 lines · David Ahern
tools/perf/builtin-kvm.c +9 -1
 	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 +
+		   ARRAY_SIZE(kvm_events_tp);

Only calculate the size of tracepoints...

tools/perf/builtin-kvm.c static int kvm_events_record(struct perf_kvm *kvm, int argc, const char **argv) · line 825

Quoted 9 lines
tools/perf/builtin-kvm.c +2 -0
 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
 
 	if (rec_argv == NULL)
@@ -818,6 +821,11 @@ static int kvm_events_record(struct perf_kvm *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";

Then you add "-e" into the array, however, it is calculated into rec_argc? Or I missed something.

Other patches look good to me, thanks for your work and look forward to the new version. :)

#16 lore raw

Re: [PATCH 09/12] perf kvm: split out tracepoints from record args

Quoted 3 lines · Xiao Guangrong

tools/perf/builtin-kvm.c static int kvm_events_record(struct perf_kvm *kvm, int argc, const char **argv) · line 815

Quoted 3 lines
tools/perf/builtin-kvm.c +2 -1
-	rec_argc = ARRAY_SIZE(record_args) + argc + 2;
+	rec_argc = ARRAY_SIZE(record_args) + argc + 2 +
+		   ARRAY_SIZE(kvm_events_tp);

Lost the 2 in re-doing the set formally for distribution. I'll send a patch. Thanks for reviewing it.

David

#17 lore raw

PATCH 10/12 perf kvm: total count is a u64, print as so

In reply to David Ahern

remove cast and use proper type in format.

Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Xiao Guangrong <[email protected]>
Cc Dong Hao <[email protected]>
---
 tools/perf/builtin-kvm.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
tools/perf/builtin-kvm.c +2 -2
index f6091d2..6412694 100644
@@ -665,8 +665,8 @@ static void print_result(struct perf_kvm *kvm)
 		pr_info("\n");
 	}
 
-	pr_info("\nTotal Samples:%lld, Total events handled time:%.2fus.\n\n",
-		(unsigned long long)kvm->total_count, kvm->total_time / 1e3);
+	pr_info("\nTotal Samples:%" PRIu64 ", Total events handled time:%.2fus.\n\n",
+		kvm->total_count, kvm->total_time / 1e3);
 }
 
 static int process_sample_event(struct perf_tool *tool,
Signature · 1 line
1.7.10.1
#18 lore raw

[tip:perf/core] perf kvm: Total count is a u64, print as so

Commit-ID:  e4f7637f8aefa2ad5159a1cda46fedea6eaf64fc
Gitweb:     http://git.kernel.org/tip/e4f7637f8aefa2ad5159a1cda46fedea6eaf64fc
Author:     David Ahern <[email protected]>
AuthorDate: Mon, 8 Oct 2012 11:17:34 -0600
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Mon, 8 Oct 2012 17:15:25 -0300

perf kvm: Total count is a u64, print as so

remove cast and use proper type in format.

---
 tools/perf/builtin-kvm.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
tools/perf/builtin-kvm.c +2 -2
index 8416754..72a302e 100644
@@ -673,8 +673,8 @@ static void print_result(struct perf_kvm *kvm)
 		pr_info("\n");
 	}
 
-	pr_info("\nTotal Samples:%lld, Total events handled time:%.2fus.\n\n",
-		(unsigned long long)kvm->total_count, kvm->total_time / 1e3);
+	pr_info("\nTotal Samples:%" PRIu64 ", Total events handled time:%.2fus.\n\n",
+		kvm->total_count, kvm->total_time / 1e3);
 }
 
 static int process_sample_event(struct perf_tool *tool,
#19 lore raw

PATCH 11/12 perf kvm: only process events for vcpus of interest

In reply to David Ahern

Minimizing processing overhead for each sample - which becomes important for the upcoming live mode when it has to deal with 100+k events per second.

Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Xiao Guangrong <[email protected]>
Cc Dong Hao <[email protected]>
---
 tools/perf/builtin-kvm.c |   18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)
tools/perf/builtin-kvm.c +16 -2
index 6412694..dcbbcc7 100644
@@ -420,7 +420,10 @@ static double kvm_event_rel_stddev(int vcpu_id, struct kvm_event *event)
 static bool update_kvm_event(struct kvm_event *event, int vcpu_id,
 			     u64 time_diff)
 {
-	kvm_update_event_stats(&event->total, time_diff);
+	if (vcpu_id == -1) {
+		kvm_update_event_stats(&event->total, time_diff);
+		return true;
+	}
 
 	if (!kvm_event_expand(event, vcpu_id))
 		return false;
@@ -436,6 +439,12 @@ static bool handle_end_event(struct perf_kvm *kvm,
 {
 	struct kvm_event *event;
 	u64 time_begin, time_diff;
+	int vcpu;
+
+	if (kvm->trace_vcpu == -1)
+		vcpu = -1;
+	else
+		vcpu = vcpu_record->vcpu_id;
 
 	event = vcpu_record->last_event;
 	time_begin = vcpu_record->start_time;
@@ -465,7 +474,7 @@ static bool handle_end_event(struct perf_kvm *kvm,
 	BUG_ON(timestamp < time_begin);
 
 	time_diff = timestamp - time_begin;
-	return update_kvm_event(event, vcpu_record->vcpu_id, time_diff);
+	return update_kvm_event(event, vcpu, time_diff);
 }
 
 static
@@ -502,6 +511,11 @@ static bool handle_kvm_event(struct perf_kvm *kvm,
 	if (!vcpu_record)
 		return true;
 
+	/* only process events for vcpus user cares about */
+	if ((kvm->trace_vcpu != -1) &&
+	    (kvm->trace_vcpu != vcpu_record->vcpu_id))
+		return true;
+
 	if (kvm->events_ops->is_begin_event(evsel, sample, &key))
 		return handle_begin_event(kvm, vcpu_record, &key, sample->time);
 
Signature · 1 line
1.7.10.1
#20 lore raw

[tip:perf/core] perf kvm: Only process events for vcpus of interest

Commit-ID:  2aa8eab029b319b2b458b3ba393bbba31961ea96
Gitweb:     http://git.kernel.org/tip/2aa8eab029b319b2b458b3ba393bbba31961ea96
Author:     David Ahern <[email protected]>
AuthorDate: Mon, 8 Oct 2012 11:17:35 -0600
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Mon, 8 Oct 2012 17:14:13 -0300

perf kvm: Only process events for vcpus of interest

Minimizing processing overhead for each sample - which becomes important for the upcoming live mode when it has to deal with 100+k events per second.

---
 tools/perf/builtin-kvm.c |   18 ++++++++++++++++--
 1 files changed, 16 insertions(+), 2 deletions(-)
tools/perf/builtin-kvm.c +16 -2
index 260abc5..b98095e 100644
@@ -416,7 +416,10 @@ static double kvm_event_rel_stddev(int vcpu_id, struct kvm_event *event)
 static bool update_kvm_event(struct kvm_event *event, int vcpu_id,
 			     u64 time_diff)
 {
-	kvm_update_event_stats(&event->total, time_diff);
+	if (vcpu_id == -1) {
+		kvm_update_event_stats(&event->total, time_diff);
+		return true;
+	}
 
 	if (!kvm_event_expand(event, vcpu_id))
 		return false;
@@ -432,6 +435,12 @@ static bool handle_end_event(struct perf_kvm *kvm,
 {
 	struct kvm_event *event;
 	u64 time_begin, time_diff;
+	int vcpu;
+
+	if (kvm->trace_vcpu == -1)
+		vcpu = -1;
+	else
+		vcpu = vcpu_record->vcpu_id;
 
 	event = vcpu_record->last_event;
 	time_begin = vcpu_record->start_time;
@@ -461,7 +470,7 @@ static bool handle_end_event(struct perf_kvm *kvm,
 	BUG_ON(timestamp < time_begin);
 
 	time_diff = timestamp - time_begin;
-	return update_kvm_event(event, vcpu_record->vcpu_id, time_diff);
+	return update_kvm_event(event, vcpu, time_diff);
 }
 
 static
@@ -498,6 +507,11 @@ static bool handle_kvm_event(struct perf_kvm *kvm,
 	if (!vcpu_record)
 		return true;
 
+	/* only process events for vcpus user cares about */
+	if ((kvm->trace_vcpu != -1) &&
+	    (kvm->trace_vcpu != vcpu_record->vcpu_id))
+		return true;
+
 	if (kvm->events_ops->is_begin_event(evsel, sample, &key))
 		return handle_begin_event(kvm, vcpu_record, &key, sample->time);
 
#21 lore raw

PATCH 12/12 perf kvm: add live mode

In reply to David Ahern

perf 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 rather 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

Same stats options for the record+report path work with the live mode. Display rate defaults to 1 and can be changed using the -d option.

Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Xiao Guangrong <[email protected]>
Cc Dong Hao <[email protected]>
---
 tools/perf/builtin-kvm.c |  649 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 623 insertions(+), 26 deletions(-)
tools/perf/builtin-kvm.c +623 -26
index dcbbcc7..9dd1921 100644
@@ -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 "util/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>
@@ -81,6 +85,8 @@ struct exit_reasons_table {
 
 struct perf_kvm {
 	struct perf_tool    tool;
+	struct perf_record_opts opts;
+	struct perf_evlist  *evlist;
 	struct perf_session *session;
 
 	const char *file_name;
@@ -95,10 +101,16 @@ struct perf_kvm {
 	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;
 };
 
 
@@ -319,6 +331,23 @@ static void init_kvm_event_record(struct perf_kvm *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);
@@ -471,7 +500,11 @@ static bool handle_end_event(struct perf_kvm *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);
@@ -637,24 +670,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 *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, &ltime)) {
+		strftime(date, sizeof(date), "%H:%M:%S", &ltime);
+		pr_info("%s.%06ld", date, tv.tv_usec);
+	} else
+		pr_info("00:00:00.000000");
+
+	return;
+}
+
 static void print_result(struct perf_kvm *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%");
@@ -681,6 +750,20 @@ static void print_result(struct perf_kvm *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 *kvm = container_of(tool, struct perf_kvm, tool);
+
+	kvm->lost_events++;
+	return 0;
 }
 
 static int process_sample_event(struct perf_tool *tool,
@@ -704,10 +787,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 *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;
@@ -715,10 +808,374 @@ 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)
+{
+	if (vcpu != -1 && vcpu < 0) {
+		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 *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 *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)
+{
+	if (sig == SIGINT)
+		done = 1;
+}
+
+static int perf_kvm__timerfd_create(struct perf_kvm *kvm)
+{
+	struct itimerspec new_value;
+	struct timespec now;
+	int rc = -1;
+
+	kvm->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
+	if (kvm->timerfd < 0) {
+		pr_err("timerfd_create failed\n");
+		goto out;
+	}
+
+	if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
+		pr_err("clock_gettime failed: %d\n", errno);
+		close(kvm->timerfd);
+		goto out;
+	}
+
+	new_value.it_value.tv_sec = now.tv_sec + kvm->display_time;
+	new_value.it_value.tv_nsec = now.tv_nsec;
+	new_value.it_interval.tv_sec = kvm->display_time;
+	new_value.it_interval.tv_nsec = 0;
+
+	if (timerfd_settime(kvm->timerfd, TFD_TIMER_ABSTIME,
+			    &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 *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 isa;
+	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;
+
+	/* TO-DO: add hook for 'd' to change display rate
+	 *        needs an update to timerfd too
+	 */
+
+	return 0;
+}
+
+static int kvm_events_live_report(struct perf_kvm *kvm)
+{
+	struct pollfd *pollfds = NULL;
+	int nr_fds, 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);
+
+
+	/* 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_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;
+
+		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);
+	}
+
+out:
+	if (kvm->timerfd >= 0)
+		close(kvm->timerfd);
+
+	if (pollfds)
+		free(pollfds);
+
+	return err;
+}
+
+
+/* TO-DO: how to merge this with perf-stat */
+static int perf_kvm__open_counters(struct perf_kvm *kvm)
+{
+	struct perf_evsel *pos;
+	struct perf_evlist *evlist = kvm->evlist;
+	int err = 0;
+
+	/*
+	 * Note: exclude_{guest,host} do not apply here.
+	 *       This command processes KVM tracepoints form host only
+	 */
+	list_for_each_entry(pos, &evlist->entries, node) {
+		struct perf_event_attr *attr = &pos->attr;
+
+		attr->sample_type = 0;
+		attr->read_format = 0;
+
+		attr->sample_type |= PERF_SAMPLE_TID;
+		attr->sample_type |= PERF_SAMPLE_TIME;
+		attr->sample_type |= PERF_SAMPLE_CPU;
+		attr->sample_type |= PERF_SAMPLE_RAW;
+
+		if (evlist->nr_entries > 1) {
+			attr->sample_type |= PERF_SAMPLE_ID;
+			attr->read_format |= PERF_FORMAT_ID;
+		}
+
+		attr->sample_id_all = kvm->opts.sample_id_all_missing ? 0 : 1;
+		attr->sample_period = 1;
+		attr->comm = !pos->idx;
+
+		attr->watermark = 0;
+		attr->wakeup_events = 100;
+
+		/* will enable all once we are ready */
+		attr->disabled = 1;
+
+retry_sample_id:
+		attr->sample_id_all = kvm->opts.sample_id_all_missing ? 0 : 1;
+		if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) {
+			err = errno;
+
+			if (err == EPERM || err == EACCES) {
+				ui__error_paranoid();
+				goto out_err;
+			} else if ((err == EINVAL) &&
+				   !kvm->opts.sample_id_all_missing) {
+				/*
+				 * Old kernel, no attr->sample_id_type_all field
+				 */
+				kvm->opts.sample_id_all_missing = true;
+				goto retry_sample_id;
+			}
+
+			/*
+			 * should never happen since the event is validated
+			 * before adding to evlist.
+			 */
+			if (err == ENOENT) {
+				ui__error("The %s event is not supported.\n",
+					  perf_evsel__name(pos));
+				goto out_err;
+			} else if (err == EMFILE) {
+				ui__error("Too many events need to be opened.\n");
+				goto out_err;
+			}
+
+			ui__error("The sys_perf_event_open() syscall "
+				  "returned with %d (%s).  /bin/dmesg "
+				  "may provide additional information.\n",
+				  err, strerror(err));
+			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;
+	}
+
+out_err:
+	return err;
 }
 
 static int read_events(struct perf_kvm *kvm)
@@ -746,30 +1203,13 @@ static int read_events(struct perf_kvm *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 *kvm)
 {
 	int ret = -EINVAL;
@@ -881,6 +1321,159 @@ static int kvm_events_report(struct perf_kvm *kvm, int argc, const char **argv)
 	return kvm_events_report_vcpu(kvm);
 }
 
+static int kvm_events_live(struct perf_kvm *kvm, int argc, const char **argv)
+{
+	struct perf_session *session = NULL;
+	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->evlist = perf_evlist__new(NULL, NULL);
+	if (kvm->evlist == NULL)
+		return -ENOMEM;
+
+	symbol__init();
+	disable_buildid_cache();
+
+	if (argc) {
+		argc = parse_options(argc, argv, live_options,
+				     live_usage, 0);
+		if (argc)
+			usage_with_options(live_usage, live_options);
+	}
+
+	use_browser = 0;
+	setup_browser(false);
+
+	/*
+	 * target related setups
+	 */
+	if (perf_target__none(&kvm->opts.target))
+		kvm->opts.target.system_wide = true;
+
+	err = perf_target__validate(&kvm->opts.target);
+	if (err) {
+		perf_target__strerror(&kvm->opts.target, err, errbuf, BUFSIZ);
+		ui__warning("%s", errbuf);
+	}
+
+	err = perf_target__parse_uid(&kvm->opts.target);
+	if (err) {
+		int saved_errno = errno;
+
+		perf_target__strerror(&kvm->opts.target, err,
+				      errbuf, BUFSIZ);
+		ui__error("%s", errbuf);
+
+		err = -saved_errno;
+		goto out;
+	}
+
+	/*
+	 * generate the event list
+	 */
+	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
+	 */
+	session = perf_session__new(NULL, O_WRONLY, false, false, &kvm->tool);
+	if (session == NULL) {
+		err = -ENOMEM;
+		goto out;
+	}
+	session->evlist = kvm->evlist;
+	kvm->session = session;
+	perf_session__set_id_hdr_size(session);
+
+
+	/*
+	 * need to get pevent initialized
+	 */
+	err = perf_evlist__trace_init(kvm->evlist, 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,
+						  &session->host_machine);
+	else
+		perf_event__synthesize_threads(&kvm->tool, perf_event__process,
+					       &session->host_machine);
+
+
+	err = perf_kvm__open_counters(kvm);
+	if (err != 0)
+		goto out;
+
+	err = kvm_events_live_report(kvm);
+
+out:
+	exit_browser(0);
+
+	if (session)
+		perf_session__delete(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");
@@ -888,6 +1481,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");
 }
@@ -905,6 +1499,9 @@ static int kvm_cmd_stat(struct perf_kvm *kvm, 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
#22 lore raw

Re: [PATCH 12/12] perf kvm: add live mode

Em Mon, Oct 08, 2012 at 11:17:36AM -0600, David Ahern escreveu:

Quoted 6 lines

perf 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

Quoted 8 lines

This is rather 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

Same stats options for the record+report path work with the live mode. Display rate defaults to 1 and can be changed using the -d option.

Overall it looks good, I like the fact you're not doing it the way perf script does, i.e. that "pipe mode" thing, more or less like I'm doing on 'perf trace', but I think this is the time to get rid of 'perf_session' too for this specific "live mode" tools.

I'm almost getting there for 'trace' and 'top', just need to move the ordered_samples code to evlist, then session, top, trace, kvm live and script will not need to use 'perf_session' at all.

Also that 'perf_kvm__open_counters' really is just a third copy of code in 'record' and 'top', right? You could move it to 'perf_ev{list,open}__open' and solve this dup :-)

I applied the cleanups/simple stuff, will try to do some of the prep work on ordered_samples to use on top and trace and then I think you could look there so that we get to something that is useful for all these !perf.data tools.

- Arnaldo

Quoted 796 lines
Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Xiao Guangrong <[email protected]>
Cc Dong Hao <[email protected]>
---
 tools/perf/builtin-kvm.c |  649 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 623 insertions(+), 26 deletions(-)
tools/perf/builtin-kvm.c +623 -26
index dcbbcc7..9dd1921 100644
@@ -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 "util/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>
@@ -81,6 +85,8 @@ struct exit_reasons_table {
 
 struct perf_kvm {
 	struct perf_tool    tool;
+	struct perf_record_opts opts;
+	struct perf_evlist  *evlist;
 	struct perf_session *session;
 
 	const char *file_name;
@@ -95,10 +101,16 @@ struct perf_kvm {
 	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;
 };
 
 
@@ -319,6 +331,23 @@ static void init_kvm_event_record(struct perf_kvm *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);
@@ -471,7 +500,11 @@ static bool handle_end_event(struct perf_kvm *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);
@@ -637,24 +670,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 *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, &ltime)) {
+		strftime(date, sizeof(date), "%H:%M:%S", &ltime);
+		pr_info("%s.%06ld", date, tv.tv_usec);
+	} else
+		pr_info("00:00:00.000000");
+
+	return;
+}
+
 static void print_result(struct perf_kvm *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%");
@@ -681,6 +750,20 @@ static void print_result(struct perf_kvm *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 *kvm = container_of(tool, struct perf_kvm, tool);
+
+	kvm->lost_events++;
+	return 0;
 }
 
 static int process_sample_event(struct perf_tool *tool,
@@ -704,10 +787,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 *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;
@@ -715,10 +808,374 @@ 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)
+{
+	if (vcpu != -1 && vcpu < 0) {
+		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 *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 *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)
+{
+	if (sig == SIGINT)
+		done = 1;
+}
+
+static int perf_kvm__timerfd_create(struct perf_kvm *kvm)
+{
+	struct itimerspec new_value;
+	struct timespec now;
+	int rc = -1;
+
+	kvm->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
+	if (kvm->timerfd < 0) {
+		pr_err("timerfd_create failed\n");
+		goto out;
+	}
+
+	if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
+		pr_err("clock_gettime failed: %d\n", errno);
+		close(kvm->timerfd);
+		goto out;
+	}
+
+	new_value.it_value.tv_sec = now.tv_sec + kvm->display_time;
+	new_value.it_value.tv_nsec = now.tv_nsec;
+	new_value.it_interval.tv_sec = kvm->display_time;
+	new_value.it_interval.tv_nsec = 0;
+
+	if (timerfd_settime(kvm->timerfd, TFD_TIMER_ABSTIME,
+			    &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 *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 isa;
+	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;
+
+	/* TO-DO: add hook for 'd' to change display rate
+	 *        needs an update to timerfd too
+	 */
+
+	return 0;
+}
+
+static int kvm_events_live_report(struct perf_kvm *kvm)
+{
+	struct pollfd *pollfds = NULL;
+	int nr_fds, 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);
+
+
+	/* 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_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;
+
+		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);
+	}
+
+out:
+	if (kvm->timerfd >= 0)
+		close(kvm->timerfd);
+
+	if (pollfds)
+		free(pollfds);
+
+	return err;
+}
+
+
+/* TO-DO: how to merge this with perf-stat */
+static int perf_kvm__open_counters(struct perf_kvm *kvm)
+{
+	struct perf_evsel *pos;
+	struct perf_evlist *evlist = kvm->evlist;
+	int err = 0;
+
+	/*
+	 * Note: exclude_{guest,host} do not apply here.
+	 *       This command processes KVM tracepoints form host only
+	 */
+	list_for_each_entry(pos, &evlist->entries, node) {
+		struct perf_event_attr *attr = &pos->attr;
+
+		attr->sample_type = 0;
+		attr->read_format = 0;
+
+		attr->sample_type |= PERF_SAMPLE_TID;
+		attr->sample_type |= PERF_SAMPLE_TIME;
+		attr->sample_type |= PERF_SAMPLE_CPU;
+		attr->sample_type |= PERF_SAMPLE_RAW;
+
+		if (evlist->nr_entries > 1) {
+			attr->sample_type |= PERF_SAMPLE_ID;
+			attr->read_format |= PERF_FORMAT_ID;
+		}
+
+		attr->sample_id_all = kvm->opts.sample_id_all_missing ? 0 : 1;
+		attr->sample_period = 1;
+		attr->comm = !pos->idx;
+
+		attr->watermark = 0;
+		attr->wakeup_events = 100;
+
+		/* will enable all once we are ready */
+		attr->disabled = 1;
+
+retry_sample_id:
+		attr->sample_id_all = kvm->opts.sample_id_all_missing ? 0 : 1;
+		if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) {
+			err = errno;
+
+			if (err == EPERM || err == EACCES) {
+				ui__error_paranoid();
+				goto out_err;
+			} else if ((err == EINVAL) &&
+				   !kvm->opts.sample_id_all_missing) {
+				/*
+				 * Old kernel, no attr->sample_id_type_all field
+				 */
+				kvm->opts.sample_id_all_missing = true;
+				goto retry_sample_id;
+			}
+
+			/*
+			 * should never happen since the event is validated
+			 * before adding to evlist.
+			 */
+			if (err == ENOENT) {
+				ui__error("The %s event is not supported.\n",
+					  perf_evsel__name(pos));
+				goto out_err;
+			} else if (err == EMFILE) {
+				ui__error("Too many events need to be opened.\n");
+				goto out_err;
+			}
+
+			ui__error("The sys_perf_event_open() syscall "
+				  "returned with %d (%s).  /bin/dmesg "
+				  "may provide additional information.\n",
+				  err, strerror(err));
+			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;
+	}
+
+out_err:
+	return err;
 }
 
 static int read_events(struct perf_kvm *kvm)
@@ -746,30 +1203,13 @@ static int read_events(struct perf_kvm *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 *kvm)
 {
 	int ret = -EINVAL;
@@ -881,6 +1321,159 @@ static int kvm_events_report(struct perf_kvm *kvm, int argc, const char **argv)
 	return kvm_events_report_vcpu(kvm);
 }
 
+static int kvm_events_live(struct perf_kvm *kvm, int argc, const char **argv)
+{
+	struct perf_session *session = NULL;
+	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->evlist = perf_evlist__new(NULL, NULL);
+	if (kvm->evlist == NULL)
+		return -ENOMEM;
+
+	symbol__init();
+	disable_buildid_cache();
+
+	if (argc) {
+		argc = parse_options(argc, argv, live_options,
+				     live_usage, 0);
+		if (argc)
+			usage_with_options(live_usage, live_options);
+	}
+
+	use_browser = 0;
+	setup_browser(false);
+
+	/*
+	 * target related setups
+	 */
+	if (perf_target__none(&kvm->opts.target))
+		kvm->opts.target.system_wide = true;
+
+	err = perf_target__validate(&kvm->opts.target);
+	if (err) {
+		perf_target__strerror(&kvm->opts.target, err, errbuf, BUFSIZ);
+		ui__warning("%s", errbuf);
+	}
+
+	err = perf_target__parse_uid(&kvm->opts.target);
+	if (err) {
+		int saved_errno = errno;
+
+		perf_target__strerror(&kvm->opts.target, err,
+				      errbuf, BUFSIZ);
+		ui__error("%s", errbuf);
+
+		err = -saved_errno;
+		goto out;
+	}
+
+	/*
+	 * generate the event list
+	 */
+	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
+	 */
+	session = perf_session__new(NULL, O_WRONLY, false, false, &kvm->tool);
+	if (session == NULL) {
+		err = -ENOMEM;
+		goto out;
+	}
+	session->evlist = kvm->evlist;
+	kvm->session = session;
+	perf_session__set_id_hdr_size(session);
+
+
+	/*
+	 * need to get pevent initialized
+	 */
+	err = perf_evlist__trace_init(kvm->evlist, 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,
+						  &session->host_machine);
+	else
+		perf_event__synthesize_threads(&kvm->tool, perf_event__process,
+					       &session->host_machine);
+
+
+	err = perf_kvm__open_counters(kvm);
+	if (err != 0)
+		goto out;
+
+	err = kvm_events_live_report(kvm);
+
+out:
+	exit_browser(0);
+
+	if (session)
+		perf_session__delete(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");
@@ -888,6 +1481,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");
 }
@@ -905,6 +1499,9 @@ static int kvm_cmd_stat(struct perf_kvm *kvm, 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
#23 lore raw

Re: [PATCH 12/12] perf kvm: add live mode

Quoted 5 lines · Arnaldo Carvalho de Melo

Em Mon, Oct 08, 2012 at 11:17:36AM -0600, David Ahern escreveu:
Overall it looks good, I like the fact you're not doing it the way perf script does, i.e. that "pipe mode" thing, more or less like I'm doing on 'perf trace', but I think this is the time to get rid of 'perf_session' too for this specific "live mode" tools.

pipe mode would never work for this case. I'm looking at nested virt case now and it shows 360,000 events per second.

Quoted 3 lines

I'm almost getting there for 'trace' and 'top', just need to move the ordered_samples code to evlist, then session, top, trace, kvm live and script will not need to use 'perf_session' at all.

ok. you going to have that soon?

Quoted 3 lines

Also that 'perf_kvm__open_counters' really is just a third copy of code in 'record' and 'top', right? You could move it to 'perf_ev{list,open}__open' and solve this dup :-)

there's a TO-DO at the top of that function for that reason ;-) I copied a fair bit from perf-stat. Now that it's working I can look at code consolidation.

Quoted 4 lines

I applied the cleanups/simple stuff, will try to do some of the prep work on ordered_samples to use on top and trace and then I think you could look there so that we get to something that is useful for all these !perf.data tools.

On my Westmere based server I am not seeing problems with timestamps and ordering (prior testing was on a Nehalem based server). It just weathered a 5 minute or so barrage of > 100,000 events per second (peaking at around 360k events/sec) with no problem.

David

#24 lore raw

Re: [PATCH 12/12] perf kvm: add live mode

Em Mon, Oct 08, 2012 at 02:54:10PM -0600, David Ahern escreveu:

Quoted 9 lines · Arnaldo Carvalho de Melo
Quoted 5 lines

Em Mon, Oct 08, 2012 at 11:17:36AM -0600, David Ahern escreveu:
Overall it looks good, I like the fact you're not doing it the way perf script does, i.e. that "pipe mode" thing, more or less like I'm doing on 'perf trace', but I think this is the time to get rid of 'perf_session' too for this specific "live mode" tools.

pipe mode would never work for this case. I'm looking at nested virt case now and it shows 360,000 events per second.

Right, 'pipe mode' is an interesting construct, but its too inneficient for same machine record/report, and for networking one we need to have splice/sendfile, i.e. feed a socket directly in the kernel with what we stash in mmap.

Quoted 5 lines
Quoted 3 lines

I'm almost getting there for 'trace' and 'top', just need to move the ordered_samples code to evlist, then session, top, trace, kvm live and script will not need to use 'perf_session' at all.

ok. you going to have that soon?

I hope to have it by the end of this week, perhaps earlier.

Quoted 7 lines
Quoted 3 lines

Also that 'perf_kvm__open_counters' really is just a third copy of code in 'record' and 'top', right? You could move it to 'perf_ev{list,open}__open' and solve this dup :-)

there's a TO-DO at the top of that function for that reason ;-) I copied a fair bit from perf-stat. Now that it's working I can look at code consolidation.

Ok, just scratching again an old itch :-)

Quoted 9 lines
Quoted 4 lines

I applied the cleanups/simple stuff, will try to do some of the prep work on ordered_samples to use on top and trace and then I think you could look there so that we get to something that is useful for all these !perf.data tools.

On my Westmere based server I am not seeing problems with timestamps and ordering (prior testing was on a Nehalem based server). It just weathered a 5 minute or so barrage of > 100,000 events per second (peaking at around 360k events/sec) with no problem.

Interesting, but does this involves consuming samples from multiple CPUs?

- Arnaldo

#25 lore raw

Re: [PATCH 12/12] perf kvm: add live mode

Quoted 7 lines · Arnaldo Carvalho de Melo
Quoted 4 lines

On my Westmere based server I am not seeing problems with timestamps and ordering (prior testing was on a Nehalem based server). It just weathered a 5 minute or so barrage of > 100,000 events per second (peaking at around 360k events/sec) with no problem.

Interesting, but does this involves consuming samples from multiple CPUs?

yes. 4 vcpus that can and do roam the pcpus.

David