The new features of this release:

- Added TRACE_DEFINE_SIZEOF() which allows trace events that use
     sizeof() it the TP_printk() to be converted to the actual size such
     that trace-cmd and perf can parse them correctly.
 
   - Some rework of the TRACE_DEFINE_ENUM() such that the above
     TRACE_DEFINE_SIZEOF() could reuse the same code.
 
   - Recording of tgid (Thread Group ID). This is similar to how
     task COMMs are recorded (cached at sched_switch), where it is
     in a table and used on output of the trace and trace_pipe files.
 
   - Have ":mod:<module>" be cached when written into set_ftrace_filter.
     Then the functions of the module will be traced at module load.
 
   - Some random clean ups and small fixes.
 -----BEGIN PGP SIGNATURE-----
 
 iQExBAABCAAbBQJZXjYuFBxyb3N0ZWR0QGdvb2RtaXMub3JnAAoJEMm5BfJq2Y3L
 fsgIAKUvhpn2igoYCR9tWqu+DovEmwxCIumbCzmCFQcRKlLttRte94yY5+W9hnV0
 JPzd9T9zBDVqq1fI7iIop1SuTwEfKW6lJom0usZ8AFpK+YKm6FHnQ28POlvHzre2
 lzO41tpRWiehLQsITZ47eByhsvEfhx86mYT/oM1JSR6Pii1OpjyNYmDMw6BaMNBT
 kSCQFgIhzAhVuHjwAnB/S++E/ou7M5bCwCb5CNh7MubKubV5upHpoJcgYGO+WWa6
 56H/iEhff4EECTGJVefd8e78MtJPL8EsuM0nAcMPlnl8AaiOpP7XCdlgTwdefLvP
 b3o+nP15voSHkARGXC6eM6gH0po=
 =rvGB
 -----END PGP SIGNATURE-----

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

Pull tracing updates from Steven Rostedt:
 "The new features of this release:

   - Added TRACE_DEFINE_SIZEOF() which allows trace events that use
     sizeof() it the TP_printk() to be converted to the actual size such
     that trace-cmd and perf can parse them correctly.

   - Some rework of the TRACE_DEFINE_ENUM() such that the above
     TRACE_DEFINE_SIZEOF() could reuse the same code.

   - Recording of tgid (Thread Group ID). This is similar to how task
     COMMs are recorded (cached at sched_switch), where it is in a table
     and used on output of the trace and trace_pipe files.

   - Have ":mod:<module>" be cached when written into set_ftrace_filter.
     Then the functions of the module will be traced at module load.

   - Some random clean ups and small fixes"

* tag 'trace-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: (26 commits)
  ftrace: Test for NULL iter->tr in regex for stack_trace_filter changes
  ftrace: Decrement count for dyn_ftrace_total_info for init functions
  ftrace: Unlock hash mutex on failed allocation in process_mod_list()
  tracing: Add support for display of tgid in trace output
  tracing: Add support for recording tgid of tasks
  ftrace: Decrement count for dyn_ftrace_total_info file
  ftrace: Remove unused function ftrace_arch_read_dyn_info()
  sh/ftrace: Remove only user of ftrace_arch_read_dyn_info()
  ftrace: Have cached module filters be an active filter
  ftrace: Implement cached modules tracing on module load
  ftrace: Have the cached module list show in set_ftrace_filter
  ftrace: Add :mod: caching infrastructure to trace_array
  tracing: Show address when function names are not found
  ftrace: Add missing comment for FTRACE_OPS_FL_RCU
  tracing: Rename update the enum_map file
  tracing: Add TRACE_DEFINE_SIZEOF() macros
  tracing: define TRACE_DEFINE_SIZEOF() macro to map sizeof's to their values
  tracing: Rename enum_replace to eval_replace
  trace: rename enum_map functions
  trace: rename trace.c enum functions
  ...
This commit is contained in:
Linus Torvalds 2017-07-06 19:45:45 -07:00
commit 2074006dac
17 changed files with 797 additions and 245 deletions

View File

@ -93,6 +93,8 @@ TRACE_EVENT(kvm_arm_set_dreg32,
TP_printk("%s: 0x%08x", __entry->name, __entry->value)
);
TRACE_DEFINE_SIZEOF(__u64);
TRACE_EVENT(kvm_arm_set_regset,
TP_PROTO(const char *type, int len, __u64 *control, __u64 *value),
TP_ARGS(type, len, control, value),

View File

@ -96,19 +96,6 @@ static int mod_code_status; /* holds return value of text write */
static void *mod_code_ip; /* holds the IP to write to */
static void *mod_code_newcode; /* holds the text to write to the IP */
static unsigned nmi_wait_count;
static atomic_t nmi_update_count = ATOMIC_INIT(0);
int ftrace_arch_read_dyn_info(char *buf, int size)
{
int r;
r = snprintf(buf, size, "%u %u",
nmi_wait_count,
atomic_read(&nmi_update_count));
return r;
}
static void clear_mod_flag(void)
{
int old = atomic_read(&nmi_running);
@ -144,7 +131,6 @@ void arch_ftrace_nmi_enter(void)
if (atomic_inc_return(&nmi_running) & MOD_CODE_WRITE_FLAG) {
smp_rmb();
ftrace_mod_code();
atomic_inc(&nmi_update_count);
}
/* Must have previous changes seen before executions */
smp_mb();
@ -165,8 +151,6 @@ static void wait_for_nmi_and_set_mod_flag(void)
do {
cpu_relax();
} while (atomic_cmpxchg(&nmi_running, 0, MOD_CODE_WRITE_FLAG));
nmi_wait_count++;
}
static void wait_for_nmi(void)
@ -177,8 +161,6 @@ static void wait_for_nmi(void)
do {
cpu_relax();
} while (atomic_read(&nmi_running));
nmi_wait_count++;
}
static int

View File

@ -125,9 +125,9 @@
VMLINUX_SYMBOL(__start_ftrace_events) = .; \
KEEP(*(_ftrace_events)) \
VMLINUX_SYMBOL(__stop_ftrace_events) = .; \
VMLINUX_SYMBOL(__start_ftrace_enum_maps) = .; \
KEEP(*(_ftrace_enum_map)) \
VMLINUX_SYMBOL(__stop_ftrace_enum_maps) = .;
VMLINUX_SYMBOL(__start_ftrace_eval_maps) = .; \
KEEP(*(_ftrace_eval_map)) \
VMLINUX_SYMBOL(__stop_ftrace_eval_maps) = .;
#else
#define FTRACE_EVENTS()
#endif

View File

