blob: c8924ece6c02427d8137979049f69d1d11f96083 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
* Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#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;
}