All trace-cmd internal functions related to VM / guest resolving and mappings are moved from trace-record.c to trace-vm.c. Internal APIs are added to access the guest database, so this functionality can be used by other logic, internally in the trace-cmd context. Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@xxxxxxxxx> --- tracecmd/Makefile | 1 + tracecmd/include/trace-local.h | 20 +++ tracecmd/trace-record.c | 232 +++------------------------------ tracecmd/trace-vm.c | 214 ++++++++++++++++++++++++++++++ 4 files changed, 250 insertions(+), 217 deletions(-) create mode 100644 tracecmd/trace-vm.c diff --git a/tracecmd/Makefile b/tracecmd/Makefile index c02851a8..2b14284b 100644 --- a/tracecmd/Makefile +++ b/tracecmd/Makefile @@ -35,6 +35,7 @@ TRACE_CMD_OBJS += trace-list.o TRACE_CMD_OBJS += trace-usage.o TRACE_CMD_OBJS += trace-dump.o TRACE_CMD_OBJS += trace-clear.o +TRACE_CMD_OBJS += trace-vm.o ifeq ($(VSOCK_DEFINED), 1) TRACE_CMD_OBJS += trace-tsync.o endif diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h index 4089de4e..7b14c68a 100644 --- a/tracecmd/include/trace-local.h +++ b/tracecmd/include/trace-local.h @@ -8,6 +8,7 @@ #include <sys/types.h> #include <dirent.h> /* for DIR */ +#include <ctype.h> /* for isdigit() */ #include <limits.h> #include "trace-cmd-private.h" @@ -285,6 +286,17 @@ void update_first_instance(struct buffer_instance *instance, int topt); void show_instance_file(struct buffer_instance *instance, const char *name); +struct trace_guest { + char *name; + int cid; + int pid; + int cpu_max; + int *cpu_pid; +}; +struct trace_guest *get_guest_by_cid(unsigned int guest_cid); +struct trace_guest *get_guest_by_name(char *name); +void read_qemu_guests(void); +int get_guest_pid(unsigned int guest_cid); int get_guest_vcpu_pid(unsigned int guest_cid, unsigned int guest_vcpu); /* moved from trace-cmd.h */ @@ -315,4 +327,12 @@ void *malloc_or_die(unsigned int size); /* Can be overridden */ void __noreturn __die(const char *fmt, ...); void __noreturn _vdie(const char *fmt, va_list ap); +static inline bool is_digits(const char *s) +{ + for (; *s; s++) + if (!isdigit(*s)) + return false; + return true; +} + #endif /* __TRACE_LOCAL_H */ diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c index 9316bbde..a0e128d6 100644 --- a/tracecmd/trace-record.c +++ b/tracecmd/trace-record.c @@ -3183,239 +3183,37 @@ static int do_accept(int sd) return -1; } -static bool is_digits(const char *s) +static char *parse_guest_name(char *gname, int *cid, int *port) { - for (; *s; s++) - if (!isdigit(*s)) - return false; - return true; -} - -struct guest { - char *name; - int cid; - int pid; - int cpu_max; - int *cpu_pid; -}; - -static struct guest *guests; -static size_t guests_len; - -static int set_vcpu_pid_mapping(struct guest *guest, int cpu, int pid) -{ - int *cpu_pid; - int i; - - if (cpu >= guest->cpu_max) { - cpu_pid = realloc(guest->cpu_pid, (cpu + 1) * sizeof(int)); - if (!cpu_pid) - return -1; - /* Handle sparse CPU numbers */ - for (i = guest->cpu_max; i < cpu; i++) - cpu_pid[i] = -1; - guest->cpu_max = cpu + 1; - guest->cpu_pid = cpu_pid; - } - guest->cpu_pid[cpu] = pid; - return 0; -} - -static struct guest *get_guest_info(unsigned int guest_cid) -{ - int i; - - if (!guests) - return NULL; - - for (i = 0; i < guests_len; i++) - if (guest_cid == guests[i].cid) - return guests + i; - return NULL; -} - -static char *get_qemu_guest_name(char *arg) -{ - char *tok, *end = arg; - - while ((tok = strsep(&end, ","))) { - if (strncmp(tok, "guest=", 6) == 0) - return tok + 6; - } - - return arg; -} - -static int read_qemu_guests_pids(char *guest_task, struct guest *guest) -{ - struct dirent *entry; - char path[PATH_MAX]; - char *buf = NULL; - size_t n = 0; - int ret = 0; - long vcpu; - long pid; - DIR *dir; - FILE *f; - - snprintf(path, sizeof(path), "/proc/%s/task", guest_task); - dir = opendir(path); - if (!dir) - return -1; - - while (!ret && (entry = readdir(dir))) { - if (!(entry->d_type == DT_DIR && is_digits(entry->d_name))) - continue; - - snprintf(path, sizeof(path), "/proc/%s/task/%s/comm", - guest_task, entry->d_name); - f = fopen(path, "r"); - if (!f) - continue; - - if (getline(&buf, &n, f) >= 0 && - strncmp(buf, "CPU ", 4) == 0) { - vcpu = strtol(buf + 4, NULL, 10); - pid = strtol(entry->d_name, NULL, 10); - if (vcpu < INT_MAX && pid < INT_MAX && - vcpu >= 0 && pid >= 0) { - if (set_vcpu_pid_mapping(guest, vcpu, pid)) - ret = -1; - } - } - - fclose(f); - } - free(buf); - return ret; -} - -static void read_qemu_guests(void) -{ - static bool initialized; - struct dirent *entry; - char path[PATH_MAX]; - DIR *dir; - - if (initialized) - return; - - initialized = true; - dir = opendir("/proc"); - if (!dir) - die("Can not open /proc"); - - while ((entry = readdir(dir))) { - bool is_qemu = false, last_was_name = false; - struct guest guest = {}; - char *p, *arg = NULL; - size_t arg_size = 0; - FILE *f; - - if (!(entry->d_type == DT_DIR && is_digits(entry->d_name))) - continue; - - guest.pid = atoi(entry->d_name); - snprintf(path, sizeof(path), "/proc/%s/cmdline", entry->d_name); - f = fopen(path, "r"); - if (!f) - continue; - - while (getdelim(&arg, &arg_size, 0, f) != -1) { - if (!is_qemu && strstr(arg, "qemu-system-")) { - is_qemu = true; - continue; - } - - if (!is_qemu) - continue; - - if (strcmp(arg, "-name") == 0) { - last_was_name = true; - continue; - } - - if (last_was_name) { - guest.name = strdup(get_qemu_guest_name(arg)); - if (!guest.name) - die("allocating guest name"); - last_was_name = false; - continue; - } - - p = strstr(arg, "guest-cid="); - if (p) { - guest.cid = atoi(p + 10); - continue; - } - } - - if (!is_qemu) - goto next; - - if (read_qemu_guests_pids(entry->d_name, &guest)) - warning("Failed to retrieve VPCU - PID mapping for guest %s", - guest.name ? guest.name : "Unknown"); - - guests = realloc(guests, (guests_len + 1) * sizeof(*guests)); - if (!guests) - die("Can not allocate guest buffer"); - guests[guests_len++] = guest; - -next: - free(arg); - fclose(f); - } - - closedir(dir); -} - -static char *parse_guest_name(char *guest, int *cid, int *port) -{ - size_t i; + struct trace_guest *guest; char *p; *port = -1; - p = strrchr(guest, ':'); + p = strrchr(gname, ':'); if (p) { *p = '\0'; *port = atoi(p + 1); } *cid = -1; - p = strrchr(guest, '@'); + p = strrchr(gname, '@'); if (p) { *p = '\0'; *cid = atoi(p + 1); - } else if (is_digits(guest)) - *cid = atoi(guest); + } else if (is_digits(gname)) + *cid = atoi(gname); read_qemu_guests(); - for (i = 0; i < guests_len; i++) { - if ((*cid > 0 && *cid == guests[i].cid) || - strcmp(guest, guests[i].name) == 0) { - *cid = guests[i].cid; - return guests[i].name; - } + if (*cid > 0) + guest = get_guest_by_cid(*cid); + else + guest = get_guest_by_name(gname); + if (guest) { + *cid = guest->cid; + return guest->name; } - return guest; -} - -int get_guest_vcpu_pid(unsigned int guest_cid, unsigned int guest_vcpu) -{ - int i; - - if (!guests) - return -1; - - for (i = 0; i < guests_len; i++) { - if (guests[i].cpu_pid < 0 || guest_vcpu >= guests[i].cpu_max) - continue; - if (guest_cid == guests[i].cid) - return guests[i].cpu_pid[guest_vcpu]; - } - return -1; + return gname; } static void set_prio(int prio) @@ -4091,7 +3889,7 @@ static void append_buffer(struct tracecmd_output *handle, static void add_guest_info(struct tracecmd_output *handle, struct buffer_instance *instance) { - struct guest *guest = get_guest_info(instance->cid); + struct trace_guest *guest = get_guest_by_cid(instance->cid); char *buf, *p; int size; int i; diff --git a/tracecmd/trace-vm.c b/tracecmd/trace-vm.c new file mode 100644 index 00000000..c8924ece --- /dev/null +++ b/tracecmd/trace-vm.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@xxxxxxxxxx> + * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@xxxxxxxxx> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <dirent.h> +#include <limits.h> + +#include "trace-local.h" +#include "trace-msg.h" + +static struct trace_guest *guests; +static size_t guests_len; + +static int set_vcpu_pid_mapping(struct trace_guest *guest, int cpu, int pid) +{ + int *cpu_pid; + int i; + + if (cpu >= guest->cpu_max) { + cpu_pid = realloc(guest->cpu_pid, (cpu + 1) * sizeof(int)); + if (!cpu_pid) + return -1; + /* Handle sparse CPU numbers */ + for (i = guest->cpu_max; i < cpu; i++) + cpu_pid[i] = -1; + guest->cpu_max = cpu + 1; + guest->cpu_pid = cpu_pid; + } + guest->cpu_pid[cpu] = pid; + return 0; +} + +struct trace_guest *get_guest_by_cid(unsigned int guest_cid) +{ + int i; + + if (!guests) + return NULL; + + for (i = 0; i < guests_len; i++) + if (guest_cid == guests[i].cid) + return guests + i; + return NULL; +} + +struct trace_guest *get_guest_by_name(char *name) +{ + int i; + + if (!guests) + return NULL; + + for (i = 0; i < guests_len; i++) + if (strcmp(name, guests[i].name) == 0) + return guests + i; + return NULL; +} + +static char *get_qemu_guest_name(char *arg) +{ + char *tok, *end = arg; + + while ((tok = strsep(&end, ","))) { + if (strncmp(tok, "guest=", 6) == 0) + return tok + 6; + } + + return arg; +} + +static int read_qemu_guests_pids(char *guest_task, struct trace_guest *guest) +{ + struct dirent *entry; + char path[PATH_MAX]; + char *buf = NULL; + size_t n = 0; + int ret = 0; + long vcpu; + long pid; + DIR *dir; + FILE *f; + + snprintf(path, sizeof(path), "/proc/%s/task", guest_task); + dir = opendir(path); + if (!dir) + return -1; + + while (!ret && (entry = readdir(dir))) { + if (!(entry->d_type == DT_DIR && is_digits(entry->d_name))) + continue; + + snprintf(path, sizeof(path), "/proc/%s/task/%s/comm", + guest_task, entry->d_name); + f = fopen(path, "r"); + if (!f) + continue; + + if (getline(&buf, &n, f) >= 0 && + strncmp(buf, "CPU ", 4) == 0) { + vcpu = strtol(buf + 4, NULL, 10); + pid = strtol(entry->d_name, NULL, 10); + if (vcpu < INT_MAX && pid < INT_MAX && + vcpu >= 0 && pid >= 0) { + if (set_vcpu_pid_mapping(guest, vcpu, pid)) + ret = -1; + } + } + + fclose(f); + } + free(buf); + return ret; +} + +void read_qemu_guests(void) +{ + static bool initialized; + struct dirent *entry; + char path[PATH_MAX]; + DIR *dir; + + if (initialized) + return; + + initialized = true; + dir = opendir("/proc"); + if (!dir) + die("Can not open /proc"); + + while ((entry = readdir(dir))) { + bool is_qemu = false, last_was_name = false; + struct trace_guest guest = {}; + char *p, *arg = NULL; + size_t arg_size = 0; + FILE *f; + + if (!(entry->d_type == DT_DIR && is_digits(entry->d_name))) + continue; + + guest.pid = atoi(entry->d_name); + snprintf(path, sizeof(path), "/proc/%s/cmdline", entry->d_name); + f = fopen(path, "r"); + if (!f) + continue; + + while (getdelim(&arg, &arg_size, 0, f) != -1) { + if (!is_qemu && strstr(arg, "qemu-system-")) { + is_qemu = true; + continue; + } + + if (!is_qemu) + continue; + + if (strcmp(arg, "-name") == 0) { + last_was_name = true; + continue; + } + + if (last_was_name) { + guest.name = strdup(get_qemu_guest_name(arg)); + if (!guest.name) + die("allocating guest name"); + last_was_name = false; + continue; + } + + p = strstr(arg, "guest-cid="); + if (p) { + guest.cid = atoi(p + 10); + continue; + } + } + + if (!is_qemu) + goto next; + + if (read_qemu_guests_pids(entry->d_name, &guest)) + warning("Failed to retrieve VPCU - PID mapping for guest %s", + guest.name ? guest.name : "Unknown"); + + guests = realloc(guests, (guests_len + 1) * sizeof(*guests)); + if (!guests) + die("Can not allocate guest buffer"); + guests[guests_len++] = guest; + +next: + free(arg); + fclose(f); + } + + closedir(dir); +} + +int get_guest_vcpu_pid(unsigned int guest_cid, unsigned int guest_vcpu) +{ + int i; + + if (!guests) + return -1; + + for (i = 0; i < guests_len; i++) { + if (guests[i].cpu_pid < 0 || guest_vcpu >= guests[i].cpu_max) + continue; + if (guest_cid == guests[i].cid) + return guests[i].cpu_pid[guest_vcpu]; + } + return -1; +} -- 2.29.2