blob: 91713217670c17a28f0b62a7d164555ab145152b [file] [log] [blame]
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/socket.h>
#include <linux/vm_sockets.h>
#include <tracefs.h>
static int open_vsock(unsigned int cid, unsigned int port)
{
struct sockaddr_vm addr = {
.svm_family = AF_VSOCK,
.svm_cid = cid,
.svm_port = port,
};
int sd;
sd = socket(AF_VSOCK, SOCK_STREAM, 0);
if (sd < 0)
return -1;
if (connect(sd, (struct sockaddr *)&addr, sizeof(addr))) {
close(sd);
return -1;
}
return sd;
}
struct pids {
struct pids *next;
int pid;
};
struct trace_info {
struct tracefs_instance *instance;
struct tep_handle *tep;
struct tep_event *wake_up;
struct tep_event *kvm_exit;
struct tep_format_field *wake_pid;
struct pids *pids;
int pid;
};
static void tear_down_trace(struct trace_info *info)
{
tracefs_event_disable(info->instance, NULL, NULL);
tep_free(info->tep);
info->tep = NULL;
}
static int add_pid(struct pids **pids, int pid)
{
struct pids *new_pid;
new_pid = malloc(sizeof(*new_pid));
if (!new_pid)
return -1;
new_pid->pid = pid;
new_pid->next = *pids;
*pids = new_pid;
return 0;
}
static bool match_pid(struct pids *pids, int pid)
{
while (pids) {
if (pids->pid == pid)
return true;
pids = pids->next;
}
return false;
}
static int waking_callback(struct tep_event *event, struct tep_record *record,
int cpu, void *data)
{
struct trace_info *info = data;
unsigned long long val;
int flags;
int pid;
int ret;
pid = tep_data_pid(event->tep, record);
if (!match_pid(info->pids, pid))
return 0;
/* Ignore wakeups in interrupts */
flags = tep_data_flags(event->tep, record);
if (flags & (TRACE_FLAG_HARDIRQ | TRACE_FLAG_SOFTIRQ))
return 0;
if (!info->wake_pid) {
info->wake_pid = tep_find_field(event, "pid");
if (!info->wake_pid)
return -1;
}
ret = tep_read_number_field(info->wake_pid, record->data, &val);
if (ret < 0)
return -1;
return add_pid(&info->pids, (int)val);
}
static int exit_callback(struct tep_event *event, struct tep_record *record,
int cpu, void *data)
{
struct trace_info *info = data;
int pid;
pid = tep_data_pid(event->tep, record);
if (!match_pid(info->pids, pid))
return 0;
info->pid = pid;
/* Found the pid we are looking for, stop the trace */
return -1;
}
static int setup_trace(struct trace_info *info)
{
const char *systems[] = { "sched", "kvm", NULL};
int ret;
info->pids = NULL;
tracefs_trace_off(info->instance);
info->tep = tracefs_local_events_system(NULL, systems);
if (!info->tep)
return -1;
/*
* Follow the wake ups, starting with this pid, to find
* the one that exits to the guest. That will be the thread
* of the vCPU of the guest.
*/
ret = tracefs_follow_event(info->tep, info->instance,
"sched", "sched_waking",
waking_callback, info);
if (ret < 0)
goto fail;
ret = tracefs_follow_event(info->tep, info->instance,
"kvm", "kvm_exit",
exit_callback, info);
if (ret < 0)
goto fail;
ret = tracefs_event_enable(info->instance, "sched", "sched_waking");
if (ret < 0)
goto fail;
ret = tracefs_event_enable(info->instance, "kvm", "kvm_exit");
if (ret < 0)
goto fail;
return 0;
fail:
tear_down_trace(info);
return -1;
}
static void free_pids(struct pids *pids)
{
struct pids *next;
while (pids) {
next = pids;
pids = pids->next;
free(next);
}
}
static int find_thread_leader(int pid)
{
FILE *fp;
char *path;
char *save;
char *buf = NULL;
size_t l = 0;
int tgid = -1;
if (asprintf(&path, "/proc/%d/status", pid) < 0)
return -1;
fp = fopen(path, "r");
free(path);
if (!fp)
return -1;
while (getline(&buf, &l, fp) > 0) {
char *tok;
if (strncmp(buf, "Tgid:", 5) != 0)
continue;
tok = strtok_r(buf, ":", &save);
if (!tok)
continue;
tok = strtok_r(NULL, ":", &save);
if (!tok)
continue;
while (isspace(*tok))
tok++;
tgid = strtol(tok, NULL, 0);
break;
}
free(buf);
return tgid > 0 ? tgid : -1;
}
int tracefs_instance_find_cid_pid(struct tracefs_instance *instance, int cid)
{
struct trace_info info = {};
int this_pid = getpid();
int ret;
int fd;
info.instance = instance;
if (setup_trace(&info) < 0)
return -1;
ret = add_pid(&info.pids, this_pid);
if (ret < 0)
goto out;
tracefs_instance_file_clear(info.instance, "trace");
tracefs_trace_on(info.instance);
fd = open_vsock(cid, -1);
tracefs_trace_off(info.instance);
if (fd >= 0)
close(fd);
info.pid = -1;
ret = tracefs_iterate_raw_events(info.tep, info.instance,
NULL, 0, NULL, &info);
if (info.pid <= 0)
ret = -1;
if (ret == 0)
ret = find_thread_leader(info.pid);
out:
free_pids(info.pids);
info.pids = NULL;
tear_down_trace(&info);
return ret;
}
int tracefs_find_cid_pid(int cid)
{
struct tracefs_instance *instance;
char *name;
int ret;
ret = asprintf(&name, "_tracefs_vsock_find-%d\n", getpid());
if (ret < 0)
return ret;
instance = tracefs_instance_create(name);
free(name);
if (!instance)
return -1;
ret = tracefs_instance_find_cid_pid(instance, cid);
tracefs_instance_destroy(instance);
tracefs_instance_free(instance);
return ret;
}