2019-05-27 09:55:06 +03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2015-07-15 12:14:07 +03:00
|
|
|
/*
|
|
|
|
* probe-file.c : operate ftrace k/uprobe events files
|
|
|
|
*
|
|
|
|
* Written by Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
|
|
|
|
*/
|
2017-04-18 16:46:11 +03:00
|
|
|
#include <errno.h>
|
2017-09-11 16:50:26 +03:00
|
|
|
#include <fcntl.h>
|
2017-04-20 02:57:47 +03:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2016-06-15 06:28:30 +03:00
|
|
|
#include <sys/uio.h>
|
2017-04-20 02:57:47 +03:00
|
|
|
#include <unistd.h>
|
2019-07-04 17:32:27 +03:00
|
|
|
#include <linux/zalloc.h>
|
2019-01-22 16:24:34 +03:00
|
|
|
#include "namespaces.h"
|
2015-07-15 12:14:07 +03:00
|
|
|
#include "event.h"
|
|
|
|
#include "strlist.h"
|
2017-04-18 16:57:25 +03:00
|
|
|
#include "strfilter.h"
|
2015-07-15 12:14:07 +03:00
|
|
|
#include "debug.h"
|
2019-09-10 18:16:27 +03:00
|
|
|
#include "build-id.h"
|
2019-08-30 15:43:25 +03:00
|
|
|
#include "dso.h"
|
2015-07-15 12:14:07 +03:00
|
|
|
#include "color.h"
|
|
|
|
#include "symbol.h"
|
2019-08-30 18:52:25 +03:00
|
|
|
#include "strbuf.h"
|
2015-09-02 10:56:45 +03:00
|
|
|
#include <api/fs/tracing_path.h>
|
2021-06-06 18:49:28 +03:00
|
|
|
#include <api/fs/fs.h>
|
2015-07-15 12:14:07 +03:00
|
|
|
#include "probe-event.h"
|
|
|
|
#include "probe-file.h"
|
|
|
|
#include "session.h"
|
2016-12-14 03:07:32 +03:00
|
|
|
#include "perf_regs.h"
|
2017-04-17 22:51:59 +03:00
|
|
|
#include "string2.h"
|
2015-07-15 12:14:07 +03:00
|
|
|
|
2017-02-07 08:45:47 +03:00
|
|
|
/* 4096 - 2 ('\n' + '\0') */
|
|
|
|
#define MAX_CMDLEN 4094
|
2015-07-15 12:14:07 +03:00
|
|
|
|
2021-06-06 18:49:28 +03:00
|
|
|
static bool print_common_warning(int err, bool readwrite)
|
|
|
|
{
|
|
|
|
if (err == -EACCES)
|
|
|
|
pr_warning("No permission to %s tracefs.\nPlease %s\n",
|
|
|
|
readwrite ? "write" : "read",
|
|
|
|
readwrite ? "run this command again with sudo." :
|
|
|
|
"try 'sudo mount -o remount,mode=755 /sys/kernel/tracing/'");
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool print_configure_probe_event(int kerr, int uerr)
|
|
|
|
{
|
|
|
|
const char *config, *file;
|
|
|
|
|
|
|
|
if (kerr == -ENOENT && uerr == -ENOENT) {
|
|
|
|
file = "{k,u}probe_events";
|
|
|
|
config = "CONFIG_KPROBE_EVENTS=y and CONFIG_UPROBE_EVENTS=y";
|
|
|
|
} else if (kerr == -ENOENT) {
|
|
|
|
file = "kprobe_events";
|
|
|
|
config = "CONFIG_KPROBE_EVENTS=y";
|
|
|
|
} else if (uerr == -ENOENT) {
|
|
|
|
file = "uprobe_events";
|
|
|
|
config = "CONFIG_UPROBE_EVENTS=y";
|
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!debugfs__configured() && !tracefs__configured())
|
|
|
|
pr_warning("Debugfs or tracefs is not mounted\n"
|
|
|
|
"Please try 'sudo mount -t tracefs nodev /sys/kernel/tracing/'\n");
|
|
|
|
else
|
|
|
|
pr_warning("%s/%s does not exist.\nPlease rebuild kernel with %s.\n",
|
|
|
|
tracing_path_mount(), file, config);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_open_warning(int err, bool uprobe, bool readwrite)
|
2015-07-15 12:14:07 +03:00
|
|
|
{
|
|
|
|
char sbuf[STRERR_BUFSIZE];
|
|
|
|
|
2021-06-06 18:49:28 +03:00
|
|
|
if (print_common_warning(err, readwrite))
|
|
|
|
return;
|
2015-07-15 12:14:07 +03:00
|
|
|
|
2021-06-06 18:49:28 +03:00
|
|
|
if (print_configure_probe_event(uprobe ? 0 : err, uprobe ? err : 0))
|
|
|
|
return;
|
2015-07-15 12:14:07 +03:00
|
|
|
|
2021-06-06 18:49:28 +03:00
|
|
|
pr_warning("Failed to open %s/%cprobe_events: %s\n",
|
|
|
|
tracing_path_mount(), uprobe ? 'u' : 'k',
|
|
|
|
str_error_r(-err, sbuf, sizeof(sbuf)));
|
2015-07-15 12:14:07 +03:00
|
|
|
}
|
|
|
|
|
2021-06-06 18:49:28 +03:00
|
|
|
static void print_both_open_warning(int kerr, int uerr, bool readwrite)
|
2015-07-15 12:14:07 +03:00
|
|
|
{
|
2021-06-06 18:49:28 +03:00
|
|
|
char sbuf[STRERR_BUFSIZE];
|
|
|
|
|
|
|
|
if (kerr == uerr && print_common_warning(kerr, readwrite))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (print_configure_probe_event(kerr, uerr))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (kerr < 0)
|
|
|
|
pr_warning("Failed to open %s/kprobe_events: %s.\n",
|
|
|
|
tracing_path_mount(),
|
tools: Introduce str_error_r()
The tools so far have been using the strerror_r() GNU variant, that
returns a string, be it the buffer passed or something else.
But that, besides being tricky in cases where we expect that the
function using strerror_r() returns the error formatted in a provided
buffer (we have to check if it returned something else and copy that
instead), breaks the build on systems not using glibc, like Alpine
Linux, where musl libc is used.
So, introduce yet another wrapper, str_error_r(), that has the GNU
interface, but uses the portable XSI variant of strerror_r(), so that
users rest asured that the provided buffer is used and it is what is
returned.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-d4t42fnf48ytlk8rjxs822tf@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-07-06 17:56:20 +03:00
|
|
|
str_error_r(-kerr, sbuf, sizeof(sbuf)));
|
2021-06-06 18:49:28 +03:00
|
|
|
if (uerr < 0)
|
|
|
|
pr_warning("Failed to open %s/uprobe_events: %s.\n",
|
|
|
|
tracing_path_mount(),
|
tools: Introduce str_error_r()
The tools so far have been using the strerror_r() GNU variant, that
returns a string, be it the buffer passed or something else.
But that, besides being tricky in cases where we expect that the
function using strerror_r() returns the error formatted in a provided
buffer (we have to check if it returned something else and copy that
instead), breaks the build on systems not using glibc, like Alpine
Linux, where musl libc is used.
So, introduce yet another wrapper, str_error_r(), that has the GNU
interface, but uses the portable XSI variant of strerror_r(), so that
users rest asured that the provided buffer is used and it is what is
returned.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-d4t42fnf48ytlk8rjxs822tf@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-07-06 17:56:20 +03:00
|
|
|
str_error_r(-uerr, sbuf, sizeof(sbuf)));
|
2015-07-15 12:14:07 +03:00
|
|
|
}
|
|
|
|
|
2017-02-23 14:37:23 +03:00
|
|
|
int open_trace_file(const char *trace_file, bool readwrite)
|
2015-07-15 12:14:07 +03:00
|
|
|
{
|
|
|
|
char buf[PATH_MAX];
|
|
|
|
int ret;
|
|
|
|
|
2018-05-16 22:27:14 +03:00
|
|
|
ret = e_snprintf(buf, PATH_MAX, "%s/%s", tracing_path_mount(), trace_file);
|
2015-07-15 12:14:07 +03:00
|
|
|
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)
|
|
|
|
{
|
2017-02-23 14:37:23 +03:00
|
|
|
return open_trace_file("kprobe_events", readwrite);
|
2015-07-15 12:14:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int open_uprobe_events(bool readwrite)
|
|
|
|
{
|
2017-02-23 14:37:23 +03:00
|
|
|
return open_trace_file("uprobe_events", readwrite);
|
2015-07-15 12:14:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2021-06-06 18:49:28 +03:00
|
|
|
print_open_warning(fd, flag & PF_FL_UPROBE, flag & PF_FL_RW);
|
2015-07-15 12:14:07 +03:00
|
|
|
|
|
|
|
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) {
|
2021-06-06 18:49:28 +03:00
|
|
|
print_both_open_warning(*kfd, *ufd, flag & PF_FL_RW);
|
2015-07-15 12:14:07 +03:00
|
|
|
return *kfd;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get raw string list of current kprobe_events or uprobe_events */
|
|
|
|
struct strlist *probe_file__get_rawlist(int fd)
|
|
|
|
{
|
2016-08-13 00:44:56 +03:00
|
|
|
int ret, idx, fddup;
|
2015-07-15 12:14:07 +03:00
|
|
|
FILE *fp;
|
|
|
|
char buf[MAX_CMDLEN];
|
|
|
|
char *p;
|
|
|
|
struct strlist *sl;
|
|
|
|
|
2015-11-06 12:50:15 +03:00
|
|
|
if (fd < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2015-07-15 12:14:07 +03:00
|
|
|
sl = strlist__new(NULL, NULL);
|
2016-08-15 23:06:47 +03:00
|
|
|
if (sl == NULL)
|
|
|
|
return NULL;
|
2015-07-15 12:14:07 +03:00
|
|
|
|
2016-08-13 00:44:56 +03:00
|
|
|
fddup = dup(fd);
|
|
|
|
if (fddup < 0)
|
|
|
|
goto out_free_sl;
|
|
|
|
|
|
|
|
fp = fdopen(fddup, "r");
|
|
|
|
if (!fp)
|
|
|
|
goto out_close_fddup;
|
|
|
|
|
2015-07-15 12:14:07 +03:00
|
|
|
while (!feof(fp)) {
|
|
|
|
p = fgets(buf, MAX_CMDLEN, fp);
|
|
|
|
if (!p)
|
|
|
|
break;
|
|
|
|
|
|
|
|
idx = strlen(p) - 1;
|
|
|
|
if (p[idx] == '\n')
|
|
|
|
p[idx] = '\0';
|
|
|
|
ret = strlist__add(sl, buf);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_debug("strlist__add failed (%d)\n", ret);
|
2016-08-15 23:06:47 +03:00
|
|
|
goto out_close_fp;
|
2015-07-15 12:14:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
return sl;
|
2016-08-13 00:44:56 +03:00
|
|
|
|
2016-08-15 23:06:47 +03:00
|
|
|
out_close_fp:
|
|
|
|
fclose(fp);
|
|
|
|
goto out_free_sl;
|
2016-08-13 00:44:56 +03:00
|
|
|
out_close_fddup:
|
|
|
|
close(fddup);
|
|
|
|
out_free_sl:
|
|
|
|
strlist__delete(sl);
|
|
|
|
return NULL;
|
2015-07-15 12:14:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct strlist *__probe_file__get_namelist(int fd, bool include_group)
|
|
|
|
{
|
|
|
|
char buf[128];
|
|
|
|
struct strlist *sl, *rawlist;
|
|
|
|
struct str_node *ent;
|
|
|
|
struct probe_trace_event tev;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
memset(&tev, 0, sizeof(tev));
|
|
|
|
rawlist = probe_file__get_rawlist(fd);
|
|
|
|
if (!rawlist)
|
|
|
|
return NULL;
|
|
|
|
sl = strlist__new(NULL, NULL);
|
2016-06-23 17:31:20 +03:00
|
|
|
strlist__for_each_entry(ent, rawlist) {
|
2015-07-15 12:14:07 +03:00
|
|
|
ret = parse_probe_trace_command(ent->s, &tev);
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
if (include_group) {
|
|
|
|
ret = e_snprintf(buf, 128, "%s:%s", tev.group,
|
|
|
|
tev.event);
|
|
|
|
if (ret >= 0)
|
|
|
|
ret = strlist__add(sl, buf);
|
|
|
|
} else
|
|
|
|
ret = strlist__add(sl, tev.event);
|
|
|
|
clear_probe_trace_event(&tev);
|
2020-02-28 10:57:42 +03:00
|
|
|
/* Skip if there is same name multi-probe event in the list */
|
|
|
|
if (ret == -EEXIST)
|
|
|
|
ret = 0;
|
2015-07-15 12:14:07 +03:00
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
strlist__delete(rawlist);
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
strlist__delete(sl);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return sl;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get current perf-probe event names */
|
|
|
|
struct strlist *probe_file__get_namelist(int fd)
|
|
|
|
{
|
|
|
|
return __probe_file__get_namelist(fd, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
int probe_file__add_event(int fd, struct probe_trace_event *tev)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
char *buf = synthesize_probe_trace_command(tev);
|
|
|
|
char sbuf[STRERR_BUFSIZE];
|
|
|
|
|
|
|
|
if (!buf) {
|
|
|
|
pr_debug("Failed to synthesize probe trace event.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_debug("Writing event: %s\n", buf);
|
|
|
|
if (!probe_event_dry_run) {
|
2016-04-26 12:03:04 +03:00
|
|
|
if (write(fd, buf, strlen(buf)) < (int)strlen(buf)) {
|
2015-07-15 12:14:07 +03:00
|
|
|
ret = -errno;
|
|
|
|
pr_warning("Failed to write event: %s\n",
|
tools: Introduce str_error_r()
The tools so far have been using the strerror_r() GNU variant, that
returns a string, be it the buffer passed or something else.
But that, besides being tricky in cases where we expect that the
function using strerror_r() returns the error formatted in a provided
buffer (we have to check if it returned something else and copy that
instead), breaks the build on systems not using glibc, like Alpine
Linux, where musl libc is used.
So, introduce yet another wrapper, str_error_r(), that has the GNU
interface, but uses the portable XSI variant of strerror_r(), so that
users rest asured that the provided buffer is used and it is what is
returned.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-d4t42fnf48ytlk8rjxs822tf@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-07-06 17:56:20 +03:00
|
|
|
str_error_r(errno, sbuf, sizeof(sbuf)));
|
2015-07-15 12:14:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
free(buf);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __del_trace_probe_event(int fd, struct str_node *ent)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
char buf[128];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Convert from perf-probe event to trace-probe event */
|
|
|
|
ret = e_snprintf(buf, 128, "-:%s", ent->s);
|
|
|
|
if (ret < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
p = strchr(buf + 2, ':');
|
|
|
|
if (!p) {
|
|
|
|
pr_debug("Internal error: %s should have ':' but not.\n",
|
|
|
|
ent->s);
|
|
|
|
ret = -ENOTSUP;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
*p = '/';
|
|
|
|
|
|
|
|
pr_debug("Writing event: %s\n", buf);
|
|
|
|
ret = write(fd, buf, strlen(buf));
|
|
|
|
if (ret < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
error:
|
|
|
|
pr_warning("Failed to delete event: %s\n",
|
tools: Introduce str_error_r()
The tools so far have been using the strerror_r() GNU variant, that
returns a string, be it the buffer passed or something else.
But that, besides being tricky in cases where we expect that the
function using strerror_r() returns the error formatted in a provided
buffer (we have to check if it returned something else and copy that
instead), breaks the build on systems not using glibc, like Alpine
Linux, where musl libc is used.
So, introduce yet another wrapper, str_error_r(), that has the GNU
interface, but uses the portable XSI variant of strerror_r(), so that
users rest asured that the provided buffer is used and it is what is
returned.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-d4t42fnf48ytlk8rjxs822tf@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-07-06 17:56:20 +03:00
|
|
|
str_error_r(-ret, buf, sizeof(buf)));
|
2015-07-15 12:14:07 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-09-04 15:16:03 +03:00
|
|
|
int probe_file__get_events(int fd, struct strfilter *filter,
|
|
|
|
struct strlist *plist)
|
2015-07-15 12:14:07 +03:00
|
|
|
{
|
|
|
|
struct strlist *namelist;
|
|
|
|
struct str_node *ent;
|
|
|
|
const char *p;
|
|
|
|
int ret = -ENOENT;
|
|
|
|
|
2015-11-06 12:50:15 +03:00
|
|
|
if (!plist)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2015-07-15 12:14:07 +03:00
|
|
|
namelist = __probe_file__get_namelist(fd, true);
|
|
|
|
if (!namelist)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2016-06-23 17:31:20 +03:00
|
|
|
strlist__for_each_entry(ent, namelist) {
|
2015-07-15 12:14:07 +03:00
|
|
|
p = strchr(ent->s, ':');
|
|
|
|
if ((p && strfilter__compare(filter, p + 1)) ||
|
|
|
|
strfilter__compare(filter, ent->s)) {
|
2020-02-26 17:30:04 +03:00
|
|
|
ret = strlist__add(plist, ent->s);
|
|
|
|
if (ret == -ENOMEM) {
|
|
|
|
pr_err("strlist__add failed with -ENOMEM\n");
|
|
|
|
goto out;
|
|
|
|
}
|
2015-09-04 15:16:02 +03:00
|
|
|
ret = 0;
|
2015-07-15 12:14:07 +03:00
|
|
|
}
|
|
|
|
}
|
2020-02-26 17:30:04 +03:00
|
|
|
out:
|
2015-07-15 12:14:07 +03:00
|
|
|
strlist__delete(namelist);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2015-09-04 15:16:02 +03:00
|
|
|
|
2015-09-04 15:16:03 +03:00
|
|
|
int probe_file__del_strlist(int fd, struct strlist *namelist)
|
2015-09-04 15:16:02 +03:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct str_node *ent;
|
|
|
|
|
2016-06-23 17:31:20 +03:00
|
|
|
strlist__for_each_entry(ent, namelist) {
|
2015-09-04 15:16:02 +03:00
|
|
|
ret = __del_trace_probe_event(fd, ent);
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int probe_file__del_events(int fd, struct strfilter *filter)
|
|
|
|
{
|
|
|
|
struct strlist *namelist;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
namelist = strlist__new(NULL, NULL);
|
|
|
|
if (!namelist)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = probe_file__get_events(fd, filter, namelist);
|
|
|
|
if (ret < 0)
|
2021-07-15 19:07:25 +03:00
|
|
|
goto out;
|
2015-09-04 15:16:02 +03:00
|
|
|
|
|
|
|
ret = probe_file__del_strlist(fd, namelist);
|
2021-07-15 19:07:25 +03:00
|
|
|
out:
|
2015-09-04 15:16:02 +03:00
|
|
|
strlist__delete(namelist);
|
|
|
|
return ret;
|
|
|
|
}
|
2016-06-15 06:28:30 +03:00
|
|
|
|
|
|
|
/* Caller must ensure to remove this entry from list */
|
|
|
|
static void probe_cache_entry__delete(struct probe_cache_entry *entry)
|
|
|
|
{
|
|
|
|
if (entry) {
|
|
|
|
BUG_ON(!list_empty(&entry->node));
|
|
|
|
|
|
|
|
strlist__delete(entry->tevlist);
|
|
|
|
clear_perf_probe_event(&entry->pev);
|
|
|
|
zfree(&entry->spev);
|
|
|
|
free(entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct probe_cache_entry *
|
|
|
|
probe_cache_entry__new(struct perf_probe_event *pev)
|
|
|
|
{
|
|
|
|
struct probe_cache_entry *entry = zalloc(sizeof(*entry));
|
|
|
|
|
|
|
|
if (entry) {
|
|
|
|
INIT_LIST_HEAD(&entry->node);
|
|
|
|
entry->tevlist = strlist__new(NULL, NULL);
|
|
|
|
if (!entry->tevlist)
|
|
|
|
zfree(&entry);
|
|
|
|
else if (pev) {
|
|
|
|
entry->spev = synthesize_perf_probe_command(pev);
|
|
|
|
if (!entry->spev ||
|
|
|
|
perf_probe_event__copy(&entry->pev, pev) < 0) {
|
|
|
|
probe_cache_entry__delete(entry);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
2016-07-12 13:05:18 +03:00
|
|
|
int probe_cache_entry__get_event(struct probe_cache_entry *entry,
|
|
|
|
struct probe_trace_event **tevs)
|
|
|
|
{
|
|
|
|
struct probe_trace_event *tev;
|
|
|
|
struct str_node *node;
|
|
|
|
int ret, i;
|
|
|
|
|
|
|
|
ret = strlist__nr_entries(entry->tevlist);
|
|
|
|
if (ret > probe_conf.max_probes)
|
|
|
|
return -E2BIG;
|
|
|
|
|
|
|
|
*tevs = zalloc(ret * sizeof(*tev));
|
|
|
|
if (!*tevs)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
strlist__for_each_entry(node, entry->tevlist) {
|
|
|
|
tev = &(*tevs)[i++];
|
|
|
|
ret = parse_probe_trace_command(node->s, tev);
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For the kernel probe caches, pass target = NULL or DSO__NAME_KALLSYMS */
|
2017-07-06 04:48:11 +03:00
|
|
|
static int probe_cache__open(struct probe_cache *pcache, const char *target,
|
|
|
|
struct nsinfo *nsi)
|
2016-06-15 06:28:30 +03:00
|
|
|
{
|
|
|
|
char cpath[PATH_MAX];
|
|
|
|
char sbuildid[SBUILD_ID_SIZE];
|
2016-07-01 11:03:26 +03:00
|
|
|
char *dir_name = NULL;
|
2016-07-12 13:05:18 +03:00
|
|
|
bool is_kallsyms = false;
|
2016-06-15 06:28:30 +03:00
|
|
|
int ret, fd;
|
2017-07-06 04:48:11 +03:00
|
|
|
struct nscookie nsc;
|
2016-06-15 06:28:30 +03:00
|
|
|
|
2016-07-01 11:03:26 +03:00
|
|
|
if (target && build_id_cache__cached(target)) {
|
|
|
|
/* This is a cached buildid */
|
2018-12-06 17:50:08 +03:00
|
|
|
strlcpy(sbuildid, target, SBUILD_ID_SIZE);
|
2016-07-01 11:03:26 +03:00
|
|
|
dir_name = build_id_cache__linkname(sbuildid, NULL, 0);
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
|
2016-07-12 13:05:18 +03:00
|
|
|
if (!target || !strcmp(target, DSO__NAME_KALLSYMS)) {
|
2016-06-15 06:28:30 +03:00
|
|
|
target = DSO__NAME_KALLSYMS;
|
2016-07-12 13:05:18 +03:00
|
|
|
is_kallsyms = true;
|
2016-06-15 06:28:30 +03:00
|
|
|
ret = sysfs__sprintf_build_id("/", sbuildid);
|
2017-07-06 04:48:11 +03:00
|
|
|
} else {
|
|
|
|
nsinfo__mountns_enter(nsi, &nsc);
|
2016-07-12 13:05:18 +03:00
|
|
|
ret = filename__sprintf_build_id(target, sbuildid);
|
2017-07-06 04:48:11 +03:00
|
|
|
nsinfo__mountns_exit(&nsc);
|
|
|
|
}
|
2016-07-12 13:05:18 +03:00
|
|
|
|
2016-06-15 06:28:30 +03:00
|
|
|
if (ret < 0) {
|
|
|
|
pr_debug("Failed to get build-id from %s.\n", target);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we have no buildid cache, make it */
|
|
|
|
if (!build_id_cache__cached(sbuildid)) {
|
2017-07-06 04:48:11 +03:00
|
|
|
ret = build_id_cache__add_s(sbuildid, target, nsi,
|
2016-06-15 06:28:30 +03:00
|
|
|
is_kallsyms, NULL);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_debug("Failed to add build-id cache: %s\n", target);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-06 04:48:11 +03:00
|
|
|
dir_name = build_id_cache__cachedir(sbuildid, target, nsi, is_kallsyms,
|
2016-06-15 06:28:30 +03:00
|
|
|
false);
|
2016-07-01 11:03:26 +03:00
|
|
|
found:
|
|
|
|
if (!dir_name) {
|
|
|
|
pr_debug("Failed to get cache from %s\n", target);
|
2016-06-15 06:28:30 +03:00
|
|
|
return -ENOMEM;
|
2016-07-01 11:03:26 +03:00
|
|
|
}
|
2016-06-15 06:28:30 +03:00
|
|
|
|
|
|
|
snprintf(cpath, PATH_MAX, "%s/probes", dir_name);
|
|
|
|
fd = open(cpath, O_CREAT | O_RDWR, 0644);
|
|
|
|
if (fd < 0)
|
|
|
|
pr_debug("Failed to open cache(%d): %s\n", fd, cpath);
|
|
|
|
free(dir_name);
|
|
|
|
pcache->fd = fd;
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int probe_cache__load(struct probe_cache *pcache)
|
|
|
|
{
|
|
|
|
struct probe_cache_entry *entry = NULL;
|
|
|
|
char buf[MAX_CMDLEN], *p;
|
2016-08-13 00:44:56 +03:00
|
|
|
int ret = 0, fddup;
|
2016-06-15 06:28:30 +03:00
|
|
|
FILE *fp;
|
|
|
|
|
2016-08-13 00:44:56 +03:00
|
|
|
fddup = dup(pcache->fd);
|
|
|
|
if (fddup < 0)
|
|
|
|
return -errno;
|
|
|
|
fp = fdopen(fddup, "r");
|
2016-08-15 23:06:47 +03:00
|
|
|
if (!fp) {
|
|
|
|
close(fddup);
|
2016-06-15 06:28:30 +03:00
|
|
|
return -EINVAL;
|
2016-08-15 23:06:47 +03:00
|
|
|
}
|
2016-06-15 06:28:30 +03:00
|
|
|
|
|
|
|
while (!feof(fp)) {
|
|
|
|
if (!fgets(buf, MAX_CMDLEN, fp))
|
|
|
|
break;
|
|
|
|
p = strchr(buf, '\n');
|
|
|
|
if (p)
|
|
|
|
*p = '\0';
|
2016-07-01 11:04:10 +03:00
|
|
|
/* #perf_probe_event or %sdt_event */
|
|
|
|
if (buf[0] == '#' || buf[0] == '%') {
|
2016-06-15 06:28:30 +03:00
|
|
|
entry = probe_cache_entry__new(NULL);
|
|
|
|
if (!entry) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2016-07-01 11:04:10 +03:00
|
|
|
if (buf[0] == '%')
|
|
|
|
entry->sdt = true;
|
2016-06-15 06:28:30 +03:00
|
|
|
entry->spev = strdup(buf + 1);
|
|
|
|
if (entry->spev)
|
|
|
|
ret = parse_perf_probe_command(buf + 1,
|
|
|
|
&entry->pev);
|
|
|
|
else
|
|
|
|
ret = -ENOMEM;
|
|
|
|
if (ret < 0) {
|
|
|
|
probe_cache_entry__delete(entry);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
list_add_tail(&entry->node, &pcache->entries);
|
|
|
|
} else { /* trace_probe_event */
|
|
|
|
if (!entry) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2020-02-26 17:30:04 +03:00
|
|
|
ret = strlist__add(entry->tevlist, buf);
|
|
|
|
if (ret == -ENOMEM) {
|
|
|
|
pr_err("strlist__add failed with -ENOMEM\n");
|
|
|
|
goto out;
|
|
|
|
}
|
2016-06-15 06:28:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
fclose(fp);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct probe_cache *probe_cache__alloc(void)
|
|
|
|
{
|
|
|
|
struct probe_cache *pcache = zalloc(sizeof(*pcache));
|
|
|
|
|
|
|
|
if (pcache) {
|
|
|
|
INIT_LIST_HEAD(&pcache->entries);
|
|
|
|
pcache->fd = -EINVAL;
|
|
|
|
}
|
|
|
|
return pcache;
|
|
|
|
}
|
|
|
|
|
|
|
|
void probe_cache__purge(struct probe_cache *pcache)
|
|
|
|
{
|
|
|
|
struct probe_cache_entry *entry, *n;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(entry, n, &pcache->entries, node) {
|
|
|
|
list_del_init(&entry->node);
|
|
|
|
probe_cache_entry__delete(entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void probe_cache__delete(struct probe_cache *pcache)
|
|
|
|
{
|
|
|
|
if (!pcache)
|
|
|
|
return;
|
|
|
|
|
|
|
|
probe_cache__purge(pcache);
|
|
|
|
if (pcache->fd > 0)
|
|
|
|
close(pcache->fd);
|
|
|
|
free(pcache);
|
|
|
|
}
|
|
|
|
|
2017-07-06 04:48:11 +03:00
|
|
|
struct probe_cache *probe_cache__new(const char *target, struct nsinfo *nsi)
|
2016-06-15 06:28:30 +03:00
|
|
|
{
|
|
|
|
struct probe_cache *pcache = probe_cache__alloc();
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!pcache)
|
|
|
|
return NULL;
|
|
|
|
|
2017-07-06 04:48:11 +03:00
|
|
|
ret = probe_cache__open(pcache, target, nsi);
|
2016-06-15 06:28:30 +03:00
|
|
|
if (ret < 0) {
|
|
|
|
pr_debug("Cache open error: %d\n", ret);
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = probe_cache__load(pcache);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_debug("Cache read error: %d\n", ret);
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcache;
|
|
|
|
|
|
|
|
out_err:
|
|
|
|
probe_cache__delete(pcache);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool streql(const char *a, const char *b)
|
|
|
|
{
|
|
|
|
if (a == b)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!a || !b)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return !strcmp(a, b);
|
|
|
|
}
|
|
|
|
|
2016-07-01 11:03:12 +03:00
|
|
|
struct probe_cache_entry *
|
2016-06-15 06:28:30 +03:00
|
|
|
probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev)
|
|
|
|
{
|
|
|
|
struct probe_cache_entry *entry = NULL;
|
|
|
|
char *cmd = synthesize_perf_probe_command(pev);
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return NULL;
|
|
|
|
|
2016-07-12 13:05:04 +03:00
|
|
|
for_each_probe_cache_entry(entry, pcache) {
|
2016-07-12 13:04:43 +03:00
|
|
|
if (pev->sdt) {
|
|
|
|
if (entry->pev.event &&
|
|
|
|
streql(entry->pev.event, pev->event) &&
|
|
|
|
(!pev->group ||
|
|
|
|
streql(entry->pev.group, pev->group)))
|
|
|
|
goto found;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2016-06-15 06:28:30 +03:00
|
|
|
/* Hit if same event name or same command-string */
|
|
|
|
if ((pev->event &&
|
|
|
|
(streql(entry->pev.group, pev->group) &&
|
|
|
|
streql(entry->pev.event, pev->event))) ||
|
|
|
|
(!strcmp(entry->spev, cmd)))
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
entry = NULL;
|
|
|
|
|
|
|
|
found:
|
|
|
|
free(cmd);
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
2016-07-01 11:03:12 +03:00
|
|
|
struct probe_cache_entry *
|
|
|
|
probe_cache__find_by_name(struct probe_cache *pcache,
|
|
|
|
const char *group, const char *event)
|
|
|
|
{
|
|
|
|
struct probe_cache_entry *entry = NULL;
|
|
|
|
|
2016-07-12 13:05:04 +03:00
|
|
|
for_each_probe_cache_entry(entry, pcache) {
|
2016-07-01 11:03:12 +03:00
|
|
|
/* Hit if same event name or same command-string */
|
|
|
|
if (streql(entry->pev.group, group) &&
|
|
|
|
streql(entry->pev.event, event))
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
entry = NULL;
|
|
|
|
|
|
|
|
found:
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
2016-06-15 06:28:30 +03:00
|
|
|
int probe_cache__add_entry(struct probe_cache *pcache,
|
|
|
|
struct perf_probe_event *pev,
|
|
|
|
struct probe_trace_event *tevs, int ntevs)
|
|
|
|
{
|
|
|
|
struct probe_cache_entry *entry = NULL;
|
|
|
|
char *command;
|
|
|
|
int i, ret = 0;
|
|
|
|
|
|
|
|
if (!pcache || !pev || !tevs || ntevs <= 0) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove old cache entry */
|
|
|
|
entry = probe_cache__find(pcache, pev);
|
|
|
|
if (entry) {
|
|
|
|
list_del_init(&entry->node);
|
|
|
|
probe_cache_entry__delete(entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
entry = probe_cache_entry__new(pev);
|
|
|
|
if (!entry)
|
|
|
|
goto out_err;
|
|
|
|
|
|
|
|
for (i = 0; i < ntevs; i++) {
|
|
|
|
if (!tevs[i].point.symbol)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
command = synthesize_probe_trace_command(&tevs[i]);
|
|
|
|
if (!command)
|
|
|
|
goto out_err;
|
2020-02-26 17:30:04 +03:00
|
|
|
ret = strlist__add(entry->tevlist, command);
|
|
|
|
if (ret == -ENOMEM) {
|
|
|
|
pr_err("strlist__add failed with -ENOMEM\n");
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
2016-06-15 06:28:30 +03:00
|
|
|
free(command);
|
|
|
|
}
|
|
|
|
list_add_tail(&entry->node, &pcache->entries);
|
|
|
|
pr_debug("Added probe cache: %d\n", ntevs);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_err:
|
|
|
|
pr_debug("Failed to add probe caches\n");
|
|
|
|
probe_cache_entry__delete(entry);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-07-12 18:19:09 +03:00
|
|
|
#ifdef HAVE_GELF_GETNOTE_SUPPORT
|
2016-07-01 11:04:10 +03:00
|
|
|
static unsigned long long sdt_note__get_addr(struct sdt_note *note)
|
|
|
|
{
|
2018-08-20 07:42:50 +03:00
|
|
|
return note->bit32 ?
|
|
|
|
(unsigned long long)note->addr.a32[SDT_NOTE_IDX_LOC] :
|
|
|
|
(unsigned long long)note->addr.a64[SDT_NOTE_IDX_LOC];
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long long sdt_note__get_ref_ctr_offset(struct sdt_note *note)
|
|
|
|
{
|
|
|
|
return note->bit32 ?
|
|
|
|
(unsigned long long)note->addr.a32[SDT_NOTE_IDX_REFCTR] :
|
|
|
|
(unsigned long long)note->addr.a64[SDT_NOTE_IDX_REFCTR];
|
2016-07-01 11:04:10 +03:00
|
|
|
}
|
|
|
|
|
2016-12-14 03:07:32 +03:00
|
|
|
static const char * const type_to_suffix[] = {
|
|
|
|
":s64", "", "", "", ":s32", "", ":s16", ":s8",
|
|
|
|
"", ":u8", ":u16", "", ":u32", "", "", "", ":u64"
|
|
|
|
};
|
|
|
|
|
2017-03-28 12:47:53 +03:00
|
|
|
/*
|
|
|
|
* Isolate the string number and convert it into a decimal value;
|
|
|
|
* this will be an index to get suffix of the uprobe name (defining
|
|
|
|
* the type)
|
|
|
|
*/
|
|
|
|
static int sdt_arg_parse_size(char *n_ptr, const char **suffix)
|
|
|
|
{
|
|
|
|
long type_idx;
|
|
|
|
|
|
|
|
type_idx = strtol(n_ptr, NULL, 10);
|
|
|
|
if (type_idx < -8 || type_idx > 8) {
|
|
|
|
pr_debug4("Failed to get a valid sdt type\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*suffix = type_to_suffix[type_idx + 8];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-14 03:07:32 +03:00
|
|
|
static int synthesize_sdt_probe_arg(struct strbuf *buf, int i, const char *arg)
|
|
|
|
{
|
2017-03-28 12:47:53 +03:00
|
|
|
char *op, *desc = strdup(arg), *new_op = NULL;
|
|
|
|
const char *suffix = "";
|
2016-12-14 03:07:32 +03:00
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (desc == NULL) {
|
|
|
|
pr_debug4("Allocation error\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-03-28 12:47:53 +03:00
|
|
|
* Argument is in N@OP format. N is size of the argument and OP is
|
|
|
|
* the actual assembly operand. N can be omitted; in that case
|
|
|
|
* argument is just OP(without @).
|
2016-12-14 03:07:32 +03:00
|
|
|
*/
|
2017-03-28 12:47:53 +03:00
|
|
|
op = strchr(desc, '@');
|
|
|
|
if (op) {
|
|
|
|
op[0] = '\0';
|
|
|
|
op++;
|
2016-12-14 03:07:32 +03:00
|
|
|
|
2017-03-28 12:47:53 +03:00
|
|
|
if (sdt_arg_parse_size(desc, &suffix))
|
|
|
|
goto error;
|
|
|
|
} else {
|
|
|
|
op = desc;
|
2016-12-14 03:07:32 +03:00
|
|
|
}
|
|
|
|
|
2017-03-28 12:47:53 +03:00
|
|
|
ret = arch_sdt_arg_parse_op(op, &new_op);
|
2016-12-14 03:07:32 +03:00
|
|
|
|
2017-03-28 12:47:53 +03:00
|
|
|
if (ret < 0)
|
|
|
|
goto error;
|
2016-12-14 03:07:32 +03:00
|
|
|
|
2017-03-28 12:47:53 +03:00
|
|
|
if (ret == SDT_ARG_VALID) {
|
|
|
|
ret = strbuf_addf(buf, " arg%d=%s%s", i + 1, new_op, suffix);
|
2016-12-14 03:07:32 +03:00
|
|
|
if (ret < 0)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
error:
|
|
|
|
free(desc);
|
2017-03-28 12:47:53 +03:00
|
|
|
free(new_op);
|
2016-12-14 03:07:32 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *synthesize_sdt_probe_command(struct sdt_note *note,
|
|
|
|
const char *pathname,
|
|
|
|
const char *sdtgrp)
|
|
|
|
{
|
|
|
|
struct strbuf buf;
|
2020-12-24 16:52:10 +03:00
|
|
|
char *ret = NULL;
|
2018-08-20 07:42:50 +03:00
|
|
|
int i, args_count, err;
|
|
|
|
unsigned long long ref_ctr_offset;
|
perf probe: Fixup Arm64 SDT arguments
Arm64 ELF section '.note.stapsdt' uses string format "-4@[sp, NUM]" if
the probe is to access data in stack, e.g. below is an example for
dumping Arm64 ELF file and shows the argument format:
Arguments: -4@[sp, 12] -4@[sp, 8] -4@[sp, 4]
Comparing against other archs' argument format, Arm64's argument
introduces an extra space character in the middle of square brackets,
due to argv_split() uses space as splitter, the argument is wrongly
divided into two items.
To support Arm64 SDT, this patch fixes up for this case, if any item
contains sub string "[sp", concatenates the two continuous items. And
adds the detailed explaination in comment.
Signed-off-by: Leo Yan <leo.yan@linaro.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexandre Truong <alexandre.truong@arm.com>
Cc: Alexis Berlemont <alexis.berlemont@gmail.com>
Cc: He Zhe <zhe.he@windriver.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: John Garry <john.garry@huawei.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sumanth Korikkar <sumanthk@linux.ibm.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Will Deacon <will@kernel.org>
Cc: linux-arm-kernel@lists.infradead.org
Link: http://lore.kernel.org/lkml/20201225052751.24513-3-leo.yan@linaro.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-12-25 08:27:50 +03:00
|
|
|
char *arg;
|
|
|
|
int arg_idx = 0;
|
2016-12-14 03:07:32 +03:00
|
|
|
|
|
|
|
if (strbuf_init(&buf, 32) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2018-08-20 07:42:50 +03:00
|
|
|
err = strbuf_addf(&buf, "p:%s/%s %s:0x%llx",
|
|
|
|
sdtgrp, note->name, pathname,
|
|
|
|
sdt_note__get_addr(note));
|
|
|
|
|
|
|
|
ref_ctr_offset = sdt_note__get_ref_ctr_offset(note);
|
|
|
|
if (ref_ctr_offset && err >= 0)
|
|
|
|
err = strbuf_addf(&buf, "(0x%llx)", ref_ctr_offset);
|
|
|
|
|
|
|
|
if (err < 0)
|
2016-12-14 03:07:32 +03:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (!note->args)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (note->args) {
|
2020-12-24 16:52:10 +03:00
|
|
|
char **args = argv_split(note->args, &args_count);
|
|
|
|
|
|
|
|
if (args == NULL)
|
|
|
|
goto error;
|
2016-12-14 03:07:32 +03:00
|
|
|
|
perf probe: Fixup Arm64 SDT arguments
Arm64 ELF section '.note.stapsdt' uses string format "-4@[sp, NUM]" if
the probe is to access data in stack, e.g. below is an example for
dumping Arm64 ELF file and shows the argument format:
Arguments: -4@[sp, 12] -4@[sp, 8] -4@[sp, 4]
Comparing against other archs' argument format, Arm64's argument
introduces an extra space character in the middle of square brackets,
due to argv_split() uses space as splitter, the argument is wrongly
divided into two items.
To support Arm64 SDT, this patch fixes up for this case, if any item
contains sub string "[sp", concatenates the two continuous items. And
adds the detailed explaination in comment.
Signed-off-by: Leo Yan <leo.yan@linaro.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexandre Truong <alexandre.truong@arm.com>
Cc: Alexis Berlemont <alexis.berlemont@gmail.com>
Cc: He Zhe <zhe.he@windriver.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: John Garry <john.garry@huawei.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sumanth Korikkar <sumanthk@linux.ibm.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Will Deacon <will@kernel.org>
Cc: linux-arm-kernel@lists.infradead.org
Link: http://lore.kernel.org/lkml/20201225052751.24513-3-leo.yan@linaro.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-12-25 08:27:50 +03:00
|
|
|
for (i = 0; i < args_count; ) {
|
|
|
|
/*
|
|
|
|
* FIXUP: Arm64 ELF section '.note.stapsdt' uses string
|
|
|
|
* format "-4@[sp, NUM]" if a probe is to access data in
|
|
|
|
* the stack, e.g. below is an example for the SDT
|
|
|
|
* Arguments:
|
|
|
|
*
|
|
|
|
* Arguments: -4@[sp, 12] -4@[sp, 8] -4@[sp, 4]
|
|
|
|
*
|
|
|
|
* Since the string introduces an extra space character
|
|
|
|
* in the middle of square brackets, the argument is
|
|
|
|
* divided into two items. Fixup for this case, if an
|
|
|
|
* item contains sub string "[sp,", need to concatenate
|
|
|
|
* the two items.
|
|
|
|
*/
|
|
|
|
if (strstr(args[i], "[sp,") && (i+1) < args_count) {
|
|
|
|
err = asprintf(&arg, "%s %s", args[i], args[i+1]);
|
|
|
|
i += 2;
|
|
|
|
} else {
|
|
|
|
err = asprintf(&arg, "%s", args[i]);
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Failed to allocate memory */
|
|
|
|
if (err < 0) {
|
2020-12-24 16:52:10 +03:00
|
|
|
argv_free(args);
|
2016-12-14 03:07:32 +03:00
|
|
|
goto error;
|
2020-12-24 16:52:10 +03:00
|
|
|
}
|
perf probe: Fixup Arm64 SDT arguments
Arm64 ELF section '.note.stapsdt' uses string format "-4@[sp, NUM]" if
the probe is to access data in stack, e.g. below is an example for
dumping Arm64 ELF file and shows the argument format:
Arguments: -4@[sp, 12] -4@[sp, 8] -4@[sp, 4]
Comparing against other archs' argument format, Arm64's argument
introduces an extra space character in the middle of square brackets,
due to argv_split() uses space as splitter, the argument is wrongly
divided into two items.
To support Arm64 SDT, this patch fixes up for this case, if any item
contains sub string "[sp", concatenates the two continuous items. And
adds the detailed explaination in comment.
Signed-off-by: Leo Yan <leo.yan@linaro.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexandre Truong <alexandre.truong@arm.com>
Cc: Alexis Berlemont <alexis.berlemont@gmail.com>
Cc: He Zhe <zhe.he@windriver.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: John Garry <john.garry@huawei.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sumanth Korikkar <sumanthk@linux.ibm.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Will Deacon <will@kernel.org>
Cc: linux-arm-kernel@lists.infradead.org
Link: http://lore.kernel.org/lkml/20201225052751.24513-3-leo.yan@linaro.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-12-25 08:27:50 +03:00
|
|
|
|
|
|
|
if (synthesize_sdt_probe_arg(&buf, arg_idx, arg) < 0) {
|
|
|
|
free(arg);
|
|
|
|
argv_free(args);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(arg);
|
|
|
|
arg_idx++;
|
2016-12-14 03:07:32 +03:00
|
|
|
}
|
2020-12-24 16:52:10 +03:00
|
|
|
|
|
|
|
argv_free(args);
|
2016-12-14 03:07:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
ret = strbuf_detach(&buf, NULL);
|
|
|
|
error:
|
|
|
|
strbuf_release(&buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-07-01 11:04:10 +03:00
|
|
|
int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname)
|
|
|
|
{
|
|
|
|
struct probe_cache_entry *entry = NULL;
|
|
|
|
struct list_head sdtlist;
|
|
|
|
struct sdt_note *note;
|
|
|
|
char *buf;
|
|
|
|
char sdtgrp[64];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&sdtlist);
|
|
|
|
ret = get_sdt_note_list(&sdtlist, pathname);
|
|
|
|
if (ret < 0) {
|
2016-09-23 17:38:40 +03:00
|
|
|
pr_debug4("Failed to get sdt note: %d\n", ret);
|
2016-07-01 11:04:10 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
list_for_each_entry(note, &sdtlist, note_list) {
|
|
|
|
ret = snprintf(sdtgrp, 64, "sdt_%s", note->provider);
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
/* Try to find same-name entry */
|
|
|
|
entry = probe_cache__find_by_name(pcache, sdtgrp, note->name);
|
|
|
|
if (!entry) {
|
|
|
|
entry = probe_cache_entry__new(NULL);
|
|
|
|
if (!entry) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
entry->sdt = true;
|
|
|
|
ret = asprintf(&entry->spev, "%s:%s=%s", sdtgrp,
|
|
|
|
note->name, note->name);
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
entry->pev.event = strdup(note->name);
|
|
|
|
entry->pev.group = strdup(sdtgrp);
|
|
|
|
list_add_tail(&entry->node, &pcache->entries);
|
|
|
|
}
|
2016-12-14 03:07:32 +03:00
|
|
|
buf = synthesize_sdt_probe_command(note, pathname, sdtgrp);
|
|
|
|
if (!buf) {
|
|
|
|
ret = -ENOMEM;
|
2016-07-01 11:04:10 +03:00
|
|
|
break;
|
2016-12-14 03:07:32 +03:00
|
|
|
}
|
|
|
|
|
2020-02-26 17:30:04 +03:00
|
|
|
ret = strlist__add(entry->tevlist, buf);
|
|
|
|
|
2016-07-01 11:04:10 +03:00
|
|
|
free(buf);
|
|
|
|
entry = NULL;
|
2020-02-26 17:30:04 +03:00
|
|
|
|
|
|
|
if (ret == -ENOMEM) {
|
|
|
|
pr_err("strlist__add failed with -ENOMEM\n");
|
|
|
|
break;
|
|
|
|
}
|
2016-07-01 11:04:10 +03:00
|
|
|
}
|
|
|
|
if (entry) {
|
|
|
|
list_del_init(&entry->node);
|
|
|
|
probe_cache_entry__delete(entry);
|
|
|
|
}
|
|
|
|
cleanup_sdt_note_list(&sdtlist);
|
|
|
|
return ret;
|
|
|
|
}
|
2016-07-12 18:19:09 +03:00
|
|
|
#endif
|
2016-07-01 11:04:10 +03:00
|
|
|
|
2016-06-15 06:28:30 +03:00
|
|
|
static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd)
|
|
|
|
{
|
|
|
|
struct str_node *snode;
|
|
|
|
struct stat st;
|
|
|
|
struct iovec iov[3];
|
2016-07-01 11:04:10 +03:00
|
|
|
const char *prefix = entry->sdt ? "%" : "#";
|
2016-06-15 06:28:30 +03:00
|
|
|
int ret;
|
|
|
|
/* Save stat for rollback */
|
|
|
|
ret = fstat(fd, &st);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2016-07-01 11:04:10 +03:00
|
|
|
pr_debug("Writing cache: %s%s\n", prefix, entry->spev);
|
|
|
|
iov[0].iov_base = (void *)prefix; iov[0].iov_len = 1;
|
2016-06-15 06:28:30 +03:00
|
|
|
iov[1].iov_base = entry->spev; iov[1].iov_len = strlen(entry->spev);
|
|
|
|
iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1;
|
|
|
|
ret = writev(fd, iov, 3);
|
|
|
|
if (ret < (int)iov[1].iov_len + 2)
|
|
|
|
goto rollback;
|
|
|
|
|
2016-06-23 17:31:20 +03:00
|
|
|
strlist__for_each_entry(snode, entry->tevlist) {
|
2016-06-15 06:28:30 +03:00
|
|
|
iov[0].iov_base = (void *)snode->s;
|
|
|
|
iov[0].iov_len = strlen(snode->s);
|
|
|
|
iov[1].iov_base = (void *)"\n"; iov[1].iov_len = 1;
|
|
|
|
ret = writev(fd, iov, 2);
|
|
|
|
if (ret < (int)iov[0].iov_len + 1)
|
|
|
|
goto rollback;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
rollback:
|
|
|
|
/* Rollback to avoid cache file corruption */
|
|
|
|
if (ret > 0)
|
|
|
|
ret = -1;
|
|
|
|
if (ftruncate(fd, st.st_size) < 0)
|
|
|
|
ret = -2;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int probe_cache__commit(struct probe_cache *pcache)
|
|
|
|
{
|
|
|
|
struct probe_cache_entry *entry;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* TBD: if we do not update existing entries, skip it */
|
|
|
|
ret = lseek(pcache->fd, 0, SEEK_SET);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = ftruncate(pcache->fd, 0);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2016-07-12 13:05:04 +03:00
|
|
|
for_each_probe_cache_entry(entry, pcache) {
|
2016-06-15 06:28:30 +03:00
|
|
|
ret = probe_cache_entry__write(entry, pcache->fd);
|
|
|
|
pr_debug("Cache committed: %d\n", ret);
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
2016-07-01 11:03:26 +03:00
|
|
|
|
2016-07-01 11:03:36 +03:00
|
|
|
static bool probe_cache_entry__compare(struct probe_cache_entry *entry,
|
|
|
|
struct strfilter *filter)
|
|
|
|
{
|
|
|
|
char buf[128], *ptr = entry->spev;
|
|
|
|
|
|
|
|
if (entry->pev.event) {
|
|
|
|
snprintf(buf, 128, "%s:%s", entry->pev.group, entry->pev.event);
|
|
|
|
ptr = buf;
|
|
|
|
}
|
|
|
|
return strfilter__compare(filter, ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
int probe_cache__filter_purge(struct probe_cache *pcache,
|
|
|
|
struct strfilter *filter)
|
|
|
|
{
|
|
|
|
struct probe_cache_entry *entry, *tmp;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(entry, tmp, &pcache->entries, node) {
|
|
|
|
if (probe_cache_entry__compare(entry, filter)) {
|
|
|
|
pr_info("Removed cached event: %s\n", entry->spev);
|
|
|
|
list_del_init(&entry->node);
|
|
|
|
probe_cache_entry__delete(entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-01 11:03:26 +03:00
|
|
|
static int probe_cache__show_entries(struct probe_cache *pcache,
|
|
|
|
struct strfilter *filter)
|
|
|
|
{
|
|
|
|
struct probe_cache_entry *entry;
|
|
|
|
|
2016-07-12 13:05:04 +03:00
|
|
|
for_each_probe_cache_entry(entry, pcache) {
|
2016-07-01 11:03:36 +03:00
|
|
|
if (probe_cache_entry__compare(entry, filter))
|
2016-07-01 11:03:26 +03:00
|
|
|
printf("%s\n", entry->spev);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Show all cached probes */
|
|
|
|
int probe_cache__show_all_caches(struct strfilter *filter)
|
|
|
|
{
|
|
|
|
struct probe_cache *pcache;
|
|
|
|
struct strlist *bidlist;
|
|
|
|
struct str_node *nd;
|
|
|
|
char *buf = strfilter__string(filter);
|
|
|
|
|
|
|
|
pr_debug("list cache with filter: %s\n", buf);
|
|
|
|
free(buf);
|
|
|
|
|
2016-07-12 13:04:54 +03:00
|
|
|
bidlist = build_id_cache__list_all(true);
|
2016-07-01 11:03:26 +03:00
|
|
|
if (!bidlist) {
|
|
|
|
pr_debug("Failed to get buildids: %d\n", errno);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
strlist__for_each_entry(nd, bidlist) {
|
2017-07-06 04:48:11 +03:00
|
|
|
pcache = probe_cache__new(nd->s, NULL);
|
2016-07-01 11:03:26 +03:00
|
|
|
if (!pcache)
|
|
|
|
continue;
|
|
|
|
if (!list_empty(&pcache->entries)) {
|
|
|
|
buf = build_id_cache__origname(nd->s);
|
|
|
|
printf("%s (%s):\n", buf, nd->s);
|
|
|
|
free(buf);
|
|
|
|
probe_cache__show_entries(pcache, filter);
|
|
|
|
}
|
|
|
|
probe_cache__delete(pcache);
|
|
|
|
}
|
|
|
|
strlist__delete(bidlist);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-08-18 11:58:31 +03:00
|
|
|
|
2017-03-08 11:26:08 +03:00
|
|
|
enum ftrace_readme {
|
|
|
|
FTRACE_README_PROBE_TYPE_X = 0,
|
2017-03-08 11:26:09 +03:00
|
|
|
FTRACE_README_KRETPROBE_OFFSET,
|
2018-08-20 07:42:50 +03:00
|
|
|
FTRACE_README_UPROBE_REF_CTR,
|
2019-05-15 08:39:05 +03:00
|
|
|
FTRACE_README_USER_ACCESS,
|
2019-11-18 11:12:30 +03:00
|
|
|
FTRACE_README_MULTIPROBE_EVENT,
|
2019-11-18 11:12:40 +03:00
|
|
|
FTRACE_README_IMMEDIATE_VALUE,
|
2017-03-08 11:26:08 +03:00
|
|
|
FTRACE_README_END,
|
|
|
|
};
|
|
|
|
|
2016-08-18 11:58:31 +03:00
|
|
|
static struct {
|
|
|
|
const char *pattern;
|
2017-03-08 11:26:08 +03:00
|
|
|
bool avail;
|
|
|
|
} ftrace_readme_table[] = {
|
|
|
|
#define DEFINE_TYPE(idx, pat) \
|
|
|
|
[idx] = {.pattern = pat, .avail = false}
|
|
|
|
DEFINE_TYPE(FTRACE_README_PROBE_TYPE_X, "*type: * x8/16/32/64,*"),
|
2017-03-08 11:26:09 +03:00
|
|
|
DEFINE_TYPE(FTRACE_README_KRETPROBE_OFFSET, "*place (kretprobe): *"),
|
2018-08-20 07:42:50 +03:00
|
|
|
DEFINE_TYPE(FTRACE_README_UPROBE_REF_CTR, "*ref_ctr_offset*"),
|
2020-06-09 11:10:18 +03:00
|
|
|
DEFINE_TYPE(FTRACE_README_USER_ACCESS, "*u]<offset>*"),
|
2019-11-18 11:12:30 +03:00
|
|
|
DEFINE_TYPE(FTRACE_README_MULTIPROBE_EVENT, "*Create/append/*"),
|
2019-11-18 11:12:40 +03:00
|
|
|
DEFINE_TYPE(FTRACE_README_IMMEDIATE_VALUE, "*\\imm-value,*"),
|
2016-08-18 11:58:31 +03:00
|
|
|
};
|
|
|
|
|
2017-03-08 11:26:08 +03:00
|
|
|
static bool scan_ftrace_readme(enum ftrace_readme type)
|
2016-08-18 11:58:31 +03:00
|
|
|
{
|
2017-03-08 11:26:08 +03:00
|
|
|
int fd;
|
2016-08-18 11:58:31 +03:00
|
|
|
FILE *fp;
|
|
|
|
char *buf = NULL;
|
|
|
|
size_t len = 0;
|
2017-03-08 11:26:08 +03:00
|
|
|
bool ret = false;
|
|
|
|
static bool scanned = false;
|
2016-08-18 11:58:31 +03:00
|
|
|
|
2017-03-08 11:26:08 +03:00
|
|
|
if (scanned)
|
|
|
|
goto result;
|
2016-08-18 11:58:31 +03:00
|
|
|
|
2017-02-23 14:37:23 +03:00
|
|
|
fd = open_trace_file("README", false);
|
|
|
|
if (fd < 0)
|
2016-08-18 11:58:31 +03:00
|
|
|
return ret;
|
|
|
|
|
2017-02-23 14:37:23 +03:00
|
|
|
fp = fdopen(fd, "r");
|
|
|
|
if (!fp) {
|
|
|
|
close(fd);
|
|
|
|
return ret;
|
|
|
|
}
|
2016-08-18 11:58:31 +03:00
|
|
|
|
2017-03-08 11:26:08 +03:00
|
|
|
while (getline(&buf, &len, fp) > 0)
|
|
|
|
for (enum ftrace_readme i = 0; i < FTRACE_README_END; i++)
|
|
|
|
if (!ftrace_readme_table[i].avail)
|
|
|
|
ftrace_readme_table[i].avail =
|
|
|
|
strglobmatch(buf, ftrace_readme_table[i].pattern);
|
|
|
|
scanned = true;
|
2016-08-18 11:58:31 +03:00
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
free(buf);
|
|
|
|
|
2017-03-08 11:26:08 +03:00
|
|
|
result:
|
|
|
|
if (type >= FTRACE_README_END)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return ftrace_readme_table[type].avail;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool probe_type_is_available(enum probe_type type)
|
|
|
|
{
|
|
|
|
if (type >= PROBE_TYPE_END)
|
|
|
|
return false;
|
|
|
|
else if (type == PROBE_TYPE_X)
|
|
|
|
return scan_ftrace_readme(FTRACE_README_PROBE_TYPE_X);
|
|
|
|
|
|
|
|
return true;
|
2016-08-18 11:58:31 +03:00
|
|
|
}
|
2017-03-08 11:26:09 +03:00
|
|
|
|
|
|
|
bool kretprobe_offset_is_supported(void)
|
|
|
|
{
|
|
|
|
return scan_ftrace_readme(FTRACE_README_KRETPROBE_OFFSET);
|
|
|
|
}
|
2018-08-20 07:42:50 +03:00
|
|
|
|
|
|
|
bool uprobe_ref_ctr_is_supported(void)
|
|
|
|
{
|
|
|
|
return scan_ftrace_readme(FTRACE_README_UPROBE_REF_CTR);
|
|
|
|
}
|
2019-05-15 08:39:05 +03:00
|
|
|
|
|
|
|
bool user_access_is_supported(void)
|
|
|
|
{
|
|
|
|
return scan_ftrace_readme(FTRACE_README_USER_ACCESS);
|
|
|
|
}
|
2019-11-18 11:12:30 +03:00
|
|
|
|
|
|
|
bool multiprobe_event_is_supported(void)
|
|
|
|
{
|
|
|
|
return scan_ftrace_readme(FTRACE_README_MULTIPROBE_EVENT);
|
|
|
|
}
|
2019-11-18 11:12:40 +03:00
|
|
|
|
|
|
|
bool immediate_value_is_supported(void)
|
|
|
|
{
|
|
|
|
return scan_ftrace_readme(FTRACE_README_IMMEDIATE_VALUE);
|
|
|
|
}
|