diff mbox series

trace-cmd library: Add API for mapping between host and guests

Message ID 20220922174847.30475299@gandalf.local.home (mailing list archive)
State Accepted
Commit 14a7aca5f90e25d7dbbb487e00941ea52a94284c
Headers show
Series trace-cmd library: Add API for mapping between host and guests | expand

Commit Message

Steven Rostedt Sept. 22, 2022, 9:48 p.m. UTC
From: "Steven Rostedt (Google)" <rostedt@goodmis.org>

After writing a few applications to analyze host guest interactions, I
found that I was writing the same code. Instead, add helper functions to
do the mapping.

    tracecmd_map_vcpus()
    tracecmd_get_cpu_map()
    tracecmd_map_find_by_host_pid()
    tracecmd_cpu_map()
    tracecmd_map_get_host_pid()
    tracecmd_map_get_guest()
    tracecmd_map_set_private()
    tracecmd_map_set_private()

Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
 .../libtracecmd/libtracecmd-maps.txt          | 177 +++++++++++++++++
 Documentation/libtracecmd/libtracecmd.txt     |  10 +
 include/trace-cmd/trace-cmd.h                 |  11 ++
 lib/trace-cmd/Makefile                        |   1 +
 lib/trace-cmd/include/trace-cmd-local.h       |   6 +
 lib/trace-cmd/trace-input.c                   |  31 +++
 lib/trace-cmd/trace-maps.c                    | 180 ++++++++++++++++++
 7 files changed, 416 insertions(+)
 create mode 100644 Documentation/libtracecmd/libtracecmd-maps.txt
 create mode 100644 lib/trace-cmd/trace-maps.c
diff mbox series

Patch

