// SPDX-License-Identifier: GPL-2.0-or-later
/*
* probe-file.c : operate ftrace k/uprobe events files
*
* Written by Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
*/
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <linux/zalloc.h>
#include "namespaces.h"
#include "event.h"
#include "strlist.h"
#include "strfilter.h"
#include "debug.h"
#include "dso.h"
#include "cache.h"
#include "color.h"
#include "symbol.h"
#include <api/fs/tracing_path.h>
#include "probe-event.h"
#include "probe-file.h"
#include "session.h"
#include "perf_regs.h"
#include "string2.h"
/* 4096 - 2 ('\n' + '\0') */
#define MAX_CMDLEN 4094
static void print_open_warning(int err, bool uprobe)
{
char sbuf[STRERR_BUFSIZE];
if (err == -ENOENT) {
const char *config;
if (uprobe)
config = "CONFIG_UPROBE_EVENTS";
else
config = "CONFIG_KPROBE_EVENTS";
pr_warning("%cprobe_events file does not exist"
" - please rebuild kernel with %s.\n",
uprobe ? 'u' : 'k', config);
} else if (err == -ENOTSUP)
pr_warning("Tracefs or debugfs is not mounted.\n");
else
pr_warning("Failed to open %cprobe_events: %s\n",
uprobe ? 'u' : 'k',
str_error_r(-err, sbuf, sizeof(sbuf)));
}
static void print_both_open_warning(int kerr, int uerr)
{
/* Both kprobes and uprobes are disabled, warn it. */
if (kerr == -ENOTSUP && uerr == -ENOTSUP)
pr_warning("Tracefs or debugfs is not mounted.\n");
else if (kerr == -ENOENT && uerr == -ENOENT)
pr_warning("Please rebuild kernel with CONFIG_KPROBE_EVENTS "
"or/and CONFIG_UPROBE_EVENTS.\n");
else {
char sbuf[STRERR_BUFSIZE];
pr_warning("Failed to open kprobe events: %s.\n",
str_error_r(-kerr, sbuf, sizeof(sbuf)));
pr_warning("Failed to open uprobe events: %s.\n",
str_error_r(-uerr, sbuf, sizeof(sbuf)));
}
}
int open_trace_file(const char *trace_file, bool readwrite)
{
char buf[PATH_MAX];
int ret;
ret = e_snprintf(buf, PATH_MAX, "%s/%s", tracing_path_mount(), trace_file);
if (ret >= 0) {
pr_debug("Opening %s write=%d\n", buf, readwrite);
if (readwrite && !probe_event_dry_run)
ret = open(buf, O_RDWR | O_APPEND, 0);
else
ret = open(buf, O_RDONLY, 0);
if (ret < 0)
ret = -errno;
}
return ret;
}
static int open_kprobe_events(bool readwrite)
{
return open_trace_file("kprobe_events", readwrite);
}
static int open_uprobe_events(bool readwrite)
{
return open_trace_file("uprobe_events", readwrite);
}
int probe_file__open(int flag)
{
int fd;
if (flag & PF_FL_UPROBE)
fd = open_uprobe_events(flag & PF_FL_RW);
else
fd = open_kprobe_events(flag & PF_FL_RW);
if (fd < 0)
print_open_warning(fd, flag & PF_FL_UPROBE);
return fd;
}
int probe_file__open_both(int *kfd, int *ufd, int flag)
{
if (!kfd || !ufd)
return -EINVAL;
*kfd = open_kprobe_events(flag & PF_FL_RW);
*ufd = open_uprobe_events(flag & PF_FL_RW);
if (*kfd < 0 && *ufd < 0) {
print_both_open_warning(*kfd, *ufd);
return *kfd;
}
return 0;
}
/* Get raw string list of current kprobe_events or uprobe_events */
struct strlist *probe_file__get_rawlist(int fd)
{
int ret, idx, fddup;
FILE *fp;
char buf[MAX_CMDLEN];
char *p;
struct strlist *sl;
if (fd < 0)
return NULL;
sl = strlist__new(NULL, NULL);
if (sl == NULL)
return NULL;
fddup = dup(fd);
if (fddup < 0)
goto out_free_sl;
fp = fdopen(fddup, "r");
if (!fp)
goto out_close_fddup;
while (!feof(fp)) {
p = fgets(buf, MAX_CMDLEN, fp);
if (!p)
break;
idx = strlen(p) - 1;
if (p[idx] == '\n')
p[idx] = '\0';
ret =