Merge branch 'perf/core' into perf/probes
Conflicts: tools/perf/Makefile Merge reason: - fix the conflict - pick up the pr_*() infrastructure to queue up dependent patch Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/ftrace_event.h>
|
||||
|
||||
#include <asm/irq_regs.h>
|
||||
|
||||
@@ -1355,7 +1356,7 @@ static void perf_ctx_adjust_freq(struct perf_event_context *ctx)
|
||||
u64 interrupts, freq;
|
||||
|
||||
spin_lock(&ctx->lock);
|
||||
list_for_each_entry(event, &ctx->group_list, group_entry) {
|
||||
list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
|
||||
if (event->state != PERF_EVENT_STATE_ACTIVE)
|
||||
continue;
|
||||
|
||||
@@ -1658,6 +1659,8 @@ static struct perf_event_context *find_get_context(pid_t pid, int cpu)
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void perf_event_free_filter(struct perf_event *event);
|
||||
|
||||
static void free_event_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct perf_event *event;
|
||||
@@ -1665,6 +1668,7 @@ static void free_event_rcu(struct rcu_head *head)
|
||||
event = container_of(head, struct perf_event, rcu_head);
|
||||
if (event->ns)
|
||||
put_pid_ns(event->ns);
|
||||
perf_event_free_filter(event);
|
||||
kfree(event);
|
||||
}
|
||||
|
||||
@@ -1974,7 +1978,8 @@ unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int perf_event_set_output(struct perf_event *event, int output_fd);
|
||||
static int perf_event_set_output(struct perf_event *event, int output_fd);
|
||||
static int perf_event_set_filter(struct perf_event *event, void __user *arg);
|
||||
|
||||
static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
@@ -2002,6 +2007,9 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
case PERF_EVENT_IOC_SET_OUTPUT:
|
||||
return perf_event_set_output(event, arg);
|
||||
|
||||
case PERF_EVENT_IOC_SET_FILTER:
|
||||
return perf_event_set_filter(event, (void __user *)arg);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
@@ -3806,9 +3814,14 @@ static int perf_swevent_is_counting(struct perf_event *event)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int perf_tp_event_match(struct perf_event *event,
|
||||
struct perf_sample_data *data);
|
||||
|
||||
static int perf_swevent_match(struct perf_event *event,
|
||||
enum perf_type_id type,
|
||||
u32 event_id, struct pt_regs *regs)
|
||||
u32 event_id,
|
||||
struct perf_sample_data *data,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
if (!perf_swevent_is_counting(event))
|
||||
return 0;
|
||||
@@ -3826,6 +3839,10 @@ static int perf_swevent_match(struct perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (event->attr.type == PERF_TYPE_TRACEPOINT &&
|
||||
!perf_tp_event_match(event, data))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -3842,7 +3859,7 @@ static void perf_swevent_ctx_event(struct perf_event_context *ctx,
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
|
||||
if (perf_swevent_match(event, type, event_id, regs))
|
||||
if (perf_swevent_match(event, type, event_id, data, regs))
|
||||
perf_swevent_add(event, nr, nmi, data, regs);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
@@ -4086,6 +4103,7 @@ static const struct pmu perf_ops_task_clock = {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_EVENT_PROFILE
|
||||
|
||||
void perf_tp_event(int event_id, u64 addr, u64 count, void *record,
|
||||
int entry_size)
|
||||
{
|
||||
@@ -4109,8 +4127,15 @@ void perf_tp_event(int event_id, u64 addr, u64 count, void *record,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_tp_event);
|
||||
|
||||
extern int ftrace_profile_enable(int);
|
||||
extern void ftrace_profile_disable(int);
|
||||
static int perf_tp_event_match(struct perf_event *event,
|
||||
struct perf_sample_data *data)
|
||||
{
|
||||
void *record = data->raw->data;
|
||||
|
||||
if (likely(!event->filter) || filter_match_preds(event->filter, record))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tp_perf_event_destroy(struct perf_event *event)
|
||||
{
|
||||
@@ -4135,12 +4160,53 @@ static const struct pmu *tp_perf_event_init(struct perf_event *event)
|
||||
|
||||
return &perf_ops_generic;
|
||||
}
|
||||
|
||||
static int perf_event_set_filter(struct perf_event *event, void __user *arg)
|
||||
{
|
||||
char *filter_str;
|
||||
int ret;
|
||||
|
||||
if (event->attr.type != PERF_TYPE_TRACEPOINT)
|
||||
return -EINVAL;
|
||||
|
||||
filter_str = strndup_user(arg, PAGE_SIZE);
|
||||
if (IS_ERR(filter_str))
|
||||
return PTR_ERR(filter_str);
|
||||
|
||||
ret = ftrace_profile_set_filter(event, event->attr.config, filter_str);
|
||||
|
||||
kfree(filter_str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void perf_event_free_filter(struct perf_event *event)
|
||||
{
|
||||
ftrace_profile_free_filter(event);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int perf_tp_event_match(struct perf_event *event,
|
||||
struct perf_sample_data *data)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct pmu *tp_perf_event_init(struct perf_event *event)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int perf_event_set_filter(struct perf_event *event, void __user *arg)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static void perf_event_free_filter(struct perf_event *event)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_EVENT_PROFILE */
|
||||
|
||||
atomic_t perf_swevent_enabled[PERF_COUNT_SW_MAX];
|
||||
|
||||
@@ -4394,7 +4460,7 @@ err_size:
|
||||
goto out;
|
||||
}
|
||||
|
||||
int perf_event_set_output(struct perf_event *event, int output_fd)
|
||||
static int perf_event_set_output(struct perf_event *event, int output_fd)
|
||||
{
|
||||
struct perf_event *output_event = NULL;
|
||||
struct file *output_file = NULL;
|
||||
|
@@ -60,6 +60,13 @@ static int last_ftrace_enabled;
|
||||
/* Quick disabling of function tracer. */
|
||||
int function_trace_stop;
|
||||
|
||||
/* List for set_ftrace_pid's pids. */
|
||||
LIST_HEAD(ftrace_pids);
|
||||
struct ftrace_pid {
|
||||
struct list_head list;
|
||||
struct pid *pid;
|
||||
};
|
||||
|
||||
/*
|
||||
* ftrace_disabled is set when an anomaly is discovered.
|
||||
* ftrace_disabled is much stronger than ftrace_enabled.
|
||||
@@ -78,6 +85,10 @@ ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;
|
||||
ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub;
|
||||
ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub;
|
||||
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
static int ftrace_set_func(unsigned long *array, int *idx, char *buffer);
|
||||
#endif
|
||||
|
||||
static void ftrace_list_func(unsigned long ip, unsigned long parent_ip)
|
||||
{
|
||||
struct ftrace_ops *op = ftrace_list;
|
||||
@@ -155,7 +166,7 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
|
||||
else
|
||||
func = ftrace_list_func;
|
||||
|
||||
if (ftrace_pid_trace) {
|
||||
if (!list_empty(&ftrace_pids)) {
|
||||
set_ftrace_pid_function(func);
|
||||
func = ftrace_pid_func;
|
||||
}
|
||||
@@ -203,7 +214,7 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
|
||||
if (ftrace_list->next == &ftrace_list_end) {
|
||||
ftrace_func_t func = ftrace_list->func;
|
||||
|
||||
if (ftrace_pid_trace) {
|
||||
if (!list_empty(&ftrace_pids)) {
|
||||
set_ftrace_pid_function(func);
|
||||
func = ftrace_pid_func;
|
||||
}
|
||||
@@ -231,7 +242,7 @@ static void ftrace_update_pid_func(void)
|
||||
func = __ftrace_trace_function;
|
||||
#endif
|
||||
|
||||
if (ftrace_pid_trace) {
|
||||
if (!list_empty(&ftrace_pids)) {
|
||||
set_ftrace_pid_function(func);
|
||||
func = ftrace_pid_func;
|
||||
} else {
|
||||
@@ -821,8 +832,6 @@ static __init void ftrace_profile_debugfs(struct dentry *d_tracer)
|
||||
}
|
||||
#endif /* CONFIG_FUNCTION_PROFILER */
|
||||
|
||||
/* set when tracing only a pid */
|
||||
struct pid *ftrace_pid_trace;
|
||||
static struct pid * const ftrace_swapper_pid = &init_struct_pid;
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
@@ -1261,12 +1270,34 @@ static int ftrace_update_code(struct module *mod)
|
||||
ftrace_new_addrs = p->newlist;
|
||||
p->flags = 0L;
|
||||
|
||||
/* convert record (i.e, patch mcount-call with NOP) */
|
||||
if (ftrace_code_disable(mod, p)) {
|
||||
p->flags |= FTRACE_FL_CONVERTED;
|
||||
ftrace_update_cnt++;
|
||||
} else
|
||||
/*
|
||||
* Do the initial record convertion from mcount jump
|
||||
* to the NOP instructions.
|
||||
*/
|
||||
if (!ftrace_code_disable(mod, p)) {
|
||||
ftrace_free_rec(p);
|
||||
continue;
|
||||
}
|
||||
|
||||
p->flags |= FTRACE_FL_CONVERTED;
|
||||
ftrace_update_cnt++;
|
||||
|
||||
/*
|
||||
* If the tracing is enabled, go ahead and enable the record.
|
||||
*
|
||||
* The reason not to enable the record immediatelly is the
|
||||
* inherent check of ftrace_make_nop/ftrace_make_call for
|
||||
* correct previous instructions. Making first the NOP
|
||||
* conversion puts the module to the correct state, thus
|
||||
* passing the ftrace_make_call check.
|
||||
*/
|
||||
if (ftrace_start_up) {
|
||||
int failed = __ftrace_replace_code(p, 1);
|
||||
if (failed) {
|
||||
ftrace_bug(failed, p->ip);
|
||||
ftrace_free_rec(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stop = ftrace_now(raw_smp_processor_id());
|
||||
@@ -1656,60 +1687,6 @@ ftrace_regex_lseek(struct file *file, loff_t offset, int origin)
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum {
|
||||
MATCH_FULL,
|
||||
MATCH_FRONT_ONLY,
|
||||
MATCH_MIDDLE_ONLY,
|
||||
MATCH_END_ONLY,
|
||||
};
|
||||
|
||||
/*
|
||||
* (static function - no need for kernel doc)
|
||||
*
|
||||
* Pass in a buffer containing a glob and this function will
|
||||
* set search to point to the search part of the buffer and
|
||||
* return the type of search it is (see enum above).
|
||||
* This does modify buff.
|
||||
*
|
||||
* Returns enum type.
|
||||
* search returns the pointer to use for comparison.
|
||||
* not returns 1 if buff started with a '!'
|
||||
* 0 otherwise.
|
||||
*/
|
||||
static int
|
||||
ftrace_setup_glob(char *buff, int len, char **search, int *not)
|
||||
{
|
||||
int type = MATCH_FULL;
|
||||
int i;
|
||||
|
||||
if (buff[0] == '!') {
|
||||
*not = 1;
|
||||
buff++;
|
||||
len--;
|
||||
} else
|
||||
*not = 0;
|
||||
|
||||
*search = buff;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (buff[i] == '*') {
|
||||
if (!i) {
|
||||
*search = buff + 1;
|
||||
type = MATCH_END_ONLY;
|
||||
} else {
|
||||
if (type == MATCH_END_ONLY)
|
||||
type = MATCH_MIDDLE_ONLY;
|
||||
else
|
||||
type = MATCH_FRONT_ONLY;
|
||||
buff[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static int ftrace_match(char *str, char *regex, int len, int type)
|
||||
{
|
||||
int matched = 0;
|
||||
@@ -1758,7 +1735,7 @@ static void ftrace_match_records(char *buff, int len, int enable)
|
||||
int not;
|
||||
|
||||
flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;
|
||||
type = ftrace_setup_glob(buff, len, &search, ¬);
|
||||
type = filter_parse_regex(buff, len, &search, ¬);
|
||||
|
||||
search_len = strlen(search);
|
||||
|
||||
@@ -1826,7 +1803,7 @@ static void ftrace_match_module_records(char *buff, char *mod, int enable)
|
||||
}
|
||||
|
||||
if (strlen(buff)) {
|
||||
type = ftrace_setup_glob(buff, strlen(buff), &search, ¬);
|
||||
type = filter_parse_regex(buff, strlen(buff), &search, ¬);
|
||||
search_len = strlen(search);
|
||||
}
|
||||
|
||||
@@ -1991,7 +1968,7 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
|
||||
int count = 0;
|
||||
char *search;
|
||||
|
||||
type = ftrace_setup_glob(glob, strlen(glob), &search, ¬);
|
||||
type = filter_parse_regex(glob, strlen(glob), &search, ¬);
|
||||
len = strlen(search);
|
||||
|
||||
/* we do not support '!' for function probes */
|
||||
@@ -2068,7 +2045,7 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
|
||||
else if (glob) {
|
||||
int not;
|
||||
|
||||
type = ftrace_setup_glob(glob, strlen(glob), &search, ¬);
|
||||
type = filter_parse_regex(glob, strlen(glob), &search, ¬);
|
||||
len = strlen(search);
|
||||
|
||||
/* we do not support '!' for function probes */
|
||||
@@ -2297,6 +2274,7 @@ void ftrace_set_notrace(unsigned char *buf, int len, int reset)
|
||||
#define FTRACE_FILTER_SIZE COMMAND_LINE_SIZE
|
||||
static char ftrace_notrace_buf[FTRACE_FILTER_SIZE] __initdata;
|
||||
static char ftrace_filter_buf[FTRACE_FILTER_SIZE] __initdata;
|
||||
static char ftrace_graph_buf[FTRACE_FILTER_SIZE] __initdata;
|
||||
|
||||
static int __init set_ftrace_notrace(char *str)
|
||||
{
|
||||
@@ -2312,6 +2290,31 @@ static int __init set_ftrace_filter(char *str)
|
||||
}
|
||||
__setup("ftrace_filter=", set_ftrace_filter);
|
||||
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
static int __init set_graph_function(char *str)
|
||||
{
|
||||
strlcpy(ftrace_graph_buf, str, FTRACE_FILTER_SIZE);
|
||||
return 1;
|
||||
}
|
||||
__setup("ftrace_graph_filter=", set_graph_function);
|
||||
|
||||
static void __init set_ftrace_early_graph(char *buf)
|
||||
{
|
||||
int ret;
|
||||
char *func;
|
||||
|
||||
while (buf) {
|
||||
func = strsep(&buf, ",");
|
||||
/* we allow only one expression at a time */
|
||||
ret = ftrace_set_func(ftrace_graph_funcs, &ftrace_graph_count,
|
||||
func);
|
||||
if (ret)
|
||||
printk(KERN_DEBUG "ftrace: function %s not "
|
||||
"traceable\n", func);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
|
||||
|
||||
static void __init set_ftrace_early_filter(char *buf, int enable)
|
||||
{
|
||||
char *func;
|
||||
@@ -2328,6 +2331,10 @@ static void __init set_ftrace_early_filters(void)
|
||||
set_ftrace_early_filter(ftrace_filter_buf, 1);
|
||||
if (ftrace_notrace_buf[0])
|
||||
set_ftrace_early_filter(ftrace_notrace_buf, 0);
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
if (ftrace_graph_buf[0])
|
||||
set_ftrace_early_graph(ftrace_graph_buf);
|
||||
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -2513,7 +2520,7 @@ ftrace_set_func(unsigned long *array, int *idx, char *buffer)
|
||||
return -ENODEV;
|
||||
|
||||
/* decode regex */
|
||||
type = ftrace_setup_glob(buffer, strlen(buffer), &search, ¬);
|
||||
type = filter_parse_regex(buffer, strlen(buffer), &search, ¬);
|
||||
if (not)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -2624,7 +2631,7 @@ static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftrace_convert_nops(struct module *mod,
|
||||
static int ftrace_process_locs(struct module *mod,
|
||||
unsigned long *start,
|
||||
unsigned long *end)
|
||||
{
|
||||
@@ -2684,7 +2691,7 @@ static void ftrace_init_module(struct module *mod,
|
||||
{
|
||||
if (ftrace_disabled || start == end)
|
||||
return;
|
||||
ftrace_convert_nops(mod, start, end);
|
||||
ftrace_process_locs(mod, start, end);
|
||||
}
|
||||
|
||||
static int ftrace_module_notify(struct notifier_block *self,
|
||||
@@ -2745,7 +2752,7 @@ void __init ftrace_init(void)
|
||||
|
||||
last_ftrace_enabled = ftrace_enabled = 1;
|
||||
|
||||
ret = ftrace_convert_nops(NULL,
|
||||
ret = ftrace_process_locs(NULL,
|
||||
__start_mcount_loc,
|
||||
__stop_mcount_loc);
|
||||
|
||||
@@ -2778,23 +2785,6 @@ static inline void ftrace_startup_enable(int command) { }
|
||||
# define ftrace_shutdown_sysctl() do { } while (0)
|
||||
#endif /* CONFIG_DYNAMIC_FTRACE */
|
||||
|
||||
static ssize_t
|
||||
ftrace_pid_read(struct file *file, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
char buf[64];
|
||||
int r;
|
||||
|
||||
if (ftrace_pid_trace == ftrace_swapper_pid)
|
||||
r = sprintf(buf, "swapper tasks\n");
|
||||
else if (ftrace_pid_trace)
|
||||
r = sprintf(buf, "%u\n", pid_vnr(ftrace_pid_trace));
|
||||
else
|
||||
r = sprintf(buf, "no pid\n");
|
||||
|
||||
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
|
||||
}
|
||||
|
||||
static void clear_ftrace_swapper(void)
|
||||
{
|
||||
struct task_struct *p;
|
||||
@@ -2845,14 +2835,12 @@ static void set_ftrace_pid(struct pid *pid)
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void clear_ftrace_pid_task(struct pid **pid)
|
||||
static void clear_ftrace_pid_task(struct pid *pid)
|
||||
{
|
||||
if (*pid == ftrace_swapper_pid)
|
||||
if (pid == ftrace_swapper_pid)
|
||||
clear_ftrace_swapper();
|
||||
else
|
||||
clear_ftrace_pid(*pid);
|
||||
|
||||
*pid = NULL;
|
||||
clear_ftrace_pid(pid);
|
||||
}
|
||||
|
||||
static void set_ftrace_pid_task(struct pid *pid)
|
||||
@@ -2863,11 +2851,140 @@ static void set_ftrace_pid_task(struct pid *pid)
|
||||
set_ftrace_pid(pid);
|
||||
}
|
||||
|
||||
static int ftrace_pid_add(int p)
|
||||
{
|
||||
struct pid *pid;
|
||||
struct ftrace_pid *fpid;
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&ftrace_lock);
|
||||
|
||||
if (!p)
|
||||
pid = ftrace_swapper_pid;
|
||||
else
|
||||
pid = find_get_pid(p);
|
||||
|
||||
if (!pid)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
|
||||
list_for_each_entry(fpid, &ftrace_pids, list)
|
||||
if (fpid->pid == pid)
|
||||
goto out_put;
|
||||
|
||||
ret = -ENOMEM;
|
||||
|
||||
fpid = kmalloc(sizeof(*fpid), GFP_KERNEL);
|
||||
if (!fpid)
|
||||
goto out_put;
|
||||
|
||||
list_add(&fpid->list, &ftrace_pids);
|
||||
fpid->pid = pid;
|
||||
|
||||
set_ftrace_pid_task(pid);
|
||||
|
||||
ftrace_update_pid_func();
|
||||
ftrace_startup_enable(0);
|
||||
|
||||
mutex_unlock(&ftrace_lock);
|
||||
return 0;
|
||||
|
||||
out_put:
|
||||
if (pid != ftrace_swapper_pid)
|
||||
put_pid(pid);
|
||||
|
||||
out:
|
||||
mutex_unlock(&ftrace_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ftrace_pid_reset(void)
|
||||
{
|
||||
struct ftrace_pid *fpid, *safe;
|
||||
|
||||
mutex_lock(&ftrace_lock);
|
||||
list_for_each_entry_safe(fpid, safe, &ftrace_pids, list) {
|
||||
struct pid *pid = fpid->pid;
|
||||
|
||||
clear_ftrace_pid_task(pid);
|
||||
|
||||
list_del(&fpid->list);
|
||||
kfree(fpid);
|
||||
}
|
||||
|
||||
ftrace_update_pid_func();
|
||||
ftrace_startup_enable(0);
|
||||
|
||||
mutex_unlock(&ftrace_lock);
|
||||
}
|
||||
|
||||
static void *fpid_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
mutex_lock(&ftrace_lock);
|
||||
|
||||
if (list_empty(&ftrace_pids) && (!*pos))
|
||||
return (void *) 1;
|
||||
|
||||
return seq_list_start(&ftrace_pids, *pos);
|
||||
}
|
||||
|
||||
static void *fpid_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
if (v == (void *)1)
|
||||
return NULL;
|
||||
|
||||
return seq_list_next(v, &ftrace_pids, pos);
|
||||
}
|
||||
|
||||
static void fpid_stop(struct seq_file *m, void *p)
|
||||
{
|
||||
mutex_unlock(&ftrace_lock);
|
||||
}
|
||||
|
||||
static int fpid_show(struct seq_file *m, void *v)
|
||||
{
|
||||
const struct ftrace_pid *fpid = list_entry(v, struct ftrace_pid, list);
|
||||
|
||||
if (v == (void *)1) {
|
||||
seq_printf(m, "no pid\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fpid->pid == ftrace_swapper_pid)
|
||||
seq_printf(m, "swapper tasks\n");
|
||||
else
|
||||
seq_printf(m, "%u\n", pid_vnr(fpid->pid));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations ftrace_pid_sops = {
|
||||
.start = fpid_start,
|
||||
.next = fpid_next,
|
||||
.stop = fpid_stop,
|
||||
.show = fpid_show,
|
||||
};
|
||||
|
||||
static int
|
||||
ftrace_pid_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if ((file->f_mode & FMODE_WRITE) &&
|
||||
(file->f_flags & O_TRUNC))
|
||||
ftrace_pid_reset();
|
||||
|
||||
if (file->f_mode & FMODE_READ)
|
||||
ret = seq_open(file, &ftrace_pid_sops);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ftrace_pid_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct pid *pid;
|
||||
char buf[64];
|
||||
long val;
|
||||
int ret;
|
||||
@@ -2880,57 +2997,38 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf,
|
||||
|
||||
buf[cnt] = 0;
|
||||
|
||||
/*
|
||||
* Allow "echo > set_ftrace_pid" or "echo -n '' > set_ftrace_pid"
|
||||
* to clean the filter quietly.
|
||||
*/
|
||||
strstrip(buf);
|
||||
if (strlen(buf) == 0)
|
||||
return 1;
|
||||
|
||||
ret = strict_strtol(buf, 10, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&ftrace_lock);
|
||||
if (val < 0) {
|
||||
/* disable pid tracing */
|
||||
if (!ftrace_pid_trace)
|
||||
goto out;
|
||||
ret = ftrace_pid_add(val);
|
||||
|
||||
clear_ftrace_pid_task(&ftrace_pid_trace);
|
||||
return ret ? ret : cnt;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* swapper task is special */
|
||||
if (!val) {
|
||||
pid = ftrace_swapper_pid;
|
||||
if (pid == ftrace_pid_trace)
|
||||
goto out;
|
||||
} else {
|
||||
pid = find_get_pid(val);
|
||||
static int
|
||||
ftrace_pid_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (file->f_mode & FMODE_READ)
|
||||
seq_release(inode, file);
|
||||
|
||||
if (pid == ftrace_pid_trace) {
|
||||
put_pid(pid);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (ftrace_pid_trace)
|
||||
clear_ftrace_pid_task(&ftrace_pid_trace);
|
||||
|
||||
if (!pid)
|
||||
goto out;
|
||||
|
||||
ftrace_pid_trace = pid;
|
||||
|
||||
set_ftrace_pid_task(ftrace_pid_trace);
|
||||
}
|
||||
|
||||
/* update the function call */
|
||||
ftrace_update_pid_func();
|
||||
ftrace_startup_enable(0);
|
||||
|
||||
out:
|
||||
mutex_unlock(&ftrace_lock);
|
||||
|
||||
return cnt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations ftrace_pid_fops = {
|
||||
.read = ftrace_pid_read,
|
||||
.write = ftrace_pid_write,
|
||||
.open = ftrace_pid_open,
|
||||
.write = ftrace_pid_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = ftrace_pid_release,
|
||||
};
|
||||
|
||||
static __init int ftrace_init_debugfs(void)
|
||||
|
@@ -397,18 +397,21 @@ int ring_buffer_print_page_header(struct trace_seq *s)
|
||||
int ret;
|
||||
|
||||
ret = trace_seq_printf(s, "\tfield: u64 timestamp;\t"
|
||||
"offset:0;\tsize:%u;\n",
|
||||
(unsigned int)sizeof(field.time_stamp));
|
||||
"offset:0;\tsize:%u;\tsigned:%u;\n",
|
||||
(unsigned int)sizeof(field.time_stamp),
|
||||
(unsigned int)is_signed_type(u64));
|
||||
|
||||
ret = trace_seq_printf(s, "\tfield: local_t commit;\t"
|
||||
"offset:%u;\tsize:%u;\n",
|
||||
"offset:%u;\tsize:%u;\tsigned:%u;\n",
|
||||
(unsigned int)offsetof(typeof(field), commit),
|
||||
(unsigned int)sizeof(field.commit));
|
||||
(unsigned int)sizeof(field.commit),
|
||||
(unsigned int)is_signed_type(long));
|
||||
|
||||
ret = trace_seq_printf(s, "\tfield: char data;\t"
|
||||
"offset:%u;\tsize:%u;\n",
|
||||
"offset:%u;\tsize:%u;\tsigned:%u;\n",
|
||||
(unsigned int)offsetof(typeof(field), data),
|
||||
(unsigned int)BUF_PAGE_SIZE);
|
||||
(unsigned int)BUF_PAGE_SIZE,
|
||||
(unsigned int)is_signed_type(char));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -129,7 +129,7 @@ static int tracing_set_tracer(const char *buf);
|
||||
static char bootup_tracer_buf[MAX_TRACER_SIZE] __initdata;
|
||||
static char *default_bootup_tracer;
|
||||
|
||||
static int __init set_ftrace(char *str)
|
||||
static int __init set_cmdline_ftrace(char *str)
|
||||
{
|
||||
strncpy(bootup_tracer_buf, str, MAX_TRACER_SIZE);
|
||||
default_bootup_tracer = bootup_tracer_buf;
|
||||
@@ -137,7 +137,7 @@ static int __init set_ftrace(char *str)
|
||||
ring_buffer_expanded = 1;
|
||||
return 1;
|
||||
}
|
||||
__setup("ftrace=", set_ftrace);
|
||||
__setup("ftrace=", set_cmdline_ftrace);
|
||||
|
||||
static int __init set_ftrace_dump_on_oops(char *str)
|
||||
{
|
||||
|
@@ -506,10 +506,6 @@ static inline int ftrace_graph_addr(unsigned long addr)
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int ftrace_trace_addr(unsigned long addr)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
static inline int ftrace_graph_addr(unsigned long addr)
|
||||
{
|
||||
return 1;
|
||||
@@ -523,12 +519,12 @@ print_graph_function(struct trace_iterator *iter)
|
||||
}
|
||||
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
|
||||
|
||||
extern struct pid *ftrace_pid_trace;
|
||||
extern struct list_head ftrace_pids;
|
||||
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
static inline int ftrace_trace_task(struct task_struct *task)
|
||||
{
|
||||
if (!ftrace_pid_trace)
|
||||
if (list_empty(&ftrace_pids))
|
||||
return 1;
|
||||
|
||||
return test_tsk_trace_trace(task);
|
||||
@@ -710,7 +706,6 @@ struct event_filter {
|
||||
int n_preds;
|
||||
struct filter_pred **preds;
|
||||
char *filter_string;
|
||||
bool no_reset;
|
||||
};
|
||||
|
||||
struct event_subsystem {
|
||||
@@ -722,22 +717,40 @@ struct event_subsystem {
|
||||
};
|
||||
|
||||
struct filter_pred;
|
||||
struct regex;
|
||||
|
||||
typedef int (*filter_pred_fn_t) (struct filter_pred *pred, void *event,
|
||||
int val1, int val2);
|
||||
|
||||
struct filter_pred {
|
||||
filter_pred_fn_t fn;
|
||||
u64 val;
|
||||
char str_val[MAX_FILTER_STR_VAL];
|
||||
int str_len;
|
||||
char *field_name;
|
||||
int offset;
|
||||
int not;
|
||||
int op;
|
||||
int pop_n;
|
||||
typedef int (*regex_match_func)(char *str, struct regex *r, int len);
|
||||
|
||||
enum regex_type {
|
||||
MATCH_FULL = 0,
|
||||
MATCH_FRONT_ONLY,
|
||||
MATCH_MIDDLE_ONLY,
|
||||
MATCH_END_ONLY,
|
||||
};
|
||||
|
||||
struct regex {
|
||||
char pattern[MAX_FILTER_STR_VAL];
|
||||
int len;
|
||||
int field_len;
|
||||
regex_match_func match;
|
||||
};
|
||||
|
||||
struct filter_pred {
|
||||
filter_pred_fn_t fn;
|
||||
u64 val;
|
||||
struct regex regex;
|
||||
char *field_name;
|
||||
int offset;
|
||||
int not;
|
||||
int op;
|
||||
int pop_n;
|
||||
};
|
||||
|
||||
extern enum regex_type
|
||||
filter_parse_regex(char *buff, int len, char **search, int *not);
|
||||
extern void print_event_filter(struct ftrace_event_call *call,
|
||||
struct trace_seq *s);
|
||||
extern int apply_event_filter(struct ftrace_event_call *call,
|
||||
@@ -753,7 +766,8 @@ filter_check_discard(struct ftrace_event_call *call, void *rec,
|
||||
struct ring_buffer *buffer,
|
||||
struct ring_buffer_event *event)
|
||||
{
|
||||
if (unlikely(call->filter_active) && !filter_match_preds(call, rec)) {
|
||||
if (unlikely(call->filter_active) &&
|
||||
!filter_match_preds(call->filter, rec)) {
|
||||
ring_buffer_discard_commit(buffer, event);
|
||||
return 1;
|
||||
}
|
||||
|
@@ -503,7 +503,7 @@ extern char *__bad_type_size(void);
|
||||
#define FIELD(type, name) \
|
||||
sizeof(type) != sizeof(field.name) ? __bad_type_size() : \
|
||||
#type, "common_" #name, offsetof(typeof(field), name), \
|
||||
sizeof(field.name)
|
||||
sizeof(field.name), is_signed_type(type)
|
||||
|
||||
static int trace_write_header(struct trace_seq *s)
|
||||
{
|
||||
@@ -511,17 +511,17 @@ static int trace_write_header(struct trace_seq *s)
|
||||
|
||||
/* struct trace_entry */
|
||||
return trace_seq_printf(s,
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
|
||||
"\n",
|
||||
FIELD(unsigned short, type),
|
||||
FIELD(unsigned char, flags),
|
||||
FIELD(unsigned char, preempt_count),
|
||||
FIELD(int, pid),
|
||||
FIELD(int, lock_depth));
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n"
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n"
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n"
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n"
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n"
|
||||
"\n",
|
||||
FIELD(unsigned short, type),
|
||||
FIELD(unsigned char, flags),
|
||||
FIELD(unsigned char, preempt_count),
|
||||
FIELD(int, pid),
|
||||
FIELD(int, lock_depth));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -874,9 +874,9 @@ event_subsystem_dir(const char *name, struct dentry *d_events)
|
||||
"'%s/filter' entry\n", name);
|
||||
}
|
||||
|
||||
entry = trace_create_file("enable", 0644, system->entry,
|
||||
(void *)system->name,
|
||||
&ftrace_system_enable_fops);
|
||||
trace_create_file("enable", 0644, system->entry,
|
||||
(void *)system->name,
|
||||
&ftrace_system_enable_fops);
|
||||
|
||||
return system->entry;
|
||||
}
|
||||
@@ -888,7 +888,6 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events,
|
||||
const struct file_operations *filter,
|
||||
const struct file_operations *format)
|
||||
{
|
||||
struct dentry *entry;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
@@ -906,12 +905,12 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events,
|
||||
}
|
||||
|
||||
if (call->regfunc)
|
||||
entry = trace_create_file("enable", 0644, call->dir, call,
|
||||
enable);
|
||||
trace_create_file("enable", 0644, call->dir, call,
|
||||
enable);
|
||||
|
||||
if (call->id && call->profile_enable)
|
||||
entry = trace_create_file("id", 0444, call->dir, call,
|
||||
id);
|
||||
trace_create_file("id", 0444, call->dir, call,
|
||||
id);
|
||||
|
||||
if (call->define_fields) {
|
||||
ret = call->define_fields(call);
|
||||
@@ -920,16 +919,16 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events,
|
||||
" events/%s\n", call->name);
|
||||
return ret;
|
||||
}
|
||||
entry = trace_create_file("filter", 0644, call->dir, call,
|
||||
filter);
|
||||
trace_create_file("filter", 0644, call->dir, call,
|
||||
filter);
|
||||
}
|
||||
|
||||
/* A trace may not want to export its format */
|
||||
if (!call->show_format)
|
||||
return 0;
|
||||
|
||||
entry = trace_create_file("format", 0444, call->dir, call,
|
||||
format);
|
||||
trace_create_file("format", 0444, call->dir, call,
|
||||
format);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -18,11 +18,10 @@
|
||||
* Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/perf_event.h>
|
||||
|
||||
#include "trace.h"
|
||||
#include "trace_output.h"
|
||||
@@ -31,6 +30,7 @@ enum filter_op_ids
|
||||
{
|
||||
OP_OR,
|
||||
OP_AND,
|
||||
OP_GLOB,
|
||||
OP_NE,
|
||||
OP_EQ,
|
||||
OP_LT,
|
||||
@@ -48,16 +48,17 @@ struct filter_op {
|
||||
};
|
||||
|
||||
static struct filter_op filter_ops[] = {
|
||||
{ OP_OR, "||", 1 },
|
||||
{ OP_AND, "&&", 2 },
|
||||
{ OP_NE, "!=", 4 },
|
||||
{ OP_EQ, "==", 4 },
|
||||
{ OP_LT, "<", 5 },
|
||||
{ OP_LE, "<=", 5 },
|
||||
{ OP_GT, ">", 5 },
|
||||
{ OP_GE, ">=", 5 },
|
||||
{ OP_NONE, "OP_NONE", 0 },
|
||||
{ OP_OPEN_PAREN, "(", 0 },
|
||||
{ OP_OR, "||", 1 },
|
||||
{ OP_AND, "&&", 2 },
|
||||
{ OP_GLOB, "~", 4 },
|
||||
{ OP_NE, "!=", 4 },
|
||||
{ OP_EQ, "==", 4 },
|
||||
{ OP_LT, "<", 5 },
|
||||
{ OP_LE, "<=", 5 },
|
||||
{ OP_GT, ">", 5 },
|
||||
{ OP_GE, ">=", 5 },
|
||||
{ OP_NONE, "OP_NONE", 0 },
|
||||
{ OP_OPEN_PAREN, "(", 0 },
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -197,9 +198,9 @@ static int filter_pred_string(struct filter_pred *pred, void *event,
|
||||
char *addr = (char *)(event + pred->offset);
|
||||
int cmp, match;
|
||||
|
||||
cmp = strncmp(addr, pred->str_val, pred->str_len);
|
||||
cmp = pred->regex.match(addr, &pred->regex, pred->regex.field_len);
|
||||
|
||||
match = (!cmp) ^ pred->not;
|
||||
match = cmp ^ pred->not;
|
||||
|
||||
return match;
|
||||
}
|
||||
@@ -211,9 +212,9 @@ static int filter_pred_pchar(struct filter_pred *pred, void *event,
|
||||
char **addr = (char **)(event + pred->offset);
|
||||
int cmp, match;
|
||||
|
||||
cmp = strncmp(*addr, pred->str_val, pred->str_len);
|
||||
cmp = pred->regex.match(*addr, &pred->regex, pred->regex.field_len);
|
||||
|
||||
match = (!cmp) ^ pred->not;
|
||||
match = cmp ^ pred->not;
|
||||
|
||||
return match;
|
||||
}
|
||||
@@ -237,9 +238,9 @@ static int filter_pred_strloc(struct filter_pred *pred, void *event,
|
||||
char *addr = (char *)(event + str_loc);
|
||||
int cmp, match;
|
||||
|
||||
cmp = strncmp(addr, pred->str_val, str_len);
|
||||
cmp = pred->regex.match(addr, &pred->regex, str_len);
|
||||
|
||||
match = (!cmp) ^ pred->not;
|
||||
match = cmp ^ pred->not;
|
||||
|
||||
return match;
|
||||
}
|
||||
@@ -250,10 +251,121 @@ static int filter_pred_none(struct filter_pred *pred, void *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return 1 if event matches, 0 otherwise (discard) */
|
||||
int filter_match_preds(struct ftrace_event_call *call, void *rec)
|
||||
/* Basic regex callbacks */
|
||||
static int regex_match_full(char *str, struct regex *r, int len)
|
||||
{
|
||||
if (strncmp(str, r->pattern, len) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int regex_match_front(char *str, struct regex *r, int len)
|
||||
{
|
||||
if (strncmp(str, r->pattern, len) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int regex_match_middle(char *str, struct regex *r, int len)
|
||||
{
|
||||
if (strstr(str, r->pattern))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int regex_match_end(char *str, struct regex *r, int len)
|
||||
{
|
||||
char *ptr = strstr(str, r->pattern);
|
||||
|
||||
if (ptr && (ptr[r->len] == 0))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* filter_parse_regex - parse a basic regex
|
||||
* @buff: the raw regex
|
||||
* @len: length of the regex
|
||||
* @search: will point to the beginning of the string to compare
|
||||
* @not: tell whether the match will have to be inverted
|
||||
*
|
||||
* This passes in a buffer containing a regex and this function will
|
||||
* set search to point to the search part of the buffer and
|
||||
* return the type of search it is (see enum above).
|
||||
* This does modify buff.
|
||||
*
|
||||
* Returns enum type.
|
||||
* search returns the pointer to use for comparison.
|
||||
* not returns 1 if buff started with a '!'
|
||||
* 0 otherwise.
|
||||
*/
|
||||
enum regex_type filter_parse_regex(char *buff, int len, char **search, int *not)
|
||||
{
|
||||
int type = MATCH_FULL;
|
||||
int i;
|
||||
|
||||
if (buff[0] == '!') {
|
||||
*not = 1;
|
||||
buff++;
|
||||
len--;
|
||||
} else
|
||||
*not = 0;
|
||||
|
||||
*search = buff;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (buff[i] == '*') {
|
||||
if (!i) {
|
||||
*search = buff + 1;
|
||||
type = MATCH_END_ONLY;
|
||||
} else {
|
||||
if (type == MATCH_END_ONLY)
|
||||
type = MATCH_MIDDLE_ONLY;
|
||||
else
|
||||
type = MATCH_FRONT_ONLY;
|
||||
buff[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static void filter_build_regex(struct filter_pred *pred)
|
||||
{
|
||||
struct regex *r = &pred->regex;
|
||||
char *search;
|
||||
enum regex_type type = MATCH_FULL;
|
||||
int not = 0;
|
||||
|
||||
if (pred->op == OP_GLOB) {
|
||||
type = filter_parse_regex(r->pattern, r->len, &search, ¬);
|
||||
r->len = strlen(search);
|
||||
memmove(r->pattern, search, r->len+1);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case MATCH_FULL:
|
||||
r->match = regex_match_full;
|
||||
break;
|
||||
case MATCH_FRONT_ONLY:
|
||||
r->match = regex_match_front;
|
||||
break;
|
||||
case MATCH_MIDDLE_ONLY:
|
||||
r->match = regex_match_middle;
|
||||
break;
|
||||
case MATCH_END_ONLY:
|
||||
r->match = regex_match_end;
|
||||
break;
|
||||
}
|
||||
|
||||
pred->not ^= not;
|
||||
}
|
||||
|
||||
/* return 1 if event matches, 0 otherwise (discard) */
|
||||
int filter_match_preds(struct event_filter *filter, void *rec)
|
||||
{
|
||||
struct event_filter *filter = call->filter;
|
||||
int match, top = 0, val1 = 0, val2 = 0;
|
||||
int stack[MAX_FILTER_PRED];
|
||||
struct filter_pred *pred;
|
||||
@@ -396,7 +508,7 @@ static void filter_clear_pred(struct filter_pred *pred)
|
||||
{
|
||||
kfree(pred->field_name);
|
||||
pred->field_name = NULL;
|
||||
pred->str_len = 0;
|
||||
pred->regex.len = 0;
|
||||
}
|
||||
|
||||
static int filter_set_pred(struct filter_pred *dest,
|
||||
@@ -426,9 +538,8 @@ static void filter_disable_preds(struct ftrace_event_call *call)
|
||||
filter->preds[i]->fn = filter_pred_none;
|
||||
}
|
||||
|
||||
void destroy_preds(struct ftrace_event_call *call)
|
||||
static void __free_preds(struct event_filter *filter)
|
||||
{
|
||||
struct event_filter *filter = call->filter;
|
||||
int i;
|
||||
|
||||
if (!filter)
|
||||
@@ -441,21 +552,24 @@ void destroy_preds(struct ftrace_event_call *call)
|
||||
kfree(filter->preds);
|
||||
kfree(filter->filter_string);
|
||||
kfree(filter);
|
||||
call->filter = NULL;
|
||||
}
|
||||
|
||||
static int init_preds(struct ftrace_event_call *call)
|
||||
void destroy_preds(struct ftrace_event_call *call)
|
||||
{
|
||||
__free_preds(call->filter);
|
||||
call->filter = NULL;
|
||||
call->filter_active = 0;
|
||||
}
|
||||
|
||||
static struct event_filter *__alloc_preds(void)
|
||||
{
|
||||
struct event_filter *filter;
|
||||
struct filter_pred *pred;
|
||||
int i;
|
||||
|
||||
if (call->filter)
|
||||
return 0;
|
||||
|
||||
filter = call->filter = kzalloc(sizeof(*filter), GFP_KERNEL);
|
||||
if (!call->filter)
|
||||
return -ENOMEM;
|
||||
filter = kzalloc(sizeof(*filter), GFP_KERNEL);
|
||||
if (!filter)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
filter->n_preds = 0;
|
||||
|
||||
@@ -471,12 +585,24 @@ static int init_preds(struct ftrace_event_call *call)
|
||||
filter->preds[i] = pred;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return filter;
|
||||
|
||||
oom:
|
||||
destroy_preds(call);
|
||||
__free_preds(filter);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
static int init_preds(struct ftrace_event_call *call)
|
||||
{
|
||||
if (call->filter)
|
||||
return 0;
|
||||
|
||||
call->filter_active = 0;
|
||||
call->filter = __alloc_preds();
|
||||
if (IS_ERR(call->filter))
|
||||
return PTR_ERR(call->filter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_subsystem_preds(struct event_subsystem *system)
|
||||
@@ -499,14 +625,7 @@ static int init_subsystem_preds(struct event_subsystem *system)
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
FILTER_DISABLE_ALL,
|
||||
FILTER_INIT_NO_RESET,
|
||||
FILTER_SKIP_NO_RESET,
|
||||
};
|
||||
|
||||
static void filter_free_subsystem_preds(struct event_subsystem *system,
|
||||
int flag)
|
||||
static void filter_free_subsystem_preds(struct event_subsystem *system)
|
||||
{
|
||||
struct ftrace_event_call *call;
|
||||
|
||||
@@ -517,14 +636,6 @@ static void filter_free_subsystem_preds(struct event_subsystem *system,
|
||||
if (strcmp(call->system, system->name) != 0)
|
||||
continue;
|
||||
|
||||
if (flag == FILTER_INIT_NO_RESET) {
|
||||
call->filter->no_reset = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (flag == FILTER_SKIP_NO_RESET && call->filter->no_reset)
|
||||
continue;
|
||||
|
||||
filter_disable_preds(call);
|
||||
remove_filter_string(call->filter);
|
||||
}
|
||||
@@ -532,10 +643,10 @@ static void filter_free_subsystem_preds(struct event_subsystem *system,
|
||||
|
||||
static int filter_add_pred_fn(struct filter_parse_state *ps,
|
||||
struct ftrace_event_call *call,
|
||||
struct event_filter *filter,
|
||||
struct filter_pred *pred,
|
||||
filter_pred_fn_t fn)
|
||||
{
|
||||
struct event_filter *filter = call->filter;
|
||||
int idx, err;
|
||||
|
||||
if (filter->n_preds == MAX_FILTER_PRED) {
|
||||
@@ -550,7 +661,6 @@ static int filter_add_pred_fn(struct filter_parse_state *ps,
|
||||
return err;
|
||||
|
||||
filter->n_preds++;
|
||||
call->filter_active = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -575,7 +685,10 @@ static bool is_string_field(struct ftrace_event_field *field)
|
||||
|
||||
static int is_legal_op(struct ftrace_event_field *field, int op)
|
||||
{
|
||||
if (is_string_field(field) && (op != OP_EQ && op != OP_NE))
|
||||
if (is_string_field(field) &&
|
||||
(op != OP_EQ && op != OP_NE && op != OP_GLOB))
|
||||
return 0;
|
||||
if (!is_string_field(field) && op == OP_GLOB)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
@@ -626,6 +739,7 @@ static filter_pred_fn_t select_comparison_fn(int op, int field_size,
|
||||
|
||||
static int filter_add_pred(struct filter_parse_state *ps,
|
||||
struct ftrace_event_call *call,
|
||||
struct event_filter *filter,
|
||||
struct filter_pred *pred,
|
||||
bool dry_run)
|
||||
{
|
||||
@@ -660,21 +774,22 @@ static int filter_add_pred(struct filter_parse_state *ps,
|
||||
}
|
||||
|
||||
if (is_string_field(field)) {
|
||||
pred->str_len = field->size;
|
||||
filter_build_regex(pred);
|
||||
|
||||
if (field->filter_type == FILTER_STATIC_STRING)
|
||||
if (field->filter_type == FILTER_STATIC_STRING) {
|
||||
fn = filter_pred_string;
|
||||
else if (field->filter_type == FILTER_DYN_STRING)
|
||||
pred->regex.field_len = field->size;
|
||||
} else if (field->filter_type == FILTER_DYN_STRING)
|
||||
fn = filter_pred_strloc;
|
||||
else {
|
||||
fn = filter_pred_pchar;
|
||||
pred->str_len = strlen(pred->str_val);
|
||||
pred->regex.field_len = strlen(pred->regex.pattern);
|
||||
}
|
||||
} else {
|
||||
if (field->is_signed)
|
||||
ret = strict_strtoll(pred->str_val, 0, &val);
|
||||
ret = strict_strtoll(pred->regex.pattern, 0, &val);
|
||||
else
|
||||
ret = strict_strtoull(pred->str_val, 0, &val);
|
||||
ret = strict_strtoull(pred->regex.pattern, 0, &val);
|
||||
if (ret) {
|
||||
parse_error(ps, FILT_ERR_ILLEGAL_INTVAL, 0);
|
||||
return -EINVAL;
|
||||
@@ -694,45 +809,7 @@ static int filter_add_pred(struct filter_parse_state *ps,
|
||||
|
||||
add_pred_fn:
|
||||
if (!dry_run)
|
||||
return filter_add_pred_fn(ps, call, pred, fn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int filter_add_subsystem_pred(struct filter_parse_state *ps,
|
||||
struct event_subsystem *system,
|
||||
struct filter_pred *pred,
|
||||
char *filter_string,
|
||||
bool dry_run)
|
||||
{
|
||||
struct ftrace_event_call *call;
|
||||
int err = 0;
|
||||
bool fail = true;
|
||||
|
||||
list_for_each_entry(call, &ftrace_events, list) {
|
||||
|
||||
if (!call->define_fields)
|
||||
continue;
|
||||
|
||||
if (strcmp(call->system, system->name))
|
||||
continue;
|
||||
|
||||
if (call->filter->no_reset)
|
||||
continue;
|
||||
|
||||
err = filter_add_pred(ps, call, pred, dry_run);
|
||||
if (err)
|
||||
call->filter->no_reset = true;
|
||||
else
|
||||
fail = false;
|
||||
|
||||
if (!dry_run)
|
||||
replace_filter_string(call->filter, filter_string);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
parse_error(ps, FILT_ERR_BAD_SUBSYS_FILTER, 0);
|
||||
return err;
|
||||
}
|
||||
return filter_add_pred_fn(ps, call, filter, pred, fn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1045,8 +1122,8 @@ static struct filter_pred *create_pred(int op, char *operand1, char *operand2)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(pred->str_val, operand2);
|
||||
pred->str_len = strlen(operand2);
|
||||
strcpy(pred->regex.pattern, operand2);
|
||||
pred->regex.len = strlen(pred->regex.pattern);
|
||||
|
||||
pred->op = op;
|
||||
|
||||
@@ -1090,8 +1167,8 @@ static int check_preds(struct filter_parse_state *ps)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int replace_preds(struct event_subsystem *system,
|
||||
struct ftrace_event_call *call,
|
||||
static int replace_preds(struct ftrace_event_call *call,
|
||||
struct event_filter *filter,
|
||||
struct filter_parse_state *ps,
|
||||
char *filter_string,
|
||||
bool dry_run)
|
||||
@@ -1138,11 +1215,7 @@ static int replace_preds(struct event_subsystem *system,
|
||||
add_pred:
|
||||
if (!pred)
|
||||
return -ENOMEM;
|
||||
if (call)
|
||||
err = filter_add_pred(ps, call, pred, false);
|
||||
else
|
||||
err = filter_add_subsystem_pred(ps, system, pred,
|
||||
filter_string, dry_run);
|
||||
err = filter_add_pred(ps, call, filter, pred, dry_run);
|
||||
filter_free_pred(pred);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -1153,10 +1226,50 @@ add_pred:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int replace_system_preds(struct event_subsystem *system,
|
||||
struct filter_parse_state *ps,
|
||||
char *filter_string)
|
||||
{
|
||||
struct event_filter *filter = system->filter;
|
||||
struct ftrace_event_call *call;
|
||||
bool fail = true;
|
||||
int err;
|
||||
|
||||
list_for_each_entry(call, &ftrace_events, list) {
|
||||
|
||||
if (!call->define_fields)
|
||||
continue;
|
||||
|
||||
if (strcmp(call->system, system->name) != 0)
|
||||
continue;
|
||||
|
||||
/* try to see if the filter can be applied */
|
||||
err = replace_preds(call, filter, ps, filter_string, true);
|
||||
if (err)
|
||||
continue;
|
||||
|
||||
/* really apply the filter */
|
||||
filter_disable_preds(call);
|
||||
err = replace_preds(call, filter, ps, filter_string, false);
|
||||
if (err)
|
||||
filter_disable_preds(call);
|
||||
else {
|
||||
call->filter_active = 1;
|
||||
replace_filter_string(filter, filter_string);
|
||||
}
|
||||
fail = false;
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
parse_error(ps, FILT_ERR_BAD_SUBSYS_FILTER, 0);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
|
||||
{
|
||||
int err;
|
||||
|
||||
struct filter_parse_state *ps;
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
@@ -1168,8 +1281,7 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
|
||||
if (!strcmp(strstrip(filter_string), "0")) {
|
||||
filter_disable_preds(call);
|
||||
remove_filter_string(call->filter);
|
||||
mutex_unlock(&event_mutex);
|
||||
return 0;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
@@ -1187,10 +1299,11 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = replace_preds(NULL, call, ps, filter_string, false);
|
||||
err = replace_preds(call, call->filter, ps, filter_string, false);
|
||||
if (err)
|
||||
append_filter_err(ps, call->filter);
|
||||
|
||||
else
|
||||
call->filter_active = 1;
|
||||
out:
|
||||
filter_opstack_clear(ps);
|
||||
postfix_clear(ps);
|
||||
@@ -1205,7 +1318,6 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
|
||||
char *filter_string)
|
||||
{
|
||||
int err;
|
||||
|
||||
struct filter_parse_state *ps;
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
@@ -1215,10 +1327,9 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
|
||||
goto out_unlock;
|
||||
|
||||
if (!strcmp(strstrip(filter_string), "0")) {
|
||||
filter_free_subsystem_preds(system, FILTER_DISABLE_ALL);
|
||||
filter_free_subsystem_preds(system);
|
||||
remove_filter_string(system->filter);
|
||||
mutex_unlock(&event_mutex);
|
||||
return 0;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
@@ -1235,23 +1346,9 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
|
||||
goto out;
|
||||
}
|
||||
|
||||
filter_free_subsystem_preds(system, FILTER_INIT_NO_RESET);
|
||||
|
||||
/* try to see the filter can be applied to which events */
|
||||
err = replace_preds(system, NULL, ps, filter_string, true);
|
||||
if (err) {
|
||||
err = replace_system_preds(system, ps, filter_string);
|
||||
if (err)
|
||||
append_filter_err(ps, system->filter);
|
||||
goto out;
|
||||
}
|
||||
|
||||
filter_free_subsystem_preds(system, FILTER_SKIP_NO_RESET);
|
||||
|
||||
/* really apply the filter to the events */
|
||||
err = replace_preds(system, NULL, ps, filter_string, false);
|
||||
if (err) {
|
||||
append_filter_err(ps, system->filter);
|
||||
filter_free_subsystem_preds(system, 2);
|
||||
}
|
||||
|
||||
out:
|
||||
filter_opstack_clear(ps);
|
||||
@@ -1263,3 +1360,73 @@ out_unlock:
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EVENT_PROFILE
|
||||
|
||||
void ftrace_profile_free_filter(struct perf_event *event)
|
||||
{
|
||||
struct event_filter *filter = event->filter;
|
||||
|
||||
event->filter = NULL;
|
||||
__free_preds(filter);
|
||||
}
|
||||
|
||||
int ftrace_profile_set_filter(struct perf_event *event, int event_id,
|
||||
char *filter_str)
|
||||
{
|
||||
int err;
|
||||
struct event_filter *filter;
|
||||
struct filter_parse_state *ps;
|
||||
struct ftrace_event_call *call = NULL;
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
|
||||
list_for_each_entry(call, &ftrace_events, list) {
|
||||
if (call->id == event_id)
|
||||
break;
|
||||
}
|
||||
|
||||
err = -EINVAL;
|
||||
if (!call)
|
||||
goto out_unlock;
|
||||
|
||||
err = -EEXIST;
|
||||
if (event->filter)
|
||||
goto out_unlock;
|
||||
|
||||
filter = __alloc_preds();
|
||||
if (IS_ERR(filter)) {
|
||||
err = PTR_ERR(filter);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
ps = kzalloc(sizeof(*ps), GFP_KERNEL);
|
||||
if (!ps)
|
||||
goto free_preds;
|
||||
|
||||
parse_init(ps, filter_ops, filter_str);
|
||||
err = filter_parse(ps);
|
||||
if (err)
|
||||
goto free_ps;
|
||||
|
||||
err = replace_preds(call, filter, ps, filter_str, false);
|
||||
if (!err)
|
||||
event->filter = filter;
|
||||
|
||||
free_ps:
|
||||
filter_opstack_clear(ps);
|
||||
postfix_clear(ps);
|
||||
kfree(ps);
|
||||
|
||||
free_preds:
|
||||
if (err)
|
||||
__free_preds(filter);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&event_mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_EVENT_PROFILE */
|
||||
|
||||
|
@@ -66,44 +66,47 @@ static void __used ____ftrace_check_##name(void) \
|
||||
#undef __field
|
||||
#define __field(type, item) \
|
||||
ret = trace_seq_printf(s, "\tfield:" #type " " #item ";\t" \
|
||||
"offset:%zu;\tsize:%zu;\n", \
|
||||
"offset:%zu;\tsize:%zu;\tsigned:%u;\n", \
|
||||
offsetof(typeof(field), item), \
|
||||
sizeof(field.item)); \
|
||||
sizeof(field.item), is_signed_type(type)); \
|
||||
if (!ret) \
|
||||
return 0;
|
||||
|
||||
#undef __field_desc
|
||||
#define __field_desc(type, container, item) \
|
||||
ret = trace_seq_printf(s, "\tfield:" #type " " #item ";\t" \
|
||||
"offset:%zu;\tsize:%zu;\n", \
|
||||
"offset:%zu;\tsize:%zu;\tsigned:%u;\n", \
|
||||
offsetof(typeof(field), container.item), \
|
||||
sizeof(field.container.item)); \
|
||||
sizeof(field.container.item), \
|
||||
is_signed_type(type)); \
|
||||
if (!ret) \
|
||||
return 0;
|
||||
|
||||
#undef __array
|
||||
#define __array(type, item, len) \
|
||||
ret = trace_seq_printf(s, "\tfield:" #type " " #item "[" #len "];\t" \
|
||||
"offset:%zu;\tsize:%zu;\n", \
|
||||
offsetof(typeof(field), item), \
|
||||
sizeof(field.item)); \
|
||||
"offset:%zu;\tsize:%zu;\tsigned:%u;\n", \
|
||||
offsetof(typeof(field), item), \
|
||||
sizeof(field.item), is_signed_type(type)); \
|
||||
if (!ret) \
|
||||
return 0;
|
||||
|
||||
#undef __array_desc
|
||||
#define __array_desc(type, container, item, len) \
|
||||
ret = trace_seq_printf(s, "\tfield:" #type " " #item "[" #len "];\t" \
|
||||
"offset:%zu;\tsize:%zu;\n", \
|
||||
"offset:%zu;\tsize:%zu;\tsigned:%u;\n", \
|
||||
offsetof(typeof(field), container.item), \
|
||||
sizeof(field.container.item)); \
|
||||
sizeof(field.container.item), \
|
||||
is_signed_type(type)); \
|
||||
if (!ret) \
|
||||
return 0;
|
||||
|
||||
#undef __dynamic_array
|
||||
#define __dynamic_array(type, item) \
|
||||
ret = trace_seq_printf(s, "\tfield:" #type " " #item ";\t" \
|
||||
"offset:%zu;\tsize:0;\n", \
|
||||
offsetof(typeof(field), item)); \
|
||||
"offset:%zu;\tsize:0;\tsigned:%u;\n", \
|
||||
offsetof(typeof(field), item), \
|
||||
is_signed_type(type)); \
|
||||
if (!ret) \
|
||||
return 0;
|
||||
|
||||
|
@@ -14,6 +14,69 @@ static int sys_refcount_exit;
|
||||
static DECLARE_BITMAP(enabled_enter_syscalls, NR_syscalls);
|
||||
static DECLARE_BITMAP(enabled_exit_syscalls, NR_syscalls);
|
||||
|
||||
extern unsigned long __start_syscalls_metadata[];
|
||||
extern unsigned long __stop_syscalls_metadata[];
|
||||
|
||||
static struct syscall_metadata **syscalls_metadata;
|
||||
|
||||
static struct syscall_metadata *find_syscall_meta(unsigned long syscall)
|
||||
{
|
||||
struct syscall_metadata *start;
|
||||
struct syscall_metadata *stop;
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
|
||||
|
||||
start = (struct syscall_metadata *)__start_syscalls_metadata;
|
||||
stop = (struct syscall_metadata *)__stop_syscalls_metadata;
|
||||
kallsyms_lookup(syscall, NULL, NULL, NULL, str);
|
||||
|
||||
for ( ; start < stop; start++) {
|
||||
/*
|
||||
* Only compare after the "sys" prefix. Archs that use
|
||||
* syscall wrappers may have syscalls symbols aliases prefixed
|
||||
* with "SyS" instead of "sys", leading to an unwanted
|
||||
* mismatch.
|
||||
*/
|
||||
if (start->name && !strcmp(start->name + 3, str + 3))
|
||||
return start;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct syscall_metadata *syscall_nr_to_meta(int nr)
|
||||
{
|
||||
if (!syscalls_metadata || nr >= NR_syscalls || nr < 0)
|
||||
return NULL;
|
||||
|
||||
return syscalls_metadata[nr];
|
||||
}
|
||||
|
||||
int syscall_name_to_nr(char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!syscalls_metadata)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < NR_syscalls; i++) {
|
||||
if (syscalls_metadata[i]) {
|
||||
if (!strcmp(syscalls_metadata[i]->name, name))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void set_syscall_enter_id(int num, int id)
|
||||
{
|
||||
syscalls_metadata[num]->enter_id = id;
|
||||
}
|
||||
|
||||
void set_syscall_exit_id(int num, int id)
|
||||
{
|
||||
syscalls_metadata[num]->exit_id = id;
|
||||
}
|
||||
|
||||
enum print_line_t
|
||||
print_syscall_enter(struct trace_iterator *iter, int flags)
|
||||
{
|
||||
@@ -103,7 +166,8 @@ extern char *__bad_type_size(void);
|
||||
#define SYSCALL_FIELD(type, name) \
|
||||
sizeof(type) != sizeof(trace.name) ? \
|
||||
__bad_type_size() : \
|
||||
#type, #name, offsetof(typeof(trace), name), sizeof(trace.name)
|
||||
#type, #name, offsetof(typeof(trace), name), \
|
||||
sizeof(trace.name), is_signed_type(type)
|
||||
|
||||
int syscall_enter_format(struct ftrace_event_call *call, struct trace_seq *s)
|
||||
{
|
||||
@@ -120,7 +184,8 @@ int syscall_enter_format(struct ftrace_event_call *call, struct trace_seq *s)
|
||||
if (!entry)
|
||||
return 0;
|
||||
|
||||
ret = trace_seq_printf(s, "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n",
|
||||
ret = trace_seq_printf(s, "\tfield:%s %s;\toffset:%zu;\tsize:%zu;"
|
||||
"\tsigned:%u;\n",
|
||||
SYSCALL_FIELD(int, nr));
|
||||
if (!ret)
|
||||
return 0;
|
||||
@@ -130,8 +195,10 @@ int syscall_enter_format(struct ftrace_event_call *call, struct trace_seq *s)
|
||||
entry->args[i]);
|
||||
if (!ret)
|
||||
return 0;
|
||||
ret = trace_seq_printf(s, "\toffset:%d;\tsize:%zu;\n", offset,
|
||||
sizeof(unsigned long));
|
||||
ret = trace_seq_printf(s, "\toffset:%d;\tsize:%zu;"
|
||||
"\tsigned:%u;\n", offset,
|
||||
sizeof(unsigned long),
|
||||
is_signed_type(unsigned long));
|
||||
if (!ret)
|
||||
return 0;
|
||||
offset += sizeof(unsigned long);
|
||||
@@ -163,8 +230,10 @@ int syscall_exit_format(struct ftrace_event_call *call, struct trace_seq *s)
|
||||
struct syscall_trace_exit trace;
|
||||
|
||||
ret = trace_seq_printf(s,
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n",
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;"
|
||||
"\tsigned:%u;\n"
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;"
|
||||
"\tsigned:%u;\n",
|
||||
SYSCALL_FIELD(int, nr),
|
||||
SYSCALL_FIELD(long, ret));
|
||||
if (!ret)
|
||||
@@ -212,7 +281,7 @@ int syscall_exit_define_fields(struct ftrace_event_call *call)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = trace_define_field(call, SYSCALL_FIELD(long, ret), 0,
|
||||
ret = trace_define_field(call, SYSCALL_FIELD(long, ret),
|
||||
FILTER_OTHER);
|
||||
|
||||
return ret;
|
||||
@@ -375,6 +444,29 @@ struct trace_event event_syscall_exit = {
|
||||
.trace = print_syscall_exit,
|
||||
};
|
||||
|
||||
int __init init_ftrace_syscalls(void)
|
||||
{
|
||||
struct syscall_metadata *meta;
|
||||
unsigned long addr;
|
||||
int i;
|
||||
|
||||
syscalls_metadata = kzalloc(sizeof(*syscalls_metadata) *
|
||||
NR_syscalls, GFP_KERNEL);
|
||||
if (!syscalls_metadata) {
|
||||
WARN_ON(1);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < NR_syscalls; i++) {
|
||||
addr = arch_syscall_addr(i);
|
||||
meta = find_syscall_meta(addr);
|
||||
syscalls_metadata[i] = meta;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
core_initcall(init_ftrace_syscalls);
|
||||
|
||||
#ifdef CONFIG_EVENT_PROFILE
|
||||
|
||||
static DECLARE_BITMAP(enabled_prof_enter_syscalls, NR_syscalls);
|
||||
|
Reference in New Issue
Block a user