diff --git a/Documentation/libtracecmd/libtracecmd-maps.txt b/Documentation/libtracecmd/libtracecmd-maps.txt
new file mode 100644
index 000000000000..8c1fb69d04ff
--- /dev/null
+++ b/Documentation/libtracecmd/libtracecmd-maps.txt
@@ -0,0 +1,177 @@ 
+libtracecmd(3)
+=============
+
+NAME
+----
+tracecmd_map_vcpus, tracecmd_get_cpu_map, tracecmd_map_find_by_host_pid, tracecmd_map_get_host_pid,
+tracecmd_map_get_guest, tracecmd_map_set_private, tracecmd_map_get_private - Mapping host and guest data
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <trace-cmd.h>*
+
+int *tracecmd_map_vcpus*(struct tracecmd_input pass:[**]handles, int nr_handles);
+struct tracecmd_cpu_map pass:[*]*tracecmd_get_cpu_map*(struct tracecmd_input pass:[*]handle, int cpu);
+struct tracecmd_cpu_map pass:[*]*tracecmd_map_find_by_host_pid*(struct tracecmd_input pass:[*]handle,
+						      int host_pid);
+int *tracecmd_map_get_host_pid*(struct tracecmd_cpu_map pass:[*]map);
+struct tracecmd_input pass:[*]*tracecmd_map_get_guest*(struct tracecmd_cpu_map pass:[*]map);
+void *tracecmd_map_set_private*(struct tracecmd_cpu_map pass:[*]map, void pass:[*]priv);
+void pass:[*]*tracecmd_map_get_private*(struct tracecmd_cpu_map pass:[*]map);
+--
+
+DESCRIPTION
+-----------
+This set of APIs is used to map host and guest trace files for to facilitate
+further tracing analysis.
+
+The *tracecmd_map_vcpus()* takes an array of _handles_ where each item in that
+array was created by one of the *tracecmd_open(3)* functions, and the number
+of handles as _nr_handles_. The first handle in the array of _handles_ is expected
+to be the descriptor for the host tracing file, and the rest are guest trace
+files that run on the host, and were created by the *trace-cmd record(1)* and
+*trace-cmd agent(1)* interactions. It returns the number of guests found in
+_handles_ that were associated with the host, or negative on error.
+
+The *tracecmd_get_cpu_map()* returns a descriptor for a given CPU for a handle.
+If the _handle_ was a guest defined from *tracecmd_map_vcpus()* then the mapping
+created from that function that is associated to this particular vCPU (denoted by
+_cpu_) from _handle_. This destriptor can be used by *tarcecmd_map_get_guest()*,
+*tracecmd_map_set_private()* and *tracecmd_map_get_private()* functions.
+
+The *tracecmd_map_find_by_host_pid()* will return a mapping for a guest virtual
+CPU that is handled by the given _host_pid_. Note, the _handle_ passed in can be
+either the host handle or one of the guest's handles for that host that was
+mapped by *tracecmd_map_vcpus()*, even if the guest handle does not have the vCPU
+that the _host_pid_ represents.
+
+The *tracecmd_map_get_host_pid()* will recturn the host_pid for a given _map_
+that was retrieved by one of the above functions.
+
+The *tracecmd_map_get_guest()* will recturn the guest_handle for a given _map_
+that was retrieved by one of the above functions.
+
+The *tracecmd_map_set_private()* allows the application to assign private data
+for a given guest vCPU to host thread mapping defined by _map_.
+
+The *tracecmd_map_get_private()* retrieves the _priv_ data from _map_ that was
+set by *tracecmd_map_set_private()*.
+
+RETURN VALUE
+------------
+*tracecmd_map_vcpus()* returns the number of guests in the _handles_ array that
+were mapped to the host handle that is the first entry in _handles_. It returns
+-1 on error.
+
+*tracecmd_get_cpu_map()* returns a map created by *tracecmd_map_vcpus()* for
+a given _cpu_ for a given _handle_, or NULL if it is not found.
+
+*tracecmd_map_find_by_host_pid()* returns a map that is associated by the host
+task with _host_pid_ as its process ID. _handle_ can be either a the host
+handle, or one of the guest handles that were mapped to the host via
+*tracecmd_map_vcpus()*, even if the guest handle is another guest than
+the one that the mapping is for. It returns NULL if not found.
+
+*tracecmd_map_get_host_pid()* returns the host process ID for an associated
+mapping defined by _map_.
+
+*tracecmd_map_get_guest()* returns the guest handle for an associated
+mapping defined by _map_.
+
+*tracecmd_map_get_private()* returns the private data of a mapping defined
+by _map_ that was set by *tracecmd_map_set_private()*.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <errno.h>
+#include <trace-cmd.h>
+
+int main(int argc, char **argv)
+{
+	struct tracecmd_input **handles = NULL;
+	int nr_handles;
+	int i;
+
+	if (argc < 2) {
+		printf("usage: host_trace.dat guest1_trace.dat [guest2_trace.dat ...]\n");
+		exit(-1);
+	}
+
+	for (i = 1; i < argc; i++) {
+		handles = realloc(handles, sizeof(*handles) * (nr_handles + 1));
+		if (!handles)
+			exit(-1);
+		handles[nr_handles] = tracecmd_open(argv[i], 0);
+		if (!handles[nr_handles]) {
+			perror(argv[1]);
+			exit(-1);
+		}
+		tracecmd_set_private(handles[nr_handles], argv[i]);
+		nr_handles++;
+	}
+
+	tracecmd_map_vcpus(handles, nr_handles);
+
+	for (i = 1; i < nr_handles; i++) {
+		struct tracecmd_cpu_map *map;
+		struct tep_handle *tep;
+		const char *file = tracecmd_get_private(handles[i]);
+		int cpus, cpu;
+
+		printf("Mappings for guest %s:\n", file);
+		tep = tracecmd_get_tep(handles[i]);
+		cpus = tep_get_cpus(tep);
+		for (cpu = 0; cpu < cpus; cpu++) {
+			printf("  [%03d] ", cpu);
+			map = tracecmd_get_cpu_map(handles[i], cpu);
+			if (!map) {
+				printf("Has no mapping!\n");
+				continue;
+			}
+			printf("host_pid: %d\n", tracecmd_map_get_host_pid(map));
+		}
+	}
+	for (i = 0; i < nr_handles; i++)
+		tracecmd_close(handles[i]);
+	free(handles);
+	exit(0);
+}
+--
+FILES
+-----
+[verse]
+--
+*trace-cmd.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracecmd*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs(3)*,
+*libtraceevent(3)*,
+*trace-cmd(1)*
+*trace-cmd.dat(5)*
+
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracecmd is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracecmd/libtracecmd.txt b/Documentation/libtracecmd/libtracecmd.txt
index d2d17f7cbaba..b1e07df56346 100644
--- a/Documentation/libtracecmd/libtracecmd.txt
+++ b/Documentation/libtracecmd/libtracecmd.txt
@@ -63,6 +63,16 @@  Get traceing peer information from a trace file:
 	unsigned long long *tracecmd_get_traceid*(struct tracecmd_input pass:[*]_handle_);
 	int *tracecmd_get_guest_cpumap*(struct tracecmd_input pass:[*]_handle_, unsigned long long _trace_id_, const char pass:[*]pass:[*]_name_, int pass:[*]_vcpu_count_, const int pass:[*]pass:[*]_cpu_pid_);
 
