This release has a few updates:

o STM can hook into the function tracer
  o Function filtering now supports more advance glob matching
  o Ftrace selftests updates and added tests
  o Softirq tag in traces now show only softirqs
  o ARM nop added to non traced locations at compile time
  o New trace_marker_raw file that allows for binary input
  o Optimizations to the ring buffer
  o Removal of kmap in trace_marker
  o Wakeup and irqsoff tracers now adhere to the set_graph_notrace file
  o Other various fixes and clean ups
 
 Note, there are two patches marked for stable. These were discovered
 near the end of the 4.9 rc release cycle. By the time I had them tested
 it was just a matter of days before 4.9 would be released, and I
 figured I would just submit them in the merge window. They are old
 bugs and not critical. Nothing non-root could abuse.
 -----BEGIN PGP SIGNATURE-----
 
 iQExBAABCAAbBQJYUrFHFBxyb3N0ZWR0QGdvb2RtaXMub3JnAAoJEMm5BfJq2Y3L
 2+AIAIr20kSQV/nA5htGAeCTobVk3WUxY6bvjd9mIJDKPP19akNLyREW0G3KnfCr
 yhx4aFRZG98fRu/6F8qieRosyN36lADDVYHelMFHMpcTOpE2aZGjaaOuNGxOEA9v
 FmMPTX+K3+dzKyFP4l68R3+5JuQ1/AqLTioTWeLW8IDQ2OOVsjD8+0BuXrNKMJDY
 o6U4Hk5U/vn+zHc6BmgBzloAXemBd7iJ1t5V3FRRGvm8yv3HU85Twc5ofGeYTWvB
 J8PboEywRlIzxg0Kd8mxnMI5PgaKZSEc2ub8E7cY/CZ5PYpDE2xDA2hJmJgfYp00
 1VW+DHRpRZfElsCcya6S6P4bs5Y=
 =MGZ/
 -----END PGP SIGNATURE-----

Merge tag 'trace-v4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace

Pull tracing updates from Steven Rostedt:
 "This release has a few updates:

   - STM can hook into the function tracer
   - Function filtering now supports more advance glob matching
   - Ftrace selftests updates and added tests
   - Softirq tag in traces now show only softirqs
   - ARM nop added to non traced locations at compile time
   - New trace_marker_raw file that allows for binary input
   - Optimizations to the ring buffer
   - Removal of kmap in trace_marker
   - Wakeup and irqsoff tracers now adhere to the set_graph_notrace file
   - Other various fixes and clean ups"

* tag 'trace-v4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: (42 commits)
  selftests: ftrace: Shift down default message verbosity
  kprobes/trace: Fix kprobe selftest for newer gcc
  tracing/kprobes: Add a helper method to return number of probe hits
  tracing/rb: Init the CPU mask on allocation
  tracing: Use SOFTIRQ_OFFSET for softirq dectection for more accurate results
  tracing/fgraph: Have wakeup and irqsoff tracers ignore graph functions too
  fgraph: Handle a case where a tracer ignores set_graph_notrace
  tracing: Replace kmap with copy_from_user() in trace_marker writing
  ftrace/x86_32: Set ftrace_stub to weak to prevent gcc from using short jumps to it
  tracing: Allow benchmark to be enabled at early_initcall()
  tracing: Have system enable return error if one of the events fail
  tracing: Do not start benchmark on boot up
  tracing: Have the reg function allow to fail
  ring-buffer: Force rb_end_commit() and rb_set_commit_to_write() inline
  ring-buffer: Froce rb_update_write_stamp() to be inlined
  ring-buffer: Force inline of hotpath helper functions
  tracing: Make __buffer_unlock_commit() always_inline
  tracing: Make tracepoint_printk a static_key
  ring-buffer: Always inline rb_event_data()
  ring-buffer: Make rb_reserve_next_event() always inlined
  ...
This commit is contained in:
Linus Torvalds 2016-12-15 13:49:34 -08:00
commit 179a7ba680
52 changed files with 1065 additions and 301 deletions

View File

