mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
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:
commit
2074006dac
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user