+Mapping host and guest trace files:
+	int *tracecmd_map_vcpus*(struct tracecmd_input pass:[**]handles, int nr_handles);
+	struct tracecmd_cpu_map pass:[*]*tracecmd_get_cpu_map*(struct tracecmd_input pass:[*]handle, int cpu);
+	struct tracecmd_cpu_map pass:[*]*tracecmd_map_find_by_host_pid*(struct tracecmd_input pass:[*]handle,
+						      int host_pid);
+	int *tracecmd_map_get_host_pid*(struct tracecmd_cpu_map pass:[*]map);
+	struct tracecmd_input pass:[*]*tracecmd_map_get_guest*(struct tracecmd_cpu_map pass:[*]map);
+	void *tracecmd_map_set_private*(struct tracecmd_cpu_map pass:[*]map, void pass:[*]priv);
+	void pass:[*]*tracecmd_map_get_private*(struct tracecmd_cpu_map pass:[*]map);
+
 Control library logs:
 	int *tracecmd_set_loglevel*(enum tep_loglevel _level_);
 --
diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index bc5edb4d695f..c34503ac7de5 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -88,6 +88,17 @@  enum tracecmd_filters {
 struct tracecmd_filter;
 struct tracecmd_filter *tracecmd_filter_add(struct tracecmd_input *handle,
 					    const char *filter_str, bool neg);
+
+struct tracecmd_cpu_map;
+int tracecmd_map_vcpus(struct tracecmd_input **handles, int nr_handles);
+struct tracecmd_cpu_map *tracecmd_get_cpu_map(struct tracecmd_input *handle, int cpu);
+struct tracecmd_cpu_map *tracecmd_map_find_by_host_pid(struct tracecmd_input *handle,
+						      int host_pid);
+struct tracecmd_input *tracecmd_map_get_guest(struct tracecmd_cpu_map *map);
+int tracecmd_map_get_host_pid(struct tracecmd_cpu_map *map);
+void tracecmd_map_set_private(struct tracecmd_cpu_map *map, void *priv);
+void *tracecmd_map_get_private(struct tracecmd_cpu_map *map);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/trace-cmd/Makefile b/lib/trace-cmd/Makefile
index 28b4afc665a6..e9d26b2bb367 100644
--- a/lib/trace-cmd/Makefile
+++ b/lib/trace-cmd/Makefile
@@ -18,6 +18,7 @@  OBJS += trace-filter-hash.o
 OBJS += trace-filter.o
 OBJS += trace-msg.o
 OBJS += trace-plugin.o
+OBJS += trace-maps.o
 ifeq ($(PERF_DEFINED), 1)
 OBJS += trace-perf.o
 endif
diff --git a/lib/trace-cmd/include/trace-cmd-local.h b/lib/trace-cmd/include/trace-cmd-local.h
index c95ec6530fdd..db0b2c21ee45 100644
--- a/lib/trace-cmd/include/trace-cmd-local.h
+++ b/lib/trace-cmd/include/trace-cmd-local.h
@@ -52,6 +52,12 @@  struct data_file_write {
 enum tracecmd_filters tracecmd_filter_match(struct tracecmd_filter *filter,
 					    struct tep_record *record);
 
+void trace_set_guest_map(struct tracecmd_input *handle, struct tracecmd_cpu_map *map);
+struct tracecmd_cpu_map *trace_get_guest_map(struct tracecmd_input *handle);
+void trace_set_guest_map_cnt(struct tracecmd_input *handle, int count);
+int trace_get_guest_map_cnt(struct tracecmd_input *handle);
+void trace_guest_map_free(struct tracecmd_cpu_map *map);
+
 void tracecmd_compress_init(void);
 void tracecmd_compress_free(void);
 
diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c
index ce3584eef201..d81918249871 100644
--- a/lib/trace-cmd/trace-input.c
+++ b/lib/trace-cmd/trace-input.c
@@ -171,6 +171,7 @@  struct tracecmd_input {
 	struct tracecmd_input	*parent;
 	struct tracecmd_filter	*filter;
 	struct follow_event	*followers;
+	struct tracecmd_cpu_map *map;
 	unsigned long		file_state;
 	unsigned long long	trace_id;
 	unsigned long long	next_offset;
@@ -191,6 +192,7 @@  struct tracecmd_input {
 	bool			read_zpage; /* uncompress pages in memory, do not use tmp files */
 	bool			cpu_compressed;
 	int			file_version;
+	int			map_cnt;
 	unsigned int		cpustats_size;
 	struct cpu_zdata	latz;
 	struct cpu_data 	*cpu_data;
@@ -320,6 +322,34 @@  static const char *show_records(struct page **pages, int nr_pages)
 }
 #endif
 
+/**
+ * trace_set_guest_map - set map to input handle
+ * @handle: The handle to set the cpu map to
+ * @map: The cpu map for this handle (to the host)
+ *
+ * Assign the mapping of host to guest for a guest handle.
+ */
+__hidden void trace_set_guest_map(struct tracecmd_input *handle,
+				  struct tracecmd_cpu_map *map)
+{
+	handle->map = map;
+}
+
+__hidden struct tracecmd_cpu_map *trace_get_guest_map(struct tracecmd_input *handle)
+{
+	return handle->map;
+}
+
+__hidden void trace_set_guest_map_cnt(struct tracecmd_input *handle, int count)
+{
+	handle->map_cnt = count;
+}
+
+__hidden int trace_get_guest_map_cnt(struct tracecmd_input *handle)
+{
+	return handle->map_cnt;
+}
+
 static int init_cpu(struct tracecmd_input *handle, int cpu);
 
 static ssize_t do_read_fd(int fd, void *data, size_t size)
@@ -4801,6 +4831,7 @@  void tracecmd_close(struct tracecmd_input *handle)
 	free(handle->trace_clock);
 	free(handle->strings);
 	free(handle->version);
+	trace_guest_map_free(handle->map);
 	close(handle->fd);
 	free(handle->latz.chunks);
 	if (handle->latz.fd >= 0) {
diff --git a/lib/trace-cmd/trace-maps.c b/lib/trace-cmd/trace-maps.c
new file mode 100644
index 000000000000..b5808ecb4a66
--- /dev/null
+++ b/lib/trace-cmd/trace-maps.c
@@ -0,0 +1,180 @@ 
+#include <stdlib.h>
+
+#include "trace-cmd-local.h"
+#include "trace-local.h"
+
+/*
+ * Structure to hold the mapping between host and guest.
+ * @self - A pointer back to the guest's mapping (for the host copy to use)
+ * @host_handle - The handle for the host for this mapping.
+ * @guest_handle - The handle for the guest for this mapping.
+ * @guest_vcpu - The vCPU # for this mapping.
+ * @host_pid - The pid of the task on the host that runs when this vCPU executes.
+ * @private - Private data for applications to use.
+ */
+struct tracecmd_cpu_map {
+	struct tracecmd_cpu_map		*self;
+	struct tracecmd_input		*host_handle;
+	struct tracecmd_input		*guest_handle;
+	int				guest_vcpu;
+	int				host_pid;
+	void				*private;
+};
+
+static int cmp_map(const void *A, const void *B)
+{
+	const struct tracecmd_cpu_map *a = A;
+	const struct tracecmd_cpu_map *b = B;
+
+	if (a->host_pid < b->host_pid)
+		return -1;
+	return a->host_pid > b->host_pid;
+}
+
+int tracecmd_map_vcpus(struct tracecmd_input **handles, int nr_handles)
+{
+	struct tracecmd_input *host_handle = handles[0];
+	unsigned long long traceid;
+	struct tracecmd_cpu_map *vcpu_maps = NULL;
+	struct tracecmd_cpu_map *gmap;
+	struct tracecmd_cpu_map *map;
+	const int *cpu_pids;
+	const char *name;
+	int nr_vcpu_maps = 0;
+	int vcpu_count;
+	int mappings = 0;
+	int ret;
+	int i, k;
+
+	/* handles[0] is the host handle, do for each guest handle */
+	for (i = 1; i < nr_handles; i++) {
+		traceid = tracecmd_get_traceid(handles[i]);
+
+		/*
+		 * Retrieve the host mapping of the guest for this handle.
+		 * cpu_pids is an array of pids that map 1-1 the host vcpus where
+		 * cpu_pids[vCPU_num] = host_task_pid
+		 */
+		ret = tracecmd_get_guest_cpumap(host_handle, traceid,
+						&name, &vcpu_count, &cpu_pids);
+		if (ret)
+			continue;
+
+		mappings++;
+
+		gmap = calloc(sizeof(*gmap), vcpu_count);
+		if (!gmap)
+			goto fail;
+
+		for (k = 0; k < vcpu_count; k++) {
+			gmap[k].host_handle = handles[0];
+			gmap[k].guest_handle = handles[i];
+			gmap[k].guest_vcpu = k;
+			gmap[k].host_pid = cpu_pids[k];
+			gmap[k].self = &gmap[k];
+		}
+
+		trace_set_guest_map(handles[i], gmap);
+		trace_set_guest_map_cnt(handles[i], vcpu_count);
+
+		/* Update the host mapping of all guests to the host */
+		map = realloc(vcpu_maps, sizeof(*map) * (nr_vcpu_maps + vcpu_count));
+		if (!map)
+			goto fail;
+		memset(map + nr_vcpu_maps, 0, sizeof(*map) * (vcpu_count - nr_vcpu_maps));
+
+		vcpu_maps = map;
+		map += nr_vcpu_maps;
+		nr_vcpu_maps += vcpu_count;
+
+		for (k = 0; k < vcpu_count; k++)
+			map[k] = gmap[k];
+	}
+	if (!vcpu_maps)
+		return 0;
+
+	/* We want to do a binary search via host_pid to find these mappings */
+	qsort(vcpu_maps, nr_vcpu_maps, sizeof(*map), cmp_map);
+
+	trace_set_guest_map(handles[0], vcpu_maps);
+	trace_set_guest_map_cnt(handles[0], nr_vcpu_maps);
+
+	return mappings;
+
+ fail:
+	free(vcpu_maps);
+	return -1;
+}
+
+__hidden void trace_guest_map_free(struct tracecmd_cpu_map *map)
+{
+	free(map);
+}
+
+struct tracecmd_cpu_map *tracecmd_map_find_by_host_pid(struct tracecmd_input *handle,
+						       int host_pid)
+{
+	struct tracecmd_cpu_map *map;
+	struct tracecmd_cpu_map key;
+	int nr_maps;
+
+	map = trace_get_guest_map(handle);
+	if (!map)
+		return NULL;
+
+	/* The handle could be from the guest, get the host handle */
+	handle = map->host_handle;
+
+	/* And again, get the mapping of the host, as it has all the mappings */
+	map = trace_get_guest_map(handle);
+	if (!map)
+		return NULL;
+
+	nr_maps = trace_get_guest_map_cnt(handle);
+
+	key.host_pid = host_pid;
+
+	map = bsearch(&key, map, nr_maps, sizeof(*map), cmp_map);
+
+	return map ? map->self : NULL;
+}
+
+void tracecmd_map_set_private(struct tracecmd_cpu_map *map, void *priv)
+{
+	/* Only set the guest private */
+	map = map->self;
+	map->private = priv;
+}
+
+void *tracecmd_map_get_private(struct tracecmd_cpu_map *map)
+{
+	/* Return the guest private */
+	map = map->self;
+	return map->private;
+}
+
+struct tracecmd_input *tracecmd_map_get_guest(struct tracecmd_cpu_map *map)
+{
+	return map->guest_handle;
+}
+
+int tracecmd_map_get_host_pid(struct tracecmd_cpu_map *map)
+{
+	return map->host_pid;
+}
+
+struct tracecmd_cpu_map *tracecmd_get_cpu_map(struct tracecmd_input *handle, int cpu)
+{
+	struct tracecmd_cpu_map *map;
+	int cnt;
+
+	map = trace_get_guest_map(handle);
+	/* Make sure it's for the guest handle, as this could be a host handle */
+	map = map->self;
+	cnt = trace_get_guest_map_cnt(map->guest_handle);
+	if (cnt <= cpu)
+		return NULL;
+
+	return map + cpu;
+}
+