@ -189,16 +189,13 @@ And for string fields they are:
==, !=, ~ ==, !=, ~
The glob (~) only accepts a wild card character (*) at the start and or The glob (~) accepts a wild card character (*,?) and character classes
end of the string. For example: ([). For example:
prev_comm ~ "*sh" prev_comm ~ "*sh"
prev_comm ~ "sh*" prev_comm ~ "sh*"
prev_comm ~ "*sh*" prev_comm ~ "*sh*"
prev_comm ~ "ba*sh"
But does not allow for it to be within the string:
prev_comm ~ "ba*sh" <-- is invalid
5.2 Setting filters 5.2 Setting filters
------------------- -------------------

View File

@ -416,6 +416,12 @@ of ftrace. Here is a list of some of the key files:
trace_fd = open("trace_marker", WR_ONLY); trace_fd = open("trace_marker", WR_ONLY);
trace_marker_raw:
This is similar to trace_marker above, but is meant for for binary data
to be written to it, where a tool can be used to parse the data
from trace_pipe_raw.
uprobe_events: uprobe_events:
Add dynamic tracepoints in programs. Add dynamic tracepoints in programs.
@ -2238,16 +2244,13 @@ hrtimer_interrupt
sys_nanosleep sys_nanosleep
Perhaps this is not enough. The filters also allow simple wild Perhaps this is not enough. The filters also allow glob(7) matching.
cards. Only the following are currently available
<match>* - will match functions that begin with <match> <match>* - will match functions that begin with <match>
*<match> - will match functions that end with <match> *<match> - will match functions that end with <match>
*<match>* - will match functions that have <match> in it *<match>* - will match functions that have <match> in it
<match1>*<match2> - will match functions that begin with
These are the only wild cards which are supported. <match1> and end with <match2>
<match>*<match> will not work.
Note: It is better to use quotes to enclose the wild cards, Note: It is better to use quotes to enclose the wild cards,
otherwise the shell may expand the parameters into names otherwise the shell may expand the parameters into names

View File

@ -54,7 +54,7 @@ DEFINE_EVENT(ppc64_interrupt_class, timer_interrupt_exit,
); );
#ifdef CONFIG_PPC_PSERIES #ifdef CONFIG_PPC_PSERIES
extern void hcall_tracepoint_regfunc(void); extern int hcall_tracepoint_regfunc(void);
extern void hcall_tracepoint_unregfunc(void); extern void hcall_tracepoint_unregfunc(void);
TRACE_EVENT_FN_COND(hcall_entry, TRACE_EVENT_FN_COND(hcall_entry,
@ -104,7 +104,7 @@ TRACE_EVENT_FN_COND(hcall_exit,
#endif #endif
#ifdef CONFIG_PPC_POWERNV #ifdef CONFIG_PPC_POWERNV
extern void opal_tracepoint_regfunc(void); extern int opal_tracepoint_regfunc(void);
extern void opal_tracepoint_unregfunc(void); extern void opal_tracepoint_unregfunc(void);
TRACE_EVENT_FN(opal_entry, TRACE_EVENT_FN(opal_entry,

View File

@ -6,9 +6,10 @@
#ifdef HAVE_JUMP_LABEL #ifdef HAVE_JUMP_LABEL
struct static_key opal_tracepoint_key = STATIC_KEY_INIT; struct static_key opal_tracepoint_key = STATIC_KEY_INIT;
void opal_tracepoint_regfunc(void) int opal_tracepoint_regfunc(void)
{ {
static_key_slow_inc(&opal_tracepoint_key); static_key_slow_inc(&opal_tracepoint_key);
return 0;
} }
void opal_tracepoint_unregfunc(void) void opal_tracepoint_unregfunc(void)
@ -25,9 +26,10 @@ void opal_tracepoint_unregfunc(void)
/* NB: reg/unreg are called while guarded with the tracepoints_mutex */ /* NB: reg/unreg are called while guarded with the tracepoints_mutex */
extern long opal_tracepoint_refcount; extern long opal_tracepoint_refcount;
void opal_tracepoint_regfunc(void) int opal_tracepoint_regfunc(void)
{ {
opal_tracepoint_refcount++; opal_tracepoint_refcount++;
return 0;
} }
void opal_tracepoint_unregfunc(void) void opal_tracepoint_unregfunc(void)

View File

@ -661,9 +661,10 @@ EXPORT_SYMBOL(arch_free_page);
#ifdef HAVE_JUMP_LABEL #ifdef HAVE_JUMP_LABEL
struct static_key hcall_tracepoint_key = STATIC_KEY_INIT; struct static_key hcall_tracepoint_key = STATIC_KEY_INIT;
void hcall_tracepoint_regfunc(void) int hcall_tracepoint_regfunc(void)
{ {
static_key_slow_inc(&hcall_tracepoint_key); static_key_slow_inc(&hcall_tracepoint_key);
return 0;
} }
void hcall_tracepoint_unregfunc(void) void hcall_tracepoint_unregfunc(void)
@ -680,9 +681,10 @@ void hcall_tracepoint_unregfunc(void)
/* NB: reg/unreg are called while guarded with the tracepoints_mutex */ /* NB: reg/unreg are called while guarded with the tracepoints_mutex */
extern long hcall_tracepoint_refcount; extern long hcall_tracepoint_refcount;
void hcall_tracepoint_regfunc(void) int hcall_tracepoint_regfunc(void)
{ {
hcall_tracepoint_refcount++; hcall_tracepoint_refcount++;
return 0;
} }
void hcall_tracepoint_unregfunc(void) void hcall_tracepoint_unregfunc(void)

View File

@ -926,8 +926,8 @@ ftrace_graph_call:
jmp ftrace_stub jmp ftrace_stub
#endif #endif
.globl ftrace_stub /* This is weak to keep gas from relaxing the jumps */
ftrace_stub: WEAK(ftrace_stub)
ret ret
END(ftrace_caller) END(ftrace_caller)

View File

@ -6,7 +6,7 @@
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
extern void trace_irq_vector_regfunc(void); extern int trace_irq_vector_regfunc(void);
extern void trace_irq_vector_unregfunc(void); extern void trace_irq_vector_unregfunc(void);
DECLARE_EVENT_CLASS(x86_exceptions, DECLARE_EVENT_CLASS(x86_exceptions,

View File

@ -6,7 +6,7 @@
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
extern void trace_irq_vector_regfunc(void); extern int trace_irq_vector_regfunc(void);
extern void trace_irq_vector_unregfunc(void); extern void trace_irq_vector_unregfunc(void);
DECLARE_EVENT_CLASS(x86_irq_vector, DECLARE_EVENT_CLASS(x86_irq_vector,

View File

@ -34,7 +34,7 @@ static void switch_idt(void *arg)
local_irq_restore(flags); local_irq_restore(flags);
} }
void trace_irq_vector_regfunc(void) int trace_irq_vector_regfunc(void)
{ {
mutex_lock(&irq_vector_mutex); mutex_lock(&irq_vector_mutex);
if (!trace_irq_vector_refcount) { if (!trace_irq_vector_refcount) {
@ -44,6 +44,7 @@ void trace_irq_vector_regfunc(void)
} }
trace_irq_vector_refcount++; trace_irq_vector_refcount++;
mutex_unlock(&irq_vector_mutex); mutex_unlock(&irq_vector_mutex);
return 0;
} }
void trace_irq_vector_unregfunc(void) void trace_irq_vector_unregfunc(void)

View File

@ -406,7 +406,7 @@ static long stm_generic_set_options(struct stm_data *stm_data,
return 0; return 0;
} }
static ssize_t stm_generic_packet(struct stm_data *stm_data, static ssize_t notrace stm_generic_packet(struct stm_data *stm_data,
unsigned int master, unsigned int master,
unsigned int channel, unsigned int channel,
unsigned int packet, unsigned int packet,

View File

@ -67,10 +67,13 @@ static void sth_iowrite(void __iomem *dest, const unsigned char *payload,
} }
} }
static ssize_t sth_stm_packet(struct stm_data *stm_data, unsigned int master, static ssize_t notrace sth_stm_packet(struct stm_data *stm_data,
unsigned int channel, unsigned int packet, unsigned int master,
unsigned int flags, unsigned int size, unsigned int channel,
const unsigned char *payload) unsigned int packet,
unsigned int flags,
unsigned int size,
const unsigned char *payload)
{ {
struct sth_device *sth = container_of(stm_data, struct sth_device, stm); struct sth_device *sth = container_of(stm_data, struct sth_device, stm);
struct intel_th_channel __iomem *out = struct intel_th_channel __iomem *out =

View File

@ -39,4 +39,15 @@ config STM_SOURCE_HEARTBEAT
If you want to send heartbeat messages over STM devices, If you want to send heartbeat messages over STM devices,
say Y. say Y.
config STM_SOURCE_FTRACE
tristate "Copy the output from kernel Ftrace to STM engine"
depends on FUNCTION_TRACER
help
This option can be used to copy the output from kernel Ftrace
to STM engine. Enabling this option will introduce a slight
timing effect.
If you want to send kernel Ftrace messages over STM devices,
say Y.
endif endif

View File

@ -6,6 +6,8 @@ obj-$(CONFIG_STM_DUMMY) += dummy_stm.o
obj-$(CONFIG_STM_SOURCE_CONSOLE) += stm_console.o obj-$(CONFIG_STM_SOURCE_CONSOLE) += stm_console.o
obj-$(CONFIG_STM_SOURCE_HEARTBEAT) += stm_heartbeat.o obj-$(CONFIG_STM_SOURCE_HEARTBEAT) += stm_heartbeat.o
obj-$(CONFIG_STM_SOURCE_FTRACE) += stm_ftrace.o
stm_console-y := console.o stm_console-y := console.o
stm_heartbeat-y := heartbeat.o stm_heartbeat-y := heartbeat.o
stm_ftrace-y := ftrace.o

View File

@ -427,7 +427,7 @@ static int stm_file_assign(struct stm_file *stmf, char *id, unsigned int width)
return ret; return ret;
} }
static ssize_t stm_write(struct stm_data *data, unsigned int master, static ssize_t notrace stm_write(struct stm_data *data, unsigned int master,
unsigned int channel, const char *buf, size_t count) unsigned int channel, const char *buf, size_t count)
{ {
unsigned int flags = STP_PACKET_TIMESTAMPED; unsigned int flags = STP_PACKET_TIMESTAMPED;
@ -1123,8 +1123,9 @@ void stm_source_unregister_device(struct stm_source_data *data)
} }
EXPORT_SYMBOL_GPL(stm_source_unregister_device); EXPORT_SYMBOL_GPL(stm_source_unregister_device);
int stm_source_write(struct stm_source_data *data, unsigned int chan, int notrace stm_source_write(struct stm_source_data *data,
const char *buf, size_t count) unsigned int chan,
const char *buf, size_t count)
{ {
struct stm_source_device *src = data->src; struct stm_source_device *src = data->src;
struct stm_device *stm; struct stm_device *stm;

View File

@ -21,7 +21,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/stm.h> #include <linux/stm.h>
static ssize_t static ssize_t notrace
dummy_stm_packet(struct stm_data *stm_data, unsigned int master, dummy_stm_packet(struct stm_data *stm_data, unsigned int master,
unsigned int channel, unsigned int packet, unsigned int flags, unsigned int channel, unsigned int packet, unsigned int flags,
unsigned int size, const unsigned char *payload) unsigned int size, const unsigned char *payload)

View File

@ -0,0 +1,87 @@
/*
* Simple kernel driver to link kernel Ftrace and an STM device
* Copyright (c) 2016, Linaro Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* STM Ftrace will be registered as a trace_export.
*/
#include <linux/module.h>
#include <linux/stm.h>
#include <linux/trace.h>
#define STM_FTRACE_NR_CHANNELS 1
#define STM_FTRACE_CHAN 0
static int stm_ftrace_link(struct stm_source_data *data);
static void stm_ftrace_unlink(struct stm_source_data *data);
static struct stm_ftrace {
struct stm_source_data data;
struct trace_export ftrace;
} stm_ftrace = {
.data = {
.name = "ftrace",
.nr_chans = STM_FTRACE_NR_CHANNELS,
.link = stm_ftrace_link,
.unlink = stm_ftrace_unlink,
},
};
/**
* stm_ftrace_write() - write data to STM via 'stm_ftrace' source
* @buf: buffer containing the data packet
* @len: length of the data packet
*/
static void notrace
stm_ftrace_write(const void *buf, unsigned int len)
{
stm_source_write(&stm_ftrace.data, STM_FTRACE_CHAN, buf, len);
}
static int stm_ftrace_link(struct stm_source_data *data)
{
struct stm_ftrace *sf = container_of(data, struct stm_ftrace, data);
sf->ftrace.write = stm_ftrace_write;
return register_ftrace_export(&sf->ftrace);
}
static void stm_ftrace_unlink(struct stm_source_data *data)
{
struct stm_ftrace *sf = container_of(data, struct stm_ftrace, data);
unregister_ftrace_export(&sf->ftrace);
}
static int __init stm_ftrace_init(void)
{
int ret;
ret = stm_source_register_device(NULL, &stm_ftrace.data);
if (ret)
pr_err("Failed to register stm_source - ftrace.\n");
return ret;
}
static void __exit stm_ftrace_exit(void)
{
stm_source_unregister_device(&stm_ftrace.data);
}
module_init(stm_ftrace_init);
module_exit(stm_ftrace_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("stm_ftrace driver");
MODULE_AUTHOR("Chunyan Zhang <zhang.chunyan@linaro.org>");

View File

@ -80,9 +80,10 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
static struct static_key i2c_trace_msg = STATIC_KEY_INIT_FALSE; static struct static_key i2c_trace_msg = STATIC_KEY_INIT_FALSE;
static bool is_registered; static bool is_registered;
void i2c_transfer_trace_reg(void) int i2c_transfer_trace_reg(void)
{ {
static_key_slow_inc(&i2c_trace_msg); static_key_slow_inc(&i2c_trace_msg);
return 0;
} }
void i2c_transfer_trace_unreg(void) void i2c_transfer_trace_unreg(void)

View File

@ -947,6 +947,10 @@ extern int __disable_trace_on_warning;
#define INIT_TRACE_RECURSION .trace_recursion = 0, #define INIT_TRACE_RECURSION .trace_recursion = 0,
#endif #endif
int tracepoint_printk_sysctl(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp,
loff_t *ppos);
#else /* CONFIG_TRACING */ #else /* CONFIG_TRACING */
static inline void disable_trace_on_warning(void) { } static inline void disable_trace_on_warning(void) { }
#endif /* CONFIG_TRACING */ #endif /* CONFIG_TRACING */

View File

@ -133,7 +133,7 @@ int stm_source_register_device(struct device *parent,
struct stm_source_data *data); struct stm_source_data *data);
void stm_source_unregister_device(struct stm_source_data *data); void stm_source_unregister_device(struct stm_source_data *data);
int stm_source_write(struct stm_source_data *data, unsigned int chan, int notrace stm_source_write(struct stm_source_data *data, unsigned int chan,
const char *buf, size_t count); const char *buf, size_t count);
#endif /* _STM_H_ */ #endif /* _STM_H_ */

28
include/linux/trace.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef _LINUX_TRACE_H
#define _LINUX_TRACE_H
#ifdef CONFIG_TRACING
/*
* The trace export - an export of Ftrace output. The trace_export
* can process traces and export them to a registered destination as
* an addition to the current only output of Ftrace - i.e. ring buffer.
*
* If you want traces to be sent to some other place rather than ring
* buffer only, just need to register a new trace_export and implement
* its own .write() function for writing traces to the storage.
*
* next - pointer to the next trace_export
* write - copy traces which have been delt with ->commit() to
* the destination
*/
struct trace_export {
struct trace_export __rcu *next;
void (*write)(const void *, unsigned int);
};
int register_ftrace_export(struct trace_export *export);
int unregister_ftrace_export(struct trace_export *export);
#endif /* CONFIG_TRACING */
#endif /* _LINUX_TRACE_H */

View File

@ -29,7 +29,7 @@ struct tracepoint_func {
struct tracepoint { struct tracepoint {
const char *name; /* Tracepoint name */ const char *name; /* Tracepoint name */
struct static_key key; struct static_key key;
void (*regfunc)(void); int (*regfunc)(void);
void (*unregfunc)(void); void (*unregfunc)(void);
struct tracepoint_func __rcu *funcs; struct tracepoint_func __rcu *funcs;
}; };

View File

@ -81,7 +81,7 @@ static inline void tracepoint_synchronize_unregister(void)
} }
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
extern void syscall_regfunc(void); extern int syscall_regfunc(void);
extern void syscall_unregfunc(void); extern void syscall_unregfunc(void);
#endif /* CONFIG_HAVE_SYSCALL_TRACEPOINTS */ #endif /* CONFIG_HAVE_SYSCALL_TRACEPOINTS */

View File

@ -20,7 +20,7 @@
/* /*
* drivers/i2c/i2c-core.c * drivers/i2c/i2c-core.c
*/ */
extern void i2c_transfer_trace_reg(void); extern int i2c_transfer_trace_reg(void);
extern void i2c_transfer_trace_unreg(void); extern void i2c_transfer_trace_unreg(void);
/* /*

View File

@ -627,7 +627,7 @@ static struct ctl_table kern_table[] = {
.data = &tracepoint_printk, .data = &tracepoint_printk,
.maxlen = sizeof(tracepoint_printk), .maxlen = sizeof(tracepoint_printk),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec, .proc_handler = tracepoint_printk_sysctl,
}, },
#endif #endif
#ifdef CONFIG_KEXEC_CORE #ifdef CONFIG_KEXEC_CORE

View File

@ -70,6 +70,7 @@ config FTRACE_NMI_ENTER
config EVENT_TRACING config EVENT_TRACING
select CONTEXT_SWITCH_TRACER select CONTEXT_SWITCH_TRACER
select GLOB
bool bool
config CONTEXT_SWITCH_TRACER config CONTEXT_SWITCH_TRACER
@ -133,6 +134,7 @@ config FUNCTION_TRACER
select KALLSYMS select KALLSYMS
select GENERIC_TRACER select GENERIC_TRACER
select CONTEXT_SWITCH_TRACER select CONTEXT_SWITCH_TRACER
select GLOB
help help
Enable the kernel to trace every kernel function. This is done Enable the kernel to trace every kernel function. This is done
by using a compiler feature to insert a small, 5-byte No-Operation by using a compiler feature to insert a small, 5-byte No-Operation

View File

@ -3511,6 +3511,10 @@ static int ftrace_match(char *str, struct ftrace_glob *g)
memcmp(str + slen - g->len, g->search, g->len) == 0) memcmp(str + slen - g->len, g->search, g->len) == 0)
matched = 1; matched = 1;
break; break;
case MATCH_GLOB:
if (glob_match(g->search, str))
matched = 1;
break;
} }
return matched; return matched;

View File

@ -245,7 +245,7 @@ unsigned ring_buffer_event_length(struct ring_buffer_event *event)
EXPORT_SYMBOL_GPL(ring_buffer_event_length); EXPORT_SYMBOL_GPL(ring_buffer_event_length);
/* inline for ring buffer fast paths */ /* inline for ring buffer fast paths */
static void * static __always_inline void *
rb_event_data(struct ring_buffer_event *event) rb_event_data(struct ring_buffer_event *event)
{ {
if (event->type_len == RINGBUF_TYPE_TIME_EXTEND) if (event->type_len == RINGBUF_TYPE_TIME_EXTEND)
@ -1798,48 +1798,48 @@ void ring_buffer_change_overwrite(struct ring_buffer *buffer, int val)
} }
EXPORT_SYMBOL_GPL(ring_buffer_change_overwrite); EXPORT_SYMBOL_GPL(ring_buffer_change_overwrite);
static inline void * static __always_inline void *
__rb_data_page_index(struct buffer_data_page *bpage, unsigned index) __rb_data_page_index(struct buffer_data_page *bpage, unsigned index)
{ {
return bpage->data + index; return bpage->data + index;
} }
static inline void *__rb_page_index(struct buffer_page *bpage, unsigned index) static __always_inline void *__rb_page_index(struct buffer_page *bpage, unsigned index)
{ {
return bpage->page->data + index; return bpage->page->data + index;
} }
static inline struct ring_buffer_event * static __always_inline struct ring_buffer_event *
rb_reader_event(struct ring_buffer_per_cpu *cpu_buffer) rb_reader_event(struct ring_buffer_per_cpu *cpu_buffer)
{ {
return __rb_page_index(cpu_buffer->reader_page, return __rb_page_index(cpu_buffer->reader_page,
cpu_buffer->reader_page->read); cpu_buffer->reader_page->read);
} }
static inline struct ring_buffer_event * static __always_inline struct ring_buffer_event *
rb_iter_head_event(struct ring_buffer_iter *iter) rb_iter_head_event(struct ring_buffer_iter *iter)
{ {
return __rb_page_index(iter->head_page, iter->head); return __rb_page_index(iter->head_page, iter->head);
} }
static inline unsigned rb_page_commit(struct buffer_page *bpage) static __always_inline unsigned rb_page_commit(struct buffer_page *bpage)
{ {
return local_read(&bpage->page->commit); return local_read(&bpage->page->commit);
} }
/* Size is determined by what has been committed */ /* Size is determined by what has been committed */
static inline unsigned rb_page_size(struct buffer_page *bpage) static __always_inline unsigned rb_page_size(struct buffer_page *bpage)
{ {
return rb_page_commit(bpage); return rb_page_commit(bpage);
} }
static inline unsigned static __always_inline unsigned
rb_commit_index(struct ring_buffer_per_cpu *cpu_buffer) rb_commit_index(struct ring_buffer_per_cpu *cpu_buffer)
{ {
return rb_page_commit(cpu_buffer->commit_page); return rb_page_commit(cpu_buffer->commit_page);
} }
static inline unsigned static __always_inline unsigned
rb_event_index(struct ring_buffer_event *event) rb_event_index(struct ring_buffer_event *event)
{ {
unsigned long addr = (unsigned long)event; unsigned long addr = (unsigned long)event;
@ -2355,7 +2355,7 @@ static void rb_start_commit(struct ring_buffer_per_cpu *cpu_buffer)
local_inc(&cpu_buffer->commits); local_inc(&cpu_buffer->commits);
} }
static void static __always_inline void
rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer) rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer)
{ {
unsigned long max_count; unsigned long max_count;
@ -2410,7 +2410,7 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer)
goto again; goto again;
} }
static inline void rb_end_commit(struct ring_buffer_per_cpu *cpu_buffer) static __always_inline void rb_end_commit(struct ring_buffer_per_cpu *cpu_buffer)
{ {
unsigned long commits; unsigned long commits;
@ -2455,7 +2455,7 @@ static inline void rb_event_discard(struct ring_buffer_event *event)
event->time_delta = 1; event->time_delta = 1;
} }
static inline bool static __always_inline bool
rb_event_is_commit(struct ring_buffer_per_cpu *cpu_buffer, rb_event_is_commit(struct ring_buffer_per_cpu *cpu_buffer,
struct ring_buffer_event *event) struct ring_buffer_event *event)
{ {
@ -2469,7 +2469,7 @@ rb_event_is_commit(struct ring_buffer_per_cpu *cpu_buffer,
rb_commit_index(cpu_buffer) == index; rb_commit_index(cpu_buffer) == index;
} }
static void static __always_inline void
rb_update_write_stamp(struct ring_buffer_per_cpu *cpu_buffer, rb_update_write_stamp(struct ring_buffer_per_cpu *cpu_buffer,
struct ring_buffer_event *event) struct ring_buffer_event *event)
{ {
@ -2702,7 +2702,7 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer,
return event; return event;
} }
static struct ring_buffer_event * static __always_inline struct ring_buffer_event *
rb_reserve_next_event(struct ring_buffer *buffer, rb_reserve_next_event(struct ring_buffer *buffer,
struct ring_buffer_per_cpu *cpu_buffer, struct ring_buffer_per_cpu *cpu_buffer,
unsigned long length) unsigned long length)

View File

@ -40,6 +40,7 @@
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/nmi.h> #include <linux/nmi.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/trace.h>
#include <linux/sched/rt.h> #include <linux/sched/rt.h>
#include "trace.h" #include "trace.h"
@ -68,6 +69,7 @@ bool __read_mostly tracing_selftest_disabled;
/* Pipe tracepoints to printk */ /* Pipe tracepoints to printk */
struct trace_iterator *tracepoint_print_iter; struct trace_iterator *tracepoint_print_iter;
int tracepoint_printk; int tracepoint_printk;
static DEFINE_STATIC_KEY_FALSE(tracepoint_printk_key);
/* For tracers that don't implement custom flags */ /* For tracers that don't implement custom flags */
static struct tracer_opt dummy_tracer_opt[] = { static struct tracer_opt dummy_tracer_opt[] = {
@ -738,6 +740,31 @@ static inline void ftrace_trace_stack(struct trace_array *tr,
#endif #endif
static __always_inline void
trace_event_setup(struct ring_buffer_event *event,
int type, unsigned long flags, int pc)
{
struct trace_entry *ent = ring_buffer_event_data(event);
tracing_generic_entry_update(ent, flags, pc);
ent->type = type;
}
static __always_inline struct ring_buffer_event *
__trace_buffer_lock_reserve(struct ring_buffer *buffer,
int type,
unsigned long len,
unsigned long flags, int pc)
{
struct ring_buffer_event *event;
event = ring_buffer_lock_reserve(buffer, len);
if (event != NULL)
trace_event_setup(event, type, flags, pc);
return event;
}
static void tracer_tracing_on(struct trace_array *tr) static void tracer_tracing_on(struct trace_array *tr)
{ {
if (tr->trace_buffer.buffer) if (tr->trace_buffer.buffer)
@ -767,6 +794,22 @@ void tracing_on(void)
} }
EXPORT_SYMBOL_GPL(tracing_on); EXPORT_SYMBOL_GPL(tracing_on);
static __always_inline void
__buffer_unlock_commit(struct ring_buffer *buffer, struct ring_buffer_event *event)
{
__this_cpu_write(trace_cmdline_save, true);
/* If this is the temp buffer, we need to commit fully */
if (this_cpu_read(trace_buffered_event) == event) {
/* Length is in event->array[0] */
ring_buffer_write(buffer, event->array[0], &event->array[1]);
/* Release the temp buffer */
this_cpu_dec(trace_buffered_event_cnt);
} else
ring_buffer_unlock_commit(buffer, event);
}
/** /**
* __trace_puts - write a constant string into the trace buffer. * __trace_puts - write a constant string into the trace buffer.
* @ip: The address of the caller * @ip: The address of the caller
@ -794,8 +837,8 @@ int __trace_puts(unsigned long ip, const char *str, int size)
local_save_flags(irq_flags); local_save_flags(irq_flags);
buffer = global_trace.trace_buffer.buffer; buffer = global_trace.trace_buffer.buffer;
event = trace_buffer_lock_reserve(buffer, TRACE_PRINT, alloc, event = __trace_buffer_lock_reserve(buffer, TRACE_PRINT, alloc,
irq_flags, pc); irq_flags, pc);
if (!event) if (!event)
return 0; return 0;
@ -842,8 +885,8 @@ int __trace_bputs(unsigned long ip, const char *str)
local_save_flags(irq_flags); local_save_flags(irq_flags);
buffer = global_trace.trace_buffer.buffer; buffer = global_trace.trace_buffer.buffer;
event = trace_buffer_lock_reserve(buffer, TRACE_BPUTS, size, event = __trace_buffer_lock_reserve(buffer, TRACE_BPUTS, size,
irq_flags, pc); irq_flags, pc);
if (!event) if (!event)
return 0; return 0;
@ -1907,35 +1950,19 @@ tracing_generic_entry_update(struct trace_entry *entry, unsigned long flags,
#endif #endif
((pc & NMI_MASK ) ? TRACE_FLAG_NMI : 0) | ((pc & NMI_MASK ) ? TRACE_FLAG_NMI : 0) |
((pc & HARDIRQ_MASK) ? TRACE_FLAG_HARDIRQ : 0) | ((pc & HARDIRQ_MASK) ? TRACE_FLAG_HARDIRQ : 0) |
((pc & SOFTIRQ_MASK) ? TRACE_FLAG_SOFTIRQ : 0) | ((pc & SOFTIRQ_OFFSET) ? TRACE_FLAG_SOFTIRQ : 0) |
(tif_need_resched() ? TRACE_FLAG_NEED_RESCHED : 0) | (tif_need_resched() ? TRACE_FLAG_NEED_RESCHED : 0) |
(test_preempt_need_resched() ? TRACE_FLAG_PREEMPT_RESCHED : 0); (test_preempt_need_resched() ? TRACE_FLAG_PREEMPT_RESCHED : 0);
} }
EXPORT_SYMBOL_GPL(tracing_generic_entry_update); EXPORT_SYMBOL_GPL(tracing_generic_entry_update);
static __always_inline void
trace_event_setup(struct ring_buffer_event *event,
int type, unsigned long flags, int pc)
{
struct trace_entry *ent = ring_buffer_event_data(event);
tracing_generic_entry_update(ent, flags, pc);
ent->type = type;
}
struct ring_buffer_event * struct ring_buffer_event *
trace_buffer_lock_reserve(struct ring_buffer *buffer, trace_buffer_lock_reserve(struct ring_buffer *buffer,
int type, int type,
unsigned long len, unsigned long len,
unsigned long flags, int pc) unsigned long flags, int pc)
{ {
struct ring_buffer_event *event; return __trace_buffer_lock_reserve(buffer, type, len, flags, pc);
event = ring_buffer_lock_reserve(buffer, len);
if (event != NULL)
trace_event_setup(event, type, flags, pc);
return event;
} }
DEFINE_PER_CPU(struct ring_buffer_event *, trace_buffered_event); DEFINE_PER_CPU(struct ring_buffer_event *, trace_buffered_event);
@ -2049,21 +2076,6 @@ void trace_buffered_event_disable(void)
preempt_enable(); preempt_enable();
} }
void
__buffer_unlock_commit(struct ring_buffer *buffer, struct ring_buffer_event *event)
{
__this_cpu_write(trace_cmdline_save, true);
/* If this is the temp buffer, we need to commit fully */
if (this_cpu_read(trace_buffered_event) == event) {
/* Length is in event->array[0] */
ring_buffer_write(buffer, event->array[0], &event->array[1]);
/* Release the temp buffer */
this_cpu_dec(trace_buffered_event_cnt);
} else
ring_buffer_unlock_commit(buffer, event);
}
static struct ring_buffer *temp_buffer; static struct ring_buffer *temp_buffer;
struct ring_buffer_event * struct ring_buffer_event *
@ -2090,8 +2102,8 @@ trace_event_buffer_lock_reserve(struct ring_buffer **current_rb,
this_cpu_dec(trace_buffered_event_cnt); this_cpu_dec(trace_buffered_event_cnt);
} }
entry = trace_buffer_lock_reserve(*current_rb, entry = __trace_buffer_lock_reserve(*current_rb,
type, len, flags, pc); type, len, flags, pc);
/* /*
* If tracing is off, but we have triggers enabled * If tracing is off, but we have triggers enabled
* we still need to look at the event data. Use the temp_buffer * we still need to look at the event data. Use the temp_buffer
@ -2100,13 +2112,88 @@ trace_event_buffer_lock_reserve(struct ring_buffer **current_rb,
*/ */
if (!entry && trace_file->flags & EVENT_FILE_FL_TRIGGER_COND) { if (!entry && trace_file->flags & EVENT_FILE_FL_TRIGGER_COND) {
*current_rb = temp_buffer; *current_rb = temp_buffer;
entry = trace_buffer_lock_reserve(*current_rb, entry = __trace_buffer_lock_reserve(*current_rb,
type, len, flags, pc); type, len, flags, pc);
} }
return entry; return entry;
} }
EXPORT_SYMBOL_GPL(trace_event_buffer_lock_reserve); EXPORT_SYMBOL_GPL(trace_event_buffer_lock_reserve);
static DEFINE_SPINLOCK(tracepoint_iter_lock);
static DEFINE_MUTEX(tracepoint_printk_mutex);
static void output_printk(struct trace_event_buffer *fbuffer)
{
struct trace_event_call *event_call;
struct trace_event *event;
unsigned long flags;
struct trace_iterator *iter = tracepoint_print_iter;
/* We should never get here if iter is NULL */
if (WARN_ON_ONCE(!iter))
return;
event_call = fbuffer->trace_file->event_call;
if (!event_call || !event_call->event.funcs ||
!event_call->event.funcs->trace)
return;
event = &fbuffer->trace_file->event_call->event;
spin_lock_irqsave(&tracepoint_iter_lock, flags);
trace_seq_init(&iter->seq);
iter->ent = fbuffer->entry;
event_call->event.funcs->trace(iter, 0, event);
trace_seq_putc(&iter->seq, 0);
printk("%s", iter->seq.buffer);
spin_unlock_irqrestore(&tracepoint_iter_lock, flags);
}
int tracepoint_printk_sysctl(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp,
loff_t *ppos)
{
int save_tracepoint_printk;
int ret;
mutex_lock(&tracepoint_printk_mutex);
save_tracepoint_printk = tracepoint_printk;
ret = proc_dointvec(table, write, buffer, lenp, ppos);
/*
* This will force exiting early, as tracepoint_printk
* is always zero when tracepoint_printk_iter is not allocated
*/
if (!tracepoint_print_iter)
tracepoint_printk = 0;
if (save_tracepoint_printk == tracepoint_printk)
goto out;
if (tracepoint_printk)
static_key_enable(&tracepoint_printk_key.key);
else
static_key_disable(&tracepoint_printk_key.key);
out:
mutex_unlock(&tracepoint_printk_mutex);
return ret;
}
void trace_event_buffer_commit(struct trace_event_buffer *fbuffer)
{
if (static_key_false(&tracepoint_printk_key.key))
output_printk(fbuffer);
event_trigger_unlock_commit(fbuffer->trace_file, fbuffer->buffer,
fbuffer->event, fbuffer->entry,
fbuffer->flags, fbuffer->pc);
}
EXPORT_SYMBOL_GPL(trace_event_buffer_commit);
void trace_buffer_unlock_commit_regs(struct trace_array *tr, void trace_buffer_unlock_commit_regs(struct trace_array *tr,
struct ring_buffer *buffer, struct ring_buffer *buffer,
struct ring_buffer_event *event, struct ring_buffer_event *event,
@ -2129,6 +2216,139 @@ void trace_buffer_unlock_commit_regs(struct trace_array *tr,
ftrace_trace_userstack(buffer, flags, pc); ftrace_trace_userstack(buffer, flags, pc);
} }
/*
* Similar to trace_buffer_unlock_commit_regs() but do not dump stack.
*/
void
trace_buffer_unlock_commit_nostack(struct ring_buffer *buffer,
struct ring_buffer_event *event)
{
__buffer_unlock_commit(buffer, event);
}
static void
trace_process_export(struct trace_export *export,
struct ring_buffer_event *event)
{
struct trace_entry *entry;
unsigned int size = 0;
entry = ring_buffer_event_data(event);
size = ring_buffer_event_length(event);
export->write(entry, size);
}
static DEFINE_MUTEX(ftrace_export_lock);
static struct trace_export __rcu *ftrace_exports_list __read_mostly;
static DEFINE_STATIC_KEY_FALSE(ftrace_exports_enabled);
static inline void ftrace_exports_enable(void)
{
static_branch_enable(&ftrace_exports_enabled);
}
static inline void ftrace_exports_disable(void)
{
static_branch_disable(&ftrace_exports_enabled);
}
void ftrace_exports(struct ring_buffer_event *event)
{
struct trace_export *export;
preempt_disable_notrace();
export = rcu_dereference_raw_notrace(ftrace_exports_list);
while (export) {
trace_process_export(export, event);
export = rcu_dereference_raw_notrace(export->next);
}
preempt_enable_notrace();
}
static inline void
add_trace_export(struct trace_export **list, struct trace_export *export)
{
rcu_assign_pointer(export->next, *list);
/*
* We are entering export into the list but another
* CPU might be walking that list. We need to make sure
* the export->next pointer is valid before another CPU sees
* the export pointer included into the list.
*/
rcu_assign_pointer(*list, export);
}
static inline int
rm_trace_export(struct trace_export **list, struct trace_export *export)
{
struct trace_export **p;
for (p = list; *p != NULL; p = &(*p)->next)
if (*p == export)
break;
if (*p != export)
return -1;
rcu_assign_pointer(*p, (*p)->next);
return 0;
}
static inline void
add_ftrace_export(struct trace_export **list, struct trace_export *export)
{
if (*list == NULL)
ftrace_exports_enable();
add_trace_export(list, export);
}
static inline int
rm_ftrace_export(struct trace_export **list, struct trace_export *export)
{
int ret;
ret = rm_trace_export(list, export);
if (*list == NULL)
ftrace_exports_disable();
return ret;
}
int register_ftrace_export(struct trace_export *export)
{
if (WARN_ON_ONCE(!export->write))
return -1;
mutex_lock(&ftrace_export_lock);
add_ftrace_export(&ftrace_exports_list, export);
mutex_unlock(&ftrace_export_lock);
return 0;
}
EXPORT_SYMBOL_GPL(register_ftrace_export);
int unregister_ftrace_export(struct trace_export *export)
{
int ret;
mutex_lock(&ftrace_export_lock);
ret = rm_ftrace_export(&ftrace_exports_list, export);
mutex_unlock(&ftrace_export_lock);
return ret;
}
EXPORT_SYMBOL_GPL(unregister_ftrace_export);
void void
trace_function(struct trace_array *tr, trace_function(struct trace_array *tr,
unsigned long ip, unsigned long parent_ip, unsigned long flags, unsigned long ip, unsigned long parent_ip, unsigned long flags,
@ -2139,16 +2359,19 @@ trace_function(struct trace_array *tr,
struct ring_buffer_event *event; struct ring_buffer_event *event;
struct ftrace_entry *entry; struct ftrace_entry *entry;
event = trace_buffer_lock_reserve(buffer, TRACE_FN, sizeof(*entry), event = __trace_buffer_lock_reserve(buffer, TRACE_FN, sizeof(*entry),
flags, pc); flags, pc);
if (!event) if (!event)
return; return;
entry = ring_buffer_event_data(event); entry = ring_buffer_event_data(event);
entry->ip = ip; entry->ip = ip;
entry->parent_ip = parent_ip; entry->parent_ip = parent_ip;
if (!call_filter_check_discard(call, entry, buffer, event)) if (!call_filter_check_discard(call, entry, buffer, event)) {
if (static_branch_unlikely(&ftrace_exports_enabled))
ftrace_exports(event);
__buffer_unlock_commit(buffer, event); __buffer_unlock_commit(buffer, event);
}
} }
#ifdef CONFIG_STACKTRACE #ifdef CONFIG_STACKTRACE
@ -2216,8 +2439,8 @@ static void __ftrace_trace_stack(struct ring_buffer *buffer,
size *= sizeof(unsigned long); size *= sizeof(unsigned long);
event = trace_buffer_lock_reserve(buffer, TRACE_STACK, event = __trace_buffer_lock_reserve(buffer, TRACE_STACK,
sizeof(*entry) + size, flags, pc); sizeof(*entry) + size, flags, pc);
if (!event) if (!event)
goto out; goto out;
entry = ring_buffer_event_data(event); entry = ring_buffer_event_data(event);
@ -2318,8 +2541,8 @@ ftrace_trace_userstack(struct ring_buffer *buffer, unsigned long flags, int pc)
__this_cpu_inc(user_stack_count); __this_cpu_inc(user_stack_count);
event = trace_buffer_lock_reserve(buffer, TRACE_USER_STACK, event = __trace_buffer_lock_reserve(buffer, TRACE_USER_STACK,
sizeof(*entry), flags, pc); sizeof(*entry), flags, pc);
if (!event) if (!event)
goto out_drop_count; goto out_drop_count;
entry = ring_buffer_event_data(event); entry = ring_buffer_event_data(event);
@ -2489,8 +2712,8 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
local_save_flags(flags); local_save_flags(flags);
size = sizeof(*entry) + sizeof(u32) * len; size = sizeof(*entry) + sizeof(u32) * len;
buffer = tr->trace_buffer.buffer; buffer = tr->trace_buffer.buffer;
event = trace_buffer_lock_reserve(buffer, TRACE_BPRINT, size, event = __trace_buffer_lock_reserve(buffer, TRACE_BPRINT, size,
flags, pc); flags, pc);
if (!event) if (!event)
goto out; goto out;
entry = ring_buffer_event_data(event); entry = ring_buffer_event_data(event);
@ -2545,8 +2768,8 @@ __trace_array_vprintk(struct ring_buffer *buffer,
local_save_flags(flags); local_save_flags(flags);
size = sizeof(*entry) + len + 1; size = sizeof(*entry) + len + 1;
event = trace_buffer_lock_reserve(buffer, TRACE_PRINT, size, event = __trace_buffer_lock_reserve(buffer, TRACE_PRINT, size,
flags, pc); flags, pc);
if (!event) if (!event)
goto out; goto out;
entry = ring_buffer_event_data(event); entry = ring_buffer_event_data(event);
@ -4055,6 +4278,7 @@ static const char readme_msg[] =
" x86-tsc: TSC cycle counter\n" " x86-tsc: TSC cycle counter\n"
#endif #endif
"\n trace_marker\t\t- Writes into this file writes into the kernel buffer\n" "\n trace_marker\t\t- Writes into this file writes into the kernel buffer\n"
"\n trace_marker_raw\t\t- Writes into this file writes binary data into the kernel buffer\n"
" tracing_cpumask\t- Limit which CPUs to trace\n" " tracing_cpumask\t- Limit which CPUs to trace\n"
" instances\t\t- Make sub-buffers with: mkdir instances/foo\n" " instances\t\t- Make sub-buffers with: mkdir instances/foo\n"
"\t\t\t Remove sub-buffer with rmdir\n" "\t\t\t Remove sub-buffer with rmdir\n"
@ -4066,7 +4290,7 @@ static const char readme_msg[] =
"\n available_filter_functions - list of functions that can be filtered on\n" "\n available_filter_functions - list of functions that can be filtered on\n"
" set_ftrace_filter\t- echo function name in here to only trace these\n" " set_ftrace_filter\t- echo function name in here to only trace these\n"
"\t\t\t functions\n" "\t\t\t functions\n"
"\t accepts: func_full_name, *func_end, func_begin*, *func_middle*\n" "\t accepts: func_full_name or glob-matching-pattern\n"
"\t modules: Can select a group via module\n" "\t modules: Can select a group via module\n"
"\t Format: :mod:<module-name>\n" "\t Format: :mod:<module-name>\n"
"\t example: echo :mod:ext3 > set_ftrace_filter\n" "\t example: echo :mod:ext3 > set_ftrace_filter\n"
@ -5519,21 +5743,18 @@ static ssize_t
tracing_mark_write(struct file *filp, const char __user *ubuf, tracing_mark_write(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *fpos) size_t cnt, loff_t *fpos)
{ {
unsigned long addr = (unsigned long)ubuf;
struct trace_array *tr = filp->private_data; struct trace_array *tr = filp->private_data;
struct ring_buffer_event *event; struct ring_buffer_event *event;
struct ring_buffer *buffer; struct ring_buffer *buffer;
struct print_entry *entry; struct print_entry *entry;
unsigned long irq_flags; unsigned long irq_flags;
struct page *pages[2]; const char faulted[] = "<faulted>";
void *map_page[2];
int nr_pages = 1;
ssize_t written; ssize_t written;
int offset;
int size; int size;
int len; int len;
int ret;
int i; /* Used in tracing_mark_raw_write() as well */
#define FAULTED_SIZE (sizeof(faulted) - 1) /* '\0' is already accounted for */
if (tracing_disabled) if (tracing_disabled)
return -EINVAL; return -EINVAL;
@ -5544,60 +5765,33 @@ tracing_mark_write(struct file *filp, const char __user *ubuf,
if (cnt > TRACE_BUF_SIZE) if (cnt > TRACE_BUF_SIZE)
cnt = TRACE_BUF_SIZE; cnt = TRACE_BUF_SIZE;
/*
* Userspace is injecting traces into the kernel trace buffer.
* We want to be as non intrusive as possible.
* To do so, we do not want to allocate any special buffers
* or take any locks, but instead write the userspace data
* straight into the ring buffer.
*
* First we need to pin the userspace buffer into memory,
* which, most likely it is, because it just referenced it.
* But there's no guarantee that it is. By using get_user_pages_fast()
* and kmap_atomic/kunmap_atomic() we can get access to the
* pages directly. We then write the data directly into the
* ring buffer.
*/
BUILD_BUG_ON(TRACE_BUF_SIZE >= PAGE_SIZE); BUILD_BUG_ON(TRACE_BUF_SIZE >= PAGE_SIZE);
/* check if we cross pages */
if ((addr & PAGE_MASK) != ((addr + cnt) & PAGE_MASK))
nr_pages = 2;
offset = addr & (PAGE_SIZE - 1);
addr &= PAGE_MASK;
ret = get_user_pages_fast(addr, nr_pages, 0, pages);
if (ret < nr_pages) {
while (--ret >= 0)
put_page(pages[ret]);
written = -EFAULT;
goto out;
}
for (i = 0; i < nr_pages; i++)
map_page[i] = kmap_atomic(pages[i]);
local_save_flags(irq_flags); local_save_flags(irq_flags);
size = sizeof(*entry) + cnt + 2; /* possible \n added */ size = sizeof(*entry) + cnt + 2; /* add '\0' and possible '\n' */
/* If less than "<faulted>", then make sure we can still add that */
if (cnt < FAULTED_SIZE)
size += FAULTED_SIZE - cnt;
buffer = tr->trace_buffer.buffer; buffer = tr->trace_buffer.buffer;
event = trace_buffer_lock_reserve(buffer, TRACE_PRINT, size, event = __trace_buffer_lock_reserve(buffer, TRACE_PRINT, size,
irq_flags, preempt_count()); irq_flags, preempt_count());
if (!event) { if (unlikely(!event))
/* Ring buffer disabled, return as if not open for write */ /* Ring buffer disabled, return as if not open for write */
written = -EBADF; return -EBADF;
goto out_unlock;
}
entry = ring_buffer_event_data(event); entry = ring_buffer_event_data(event);
entry->ip = _THIS_IP_; entry->ip = _THIS_IP_;
if (nr_pages == 2) { len = __copy_from_user_inatomic(&entry->buf, ubuf, cnt);
len = PAGE_SIZE - offset; if (len) {
memcpy(&entry->buf, map_page[0] + offset, len); memcpy(&entry->buf, faulted, FAULTED_SIZE);
memcpy(&entry->buf[len], map_page[1], cnt - len); cnt = FAULTED_SIZE;
written = -EFAULT;
} else } else
memcpy(&entry->buf, map_page[0] + offset, cnt); written = cnt;
len = cnt;
if (entry->buf[cnt - 1] != '\n') { if (entry->buf[cnt - 1] != '\n') {
entry->buf[cnt] = '\n'; entry->buf[cnt] = '\n';
@ -5607,16 +5801,73 @@ tracing_mark_write(struct file *filp, const char __user *ubuf,
__buffer_unlock_commit(buffer, event); __buffer_unlock_commit(buffer, event);
written = cnt; if (written > 0)
*fpos += written;
*fpos += written; return written;
}
/* Limit it for now to 3K (including tag) */
#define RAW_DATA_MAX_SIZE (1024*3)
static ssize_t
tracing_mark_raw_write(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *fpos)
{
struct trace_array *tr = filp->private_data;
struct ring_buffer_event *event;
struct ring_buffer *buffer;
struct raw_data_entry *entry;
const char faulted[] = "<faulted>";
unsigned long irq_flags;
ssize_t written;
int size;
int len;
#define FAULT_SIZE_ID (FAULTED_SIZE + sizeof(int))
if (tracing_disabled)
return -EINVAL;
if (!(tr->trace_flags & TRACE_ITER_MARKERS))
return -EINVAL;
/* The marker must at least have a tag id */
if (cnt < sizeof(unsigned int) || cnt > RAW_DATA_MAX_SIZE)
return -EINVAL;
if (cnt > TRACE_BUF_SIZE)
cnt = TRACE_BUF_SIZE;
BUILD_BUG_ON(TRACE_BUF_SIZE >= PAGE_SIZE);
local_save_flags(irq_flags);
size = sizeof(*entry) + cnt;
if (cnt < FAULT_SIZE_ID)
size += FAULT_SIZE_ID - cnt;
buffer = tr->trace_buffer.buffer;
event = __trace_buffer_lock_reserve(buffer, TRACE_RAW_DATA, size,
irq_flags, preempt_count());
if (!event)
/* Ring buffer disabled, return as if not open for write */
return -EBADF;
entry = ring_buffer_event_data(event);
len = __copy_from_user_inatomic(&entry->id, ubuf, cnt);
if (len) {
entry->id = -1;
memcpy(&entry->buf, faulted, FAULTED_SIZE);
written = -EFAULT;
} else
written = cnt;
__buffer_unlock_commit(buffer, event);
if (written > 0)
*fpos += written;
out_unlock:
for (i = nr_pages - 1; i >= 0; i--) {
kunmap_atomic(map_page[i]);
put_page(pages[i]);
}
out:
return written; return written;
} }
@ -5946,6 +6197,13 @@ static const struct file_operations tracing_mark_fops = {
.release = tracing_release_generic_tr, .release = tracing_release_generic_tr,
}; };
static const struct file_operations tracing_mark_raw_fops = {
.open = tracing_open_generic_tr,
.write = tracing_mark_raw_write,
.llseek = generic_file_llseek,
.release = tracing_release_generic_tr,
};
static const struct file_operations trace_clock_fops = { static const struct file_operations trace_clock_fops = {
.open = tracing_clock_open, .open = tracing_clock_open,
.read = seq_read, .read = seq_read,
@ -7215,6 +7473,9 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)
trace_create_file("trace_marker", 0220, d_tracer, trace_create_file("trace_marker", 0220, d_tracer,
tr, &tracing_mark_fops); tr, &tracing_mark_fops);
trace_create_file("trace_marker_raw", 0220, d_tracer,
tr, &tracing_mark_raw_fops);
trace_create_file("trace_clock", 0644, d_tracer, tr, trace_create_file("trace_clock", 0644, d_tracer, tr,
&trace_clock_fops); &trace_clock_fops);
@ -7752,6 +8013,8 @@ void __init trace_init(void)
kmalloc(sizeof(*tracepoint_print_iter), GFP_KERNEL); kmalloc(sizeof(*tracepoint_print_iter), GFP_KERNEL);
if (WARN_ON(!tracepoint_print_iter)) if (WARN_ON(!tracepoint_print_iter))
tracepoint_printk = 0; tracepoint_printk = 0;
else
static_key_enable(&tracepoint_printk_key.key);
} }
tracer_alloc_buffers(); tracer_alloc_buffers();
trace_event_init(); trace_event_init();

View File

@ -15,6 +15,7 @@
#include <linux/trace_events.h> #include <linux/trace_events.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/trace_seq.h> #include <linux/trace_seq.h>
#include <linux/glob.h>
#ifdef CONFIG_FTRACE_SYSCALLS #ifdef CONFIG_FTRACE_SYSCALLS
#include <asm/unistd.h> /* For NR_SYSCALLS */ #include <asm/unistd.h> /* For NR_SYSCALLS */
@ -39,6 +40,7 @@ enum trace_type {
TRACE_BLK, TRACE_BLK,
TRACE_BPUTS, TRACE_BPUTS,
TRACE_HWLAT, TRACE_HWLAT,
TRACE_RAW_DATA,
__TRACE_LAST_TYPE, __TRACE_LAST_TYPE,
}; };
@ -330,6 +332,7 @@ extern void __ftrace_bad_type(void);
IF_ASSIGN(var, ent, struct bprint_entry, TRACE_BPRINT); \ IF_ASSIGN(var, ent, struct bprint_entry, TRACE_BPRINT); \
IF_ASSIGN(var, ent, struct bputs_entry, TRACE_BPUTS); \ IF_ASSIGN(var, ent, struct bputs_entry, TRACE_BPUTS); \
IF_ASSIGN(var, ent, struct hwlat_entry, TRACE_HWLAT); \ IF_ASSIGN(var, ent, struct hwlat_entry, TRACE_HWLAT); \
IF_ASSIGN(var, ent, struct raw_data_entry, TRACE_RAW_DATA);\
IF_ASSIGN(var, ent, struct trace_mmiotrace_rw, \ IF_ASSIGN(var, ent, struct trace_mmiotrace_rw, \
TRACE_MMIO_RW); \ TRACE_MMIO_RW); \
IF_ASSIGN(var, ent, struct trace_mmiotrace_map, \ IF_ASSIGN(var, ent, struct trace_mmiotrace_map, \
@ -599,8 +602,8 @@ struct trace_entry *tracing_get_trace_entry(struct trace_array *tr,
struct trace_entry *trace_find_next_entry(struct trace_iterator *iter, struct trace_entry *trace_find_next_entry(struct trace_iterator *iter,
int *ent_cpu, u64 *ent_ts); int *ent_cpu, u64 *ent_ts);
void __buffer_unlock_commit(struct ring_buffer *buffer, void trace_buffer_unlock_commit_nostack(struct ring_buffer *buffer,
struct ring_buffer_event *event); struct ring_buffer_event *event);
int trace_empty(struct trace_iterator *iter); int trace_empty(struct trace_iterator *iter);
@ -843,6 +846,17 @@ static inline int ftrace_graph_notrace_addr(unsigned long addr)
return 0; return 0;
} }
#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_DYNAMIC_FTRACE */
extern unsigned int fgraph_max_depth;
static inline bool ftrace_graph_ignore_func(struct ftrace_graph_ent *trace)
{
/* trace it when it is-nested-in or is a function enabled. */
return !(trace->depth || ftrace_graph_addr(trace->func)) ||
(trace->depth < 0) ||
(fgraph_max_depth && trace->depth >= fgraph_max_depth);
}
#else /* CONFIG_FUNCTION_GRAPH_TRACER */ #else /* CONFIG_FUNCTION_GRAPH_TRACER */
static inline enum print_line_t static inline enum print_line_t
print_graph_function_flags(struct trace_iterator *iter, u32 flags) print_graph_function_flags(struct trace_iterator *iter, u32 flags)
@ -1257,6 +1271,7 @@ enum regex_type {
MATCH_FRONT_ONLY, MATCH_FRONT_ONLY,
MATCH_MIDDLE_ONLY, MATCH_MIDDLE_ONLY,
MATCH_END_ONLY, MATCH_END_ONLY,
MATCH_GLOB,
}; };
struct regex { struct regex {

View File

@ -21,6 +21,8 @@ static u64 bm_stddev;
static unsigned int bm_avg; static unsigned int bm_avg;
static unsigned int bm_std; static unsigned int bm_std;
static bool ok_to_run;
/* /*
* This gets called in a loop recording the time it took to write * This gets called in a loop recording the time it took to write
* the tracepoint. What it writes is the time statistics of the last * the tracepoint. What it writes is the time statistics of the last
@ -164,11 +166,21 @@ static int benchmark_event_kthread(void *arg)
* When the benchmark tracepoint is enabled, it calls this * When the benchmark tracepoint is enabled, it calls this
* function and the thread that calls the tracepoint is created. * function and the thread that calls the tracepoint is created.
*/ */
void trace_benchmark_reg(void) int trace_benchmark_reg(void)
{ {
if (!ok_to_run) {
pr_warning("trace benchmark cannot be started via kernel command line\n");
return -EBUSY;
}
bm_event_thread = kthread_run(benchmark_event_kthread, bm_event_thread = kthread_run(benchmark_event_kthread,
NULL, "event_benchmark"); NULL, "event_benchmark");
WARN_ON(!bm_event_thread); if (!bm_event_thread) {
pr_warning("trace benchmark failed to create kernel thread\n");
return -ENOMEM;
}
return 0;
} }
/* /*
@ -182,6 +194,7 @@ void trace_benchmark_unreg(void)
return; return;
kthread_stop(bm_event_thread); kthread_stop(bm_event_thread);
bm_event_thread = NULL;
strcpy(bm_str, "START"); strcpy(bm_str, "START");
bm_total = 0; bm_total = 0;
@ -196,3 +209,12 @@ void trace_benchmark_unreg(void)
bm_avg = 0; bm_avg = 0;
bm_stddev = 0; bm_stddev = 0;
} }
static __init int ok_to_run_trace_benchmark(void)
{
ok_to_run = true;
return 0;
}
early_initcall(ok_to_run_trace_benchmark);

View File

@ -6,7 +6,7 @@
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
extern void trace_benchmark_reg(void); extern int trace_benchmark_reg(void);
extern void trace_benchmark_unreg(void); extern void trace_benchmark_unreg(void);
#define BENCHMARK_EVENT_STRLEN 128 #define BENCHMARK_EVENT_STRLEN 128

View File

@ -81,7 +81,7 @@ probe_likely_condition(struct ftrace_branch_data *f, int val, int expect)
entry->correct = val == expect; entry->correct = val == expect;
if (!call_filter_check_discard(call, entry, buffer, event)) if (!call_filter_check_discard(call, entry, buffer, event))
__buffer_unlock_commit(buffer, event); trace_buffer_unlock_commit_nostack(buffer, event);
out: out:
current->trace_recursion &= ~TRACE_BRANCH_BIT; current->trace_recursion &= ~TRACE_BRANCH_BIT;

View File

@ -244,6 +244,21 @@ FTRACE_ENTRY(print, print_entry,
FILTER_OTHER FILTER_OTHER
); );
FTRACE_ENTRY(raw_data, raw_data_entry,
TRACE_RAW_DATA,
F_STRUCT(
__field( unsigned int, id )
__dynamic_array( char, buf )
),
F_printk("id:%04x %08x",
__entry->id, (int)__entry->buf[0]),
FILTER_OTHER
);
FTRACE_ENTRY(bputs, bputs_entry, FTRACE_ENTRY(bputs, bputs_entry,
TRACE_BPUTS, TRACE_BPUTS,

View File

@ -283,46 +283,6 @@ void *trace_event_buffer_reserve(struct trace_event_buffer *fbuffer,
} }
EXPORT_SYMBOL_GPL(trace_event_buffer_reserve); EXPORT_SYMBOL_GPL(trace_event_buffer_reserve);
static DEFINE_SPINLOCK(tracepoint_iter_lock);
static void output_printk(struct trace_event_buffer *fbuffer)
{
struct trace_event_call *event_call;
struct trace_event *event;
unsigned long flags;
struct trace_iterator *iter = tracepoint_print_iter;
if (!iter)
return;
event_call = fbuffer->trace_file->event_call;
if (!event_call || !event_call->event.funcs ||
!event_call->event.funcs->trace)
return;
event = &fbuffer->trace_file->event_call->event;
spin_lock_irqsave(&tracepoint_iter_lock, flags);
trace_seq_init(&iter->seq);
iter->ent = fbuffer->entry;
event_call->event.funcs->trace(iter, 0, event);
trace_seq_putc(&iter->seq, 0);
printk("%s", iter->seq.buffer);
spin_unlock_irqrestore(&tracepoint_iter_lock, flags);
}
void trace_event_buffer_commit(struct trace_event_buffer *fbuffer)
{
if (tracepoint_printk)
output_printk(fbuffer);
event_trigger_unlock_commit(fbuffer->trace_file, fbuffer->buffer,
fbuffer->event, fbuffer->entry,
fbuffer->flags, fbuffer->pc);
}
EXPORT_SYMBOL_GPL(trace_event_buffer_commit);
int trace_event_reg(struct trace_event_call *call, int trace_event_reg(struct trace_event_call *call,
enum trace_reg type, void *data) enum trace_reg type, void *data)
{ {
@ -742,6 +702,7 @@ __ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match,
struct trace_event_call *call; struct trace_event_call *call;
const char *name; const char *name;
int ret = -EINVAL; int ret = -EINVAL;
int eret = 0;
list_for_each_entry(file, &tr->events, list) { list_for_each_entry(file, &tr->events, list) {
@ -765,9 +726,17 @@ __ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match,
if (event && strcmp(event, name) != 0) if (event && strcmp(event, name) != 0)
continue; continue;
ftrace_event_enable_disable(file, set); ret = ftrace_event_enable_disable(file, set);
ret = 0; /*
* Save the first error and return that. Some events
* may still have been enabled, but let the user
* know that something went wrong.
*/
if (ret && !eret)
eret = ret;
ret = eret;
} }
return ret; return ret;
@ -2843,20 +2812,32 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
return -ENOMEM; return -ENOMEM;
} }
entry = trace_create_file("enable", 0644, d_events,
tr, &ftrace_tr_enable_fops);
if (!entry) {
pr_warn("Could not create tracefs 'enable' entry\n");
return -ENOMEM;
}
/* There are not as crucial, just warn if they are not created */
entry = tracefs_create_file("set_event_pid", 0644, parent, entry = tracefs_create_file("set_event_pid", 0644, parent,
tr, &ftrace_set_event_pid_fops); tr, &ftrace_set_event_pid_fops);
if (!entry)
pr_warn("Could not create tracefs 'set_event_pid' entry\n");
/* ring buffer internal formats */ /* ring buffer internal formats */
trace_create_file("header_page", 0444, d_events, entry = trace_create_file("header_page", 0444, d_events,
ring_buffer_print_page_header, ring_buffer_print_page_header,
&ftrace_show_header_fops); &ftrace_show_header_fops);
if (!entry)
pr_warn("Could not create tracefs 'header_page' entry\n");
trace_create_file("header_event", 0444, d_events, entry = trace_create_file("header_event", 0444, d_events,
ring_buffer_print_entry_header, ring_buffer_print_entry_header,
&ftrace_show_header_fops); &ftrace_show_header_fops);
if (!entry)
trace_create_file("enable", 0644, d_events, pr_warn("Could not create tracefs 'header_event' entry\n");
tr, &ftrace_tr_enable_fops);
tr->event_dir = d_events; tr->event_dir = d_events;

View File

@ -108,12 +108,12 @@ static char *err_text[] = {
}; };
struct opstack_op { struct opstack_op {
int op; enum filter_op_ids op;
struct list_head list; struct list_head list;
}; };
struct postfix_elt { struct postfix_elt {
int op; enum filter_op_ids op;
char *operand; char *operand;
struct list_head list; struct list_head list;
}; };
@ -145,34 +145,50 @@ struct pred_stack {
/* If not of not match is equal to not of not, then it is a match */ /* If not of not match is equal to not of not, then it is a match */
#define DEFINE_COMPARISON_PRED(type) \ #define DEFINE_COMPARISON_PRED(type) \
static int filter_pred_##type(struct filter_pred *pred, void *event) \ static int filter_pred_LT_##type(struct filter_pred *pred, void *event) \
{ \ { \
type *addr = (type *)(event + pred->offset); \ type *addr = (type *)(event + pred->offset); \
type val = (type)pred->val; \ type val = (type)pred->val; \
int match = 0; \ int match = (*addr < val); \
\
switch (pred->op) { \
case OP_LT: \
match = (*addr < val); \
break; \
case OP_LE: \
match = (*addr <= val); \
break; \
case OP_GT: \
match = (*addr > val); \
break; \
case OP_GE: \
match = (*addr >= val); \
break; \
case OP_BAND: \
match = (*addr & val); \
break; \
default: \
break; \
} \
\
return !!match == !pred->not; \ return !!match == !pred->not; \
} } \
static int filter_pred_LE_##type(struct filter_pred *pred, void *event) \
{ \
type *addr = (type *)(event + pred->offset); \
type val = (type)pred->val; \
int match = (*addr <= val); \
return !!match == !pred->not; \
} \
static int filter_pred_GT_##type(struct filter_pred *pred, void *event) \
{ \
type *addr = (type *)(event + pred->offset); \
type val = (type)pred->val; \
int match = (*addr > val); \
return !!match == !pred->not; \
} \
static int filter_pred_GE_##type(struct filter_pred *pred, void *event) \
{ \
type *addr = (type *)(event + pred->offset); \
type val = (type)pred->val; \
int match = (*addr >= val); \
return !!match == !pred->not; \
} \
static int filter_pred_BAND_##type(struct filter_pred *pred, void *event) \
{ \
type *addr = (type *)(event + pred->offset); \
type val = (type)pred->val; \
int match = !!(*addr & val); \
return match == !pred->not; \
} \
static const filter_pred_fn_t pred_funcs_##type[] = { \
filter_pred_LT_##type, \
filter_pred_LE_##type, \
filter_pred_GT_##type, \
filter_pred_GE_##type, \
filter_pred_BAND_##type, \
};
#define PRED_FUNC_START OP_LT
#define DEFINE_EQUALITY_PRED(size) \ #define DEFINE_EQUALITY_PRED(size) \
static int filter_pred_##size(struct filter_pred *pred, void *event) \ static int filter_pred_##size(struct filter_pred *pred, void *event) \
@ -344,6 +360,12 @@ static int regex_match_end(char *str, struct regex *r, int len)
return 0; return 0;
} }
static int regex_match_glob(char *str, struct regex *r, int len __maybe_unused)
{
if (glob_match(r->pattern, str))
return 1;
return 0;
}
/** /**
* filter_parse_regex - parse a basic regex * filter_parse_regex - parse a basic regex
* @buff: the raw regex * @buff: the raw regex
@ -380,14 +402,20 @@ enum regex_type filter_parse_regex(char *buff, int len, char **search, int *not)
if (!i) { if (!i) {
*search = buff + 1; *search = buff + 1;
type = MATCH_END_ONLY; type = MATCH_END_ONLY;
} else { } else if (i == len - 1) {
if (type == MATCH_END_ONLY) if (type == MATCH_END_ONLY)
type = MATCH_MIDDLE_ONLY; type = MATCH_MIDDLE_ONLY;
else else
type = MATCH_FRONT_ONLY; type = MATCH_FRONT_ONLY;
buff[i] = 0; buff[i] = 0;
break; break;
} else { /* pattern continues, use full glob */
type = MATCH_GLOB;
break;
} }
} else if (strchr("[?\\", buff[i])) {
type = MATCH_GLOB;
break;
} }
} }
@ -420,6 +448,9 @@ static void filter_build_regex(struct filter_pred *pred)
case MATCH_END_ONLY: case MATCH_END_ONLY:
r->match = regex_match_end; r->match = regex_match_end;
break; break;
case MATCH_GLOB:
r->match = regex_match_glob;
break;
} }
pred->not ^= not; pred->not ^= not;
@ -946,7 +977,7 @@ int filter_assign_type(const char *type)
return FILTER_OTHER; return FILTER_OTHER;
} }
static bool is_legal_op(struct ftrace_event_field *field, int op) static bool is_legal_op(struct ftrace_event_field *field, enum filter_op_ids op)
{ {
if (is_string_field(field) && if (is_string_field(field) &&
(op != OP_EQ && op != OP_NE && op != OP_GLOB)) (op != OP_EQ && op != OP_NE && op != OP_GLOB))
@ -957,8 +988,8 @@ static bool is_legal_op(struct ftrace_event_field *field, int op)
return true; return true;
} }
static filter_pred_fn_t select_comparison_fn(int op, int field_size, static filter_pred_fn_t select_comparison_fn(enum filter_op_ids op,
int field_is_signed) int field_size, int field_is_signed)
{ {
filter_pred_fn_t fn = NULL; filter_pred_fn_t fn = NULL;
@ -967,33 +998,33 @@ static filter_pred_fn_t select_comparison_fn(int op, int field_size,
if (op == OP_EQ || op == OP_NE) if (op == OP_EQ || op == OP_NE)
fn = filter_pred_64; fn = filter_pred_64;
else if (field_is_signed) else if (field_is_signed)
fn = filter_pred_s64; fn = pred_funcs_s64[op - PRED_FUNC_START];
else else
fn = filter_pred_u64; fn = pred_funcs_u64[op - PRED_FUNC_START];
break; break;
case 4: case 4:
if (op == OP_EQ || op == OP_NE) if (op == OP_EQ || op == OP_NE)
fn = filter_pred_32; fn = filter_pred_32;
else if (field_is_signed) else if (field_is_signed)
fn = filter_pred_s32; fn = pred_funcs_s32[op - PRED_FUNC_START];
else else
fn = filter_pred_u32; fn = pred_funcs_u32[op - PRED_FUNC_START];
break; break;
case 2: case 2:
if (op == OP_EQ || op == OP_NE) if (op == OP_EQ || op == OP_NE)
fn = filter_pred_16; fn = filter_pred_16;
else if (field_is_signed) else if (field_is_signed)
fn = filter_pred_s16; fn = pred_funcs_s16[op - PRED_FUNC_START];
else else
fn = filter_pred_u16; fn = pred_funcs_u16[op - PRED_FUNC_START];
break; break;
case 1: case 1:
if (op == OP_EQ || op == OP_NE) if (op == OP_EQ || op == OP_NE)
fn = filter_pred_8; fn = filter_pred_8;
else if (field_is_signed) else if (field_is_signed)
fn = filter_pred_s8; fn = pred_funcs_s8[op - PRED_FUNC_START];
else else
fn = filter_pred_u8; fn = pred_funcs_u8[op - PRED_FUNC_START];
break; break;
} }
@ -1166,7 +1197,8 @@ static inline int append_operand_char(struct filter_parse_state *ps, char c)
return 0; return 0;
} }
static int filter_opstack_push(struct filter_parse_state *ps, int op) static int filter_opstack_push(struct filter_parse_state *ps,
enum filter_op_ids op)
{ {
struct opstack_op *opstack_op; struct opstack_op *opstack_op;
@ -1200,7 +1232,7 @@ static int filter_opstack_top(struct filter_parse_state *ps)
static int filter_opstack_pop(struct filter_parse_state *ps) static int filter_opstack_pop(struct filter_parse_state *ps)
{ {
struct opstack_op *opstack_op; struct opstack_op *opstack_op;
int op; enum filter_op_ids op;
if (filter_opstack_empty(ps)) if (filter_opstack_empty(ps))
return OP_NONE; return OP_NONE;
@ -1245,7 +1277,7 @@ static int postfix_append_operand(struct filter_parse_state *ps, char *operand)
return 0; return 0;
} }
static int postfix_append_op(struct filter_parse_state *ps, int op) static int postfix_append_op(struct filter_parse_state *ps, enum filter_op_ids op)
{ {
struct postfix_elt *elt; struct postfix_elt *elt;
@ -1275,8 +1307,8 @@ static void postfix_clear(struct filter_parse_state *ps)
static int filter_parse(struct filter_parse_state *ps) static int filter_parse(struct filter_parse_state *ps)
{ {
enum filter_op_ids op, top_op;
int in_string = 0; int in_string = 0;
int op, top_op;
char ch; char ch;
while ((ch = infix_next(ps))) { while ((ch = infix_next(ps))) {
@ -1367,7 +1399,8 @@ parse_operand:
static struct filter_pred *create_pred(struct filter_parse_state *ps, static struct filter_pred *create_pred(struct filter_parse_state *ps,
struct trace_event_call *call, struct trace_event_call *call,
int op, char *operand1, char *operand2) enum filter_op_ids op,
char *operand1, char *operand2)
{ {
struct ftrace_event_field *field; struct ftrace_event_field *field;
static struct filter_pred pred; static struct filter_pred pred;

View File

@ -65,7 +65,7 @@ struct fgraph_data {
#define TRACE_GRAPH_INDENT 2 #define TRACE_GRAPH_INDENT 2
static unsigned int max_depth; unsigned int fgraph_max_depth;
static struct tracer_opt trace_opts[] = { static struct tracer_opt trace_opts[] = {
/* Display overruns? (for self-debug purpose) */ /* Display overruns? (for self-debug purpose) */
@ -358,7 +358,7 @@ int __trace_graph_entry(struct trace_array *tr,
entry = ring_buffer_event_data(event); entry = ring_buffer_event_data(event);
entry->graph_ent = *trace; entry->graph_ent = *trace;
if (!call_filter_check_discard(call, entry, buffer, event)) if (!call_filter_check_discard(call, entry, buffer, event))
__buffer_unlock_commit(buffer, event); trace_buffer_unlock_commit_nostack(buffer, event);
return 1; return 1;
} }
@ -384,10 +384,10 @@ int trace_graph_entry(struct ftrace_graph_ent *trace)
if (!ftrace_trace_task(tr)) if (!ftrace_trace_task(tr))
return 0; return 0;
/* trace it when it is-nested-in or is a function enabled. */ if (ftrace_graph_ignore_func(trace))
if ((!(trace->depth || ftrace_graph_addr(trace->func)) || return 0;
ftrace_graph_ignore_irqs()) || (trace->depth < 0) ||
(max_depth && trace->depth >= max_depth)) if (ftrace_graph_ignore_irqs())
return 0; return 0;
/* /*
@ -469,7 +469,7 @@ void __trace_graph_return(struct trace_array *tr,
entry = ring_buffer_event_data(event); entry = ring_buffer_event_data(event);
entry->ret = *trace; entry->ret = *trace;
if (!call_filter_check_discard(call, entry, buffer, event)) if (!call_filter_check_discard(call, entry, buffer, event))
__buffer_unlock_commit(buffer, event); trace_buffer_unlock_commit_nostack(buffer, event);
} }
void trace_graph_return(struct ftrace_graph_ret *trace) void trace_graph_return(struct ftrace_graph_ret *trace)
@ -842,6 +842,10 @@ print_graph_entry_leaf(struct trace_iterator *iter,
cpu_data = per_cpu_ptr(data->cpu_data, cpu); cpu_data = per_cpu_ptr(data->cpu_data, cpu);
/* If a graph tracer ignored set_graph_notrace */
if (call->depth < -1)
call->depth += FTRACE_NOTRACE_DEPTH;
/* /*
* Comments display at + 1 to depth. Since * Comments display at + 1 to depth. Since
* this is a leaf function, keep the comments * this is a leaf function, keep the comments
@ -850,7 +854,8 @@ print_graph_entry_leaf(struct trace_iterator *iter,
cpu_data->depth = call->depth - 1; cpu_data->depth = call->depth - 1;
/* No need to keep this function around for this depth */ /* No need to keep this function around for this depth */
if (call->depth < FTRACE_RETFUNC_DEPTH) if (call->depth < FTRACE_RETFUNC_DEPTH &&
!WARN_ON_ONCE(call->depth < 0))
cpu_data->enter_funcs[call->depth] = 0; cpu_data->enter_funcs[call->depth] = 0;
} }
@ -880,11 +885,16 @@ print_graph_entry_nested(struct trace_iterator *iter,
struct fgraph_cpu_data *cpu_data; struct fgraph_cpu_data *cpu_data;
int cpu = iter->cpu; int cpu = iter->cpu;
/* If a graph tracer ignored set_graph_notrace */
if (call->depth < -1)
call->depth += FTRACE_NOTRACE_DEPTH;
cpu_data = per_cpu_ptr(data->cpu_data, cpu); cpu_data = per_cpu_ptr(data->cpu_data, cpu);
cpu_data->depth = call->depth; cpu_data->depth = call->depth;
/* Save this function pointer to see if the exit matches */ /* Save this function pointer to see if the exit matches */
if (call->depth < FTRACE_RETFUNC_DEPTH) if (call->depth < FTRACE_RETFUNC_DEPTH &&
!WARN_ON_ONCE(call->depth < 0))
cpu_data->enter_funcs[call->depth] = call->func; cpu_data->enter_funcs[call->depth] = call->func;
} }
@ -1114,7 +1124,8 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
*/ */
cpu_data->depth = trace->depth - 1; cpu_data->depth = trace->depth - 1;
if (trace->depth < FTRACE_RETFUNC_DEPTH) { if (trace->depth < FTRACE_RETFUNC_DEPTH &&
!WARN_ON_ONCE(trace->depth < 0)) {
if (cpu_data->enter_funcs[trace->depth] != trace->func) if (cpu_data->enter_funcs[trace->depth] != trace->func)
func_match = 0; func_match = 0;
cpu_data->enter_funcs[trace->depth] = 0; cpu_data->enter_funcs[trace->depth] = 0;
@ -1489,7 +1500,7 @@ graph_depth_write(struct file *filp, const char __user *ubuf, size_t cnt,
if (ret) if (ret)
return ret; return ret;
max_depth = val; fgraph_max_depth = val;
*ppos += cnt; *ppos += cnt;
@ -1503,7 +1514,7 @@ graph_depth_read(struct file *filp, char __user *ubuf, size_t cnt,
char buf[15]; /* More than enough to hold UINT_MAX + "\n"*/ char buf[15]; /* More than enough to hold UINT_MAX + "\n"*/
int n; int n;
n = sprintf(buf, "%d\n", max_depth); n = sprintf(buf, "%d\n", fgraph_max_depth);
return simple_read_from_buffer(ubuf, cnt, ppos, buf, n); return simple_read_from_buffer(ubuf, cnt, ppos, buf, n);
} }

View File

@ -127,7 +127,7 @@ static void trace_hwlat_sample(struct hwlat_sample *sample)
entry->nmi_count = sample->nmi_count; entry->nmi_count = sample->nmi_count;
if (!call_filter_check_discard(call, entry, buffer, event)) if (!call_filter_check_discard(call, entry, buffer, event))
__buffer_unlock_commit(buffer, event); trace_buffer_unlock_commit_nostack(buffer, event);
} }
/* Macros to encapsulate the time capturing infrastructure */ /* Macros to encapsulate the time capturing infrastructure */

View File

@ -175,6 +175,18 @@ static int irqsoff_graph_entry(struct ftrace_graph_ent *trace)
int ret; int ret;
int pc; int pc;
if (ftrace_graph_ignore_func(trace))
return 0;
/*
* Do not trace a function if it's filtered by set_graph_notrace.
* Make the index of ret stack negative to indicate that it should
* ignore further functions. But it needs its own ret stack entry
* to recover the original index in order to continue tracing after
* returning from the function.
*/
if (ftrace_graph_notrace_addr(trace->func))
return 1;
if (!func_prolog_dec(tr, &data, &flags)) if (!func_prolog_dec(tr, &data, &flags))
return 0; return 0;

View File

@ -73,6 +73,17 @@ static nokprobe_inline bool trace_kprobe_is_on_module(struct trace_kprobe *tk)
return !!strchr(trace_kprobe_symbol(tk), ':'); return !!strchr(trace_kprobe_symbol(tk), ':');
} }
static nokprobe_inline unsigned long trace_kprobe_nhit(struct trace_kprobe *tk)
{
unsigned long nhit = 0;
int cpu;
for_each_possible_cpu(cpu)
nhit += *per_cpu_ptr(tk->nhit, cpu);
return nhit;
}
static int register_kprobe_event(struct trace_kprobe *tk); static int register_kprobe_event(struct trace_kprobe *tk);
static int unregister_kprobe_event(struct trace_kprobe *tk); static int unregister_kprobe_event(struct trace_kprobe *tk);
@ -882,14 +893,10 @@ static const struct file_operations kprobe_events_ops = {
static int probes_profile_seq_show(struct seq_file *m, void *v) static int probes_profile_seq_show(struct seq_file *m, void *v)
{ {
struct trace_kprobe *tk = v; struct trace_kprobe *tk = v;
unsigned long nhit = 0;
int cpu;
for_each_possible_cpu(cpu)
nhit += *per_cpu_ptr(tk->nhit, cpu);
seq_printf(m, " %-44s %15lu %15lu\n", seq_printf(m, " %-44s %15lu %15lu\n",
trace_event_name(&tk->tp.call), nhit, trace_event_name(&tk->tp.call),
trace_kprobe_nhit(tk),
tk->rp.kp.nmissed); tk->rp.kp.nmissed);
return 0; return 0;
@ -1354,18 +1361,18 @@ fs_initcall(init_kprobe_trace);
#ifdef CONFIG_FTRACE_STARTUP_TEST #ifdef CONFIG_FTRACE_STARTUP_TEST
/* /*
* The "__used" keeps gcc from removing the function symbol * The "__used" keeps gcc from removing the function symbol
* from the kallsyms table. * from the kallsyms table. 'noinline' makes sure that there
* isn't an inlined version used by the test method below
*/ */
static __used int kprobe_trace_selftest_target(int a1, int a2, int a3, static __used __init noinline int
int a4, int a5, int a6) kprobe_trace_selftest_target(int a1, int a2, int a3, int a4, int a5, int a6)
{ {
return a1 + a2 + a3 + a4 + a5 + a6; return a1 + a2 + a3 + a4 + a5 + a6;
} }
static struct trace_event_file * static struct __init trace_event_file *
find_trace_probe_file(struct trace_kprobe *tk, struct trace_array *tr) find_trace_probe_file(struct trace_kprobe *tk, struct trace_array *tr)
{ {
struct trace_event_file *file; struct trace_event_file *file;
@ -1443,12 +1450,25 @@ static __init int kprobe_trace_self_tests_init(void)
ret = target(1, 2, 3, 4, 5, 6); ret = target(1, 2, 3, 4, 5, 6);
/*
* Not expecting an error here, the check is only to prevent the
* optimizer from removing the call to target() as otherwise there
* are no side-effects and the call is never performed.
*/
if (ret != 21)
warn++;
/* Disable trace points before removing it */ /* Disable trace points before removing it */
tk = find_trace_kprobe("testprobe", KPROBE_EVENT_SYSTEM); tk = find_trace_kprobe("testprobe", KPROBE_EVENT_SYSTEM);
if (WARN_ON_ONCE(tk == NULL)) { if (WARN_ON_ONCE(tk == NULL)) {
pr_warn("error on getting test probe.\n"); pr_warn("error on getting test probe.\n");
warn++; warn++;
} else { } else {
if (trace_kprobe_nhit(tk) != 1) {
pr_warn("incorrect number of testprobe hits\n");
warn++;
}
file = find_trace_probe_file(tk, top_trace_array()); file = find_trace_probe_file(tk, top_trace_array());
if (WARN_ON_ONCE(file == NULL)) { if (WARN_ON_ONCE(file == NULL)) {
pr_warn("error on getting probe file.\n"); pr_warn("error on getting probe file.\n");
@ -1462,6 +1482,11 @@ static __init int kprobe_trace_self_tests_init(void)
pr_warn("error on getting 2nd test probe.\n"); pr_warn("error on getting 2nd test probe.\n");
warn++; warn++;
} else { } else {
if (trace_kprobe_nhit(tk) != 1) {
pr_warn("incorrect number of testprobe2 hits\n");
warn++;
}
file = find_trace_probe_file(tk, top_trace_array()); file = find_trace_probe_file(tk, top_trace_array());
if (WARN_ON_ONCE(file == NULL)) { if (WARN_ON_ONCE(file == NULL)) {
pr_warn("error on getting probe file.\n"); pr_warn("error on getting probe file.\n");

View File

@ -1288,6 +1288,35 @@ static struct trace_event trace_print_event = {
.funcs = &trace_print_funcs, .funcs = &trace_print_funcs,
}; };
static enum print_line_t trace_raw_data(struct trace_iterator *iter, int flags,
struct trace_event *event)
{
struct raw_data_entry *field;
int i;
trace_assign_type(field, iter->ent);
trace_seq_printf(&iter->seq, "# %x buf:", field->id);
for (i = 0; i < iter->ent_size - offsetof(struct raw_data_entry, buf); i++)
trace_seq_printf(&iter->seq, " %02x",
(unsigned char)field->buf[i]);
trace_seq_putc(&iter->seq, '\n');
return trace_handle_return(&iter->seq);
}
static struct trace_event_functions trace_raw_data_funcs = {
.trace = trace_raw_data,
.raw = trace_raw_data,
};
static struct trace_event trace_raw_data_event = {
.type = TRACE_RAW_DATA,
.funcs = &trace_raw_data_funcs,
};
static struct trace_event *events[] __initdata = { static struct trace_event *events[] __initdata = {
&trace_fn_event, &trace_fn_event,
@ -1299,6 +1328,7 @@ static struct trace_event *events[] __initdata = {
&trace_bprint_event, &trace_bprint_event,
&trace_print_event, &trace_print_event,
&trace_hwlat_event, &trace_hwlat_event,
&trace_raw_data_event,
NULL NULL
}; };

View File

@ -239,6 +239,18 @@ static int wakeup_graph_entry(struct ftrace_graph_ent *trace)
unsigned long flags; unsigned long flags;
int pc, ret = 0; int pc, ret = 0;
if (ftrace_graph_ignore_func(trace))
return 0;
/*
* Do not trace a function if it's filtered by set_graph_notrace.
* Make the index of ret stack negative to indicate that it should
* ignore further functions. But it needs its own ret stack entry
* to recover the original index in order to continue tracing after
* returning from the function.
*/
if (ftrace_graph_notrace_addr(trace->func))
return 1;
if (!func_prolog_preempt_disable(tr, &data, &pc)) if (!func_prolog_preempt_disable(tr, &data, &pc))
return 0; return 0;
@ -790,6 +802,7 @@ static struct tracer wakeup_dl_tracer __read_mostly =
#endif #endif
.open = wakeup_trace_open, .open = wakeup_trace_open,
.close = wakeup_trace_close, .close = wakeup_trace_close,
.allow_instances = true,
.use_max_tr = true, .use_max_tr = true,
}; };

View File

@ -194,9 +194,13 @@ static int tracepoint_add_func(struct tracepoint *tp,
struct tracepoint_func *func, int prio) struct tracepoint_func *func, int prio)
{ {
struct tracepoint_func *old, *tp_funcs; struct tracepoint_func *old, *tp_funcs;
int ret;
if (tp->regfunc && !static_key_enabled(&tp->key)) if (tp->regfunc && !static_key_enabled(&tp->key)) {
tp->regfunc(); ret = tp->regfunc();
if (ret < 0)
return ret;
}
tp_funcs = rcu_dereference_protected(tp->funcs, tp_funcs = rcu_dereference_protected(tp->funcs,
lockdep_is_held(&tracepoints_mutex)); lockdep_is_held(&tracepoints_mutex));
@ -529,7 +533,7 @@ EXPORT_SYMBOL_GPL(for_each_kernel_tracepoint);
/* NB: reg/unreg are called while guarded with the tracepoints_mutex */ /* NB: reg/unreg are called while guarded with the tracepoints_mutex */
static int sys_tracepoint_refcount; static int sys_tracepoint_refcount;
void syscall_regfunc(void) int syscall_regfunc(void)
{ {
struct task_struct *p, *t; struct task_struct *p, *t;
@ -541,6 +545,8 @@ void syscall_regfunc(void)
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
} }
sys_tracepoint_refcount++; sys_tracepoint_refcount++;
return 0;
} }
void syscall_unregfunc(void) void syscall_unregfunc(void)

View File

@ -79,7 +79,7 @@ static int simple_thread_fn(void *arg)
static DEFINE_MUTEX(thread_mutex); static DEFINE_MUTEX(thread_mutex);
void foo_bar_reg(void) int foo_bar_reg(void)
{ {
pr_info("Starting thread for foo_bar_fn\n"); pr_info("Starting thread for foo_bar_fn\n");
/* /*
@ -90,6 +90,7 @@ void foo_bar_reg(void)
mutex_lock(&thread_mutex); mutex_lock(&thread_mutex);
simple_tsk_fn = kthread_run(simple_thread_fn, NULL, "event-sample-fn"); simple_tsk_fn = kthread_run(simple_thread_fn, NULL, "event-sample-fn");
mutex_unlock(&thread_mutex); mutex_unlock(&thread_mutex);
return 0;
} }
void foo_bar_unreg(void) void foo_bar_unreg(void)

View File

@ -354,7 +354,7 @@ TRACE_EVENT_CONDITION(foo_bar_with_cond,
TP_printk("foo %s %d", __get_str(foo), __entry->bar) TP_printk("foo %s %d", __get_str(foo), __entry->bar)
); );
void foo_bar_reg(void); int foo_bar_reg(void);
void foo_bar_unreg(void); void foo_bar_unreg(void);
/* /*

View File

@ -213,6 +213,59 @@ static int make_nop_x86(void *map, size_t const offset)
return 0; return 0;
} }
static unsigned char ideal_nop4_arm_le[4] = { 0x00, 0x00, 0xa0, 0xe1 }; /* mov r0, r0 */
static unsigned char ideal_nop4_arm_be[4] = { 0xe1, 0xa0, 0x00, 0x00 }; /* mov r0, r0 */
static unsigned char *ideal_nop4_arm;
static unsigned char bl_mcount_arm_le[4] = { 0xfe, 0xff, 0xff, 0xeb }; /* bl */
static unsigned char bl_mcount_arm_be[4] = { 0xeb, 0xff, 0xff, 0xfe }; /* bl */
static unsigned char *bl_mcount_arm;
static unsigned char push_arm_le[4] = { 0x04, 0xe0, 0x2d, 0xe5 }; /* push {lr} */
static unsigned char push_arm_be[4] = { 0xe5, 0x2d, 0xe0, 0x04 }; /* push {lr} */
static unsigned char *push_arm;
static unsigned char ideal_nop2_thumb_le[2] = { 0x00, 0xbf }; /* nop */
static unsigned char ideal_nop2_thumb_be[2] = { 0xbf, 0x00 }; /* nop */
static unsigned char *ideal_nop2_thumb;
static unsigned char push_bl_mcount_thumb_le[6] = { 0x00, 0xb5, 0xff, 0xf7, 0xfe, 0xff }; /* push {lr}, bl */
static unsigned char push_bl_mcount_thumb_be[6] = { 0xb5, 0x00, 0xf7, 0xff, 0xff, 0xfe }; /* push {lr}, bl */
static unsigned char *push_bl_mcount_thumb;
static int make_nop_arm(void *map, size_t const offset)
{
char *ptr;
int cnt = 1;
int nop_size;
size_t off = offset;
ptr = map + offset;
if (memcmp(ptr, bl_mcount_arm, 4) == 0) {
if (memcmp(ptr - 4, push_arm, 4) == 0) {
off -= 4;
cnt = 2;
}
ideal_nop = ideal_nop4_arm;
nop_size = 4;
} else if (memcmp(ptr - 2, push_bl_mcount_thumb, 6) == 0) {
cnt = 3;
nop_size = 2;
off -= 2;
ideal_nop = ideal_nop2_thumb;
} else
return -1;
/* Convert to nop */
ulseek(fd_map, off, SEEK_SET);
do {
uwrite(fd_map, ideal_nop, nop_size);
} while (--cnt > 0);
return 0;
}
static unsigned char ideal_nop4_arm64[4] = {0x1f, 0x20, 0x03, 0xd5}; static unsigned char ideal_nop4_arm64[4] = {0x1f, 0x20, 0x03, 0xd5};
static int make_nop_arm64(void *map, size_t const offset) static int make_nop_arm64(void *map, size_t const offset)
{ {
@ -430,6 +483,11 @@ do_file(char const *const fname)
w2 = w2rev; w2 = w2rev;
w8 = w8rev; w8 = w8rev;
} }
ideal_nop4_arm = ideal_nop4_arm_le;
bl_mcount_arm = bl_mcount_arm_le;
push_arm = push_arm_le;
ideal_nop2_thumb = ideal_nop2_thumb_le;
push_bl_mcount_thumb = push_bl_mcount_thumb_le;
break; break;
case ELFDATA2MSB: case ELFDATA2MSB:
if (*(unsigned char const *)&endian != 0) { if (*(unsigned char const *)&endian != 0) {
@ -438,6 +496,11 @@ do_file(char const *const fname)
w2 = w2rev; w2 = w2rev;
w8 = w8rev; w8 = w8rev;
} }
ideal_nop4_arm = ideal_nop4_arm_be;
bl_mcount_arm = bl_mcount_arm_be;
push_arm = push_arm_be;
ideal_nop2_thumb = ideal_nop2_thumb_be;
push_bl_mcount_thumb = push_bl_mcount_thumb_be;
break; break;
} /* end switch */ } /* end switch */
if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0 if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0
@ -463,6 +526,8 @@ do_file(char const *const fname)
break; break;
case EM_ARM: reltype = R_ARM_ABS32; case EM_ARM: reltype = R_ARM_ABS32;
altmcount = "__gnu_mcount_nc"; altmcount = "__gnu_mcount_nc";
make_nop = make_nop_arm;
rel_type_nop = R_ARM_NONE;
break; break;
case EM_AARCH64: case EM_AARCH64:
reltype = R_AARCH64_ABS64; reltype = R_AARCH64_ABS64;

View File

@ -0,0 +1 @@
logs

View File

@ -13,7 +13,8 @@ echo "Usage: ftracetest [options] [testcase(s)] [testcase-directory(s)]"
echo " Options:" echo " Options:"
echo " -h|--help Show help message" echo " -h|--help Show help message"
echo " -k|--keep Keep passed test logs" echo " -k|--keep Keep passed test logs"
echo " -v|--verbose Show all stdout messages in testcases" echo " -v|--verbose Increase verbosity of test messages"
echo " -vv Alias of -v -v (Show all results in stdout)"
echo " -d|--debug Debug mode (trace all shell commands)" echo " -d|--debug Debug mode (trace all shell commands)"
exit $1 exit $1
} }
@ -54,8 +55,9 @@ parse_opts() { # opts
KEEP_LOG=1 KEEP_LOG=1
shift 1 shift 1
;; ;;
--verbose|-v) --verbose|-v|-vv)
VERBOSE=1 VERBOSE=$((VERBOSE + 1))
[ $1 == '-vv' ] && VERBOSE=$((VERBOSE + 1))
shift 1 shift 1
;; ;;
--debug|-d) --debug|-d)
@ -228,7 +230,7 @@ trap 'SIG_RESULT=$XFAIL' $SIG_XFAIL
__run_test() { # testfile __run_test() { # testfile
# setup PID and PPID, $$ is not updated. # setup PID and PPID, $$ is not updated.
(cd $TRACING_DIR; read PID _ < /proc/self/stat ; set -e; set -x; . $1) (cd $TRACING_DIR; read PID _ < /proc/self/stat; set -e; set -x; initialize_ftrace; . $1)
[ $? -ne 0 ] && kill -s $SIG_FAIL $SIG_PID [ $? -ne 0 ] && kill -s $SIG_FAIL $SIG_PID
} }
@ -236,10 +238,11 @@ __run_test() { # testfile
run_test() { # testfile run_test() { # testfile
local testname=`basename $1` local testname=`basename $1`
local testlog=`mktemp $LOG_DIR/${testname}-log.XXXXXX` local testlog=`mktemp $LOG_DIR/${testname}-log.XXXXXX`
export TMPDIR=`mktemp -d /tmp/ftracetest-dir.XXXXXX`
testcase $1 testcase $1
echo "execute: "$1 > $testlog echo "execute: "$1 > $testlog
SIG_RESULT=0 SIG_RESULT=0
if [ $VERBOSE -ne 0 ]; then if [ $VERBOSE -ge 2 ]; then
__run_test $1 2>> $testlog | tee -a $testlog __run_test $1 2>> $testlog | tee -a $testlog
else else
__run_test $1 >> $testlog 2>&1 __run_test $1 >> $testlog 2>&1
@ -249,9 +252,10 @@ run_test() { # testfile
# Remove test log if the test was done as it was expected. # Remove test log if the test was done as it was expected.
[ $KEEP_LOG -eq 0 ] && rm $testlog [ $KEEP_LOG -eq 0 ] && rm $testlog
else else
catlog $testlog [ $VERBOSE -ge 1 ] && catlog $testlog
TOTAL_RESULT=1 TOTAL_RESULT=1
fi fi
rm -rf $TMPDIR
} }
# load in the helper functions # load in the helper functions

View File

@ -0,0 +1,49 @@
#!/bin/sh
# description: ftrace - function glob filters
# Make sure that function glob matching filter works.
if ! grep -q function available_tracers; then
echo "no function tracer configured"
exit_unsupported
fi
disable_tracing
clear_trace
# filter by ?, schedule is always good
if ! echo "sch?dule" > set_ftrace_filter; then
# test for powerpc 64
if ! echo ".sch?dule" > set_ftrace_filter; then
fail "can not enable schedule filter"
fi
cat set_ftrace_filter | grep '^.schedule$'
else
cat set_ftrace_filter | grep '^schedule$'
fi
ftrace_filter_check() { # glob grep
echo "$1" > set_ftrace_filter
cut -f1 -d" " set_ftrace_filter > $TMPDIR/actual
cut -f1 -d" " available_filter_functions | grep "$2" > $TMPDIR/expected
DIFF=`diff $TMPDIR/actual $TMPDIR/expected`
test -z "$DIFF"
}
# filter by *, front match
ftrace_filter_check '*schedule' '^.*schedule$'
# filter by *, middle match
ftrace_filter_check '*schedule*' '^.*schedule.*$'
# filter by *, end match
ftrace_filter_check 'schedule*' '^schedule.*$'
# filter by *, both side match
ftrace_filter_check 'sch*ule' '^sch.*ule$'
# filter by char class.
ftrace_filter_check '[Ss]y[Ss]_*' '^[Ss]y[Ss]_.*$'
echo > set_ftrace_filter
enable_tracing

View File

@ -23,3 +23,31 @@ reset_trigger() { # reset all current setting triggers
done done
} }
reset_events_filter() { # reset all current setting filters
grep -v ^none events/*/*/filter |
while read line; do
echo 0 > `echo $line | cut -f1 -d:`
done
}
disable_events() {
echo 0 > events/enable
}
initialize_ftrace() { # Reset ftrace to initial-state
# As the initial state, ftrace will be set to nop tracer,
# no events, no triggers, no filters, no function filters,
# no probes, and tracing on.
disable_tracing
reset_tracer
reset_trigger
reset_events_filter
disable_events
echo > set_event_pid # event tracer is always on
[ -f set_ftrace_filter ] && echo | tee set_ftrace_*
[ -f set_graph_function ] && echo | tee set_graph_*
[ -f stack_trace_filter ] && echo > stack_trace_filter
[ -f kprobe_events ] && echo > kprobe_events
[ -f uprobe_events ] && echo > uprobe_events
enable_tracing
}

View File

@ -0,0 +1,37 @@
#!/bin/sh
# description: Kprobes event arguments with types
[ -f kprobe_events ] || exit_unsupported # this is configurable
grep "x8/16/32/64" README > /dev/null || exit_unsupported # version issue
echo 0 > events/enable
echo > kprobe_events
enable_tracing
echo 'p:testprobe _do_fork $stack0:s32 $stack0:u32 $stack0:x32 $stack0:b8@4/32' > kprobe_events
grep testprobe kprobe_events
test -d events/kprobes/testprobe
echo 1 > events/kprobes/testprobe/enable
( echo "forked")
echo 0 > events/kprobes/testprobe/enable
ARGS=`tail -n 1 trace | sed -e 's/.* arg1=\(.*\) arg2=\(.*\) arg3=\(.*\) arg4=\(.*\)/\1 \2 \3 \4/'`
check_types() {
X1=`printf "%x" $1 | tail -c 8`
X2=`printf "%x" $2`
X3=`printf "%x" $3`
test $X1 = $X2
test $X2 = $X3
test 0x$X3 = $3
B4=`printf "%x" $4`
B3=`echo -n $X3 | tail -c 3 | head -c 2`
test $B3 = $B4
}
check_types $ARGS
echo "-:testprobe" >> kprobe_events
clear_trace
test -d events/kprobes/testprobe && exit 1 || exit 0

View File

@ -56,7 +56,7 @@ echo "Test histogram with syscall modifier"
echo 'hist:keys=id.syscall' > events/raw_syscalls/sys_exit/trigger echo 'hist:keys=id.syscall' > events/raw_syscalls/sys_exit/trigger
for i in `seq 1 10` ; do ( echo "forked" > /dev/null); done for i in `seq 1 10` ; do ( echo "forked" > /dev/null); done
grep "id: sys_" events/raw_syscalls/sys_exit/hist > /dev/null || \ grep "id: \(unknown_\|sys_\)" events/raw_syscalls/sys_exit/hist > /dev/null || \
fail "syscall modifier on raw_syscalls/sys_exit did not work" fail "syscall modifier on raw_syscalls/sys_exit did not work"

View File

@ -23,6 +23,11 @@ if [ ! -f events/sched/sched_process_fork/trigger ]; then
exit_unsupported exit_unsupported
fi fi
if [ ! -f snapshot ]; then
echo "snapshot is not supported"
exit_unsupported
fi
reset_tracer reset_tracer
do_reset do_reset