aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt (Google) <rostedt@goodmis.org>2022-09-22 17:48:47 -0400
committerSteven Rostedt (Google) <rostedt@goodmis.org>2022-09-22 18:02:04 -0400
commit14a7aca5f90e25d7dbbb487e00941ea52a94284c (patch)
treee48f8f67a97793992f4914a058adf11875b597cf
parent9191b8e4278f0a1988b4ada6f9dd5c70e6061d89 (diff)
downloadtrace-cmd-14a7aca5f90e25d7dbbb487e00941ea52a94284c.tar.gz
trace-cmd library: Add API for mapping between host and guests
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() Link: https://lore.kernel.org/linux-trace-devel/20220922174847.30475299@gandalf.local.home Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
-rw-r--r--Documentation/libtracecmd/libtracecmd-maps.txt177
-rw-r--r--Documentation/libtracecmd/libtracecmd.txt10
-rw-r--r--include/trace-cmd/trace-cmd.h11
-rw-r--r--lib/trace-cmd/Makefile1
-rw-r--r--lib/trace-cmd/include/trace-cmd-local.h6
-rw-r--r--lib/trace-cmd/trace-input.c31
-rw-r--r--lib/trace-cmd/trace-maps.c179
7 files changed, 415 insertions, 0 deletions
diff --git a/Documentation/libtracecmd/libtracecmd-maps.txt b/Documentation/libtracecmd/libtracecmd-maps.txt
new file mode 100644
index 00000000..8c1fb69d
--- /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 d2d17f7c..b1e07df5 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 bc5edb4d..c34503ac 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 28b4afc6..e9d26b2b 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 c95ec653..db0b2c21 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 ce3584ee..d8191824 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 00000000..bd16486f
--- /dev/null
+++ b/lib/trace-cmd/trace-maps.c
@@ -0,0 +1,179 @@
+#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;
+}