@ -119,6 +119,8 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
* for any of the functions that this ops will be registered for, then
* this ops will fail to register or set_filter_ip.
* PID - Is affected by set_ftrace_pid (allows filtering on those pids)
* RCU - Set when the ops can only be called when RCU is watching.
* TRACE_ARRAY - The ops->private points to a trace_array descriptor.
*/
enum {
FTRACE_OPS_FL_ENABLED = 1 << 0,
@ -137,6 +139,7 @@ enum {
FTRACE_OPS_FL_IPMODIFY = 1 << 13,
FTRACE_OPS_FL_PID = 1 << 14,
FTRACE_OPS_FL_RCU = 1 << 15,
FTRACE_OPS_FL_TRACE_ARRAY = 1 << 16,
};
#ifdef CONFIG_DYNAMIC_FTRACE
@ -445,7 +448,8 @@ enum {
FTRACE_ITER_PRINTALL = (1 << 2),
FTRACE_ITER_DO_PROBES = (1 << 3),
FTRACE_ITER_PROBE = (1 << 4),
FTRACE_ITER_ENABLED = (1 << 5),
FTRACE_ITER_MOD = (1 << 5),
FTRACE_ITER_ENABLED = (1 << 6),
};
void arch_ftrace_update_code(int command);

View File

@ -442,8 +442,8 @@ struct module {
#ifdef CONFIG_EVENT_TRACING
struct trace_event_call **trace_events;
unsigned int num_trace_events;
struct trace_enum_map **trace_enums;
unsigned int num_trace_enums;
struct trace_eval_map **trace_evals;
unsigned int num_trace_evals;
#endif
#ifdef CONFIG_FTRACE_MCOUNT_RECORD
unsigned int num_ftrace_callsites;

View File

@ -151,7 +151,15 @@ trace_event_buffer_lock_reserve(struct ring_buffer **current_buffer,
int type, unsigned long len,
unsigned long flags, int pc);
void tracing_record_cmdline(struct task_struct *tsk);
#define TRACE_RECORD_CMDLINE BIT(0)
#define TRACE_RECORD_TGID BIT(1)
void tracing_record_taskinfo(struct task_struct *task, int flags);
void tracing_record_taskinfo_sched_switch(struct task_struct *prev,
struct task_struct *next, int flags);
void tracing_record_cmdline(struct task_struct *task);
void tracing_record_tgid(struct task_struct *task);
int trace_output_call(struct trace_iterator *iter, char *name, char *fmt, ...);
@ -290,6 +298,7 @@ struct trace_subsystem_dir;
enum {
EVENT_FILE_FL_ENABLED_BIT,
EVENT_FILE_FL_RECORDED_CMD_BIT,
EVENT_FILE_FL_RECORDED_TGID_BIT,
EVENT_FILE_FL_FILTERED_BIT,
EVENT_FILE_FL_NO_SET_FILTER_BIT,
EVENT_FILE_FL_SOFT_MODE_BIT,
@ -303,6 +312,7 @@ enum {
* Event file flags:
* ENABLED - The event is enabled
* RECORDED_CMD - The comms should be recorded at sched_switch
* RECORDED_TGID - The tgids should be recorded at sched_switch
* FILTERED - The event has a filter attached
* NO_SET_FILTER - Set when filter has error and is to be ignored
* SOFT_MODE - The event is enabled/disabled by SOFT_DISABLED
@ -315,6 +325,7 @@ enum {
enum {
EVENT_FILE_FL_ENABLED = (1 << EVENT_FILE_FL_ENABLED_BIT),
EVENT_FILE_FL_RECORDED_CMD = (1 << EVENT_FILE_FL_RECORDED_CMD_BIT),
EVENT_FILE_FL_RECORDED_TGID = (1 << EVENT_FILE_FL_RECORDED_TGID_BIT),
EVENT_FILE_FL_FILTERED = (1 << EVENT_FILE_FL_FILTERED_BIT),
EVENT_FILE_FL_NO_SET_FILTER = (1 << EVENT_FILE_FL_NO_SET_FILTER_BIT),
EVENT_FILE_FL_SOFT_MODE = (1 << EVENT_FILE_FL_SOFT_MODE_BIT),

View File

@ -25,10 +25,10 @@ struct module;
struct tracepoint;
struct notifier_block;
struct trace_enum_map {
struct trace_eval_map {
const char *system;
const char *enum_string;
unsigned long enum_value;
const char *eval_string;
unsigned long eval_value;
};
#define TRACEPOINT_DEFAULT_PRIO 10
@ -88,6 +88,7 @@ extern void syscall_unregfunc(void);
#define PARAMS(args...) args
#define TRACE_DEFINE_ENUM(x)
#define TRACE_DEFINE_SIZEOF(x)
#endif /* _LINUX_TRACEPOINT_H */

View File

@ -30,6 +30,8 @@ DECLARE_EVENT_CLASS(xen_mc__batch,
DEFINE_XEN_MC_BATCH(xen_mc_batch);
DEFINE_XEN_MC_BATCH(xen_mc_issue);
TRACE_DEFINE_SIZEOF(ulong);
TRACE_EVENT(xen_mc_entry,
TP_PROTO(struct multicall_entry *mc, unsigned nargs),
TP_ARGS(mc, nargs),
@ -40,8 +42,8 @@ TRACE_EVENT(xen_mc_entry,
),
TP_fast_assign(__entry->op = mc->op;
__entry->nargs = nargs;
memcpy(__entry->args, mc->args, sizeof(unsigned long) * nargs);
memset(__entry->args + nargs, 0, sizeof(unsigned long) * (6 - nargs));
memcpy(__entry->args, mc->args, sizeof(ulong) * nargs);
memset(__entry->args + nargs, 0, sizeof(ulong) * (6 - nargs));
),
TP_printk("op %u%s args [%lx, %lx, %lx, %lx, %lx, %lx]",
__entry->op, xen_hypercall_name(__entry->op),
@ -122,6 +124,7 @@ TRACE_EVENT(xen_mc_extend_args,
__entry->res == XEN_MC_XE_NO_SPACE ? "NO_SPACE" : "???")
);
TRACE_DEFINE_SIZEOF(pteval_t);
/* mmu */
DECLARE_EVENT_CLASS(xen_mmu__set_pte,
TP_PROTO(pte_t *ptep, pte_t pteval),
@ -199,6 +202,8 @@ TRACE_EVENT(xen_mmu_pte_clear,
__entry->mm, __entry->addr, __entry->ptep)
);
TRACE_DEFINE_SIZEOF(pmdval_t);
TRACE_EVENT(xen_mmu_set_pmd,
TP_PROTO(pmd_t *pmdp, pmd_t pmdval),
TP_ARGS(pmdp, pmdval),
@ -226,6 +231,8 @@ TRACE_EVENT(xen_mmu_pmd_clear,
#if CONFIG_PGTABLE_LEVELS >= 4
TRACE_DEFINE_SIZEOF(pudval_t);
TRACE_EVENT(xen_mmu_set_pud,
TP_PROTO(pud_t *pudp, pud_t pudval),
TP_ARGS(pudp, pudval),
@ -241,6 +248,8 @@ TRACE_EVENT(xen_mmu_set_pud,
(int)sizeof(pudval_t) * 2, (unsigned long long)__entry->pudval)
);
TRACE_DEFINE_SIZEOF(p4dval_t);
TRACE_EVENT(xen_mmu_set_p4d,
TP_PROTO(p4d_t *p4dp, p4d_t *user_p4dp, p4d_t p4dval),
TP_ARGS(p4dp, user_p4dp, p4dval),

View File

@ -35,15 +35,28 @@ TRACE_MAKE_SYSTEM_STR();
#undef TRACE_DEFINE_ENUM
#define TRACE_DEFINE_ENUM(a) \
static struct trace_enum_map __used __initdata \
static struct trace_eval_map __used __initdata \
__##TRACE_SYSTEM##_##a = \
{ \
.system = TRACE_SYSTEM_STRING, \
.enum_string = #a, \
.enum_value = a \
.eval_string = #a, \
.eval_value = a \
}; \
static struct trace_enum_map __used \
__attribute__((section("_ftrace_enum_map"))) \
static struct trace_eval_map __used \
__attribute__((section("_ftrace_eval_map"))) \
*TRACE_SYSTEM##_##a = &__##TRACE_SYSTEM##_##a
#undef TRACE_DEFINE_SIZEOF
#define TRACE_DEFINE_SIZEOF(a) \
static struct trace_eval_map __used __initdata \
__##TRACE_SYSTEM##_##a = \
{ \
.system = TRACE_SYSTEM_STRING, \
.eval_string = "sizeof(" #a ")", \
.eval_value = sizeof(a) \
}; \
static struct trace_eval_map __used \
__attribute__((section("_ftrace_eval_map"))) \
*TRACE_SYSTEM##_##a = &__##TRACE_SYSTEM##_##a
/*
@ -158,6 +171,9 @@ TRACE_MAKE_SYSTEM_STR();
#undef TRACE_DEFINE_ENUM
#define TRACE_DEFINE_ENUM(a)
#undef TRACE_DEFINE_SIZEOF
#define TRACE_DEFINE_SIZEOF(a)
#undef __field
#define __field(type, item)

View File

@ -3074,9 +3074,9 @@ static int find_module_sections(struct module *mod, struct load_info *info)
mod->trace_events = section_objs(info, "_ftrace_events",
sizeof(*mod->trace_events),
&mod->num_trace_events);
mod->trace_enums = section_objs(info, "_ftrace_enum_map",
sizeof(*mod->trace_enums),
&mod->num_trace_enums);
mod->trace_evals = section_objs(info, "_ftrace_eval_map",
sizeof(*mod->trace_evals),
&mod->num_trace_evals);
#endif
#ifdef CONFIG_TRACING
mod->trace_bprintk_fmt_start = section_objs(info, "__trace_printk_fmt",

View File

@ -667,30 +667,30 @@ config RING_BUFFER_STARTUP_TEST
If unsure, say N
config TRACE_ENUM_MAP_FILE
bool "Show enum mappings for trace events"
config TRACE_EVAL_MAP_FILE
bool "Show eval mappings for trace events"
depends on TRACING
help
The "print fmt" of the trace events will show the enum names instead
of their values. This can cause problems for user space tools that
use this string to parse the raw data as user space does not know
The "print fmt" of the trace events will show the enum/sizeof names
instead of their values. This can cause problems for user space tools
that use this string to parse the raw data as user space does not know
how to convert the string to its value.
To fix this, there's a special macro in the kernel that can be used
to convert the enum into its value. If this macro is used, then the
print fmt strings will have the enums converted to their values.
to convert an enum/sizeof into its value. If this macro is used, then
the print fmt strings will be converted to their values.
If something does not get converted properly, this option can be
used to show what enums the kernel tried to convert.
used to show what enums/sizeof the kernel tried to convert.
This option is for debugging the enum conversions. A file is created
in the tracing directory called "enum_map" that will show the enum
This option is for debugging the conversions. A file is created
in the tracing directory called "eval_map" that will show the
names matched with their values and what trace event system they
belong too.
Normally, the mapping of the strings to values will be freed after
boot up or module load. With this option, they will not be freed, as
they are needed for the "enum_map" file. Enabling this option will
they are needed for the "eval_map" file. Enabling this option will
increase the memory footprint of the running kernel.
If unsure, say N

View File

@ -1293,6 +1293,28 @@ static void ftrace_hash_clear(struct ftrace_hash *hash)
FTRACE_WARN_ON(hash->count);
}
static void free_ftrace_mod(struct ftrace_mod_load *ftrace_mod)
{
list_del(&ftrace_mod->list);
kfree(ftrace_mod->module);
kfree(ftrace_mod->func);
kfree(ftrace_mod);
}
static void clear_ftrace_mod_list(struct list_head *head)
{
struct ftrace_mod_load *p, *n;
/* stack tracer isn't supported yet */
if (!head)
return;
mutex_lock(&ftrace_lock);
list_for_each_entry_safe(p, n, head, list)
free_ftrace_mod(p);
mutex_unlock(&ftrace_lock);
}
static void free_ftrace_hash(struct ftrace_hash *hash)
{
if (!hash || hash == EMPTY_HASH)
@ -1346,6 +1368,35 @@ static struct ftrace_hash *alloc_ftrace_hash(int size_bits)
return hash;
}
static int ftrace_add_mod(struct trace_array *tr,
const char *func, const char *module,
int enable)
{
struct ftrace_mod_load *ftrace_mod;
struct list_head *mod_head = enable ? &tr->mod_trace : &tr->mod_notrace;
ftrace_mod = kzalloc(sizeof(*ftrace_mod), GFP_KERNEL);
if (!ftrace_mod)
return -ENOMEM;
ftrace_mod->func = kstrdup(func, GFP_KERNEL);
ftrace_mod->module = kstrdup(module, GFP_KERNEL);
ftrace_mod->enable = enable;
if (!ftrace_mod->func || !ftrace_mod->module)
goto out_free;
list_add(&ftrace_mod->list, mod_head);
return 0;
out_free:
free_ftrace_mod(ftrace_mod);
return -ENOMEM;
}
static struct ftrace_hash *
alloc_and_copy_ftrace_hash(int size_bits, struct ftrace_hash *hash)
{
@ -1359,6 +1410,9 @@ alloc_and_copy_ftrace_hash(int size_bits, struct ftrace_hash *hash)
if (!new_hash)
return NULL;
if (hash)
new_hash->flags = hash->flags;
/* Empty hash? */
if (ftrace_hash_empty(hash))
return new_hash;
@ -1403,7 +1457,7 @@ __ftrace_hash_move(struct ftrace_hash *src)
/*
* If the new source is empty, just return the empty_hash.
*/
if (!src->count)
if (ftrace_hash_empty(src))
return EMPTY_HASH;
/*
@ -1420,6 +1474,8 @@ __ftrace_hash_move(struct ftrace_hash *src)
if (!new_hash)
return NULL;
new_hash->flags = src->flags;
size = 1 << src->size_bits;
for (i = 0; i < size; i++) {
hhd = &src->buckets[i];
@ -1650,7 +1706,7 @@ static bool __ftrace_hash_rec_update(struct ftrace_ops *ops,
struct dyn_ftrace *rec;
bool update = false;
int count = 0;
int all = 0;
int all = false;
/* Only update if the ops has been registered */
if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
@ -1671,7 +1727,7 @@ static bool __ftrace_hash_rec_update(struct ftrace_ops *ops,
hash = ops->func_hash->filter_hash;
other_hash = ops->func_hash->notrace_hash;
if (ftrace_hash_empty(hash))
all = 1;
all = true;
} else {
inc = !inc;
hash = ops->func_hash->notrace_hash;
@ -3061,6 +3117,7 @@ ftrace_allocate_pages(unsigned long num_to_init)
struct ftrace_iterator {
loff_t pos;
loff_t func_pos;
loff_t mod_pos;
struct ftrace_page *pg;
struct dyn_ftrace *func;
struct ftrace_func_probe *probe;
@ -3068,6 +3125,8 @@ struct ftrace_iterator {
struct trace_parser parser;
struct ftrace_hash *hash;
struct ftrace_ops *ops;
struct trace_array *tr;
struct list_head *mod_list;
int pidx;
int idx;
unsigned flags;
@ -3152,13 +3211,13 @@ static void *t_probe_start(struct seq_file *m, loff_t *pos)
if (!(iter->flags & FTRACE_ITER_DO_PROBES))
return NULL;
if (iter->func_pos > *pos)
if (iter->mod_pos > *pos)
return NULL;
iter->probe = NULL;
iter->probe_entry = NULL;
iter->pidx = 0;
for (l = 0; l <= (*pos - iter->func_pos); ) {
for (l = 0; l <= (*pos - iter->mod_pos); ) {
p = t_probe_next(m, &l);
if (!p)
break;
@ -3196,6 +3255,82 @@ t_probe_show(struct seq_file *m, struct ftrace_iterator *iter)
return 0;
}
static void *
t_mod_next(struct seq_file *m, loff_t *pos)
{
struct ftrace_iterator *iter = m->private;
struct trace_array *tr = iter->tr;
(*pos)++;
iter->pos = *pos;
iter->mod_list = iter->mod_list->next;
if (iter->mod_list == &tr->mod_trace ||
iter->mod_list == &tr->mod_notrace) {
iter->flags &= ~FTRACE_ITER_MOD;
return NULL;
}
iter->mod_pos = *pos;
return iter;
}
static void *t_mod_start(struct seq_file *m, loff_t *pos)
{
struct ftrace_iterator *iter = m->private;
void *p = NULL;
loff_t l;
if (iter->func_pos > *pos)
return NULL;
iter->mod_pos = iter->func_pos;
/* probes are only available if tr is set */
if (!iter->tr)
return NULL;
for (l = 0; l <= (*pos - iter->func_pos); ) {
p = t_mod_next(m, &l);
if (!p)
break;
}
if (!p) {
iter->flags &= ~FTRACE_ITER_MOD;
return t_probe_start(m, pos);
}
/* Only set this if we have an item */
iter->flags |= FTRACE_ITER_MOD;
return iter;
}
static int
t_mod_show(struct seq_file *m, struct ftrace_iterator *iter)
{
struct ftrace_mod_load *ftrace_mod;
struct trace_array *tr = iter->tr;
if (WARN_ON_ONCE(!iter->mod_list) ||
iter->mod_list == &tr->mod_trace ||
iter->mod_list == &tr->mod_notrace)
return -EIO;
ftrace_mod = list_entry(iter->mod_list, struct ftrace_mod_load, list);
if (ftrace_mod->func)
seq_printf(m, "%s", ftrace_mod->func);
else
seq_putc(m, '*');
seq_printf(m, ":mod:%s\n", ftrace_mod->module);
return 0;
}
static void *
t_func_next(struct seq_file *m, loff_t *pos)
{
@ -3237,7 +3372,7 @@ static void *
t_next(struct seq_file *m, void *v, loff_t *pos)
{
struct ftrace_iterator *iter = m->private;
loff_t l = *pos; /* t_hash_start() must use original pos */
loff_t l = *pos; /* t_probe_start() must use original pos */
void *ret;
if (unlikely(ftrace_disabled))
@ -3246,16 +3381,19 @@ t_next(struct seq_file *m, void *v, loff_t *pos)
if (iter->flags & FTRACE_ITER_PROBE)
return t_probe_next(m, pos);
if (iter->flags & FTRACE_ITER_MOD)
return t_mod_next(m, pos);
if (iter->flags & FTRACE_ITER_PRINTALL) {
/* next must increment pos, and t_probe_start does not */
(*pos)++;
return t_probe_start(m, &l);
return t_mod_start(m, &l);
}
ret = t_func_next(m, pos);
if (!ret)
return t_probe_start(m, &l);
return t_mod_start(m, &l);
return ret;
}
@ -3264,7 +3402,7 @@ static void reset_iter_read(struct ftrace_iterator *iter)
{
iter->pos = 0;
iter->func_pos = 0;
iter->flags &= ~(FTRACE_ITER_PRINTALL | FTRACE_ITER_PROBE);
iter->flags &= ~(FTRACE_ITER_PRINTALL | FTRACE_ITER_PROBE | FTRACE_ITER_MOD);
}
static void *t_start(struct seq_file *m, loff_t *pos)
@ -3293,15 +3431,15 @@ static void *t_start(struct seq_file *m, loff_t *pos)
ftrace_hash_empty(iter->hash)) {
iter->func_pos = 1; /* Account for the message */
if (*pos > 0)
return t_probe_start(m, pos);
return t_mod_start(m, pos);
iter->flags |= FTRACE_ITER_PRINTALL;
/* reset in case of seek/pread */
iter->flags &= ~FTRACE_ITER_PROBE;
return iter;
}
if (iter->flags & FTRACE_ITER_PROBE)
return t_probe_start(m, pos);
if (iter->flags & FTRACE_ITER_MOD)
return t_mod_start(m, pos);
/*
* Unfortunately, we need to restart at ftrace_pages_start
@ -3317,7 +3455,7 @@ static void *t_start(struct seq_file *m, loff_t *pos)
}
if (!p)
return t_probe_start(m, pos);
return t_mod_start(m, pos);
return iter;
}
@ -3351,6 +3489,9 @@ static int t_show(struct seq_file *m, void *v)
if (iter->flags & FTRACE_ITER_PROBE)
return t_probe_show(m, iter);
if (iter->flags & FTRACE_ITER_MOD)
return t_mod_show(m, iter);
if (iter->flags & FTRACE_ITER_PRINTALL) {
if (iter->flags & FTRACE_ITER_NOTRACE)
seq_puts(m, "#### no functions disabled ####\n");
@ -3457,6 +3598,8 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag,
{
struct ftrace_iterator *iter;
struct ftrace_hash *hash;
struct list_head *mod_head;
struct trace_array *tr = ops->private;
int ret = 0;
ftrace_ops_init(ops);
@ -3475,21 +3618,29 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag,
iter->ops = ops;
iter->flags = flag;
iter->tr = tr;
mutex_lock(&ops->func_hash->regex_lock);
if (flag & FTRACE_ITER_NOTRACE)
if (flag & FTRACE_ITER_NOTRACE) {
hash = ops->func_hash->notrace_hash;
else
mod_head = tr ? &tr->mod_notrace : NULL;
} else {
hash = ops->func_hash->filter_hash;
mod_head = tr ? &tr->mod_trace : NULL;
}
iter->mod_list = mod_head;
if (file->f_mode & FMODE_WRITE) {
const int size_bits = FTRACE_HASH_DEFAULT_BITS;
if (file->f_flags & O_TRUNC)
if (file->f_flags & O_TRUNC) {
iter->hash = alloc_ftrace_hash(size_bits);
else
clear_ftrace_mod_list(mod_head);
} else {
iter->hash = alloc_and_copy_ftrace_hash(size_bits, hash);
}
if (!iter->hash) {
trace_parser_put(&iter->parser);
@ -3761,6 +3912,163 @@ static int ftrace_hash_move_and_update_ops(struct ftrace_ops *ops,
return ret;
}
static bool module_exists(const char *module)
{
/* All modules have the symbol __this_module */
const char this_mod[] = "__this_module";
const int modname_size = MAX_PARAM_PREFIX_LEN + sizeof(this_mod) + 1;
char modname[modname_size + 1];
unsigned long val;
int n;
n = snprintf(modname, modname_size + 1, "%s:%s", module, this_mod);
if (n > modname_size)
return false;
val = module_kallsyms_lookup_name(modname);
return val != 0;
}
static int cache_mod(struct trace_array *tr,
const char *func, char *module, int enable)
{
struct ftrace_mod_load *ftrace_mod, *n;
struct list_head *head = enable ? &tr->mod_trace : &tr->mod_notrace;
int ret;
mutex_lock(&ftrace_lock);
/* We do not cache inverse filters */
if (func[0] == '!') {
func++;
ret = -EINVAL;
/* Look to remove this hash */
list_for_each_entry_safe(ftrace_mod, n, head, list) {
if (strcmp(ftrace_mod->module, module) != 0)
continue;
/* no func matches all */
if (!func || strcmp(func, "*") == 0 ||
(ftrace_mod->func &&
strcmp(ftrace_mod->func, func) == 0)) {
ret = 0;
free_ftrace_mod(ftrace_mod);
continue;
}
}
goto out;
}
ret = -EINVAL;
/* We only care about modules that have not been loaded yet */
if (module_exists(module))
goto out;
/* Save this string off, and execute it when the module is loaded */
ret = ftrace_add_mod(tr, func, module, enable);
out:
mutex_unlock(&ftrace_lock);
return ret;
}
static int
ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len,
int reset, int enable);
static void process_mod_list(struct list_head *head, struct ftrace_ops *ops,
char *mod, bool enable)
{
struct ftrace_mod_load *ftrace_mod, *n;
struct ftrace_hash **orig_hash, *new_hash;
LIST_HEAD(process_mods);
char *func;
int ret;
mutex_lock(&ops->func_hash->regex_lock);
if (enable)
orig_hash = &ops->func_hash->filter_hash;
else
orig_hash = &ops->func_hash->notrace_hash;
new_hash = alloc_and_copy_ftrace_hash(FTRACE_HASH_DEFAULT_BITS,
*orig_hash);
if (!new_hash)
goto out; /* warn? */
mutex_lock(&ftrace_lock);
list_for_each_entry_safe(ftrace_mod, n, head, list) {
if (strcmp(ftrace_mod->module, mod) != 0)
continue;
if (ftrace_mod->func)
func = kstrdup(ftrace_mod->func, GFP_KERNEL);
else
func = kstrdup("*", GFP_KERNEL);
if (!func) /* warn? */
continue;
list_del(&ftrace_mod->list);
list_add(&ftrace_mod->list, &process_mods);
/* Use the newly allocated func, as it may be "*" */
kfree(ftrace_mod->func);
ftrace_mod->func = func;
}
mutex_unlock(&ftrace_lock);
list_for_each_entry_safe(ftrace_mod, n, &process_mods, list) {
func = ftrace_mod->func;
/* Grabs ftrace_lock, which is why we have this extra step */
match_records(new_hash, func, strlen(func), mod);
free_ftrace_mod(ftrace_mod);
}
if (enable && list_empty(head))
new_hash->flags &= ~FTRACE_HASH_FL_MOD;
mutex_lock(&ftrace_lock);
ret = ftrace_hash_move_and_update_ops(ops, orig_hash,
new_hash, enable);
mutex_unlock(&ftrace_lock);
out:
mutex_unlock(&ops->func_hash->regex_lock);
free_ftrace_hash(new_hash);
}
static void process_cached_mods(const char *mod_name)
{
struct trace_array *tr;
char *mod;
mod = kstrdup(mod_name, GFP_KERNEL);
if (!mod)
return;
mutex_lock(&trace_types_lock);
list_for_each_entry(tr, &ftrace_trace_arrays, list) {
if (!list_empty(&tr->mod_trace))
process_mod_list(&tr->mod_trace, tr->ops, mod, true);
if (!list_empty(&tr->mod_notrace))
process_mod_list(&tr->mod_notrace, tr->ops, mod, false);
}
mutex_unlock(&trace_types_lock);
kfree(mod);
}
/*
* We register the module command as a template to show others how
* to register the a command as well.
@ -3768,10 +4076,16 @@ static int ftrace_hash_move_and_update_ops(struct ftrace_ops *ops,
static int
ftrace_mod_callback(struct trace_array *tr, struct ftrace_hash *hash,
char *func, char *cmd, char *module, int enable)
char *func_orig, char *cmd, char *module, int enable)
{
char *func;
int ret;
/* match_records() modifies func, and we need the original */
func = kstrdup(func_orig, GFP_KERNEL);
if (!func)
return -ENOMEM;
/*
* cmd == 'mod' because we only registered this func
* for the 'mod' ftrace_func_command.
@ -3780,8 +4094,10 @@ ftrace_mod_callback(struct trace_array *tr, struct ftrace_hash *hash,
* parameter.
*/
ret = match_records(hash, func, strlen(func), module);
kfree(func);
if (!ret)
return -EINVAL;
return cache_mod(tr, func_orig, module, enable);
if (ret < 0)
return ret;
return 0;
@ -4725,9 +5041,11 @@ int ftrace_regex_release(struct inode *inode, struct file *file)
if (file->f_mode & FMODE_WRITE) {
filter_hash = !!(iter->flags & FTRACE_ITER_FILTER);
if (filter_hash)
if (filter_hash) {
orig_hash = &iter->ops->func_hash->filter_hash;
else
if (iter->tr && !list_empty(&iter->tr->mod_trace))
iter->hash->flags |= FTRACE_HASH_FL_MOD;
} else
orig_hash = &iter->ops->func_hash->notrace_hash;
mutex_lock(&ftrace_lock);
@ -5385,6 +5703,7 @@ void ftrace_release_mod(struct module *mod)
if (pg == ftrace_pages)
ftrace_pages = next_to_ftrace_page(last_pg);
ftrace_update_tot_cnt -= pg->index;
*last_pg = pg->next;
order = get_count_order(pg->size / ENTRIES_PER_PAGE);
free_pages((unsigned long)pg->records, order);
@ -5463,6 +5782,8 @@ void ftrace_module_enable(struct module *mod)
out_unlock:
mutex_unlock(&ftrace_lock);
process_cached_mods(mod->name);
}
void ftrace_module_init(struct module *mod)
@ -5501,6 +5822,7 @@ void __init ftrace_free_init_mem(void)
if (!rec)
continue;
pg->index--;
ftrace_update_tot_cnt--;
if (!pg->index) {
*last_pg = pg->next;
order = get_count_order(pg->size / ENTRIES_PER_PAGE);
@ -5567,6 +5889,8 @@ static void ftrace_update_trampoline(struct ftrace_ops *ops)
void ftrace_init_trace_array(struct trace_array *tr)
{
INIT_LIST_HEAD(&tr->func_probes);
INIT_LIST_HEAD(&tr->mod_trace);
INIT_LIST_HEAD(&tr->mod_notrace);
}
#else

View File

@ -87,7 +87,7 @@ dummy_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
* tracing is active, only save the comm when a trace event
* occurred.
*/
static DEFINE_PER_CPU(bool, trace_cmdline_save);
static DEFINE_PER_CPU(bool, trace_taskinfo_save);
/*
* Kill all tracing for good (never come back).
@ -120,41 +120,41 @@ enum ftrace_dump_mode ftrace_dump_on_oops;
/* When set, tracing will stop when a WARN*() is hit */
int __disable_trace_on_warning;
#ifdef CONFIG_TRACE_ENUM_MAP_FILE
/* Map of enums to their values, for "enum_map" file */
struct trace_enum_map_head {
#ifdef CONFIG_TRACE_EVAL_MAP_FILE
/* Map of enums to their values, for "eval_map" file */
struct trace_eval_map_head {
struct module *mod;
unsigned long length;
};
union trace_enum_map_item;
union trace_eval_map_item;
struct trace_enum_map_tail {
struct trace_eval_map_tail {
/*
* "end" is first and points to NULL as it must be different
* than "mod" or "enum_string"
* than "mod" or "eval_string"
*/
union trace_enum_map_item *next;
union trace_eval_map_item *next;
const char *end; /* points to NULL */
};
static DEFINE_MUTEX(trace_enum_mutex);
static DEFINE_MUTEX(trace_eval_mutex);
/*
* The trace_enum_maps are saved in an array with two extra elements,
* The trace_eval_maps are saved in an array with two extra elements,
* one at the beginning, and one at the end. The beginning item contains
* the count of the saved maps (head.length), and the module they
* belong to if not built in (head.mod). The ending item contains a
* pointer to the next array of saved enum_map items.
* pointer to the next array of saved eval_map items.
*/
union trace_enum_map_item {
struct trace_enum_map map;
struct trace_enum_map_head head;
struct trace_enum_map_tail tail;
union trace_eval_map_item {
struct trace_eval_map map;
struct trace_eval_map_head head;
struct trace_eval_map_tail tail;
};
static union trace_enum_map_item *trace_enum_maps;
#endif /* CONFIG_TRACE_ENUM_MAP_FILE */
static union trace_eval_map_item *trace_eval_maps;
#endif /* CONFIG_TRACE_EVAL_MAP_FILE */
static int tracing_set_tracer(struct trace_array *tr, const char *buf);
@ -790,7 +790,7 @@ 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);
__this_cpu_write(trace_taskinfo_save, true);
/* If this is the temp buffer, we need to commit fully */
if (this_cpu_read(trace_buffered_event) == event) {
@ -1141,9 +1141,9 @@ unsigned long nsecs_to_usecs(unsigned long nsecs)
/*
* TRACE_FLAGS is defined as a tuple matching bit masks with strings.
* It uses C(a, b) where 'a' is the enum name and 'b' is the string that
* It uses C(a, b) where 'a' is the eval (enum) name and 'b' is the string that
* matches it. By defining "C(a, b) b", TRACE_FLAGS becomes a list
* of strings in the order that the enums were defined.
* of strings in the order that the evals (enum) were defined.
*/
#undef C
#define C(a, b) b
@ -1709,6 +1709,8 @@ void tracing_reset_all_online_cpus(void)
}
}
static int *tgid_map;
#define SAVED_CMDLINES_DEFAULT 128
#define NO_CMDLINE_MAP UINT_MAX
static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED;
@ -1722,7 +1724,7 @@ struct saved_cmdlines_buffer {
static struct saved_cmdlines_buffer *savedcmd;
/* temporary disable recording */
static atomic_t trace_record_cmdline_disabled __read_mostly;
static atomic_t trace_record_taskinfo_disabled __read_mostly;
static inline char *get_saved_cmdlines(int idx)
{
@ -1910,8 +1912,6 @@ static void tracing_stop_tr(struct trace_array *tr)
raw_spin_unlock_irqrestore(&tr->start_lock, flags);
}
void trace_stop_cmdline_recording(void);
static int trace_save_cmdline(struct task_struct *tsk)
{
unsigned pid, idx;
@ -1992,16 +1992,87 @@ void trace_find_cmdline(int pid, char comm[])
preempt_enable();
}
void tracing_record_cmdline(struct task_struct *tsk)
int trace_find_tgid(int pid)
{
if (atomic_read(&trace_record_cmdline_disabled) || !tracing_is_on())
if (unlikely(!tgid_map || !pid || pid > PID_MAX_DEFAULT))
return 0;
return tgid_map[pid];
}
static int trace_save_tgid(struct task_struct *tsk)
{
if (unlikely(!tgid_map || !tsk->pid || tsk->pid > PID_MAX_DEFAULT))
return 0;
tgid_map[tsk->pid] = tsk->tgid;
return 1;
}
static bool tracing_record_taskinfo_skip(int flags)
{
if (unlikely(!(flags & (TRACE_RECORD_CMDLINE | TRACE_RECORD_TGID))))
return true;
if (atomic_read(&trace_record_taskinfo_disabled) || !tracing_is_on())
return true;
if (!__this_cpu_read(trace_taskinfo_save))
return true;
return false;
}
/**
* tracing_record_taskinfo - record the task info of a task
*
* @task - task to record
* @flags - TRACE_RECORD_CMDLINE for recording comm
* - TRACE_RECORD_TGID for recording tgid
*/
void tracing_record_taskinfo(struct task_struct *task, int flags)
{
if (tracing_record_taskinfo_skip(flags))
return;
if ((flags & TRACE_RECORD_CMDLINE) && !trace_save_cmdline(task))
return;
if ((flags & TRACE_RECORD_TGID) && !trace_save_tgid(task))
return;
if (!__this_cpu_read(trace_cmdline_save))
__this_cpu_write(trace_taskinfo_save, false);
}
/**
* tracing_record_taskinfo_sched_switch - record task info for sched_switch
*
* @prev - previous task during sched_switch
* @next - next task during sched_switch
* @flags - TRACE_RECORD_CMDLINE for recording comm
* TRACE_RECORD_TGID for recording tgid
*/
void tracing_record_taskinfo_sched_switch(struct task_struct *prev,
struct task_struct *next, int flags)
{
if (tracing_record_taskinfo_skip(flags))
return;
if (trace_save_cmdline(tsk))
__this_cpu_write(trace_cmdline_save, false);
if ((flags & TRACE_RECORD_CMDLINE) &&
(!trace_save_cmdline(prev) || !trace_save_cmdline(next)))
return;
if ((flags & TRACE_RECORD_TGID) &&
(!trace_save_tgid(prev) || !trace_save_tgid(next)))
return;
__this_cpu_write(trace_taskinfo_save, false);
}
/* Helpers to record a specific task information */
void tracing_record_cmdline(struct task_struct *task)
{
tracing_record_taskinfo(task, TRACE_RECORD_CMDLINE);
}
void tracing_record_tgid(struct task_struct *task)
{
tracing_record_taskinfo(task, TRACE_RECORD_TGID);
}
/*
@ -3146,7 +3217,7 @@ static void *s_start(struct seq_file *m, loff_t *pos)
#endif
if (!iter->snapshot)
atomic_inc(&trace_record_cmdline_disabled);
atomic_inc(&trace_record_taskinfo_disabled);
if (*pos != iter->pos) {
iter->ent = NULL;
@ -3191,7 +3262,7 @@ static void s_stop(struct seq_file *m, void *p)
#endif
if (!iter->snapshot)
atomic_dec(&trace_record_cmdline_disabled);
atomic_dec(&trace_record_taskinfo_disabled);
trace_access_unlock(iter->cpu_file);
trace_event_read_unlock();
@ -3248,23 +3319,29 @@ static void print_event_info(struct trace_buffer *buf, struct seq_file *m)
seq_puts(m, "#\n");
}
static void print_func_help_header(struct trace_buffer *buf, struct seq_file *m)
static void print_func_help_header(struct trace_buffer *buf, struct seq_file *m,
unsigned int flags)
{
bool tgid = flags & TRACE_ITER_RECORD_TGID;
print_event_info(buf, m);
seq_puts(m, "# TASK-PID CPU# TIMESTAMP FUNCTION\n"
"# | | | | |\n");
seq_printf(m, "# TASK-PID CPU# %s TIMESTAMP FUNCTION\n", tgid ? "TGID " : "");
seq_printf(m, "# | | | %s | |\n", tgid ? " | " : "");
}
static void print_func_help_header_irq(struct trace_buffer *buf, struct seq_file *m)
static void print_func_help_header_irq(struct trace_buffer *buf, struct seq_file *m,
unsigned int flags)
{
print_event_info(buf, m);
seq_puts(m, "# _-----=> irqs-off\n"
"# / _----=> need-resched\n"
"# | / _---=> hardirq/softirq\n"
"# || / _--=> preempt-depth\n"
"# ||| / delay\n"
"# TASK-PID CPU# |||| TIMESTAMP FUNCTION\n"
"# | | | |||| | |\n");
bool tgid = flags & TRACE_ITER_RECORD_TGID;
seq_printf(m, "# %s _-----=> irqs-off\n", tgid ? " " : "");
seq_printf(m, "# %s / _----=> need-resched\n", tgid ? " " : "");
seq_printf(m, "# %s| / _---=> hardirq/softirq\n", tgid ? " " : "");
seq_printf(m, "# %s|| / _--=> preempt-depth\n", tgid ? " " : "");
seq_printf(m, "# %s||| / delay\n", tgid ? " " : "");
seq_printf(m, "# TASK-PID CPU#%s|||| TIMESTAMP FUNCTION\n", tgid ? " TGID " : "");
seq_printf(m, "# | | | %s|||| | |\n", tgid ? " | " : "");
}
void
@ -3580,9 +3657,11 @@ void trace_default_header(struct seq_file *m)
} else {
if (!(trace_flags & TRACE_ITER_VERBOSE)) {
if (trace_flags & TRACE_ITER_IRQ_INFO)
print_func_help_header_irq(iter->trace_buffer, m);
print_func_help_header_irq(iter->trace_buffer,
m, trace_flags);
else
print_func_help_header(iter->trace_buffer, m);
print_func_help_header(iter->trace_buffer, m,
trace_flags);
}
}
}
@ -4238,6 +4317,18 @@ int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled)
if (mask == TRACE_ITER_RECORD_CMD)
trace_event_enable_cmd_record(enabled);
if (mask == TRACE_ITER_RECORD_TGID) {
if (!tgid_map)
tgid_map = kzalloc((PID_MAX_DEFAULT + 1) * sizeof(*tgid_map),
GFP_KERNEL);
if (!tgid_map) {
tr->trace_flags &= ~TRACE_ITER_RECORD_TGID;
return -ENOMEM;
}
trace_event_enable_tgid_record(enabled);
}
if (mask == TRACE_ITER_EVENT_FORK)
trace_event_follow_fork(tr, enabled);
@ -4746,11 +4837,11 @@ static const struct file_operations tracing_saved_cmdlines_size_fops = {
.write = tracing_saved_cmdlines_size_write,
};
#ifdef CONFIG_TRACE_ENUM_MAP_FILE
static union trace_enum_map_item *
update_enum_map(union trace_enum_map_item *ptr)
#ifdef CONFIG_TRACE_EVAL_MAP_FILE
static union trace_eval_map_item *
update_eval_map(union trace_eval_map_item *ptr)
{
if (!ptr->map.enum_string) {
if (!ptr->map.eval_string) {
if (ptr->tail.next) {
ptr = ptr->tail.next;
/* Set ptr to the next real item (skip head) */
@ -4761,15 +4852,15 @@ update_enum_map(union trace_enum_map_item *ptr)
return ptr;
}
static void *enum_map_next(struct seq_file *m, void *v, loff_t *pos)
static void *eval_map_next(struct seq_file *m, void *v, loff_t *pos)
{
union trace_enum_map_item *ptr = v;
union trace_eval_map_item *ptr = v;
/*
* Paranoid! If ptr points to end, we don't want to increment past it.
* This really should never happen.
*/
ptr = update_enum_map(ptr);
ptr = update_eval_map(ptr);
if (WARN_ON_ONCE(!ptr))
return NULL;
@ -4777,104 +4868,104 @@ static void *enum_map_next(struct seq_file *m, void *v, loff_t *pos)
(*pos)++;
ptr = update_enum_map(ptr);
ptr = update_eval_map(ptr);
return ptr;
}
static void *enum_map_start(struct seq_file *m, loff_t *pos)
static void *eval_map_start(struct seq_file *m, loff_t *pos)
{
union trace_enum_map_item *v;
union trace_eval_map_item *v;
loff_t l = 0;
mutex_lock(&trace_enum_mutex);
mutex_lock(&trace_eval_mutex);
v = trace_enum_maps;
v = trace_eval_maps;
if (v)
v++;
while (v && l < *pos) {
v = enum_map_next(m, v, &l);
v = eval_map_next(m, v, &l);
}
return v;
}
static void enum_map_stop(struct seq_file *m, void *v)
static void eval_map_stop(struct seq_file *m, void *v)
{
mutex_unlock(&trace_enum_mutex);
mutex_unlock(&trace_eval_mutex);
}
static int enum_map_show(struct seq_file *m, void *v)
static int eval_map_show(struct seq_file *m, void *v)
{
union trace_enum_map_item *ptr = v;
union trace_eval_map_item *ptr = v;
seq_printf(m, "%s %ld (%s)\n",
ptr->map.enum_string, ptr->map.enum_value,
ptr->map.eval_string, ptr->map.eval_value,
ptr->map.system);
return 0;
}
static const struct seq_operations tracing_enum_map_seq_ops = {
.start = enum_map_start,
.next = enum_map_next,
.stop = enum_map_stop,
.show = enum_map_show,
static const struct seq_operations tracing_eval_map_seq_ops = {
.start = eval_map_start,
.next = eval_map_next,
.stop = eval_map_stop,
.show = eval_map_show,
};
static int tracing_enum_map_open(struct inode *inode, struct file *filp)
static int tracing_eval_map_open(struct inode *inode, struct file *filp)
{
if (tracing_disabled)
return -ENODEV;
return seq_open(filp, &tracing_enum_map_seq_ops);
return seq_open(filp, &tracing_eval_map_seq_ops);
}
static const struct file_operations tracing_enum_map_fops = {
.open = tracing_enum_map_open,
static const struct file_operations tracing_eval_map_fops = {
.open = tracing_eval_map_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static inline union trace_enum_map_item *
trace_enum_jmp_to_tail(union trace_enum_map_item *ptr)
static inline union trace_eval_map_item *
trace_eval_jmp_to_tail(union trace_eval_map_item *ptr)
{
/* Return tail of array given the head */
return ptr + ptr->head.length + 1;
}
static void
trace_insert_enum_map_file(struct module *mod, struct trace_enum_map **start,
trace_insert_eval_map_file(struct module *mod, struct trace_eval_map **start,
int len)
{
struct trace_enum_map **stop;
struct trace_enum_map **map;
union trace_enum_map_item *map_array;
union trace_enum_map_item *ptr;
struct trace_eval_map **stop;
struct trace_eval_map **map;
union trace_eval_map_item *map_array;
union trace_eval_map_item *ptr;
stop = start + len;
/*
* The trace_enum_maps contains the map plus a head and tail item,
* The trace_eval_maps contains the map plus a head and tail item,
* where the head holds the module and length of array, and the
* tail holds a pointer to the next list.
*/
map_array = kmalloc(sizeof(*map_array) * (len + 2), GFP_KERNEL);
if (!map_array) {
pr_warn("Unable to allocate trace enum mapping\n");
pr_warn("Unable to allocate trace eval mapping\n");
return;
}
mutex_lock(&trace_enum_mutex);
mutex_lock(&trace_eval_mutex);
if (!trace_enum_maps)
trace_enum_maps = map_array;
if (!trace_eval_maps)
trace_eval_maps = map_array;
else {
ptr = trace_enum_maps;
ptr = trace_eval_maps;
for (;;) {
ptr = trace_enum_jmp_to_tail(ptr);
ptr = trace_eval_jmp_to_tail(ptr);
if (!ptr->tail.next)
break;
ptr = ptr->tail.next;
@ -4892,34 +4983,34 @@ trace_insert_enum_map_file(struct module *mod, struct trace_enum_map **start,
}
memset(map_array, 0, sizeof(*map_array));
mutex_unlock(&trace_enum_mutex);
mutex_unlock(&trace_eval_mutex);
}
static void trace_create_enum_file(struct dentry *d_tracer)
static void trace_create_eval_file(struct dentry *d_tracer)
{
trace_create_file("enum_map", 0444, d_tracer,
NULL, &tracing_enum_map_fops);
trace_create_file("eval_map", 0444, d_tracer,
NULL, &tracing_eval_map_fops);
}
#else /* CONFIG_TRACE_ENUM_MAP_FILE */
static inline void trace_create_enum_file(struct dentry *d_tracer) { }
static inline void trace_insert_enum_map_file(struct module *mod,
struct trace_enum_map **start, int len) { }
#endif /* !CONFIG_TRACE_ENUM_MAP_FILE */
#else /* CONFIG_TRACE_EVAL_MAP_FILE */
static inline void trace_create_eval_file(struct dentry *d_tracer) { }
static inline void trace_insert_eval_map_file(struct module *mod,
struct trace_eval_map **start, int len) { }
#endif /* !CONFIG_TRACE_EVAL_MAP_FILE */
static void trace_insert_enum_map(struct module *mod,
struct trace_enum_map **start, int len)
static void trace_insert_eval_map(struct module *mod,
struct trace_eval_map **start, int len)
{
struct trace_enum_map **map;
struct trace_eval_map **map;
if (len <= 0)
return;
map = start;
trace_event_enum_update(map, len);
trace_event_eval_update(map, len);
trace_insert_enum_map_file(mod, start, len);
trace_insert_eval_map_file(mod, start, len);
}
static ssize_t
@ -6739,33 +6830,18 @@ static const struct file_operations tracing_stats_fops = {
#ifdef CONFIG_DYNAMIC_FTRACE
int __weak ftrace_arch_read_dyn_info(char *buf, int size)
{
return 0;
}
static ssize_t
tracing_read_dyn_info(struct file *filp, char __user *ubuf,
size_t cnt, loff_t *ppos)
{
static char ftrace_dyn_info_buffer[1024];
static DEFINE_MUTEX(dyn_info_mutex);
unsigned long *p = filp->private_data;
char *buf = ftrace_dyn_info_buffer;
int size = ARRAY_SIZE(ftrace_dyn_info_buffer);
char buf[64]; /* Not too big for a shallow stack */
int r;
mutex_lock(&dyn_info_mutex);
r = sprintf(buf, "%ld ", *p);
r += ftrace_arch_read_dyn_info(buf+r, (size-1)-r);
r = scnprintf(buf, 63, "%ld", *p);
buf[r++] = '\n';
r = simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
mutex_unlock(&dyn_info_mutex);
return r;
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
}
static const struct file_operations tracing_dyn_info_fops = {
@ -7737,21 +7813,21 @@ struct dentry *tracing_init_dentry(void)
return NULL;
}
extern struct trace_enum_map *__start_ftrace_enum_maps[];
extern struct trace_enum_map *__stop_ftrace_enum_maps[];
extern struct trace_eval_map *__start_ftrace_eval_maps[];
extern struct trace_eval_map *__stop_ftrace_eval_maps[];
static void __init trace_enum_init(void)
static void __init trace_eval_init(void)
{
int len;
len = __stop_ftrace_enum_maps - __start_ftrace_enum_maps;
trace_insert_enum_map(NULL, __start_ftrace_enum_maps, len);
len = __stop_ftrace_eval_maps - __start_ftrace_eval_maps;
trace_insert_eval_map(NULL, __start_ftrace_eval_maps, len);
}
#ifdef CONFIG_MODULES
static void trace_module_add_enums(struct module *mod)
static void trace_module_add_evals(struct module *mod)
{
if (!mod->num_trace_enums)
if (!mod->num_trace_evals)
return;
/*
@ -7761,40 +7837,40 @@ static void trace_module_add_enums(struct module *mod)
if (trace_module_has_bad_taint(mod))
return;
trace_insert_enum_map(mod, mod->trace_enums, mod->num_trace_enums);
trace_insert_eval_map(mod, mod->trace_evals, mod->num_trace_evals);
}
#ifdef CONFIG_TRACE_ENUM_MAP_FILE
static void trace_module_remove_enums(struct module *mod)
#ifdef CONFIG_TRACE_EVAL_MAP_FILE
static void trace_module_remove_evals(struct module *mod)
{
union trace_enum_map_item *map;
union trace_enum_map_item **last = &trace_enum_maps;
union trace_eval_map_item *map;
union trace_eval_map_item **last = &trace_eval_maps;
if (!mod->num_trace_enums)
if (!mod->num_trace_evals)
return;
mutex_lock(&trace_enum_mutex);
mutex_lock(&trace_eval_mutex);
map = trace_enum_maps;
map = trace_eval_maps;
while (map) {
if (map->head.mod == mod)
break;
map = trace_enum_jmp_to_tail(map);
map = trace_eval_jmp_to_tail(map);
last = &map->tail.next;
map = map->tail.next;
}
if (!map)
goto out;
*last = trace_enum_jmp_to_tail(map)->tail.next;
*last = trace_eval_jmp_to_tail(map)->tail.next;
kfree(map);
out:
mutex_unlock(&trace_enum_mutex);
mutex_unlock(&trace_eval_mutex);
}
#else
static inline void trace_module_remove_enums(struct module *mod) { }
#endif /* CONFIG_TRACE_ENUM_MAP_FILE */
static inline void trace_module_remove_evals(struct module *mod) { }
#endif /* CONFIG_TRACE_EVAL_MAP_FILE */
static int trace_module_notify(struct notifier_block *self,
unsigned long val, void *data)
@ -7803,10 +7879,10 @@ static int trace_module_notify(struct notifier_block *self,
switch (val) {
case MODULE_STATE_COMING:
trace_module_add_enums(mod);
trace_module_add_evals(mod);
break;
case MODULE_STATE_GOING:
trace_module_remove_enums(mod);
trace_module_remove_evals(mod);
break;
}
@ -7844,9 +7920,9 @@ static __init int tracer_init_tracefs(void)
trace_create_file("saved_cmdlines_size", 0644, d_tracer,
NULL, &tracing_saved_cmdlines_size_fops);
trace_enum_init();
trace_eval_init();
trace_create_enum_file(d_tracer);
trace_create_eval_file(d_tracer);
#ifdef CONFIG_MODULES
register_module_notifier(&trace_module_nb);

View File

@ -263,7 +263,10 @@ struct trace_array {
struct ftrace_ops *ops;
struct trace_pid_list __rcu *function_pids;
#ifdef CONFIG_DYNAMIC_FTRACE
/* All of these are protected by the ftrace_lock */
struct list_head func_probes;
struct list_head mod_trace;
struct list_head mod_notrace;
#endif
/* function tracing enabled */
int function_enabled;
@ -637,6 +640,9 @@ void set_graph_array(struct trace_array *tr);
void tracing_start_cmdline_record(void);
void tracing_stop_cmdline_record(void);
void tracing_start_tgid_record(void);
void tracing_stop_tgid_record(void);
int register_tracer(struct tracer *type);
int is_tracing_stopped(void);
@ -697,6 +703,7 @@ static inline void __trace_stack(struct trace_array *tr, unsigned long flags,
extern u64 ftrace_now(int cpu);
extern void trace_find_cmdline(int pid, char comm[]);
extern int trace_find_tgid(int pid);
extern void trace_event_follow_fork(struct trace_array *tr, bool enable);
#ifdef CONFIG_DYNAMIC_FTRACE
@ -761,10 +768,24 @@ enum print_line_t print_trace_line(struct trace_iterator *iter);
extern char trace_find_mark(unsigned long long duration);
struct ftrace_hash;
struct ftrace_mod_load {
struct list_head list;
char *func;
char *module;
int enable;
};
enum {
FTRACE_HASH_FL_MOD = (1 << 0),
};
struct ftrace_hash {
unsigned long size_bits;
struct hlist_head *buckets;
unsigned long count;
unsigned long flags;
struct rcu_head rcu;
};
@ -773,7 +794,7 @@ ftrace_lookup_ip(struct ftrace_hash *hash, unsigned long ip);
static __always_inline bool ftrace_hash_empty(struct ftrace_hash *hash)
{
return !hash || !hash->count;
return !hash || !(hash->count || (hash->flags & FTRACE_HASH_FL_MOD));
}
/* Standard output formatting function used for function return traces */
@ -1107,6 +1128,7 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
C(CONTEXT_INFO, "context-info"), /* Print pid/cpu/time */ \
C(LATENCY_FMT, "latency-format"), \
C(RECORD_CMD, "record-cmd"), \
C(RECORD_TGID, "record-tgid"), \
C(OVERWRITE, "overwrite"), \
C(STOP_ON_FREE, "disable_on_free"), \
C(IRQ_INFO, "irq-info"), \
@ -1423,6 +1445,8 @@ struct ftrace_event_field *
trace_find_event_field(struct trace_event_call *call, char *name);
extern void trace_event_enable_cmd_record(bool enable);
extern void trace_event_enable_tgid_record(bool enable);
extern int event_trace_add_tracer(struct dentry *parent, struct trace_array *tr);
extern int event_trace_del_tracer(struct trace_array *tr);
@ -1773,10 +1797,10 @@ static inline const char *get_syscall_name(int syscall)
#ifdef CONFIG_EVENT_TRACING
void trace_event_init(void);
void trace_event_enum_update(struct trace_enum_map **map, int len);
void trace_event_eval_update(struct trace_eval_map **map, int len);
#else
static inline void __init trace_event_init(void) { }
static inline void trace_event_enum_update(struct trace_enum_map **map, int len) { }
static inline void trace_event_eval_update(struct trace_eval_map **map, int len) { }
#endif
extern struct trace_iterator *tracepoint_print_iter;

View File

@ -343,6 +343,28 @@ void trace_event_enable_cmd_record(bool enable)
mutex_unlock(&event_mutex);
}
void trace_event_enable_tgid_record(bool enable)
{
struct trace_event_file *file;
struct trace_array *tr;
mutex_lock(&event_mutex);
do_for_each_event_file(tr, file) {
if (!(file->flags & EVENT_FILE_FL_ENABLED))
continue;
if (enable) {
tracing_start_tgid_record();
set_bit(EVENT_FILE_FL_RECORDED_TGID_BIT, &file->flags);
} else {
tracing_stop_tgid_record();
clear_bit(EVENT_FILE_FL_RECORDED_TGID_BIT,
&file->flags);
}
} while_for_each_event_file();
mutex_unlock(&event_mutex);
}
static int __ftrace_event_enable_disable(struct trace_event_file *file,
int enable, int soft_disable)
{
@ -381,6 +403,12 @@ static int __ftrace_event_enable_disable(struct trace_event_file *file,
tracing_stop_cmdline_record();
clear_bit(EVENT_FILE_FL_RECORDED_CMD_BIT, &file->flags);
}
if (file->flags & EVENT_FILE_FL_RECORDED_TGID) {
tracing_stop_tgid_record();
clear_bit(EVENT_FILE_FL_RECORDED_CMD_BIT, &file->flags);
}
call->class->reg(call, TRACE_REG_UNREGISTER, file);
}
/* If in SOFT_MODE, just set the SOFT_DISABLE_BIT, else clear it */
@ -407,18 +435,30 @@ static int __ftrace_event_enable_disable(struct trace_event_file *file,
}
if (!(file->flags & EVENT_FILE_FL_ENABLED)) {
bool cmd = false, tgid = false;
/* Keep the event disabled, when going to SOFT_MODE. */
if (soft_disable)
set_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags);
if (tr->trace_flags & TRACE_ITER_RECORD_CMD) {
cmd = true;
tracing_start_cmdline_record();
set_bit(EVENT_FILE_FL_RECORDED_CMD_BIT, &file->flags);
}
if (tr->trace_flags & TRACE_ITER_RECORD_TGID) {
tgid = true;
tracing_start_tgid_record();
set_bit(EVENT_FILE_FL_RECORDED_TGID_BIT, &file->flags);
}
ret = call->class->reg(call, TRACE_REG_REGISTER, file);
if (ret) {
tracing_stop_cmdline_record();
if (cmd)
tracing_stop_cmdline_record();
if (tgid)
tracing_stop_tgid_record();
pr_info("event trace: Could not enable event "
"%s\n", trace_event_name(call));
break;
@ -2067,18 +2107,18 @@ __register_event(struct trace_event_call *call, struct module *mod)
return 0;
}
static char *enum_replace(char *ptr, struct trace_enum_map *map, int len)
static char *eval_replace(char *ptr, struct trace_eval_map *map, int len)
{
int rlen;
int elen;
/* Find the length of the enum value as a string */
elen = snprintf(ptr, 0, "%ld", map->enum_value);
/* Find the length of the eval value as a string */
elen = snprintf(ptr, 0, "%ld", map->eval_value);
/* Make sure there's enough room to replace the string with the value */
if (len < elen)
return NULL;
snprintf(ptr, elen + 1, "%ld", map->enum_value);
snprintf(ptr, elen + 1, "%ld", map->eval_value);
/* Get the rest of the string of ptr */
rlen = strlen(ptr + len);
@ -2090,11 +2130,11 @@ static char *enum_replace(char *ptr, struct trace_enum_map *map, int len)
}
static void update_event_printk(struct trace_event_call *call,
struct trace_enum_map *map)
struct trace_eval_map *map)
{
char *ptr;
int quote = 0;
int len = strlen(map->enum_string);
int len = strlen(map->eval_string);
for (ptr = call->print_fmt; *ptr; ptr++) {
if (*ptr == '\\') {
@ -2125,16 +2165,16 @@ static void update_event_printk(struct trace_event_call *call,
continue;
}
if (isalpha(*ptr) || *ptr == '_') {
if (strncmp(map->enum_string, ptr, len) == 0 &&
if (strncmp(map->eval_string, ptr, len) == 0 &&
!isalnum(ptr[len]) && ptr[len] != '_') {
ptr = enum_replace(ptr, map, len);
/* Hmm, enum string smaller than value */
ptr = eval_replace(ptr, map, len);
/* enum/sizeof string smaller than value */
if (WARN_ON_ONCE(!ptr))
return;
/*
* No need to decrement here, as enum_replace()
* No need to decrement here, as eval_replace()
* returns the pointer to the character passed
* the enum, and two enums can not be placed
* the eval, and two evals can not be placed
* back to back without something in between.
* We can skip that something in between.
*/
@ -2165,7 +2205,7 @@ static void update_event_printk(struct trace_event_call *call,
}
}
void trace_event_enum_update(struct trace_enum_map **map, int len)
void trace_event_eval_update(struct trace_eval_map **map, int len)
{
struct trace_event_call *call, *p;
const char *last_system = NULL;

View File

@ -340,31 +340,41 @@ static inline const char *kretprobed(const char *name)
static void
seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address)
{
#ifdef CONFIG_KALLSYMS
char str[KSYM_SYMBOL_LEN];
#ifdef CONFIG_KALLSYMS
const char *name;
kallsyms_lookup(address, NULL, NULL, NULL, str);
name = kretprobed(str);
trace_seq_printf(s, fmt, name);
if (name && strlen(name)) {
trace_seq_printf(s, fmt, name);
return;
}
#endif
snprintf(str, KSYM_SYMBOL_LEN, "0x%08lx", address);
trace_seq_printf(s, fmt, str);
}
static void
seq_print_sym_offset(struct trace_seq *s, const char *fmt,
unsigned long address)
{
#ifdef CONFIG_KALLSYMS
char str[KSYM_SYMBOL_LEN];
#ifdef CONFIG_KALLSYMS
const char *name;
sprint_symbol(str, address);
name = kretprobed(str);
trace_seq_printf(s, fmt, name);
if (name && strlen(name)) {
trace_seq_printf(s, fmt, name);
return;
}
#endif
snprintf(str, KSYM_SYMBOL_LEN, "0x%08lx", address);
trace_seq_printf(s, fmt, str);
}
#ifndef CONFIG_64BIT
@ -587,6 +597,15 @@ int trace_print_context(struct trace_iterator *iter)
trace_seq_printf(s, "%16s-%-5d [%03d] ",
comm, entry->pid, iter->cpu);
if (tr->trace_flags & TRACE_ITER_RECORD_TGID) {
unsigned int tgid = trace_find_tgid(entry->pid);
if (!tgid)
trace_seq_printf(s, "(-----) ");
else
trace_seq_printf(s, "(%5d) ", tgid);
}
if (tr->trace_flags & TRACE_ITER_IRQ_INFO)
trace_print_lat_fmt(s, entry);

View File

@ -12,27 +12,38 @@
#include "trace.h"
static int sched_ref;
#define RECORD_CMDLINE 1
#define RECORD_TGID 2
static int sched_cmdline_ref;
static int sched_tgid_ref;
static DEFINE_MUTEX(sched_register_mutex);
static void
probe_sched_switch(void *ignore, bool preempt,
struct task_struct *prev, struct task_struct *next)
{
if (unlikely(!sched_ref))
return;
int flags;
tracing_record_cmdline(prev);
tracing_record_cmdline(next);
flags = (RECORD_TGID * !!sched_tgid_ref) +
(RECORD_CMDLINE * !!sched_cmdline_ref);
if (!flags)
return;
tracing_record_taskinfo_sched_switch(prev, next, flags);
}
static void
probe_sched_wakeup(void *ignore, struct task_struct *wakee)
{
if (unlikely(!sched_ref))
return;
int flags;
tracing_record_cmdline(current);
flags = (RECORD_TGID * !!sched_tgid_ref) +
(RECORD_CMDLINE * !!sched_cmdline_ref);
if (!flags)
return;
tracing_record_taskinfo(current, flags);
}
static int tracing_sched_register(void)
@ -75,28 +86,61 @@ static void tracing_sched_unregister(void)
unregister_trace_sched_wakeup(probe_sched_wakeup, NULL);
}
static void tracing_start_sched_switch(void)
static void tracing_start_sched_switch(int ops)
{
bool sched_register = (!sched_cmdline_ref && !sched_tgid_ref);
mutex_lock(&sched_register_mutex);
if (!(sched_ref++))
switch (ops) {
case RECORD_CMDLINE:
sched_cmdline_ref++;
break;
case RECORD_TGID:
sched_tgid_ref++;
break;
}
if (sched_register && (sched_cmdline_ref || sched_tgid_ref))
tracing_sched_register();
mutex_unlock(&sched_register_mutex);
}
static void tracing_stop_sched_switch(void)
static void tracing_stop_sched_switch(int ops)
{
mutex_lock(&sched_register_mutex);
if (!(--sched_ref))
switch (ops) {
case RECORD_CMDLINE:
sched_cmdline_ref--;
break;
case RECORD_TGID:
sched_tgid_ref--;
break;
}
if (!sched_cmdline_ref && !sched_tgid_ref)
tracing_sched_unregister();
mutex_unlock(&sched_register_mutex);
}
void tracing_start_cmdline_record(void)
{
tracing_start_sched_switch();
tracing_start_sched_switch(RECORD_CMDLINE);
}
void tracing_stop_cmdline_record(void)
{
tracing_stop_sched_switch();
tracing_stop_sched_switch(RECORD_CMDLINE);
}
void tracing_start_tgid_record(void)
{
tracing_start_sched_switch(RECORD_TGID);
}
void tracing_stop_tgid_record(void)
{
tracing_stop_sched_switch(RECORD_TGID);
}