diff mbox series

trace-cmd report: Add callback for kvm plugin to show guest functions

Message ID 20220921202642.44fec719@gandalf.local.home (mailing list archive)
State Accepted
Commit d63ae356854e317564d0f5f3d13341b0830f3ec4
Headers show
Series trace-cmd report: Add callback for kvm plugin to show guest functions | expand

Commit Message

Steven Rostedt Sept. 22, 2022, 12:26 a.m. UTC
From: "Steven Rostedt (Google)" <rostedt@goodmis.org>

Starting with libtraceevent 1.6.2, the kvm plugin will call a weak
function that can be overridden by the application (in this case
trace-cmd) called tep_plugin_kvm_get_func() that passes in the event, the
record, and an address to a value containing the address of the function
being requested. If the application has the guest mapping of addresses to
function names (usually taken from kallsyms), then it can define this
function to return the name of the function at the given address of the
guest.

Optionally, it can update the value holding the address, to the start of
the function such that the tep kvm plugin can print not only the function
name, but also the address offset of the original location and the
function itself.

Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
 tracecmd/trace-read.c | 94 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 94 insertions(+)

Comments

Steven Rostedt Sept. 28, 2022, 12:17 p.m. UTC | #1
On Wed, 28 Sep 2022 09:24:14 +0900
Junichi Uekawa (上川純一) <uekawa@google.com> wrote:

> +
> +
> +const char *tep_plugin_kvm_get_func(struct tep_event *event,
> +                                   struct tep_record *record,
> +                                   unsigned long long *val)
> +{
> 
> I think this could be implemented in a .so plugin that is dlopen()'d which might be more intuitive (along with the others).
> I think using dlopen RTLD_GLOBAL will use the symbols instead of the weak symbols (but I need to check if that was the actual behavior).

So we should have a dlopen on an .so from this .so that is dlopen()ed?

The issue here is that libtraceevent has several .so plugins loaded with
dlopen(). One of them is the plugin_kvm.so, which needs a way to translate
the rip that the host receives with the function it maps to in the guest.
The issue is that the libtraceevent only handles one set of tracing data,
and that's for just the host. Now because plugin_kvm.so is a plugin, it
only interacts with libtraceevent.

Now we could add a call to libtraceeveent that is
"tep_load_guest_mappings()" but then we need to make it so that it can load
several guest mappings, and the place in the plugin_kvm.so will need to
also know what guest it is for.

In which case I feel it is losing the scope of what libtraceevent is for
(just parsing of the event's raw data). Which is why I added this hack.

Or maybe I misunderstood what you were suggesting.

-- Steve


>  
> +       struct tep_handle *tep;
> +       struct kvm_cpu_map *map;
> +       struct kvm_cpu_map key;
> +       unsigned long long rip = *val;
> +       const char *func;
> +       int pid;
diff mbox series

Patch

diff --git a/tracecmd/trace-read.c b/tracecmd/trace-read.c
index db9bb2e5219f..b8931cf95543 100644
--- a/tracecmd/trace-read.c
+++ b/tracecmd/trace-read.c
@@ -1040,6 +1040,98 @@  static bool skip_record(struct handle_list *handles, struct tep_record *record,
 	return !found;
 }
 
+struct kvm_cpu_map {
+	struct tracecmd_input		*guest_handle;
+	int				guest_vcpu;
+	int				host_pid;
+};
+
+static struct kvm_cpu_map *vcpu_maps;
+static int nr_vcpu_maps;
+
+static int cmp_map(const void *A, const void *B)
+{
+	const struct kvm_cpu_map *a = A;
+	const struct kvm_cpu_map *b = B;
+
+	if (a->host_pid < b->host_pid)
+		return -1;
+	return a->host_pid > b->host_pid;
+}
+
+static void map_vcpus(struct tracecmd_input **handles, int nr_handles)
+{
+	struct tracecmd_input *host_handle = handles[0];
+	unsigned long long traceid;
+	struct kvm_cpu_map *map;
+	const int *cpu_pids;
+	const char *name;
+	int vcpu_count;
+	int ret;
+	int i, k;
+
+	for (i = 1; i < nr_handles; i++) {
+		traceid = tracecmd_get_traceid(handles[i]);
+		ret = tracecmd_get_guest_cpumap(host_handle, traceid,
+						&name, &vcpu_count, &cpu_pids);
+		if (ret)
+			continue;
+		map = realloc(vcpu_maps, sizeof(*map) * (nr_vcpu_maps + vcpu_count));
+		if (!map)
+			die("Could not allocate vcpu maps");
+
+		vcpu_maps = map;
+		map += nr_vcpu_maps;
+		nr_vcpu_maps += vcpu_count;
+
+		for (k = 0; k < vcpu_count; k++) {
+			map[k].guest_handle = handles[i];
+			map[k].guest_vcpu = k;
+			map[k].host_pid = cpu_pids[k];
+		}
+	}
+	if (!vcpu_maps)
+		return;
+
+	qsort(vcpu_maps, nr_vcpu_maps, sizeof(*map), cmp_map);
+}
+
+
+const char *tep_plugin_kvm_get_func(struct tep_event *event,
+				    struct tep_record *record,
+				    unsigned long long *val)
+{
+	struct tep_handle *tep;
+	struct kvm_cpu_map *map;
+	struct kvm_cpu_map key;
+	unsigned long long rip = *val;
+	const char *func;
+	int pid;
+
+	if (!vcpu_maps || !nr_vcpu_maps)
+		return NULL;
+
+	/*
+	 * A kvm event is referencing an address of the guest.
+	 * get the PID of this event, and then find which guest
+	 * it belongs to. Then return the function name from that guest's
+	 * handle.
+	 */
+	pid = tep_data_pid(event->tep, record);
+
+	key.host_pid = pid;
+	map = bsearch(&key, vcpu_maps, nr_vcpu_maps, sizeof(*vcpu_maps), cmp_map);
+
+	if (!map)
+		return NULL;
+
+	tep = tracecmd_get_tep(map->guest_handle);
+	func = tep_find_function(tep, rip);
+	if (func)
+		*val = tep_find_function_address(tep, rip);
+	return func;
+}
+
 static int process_record(struct tracecmd_input *handle, struct tep_record *record,
 			  int cpu, void *data)
 {
@@ -1192,6 +1284,8 @@  static void read_data_info(struct list_head *handle_list, enum output_type otype
 		handle_array[nr_handles++] = handles->handle;
 	}
 
+	map_vcpus(handle_array, nr_handles);
+
 	tracecmd_iterate_events_multi(handle_array, nr_handles,
 				      process_record, &last_timestamp);