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)
|
TP_printk("%s: 0x%08x", __entry->name, __entry->value)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_DEFINE_SIZEOF(__u64);
|
||||||
|
|
||||||
TRACE_EVENT(kvm_arm_set_regset,
|
TRACE_EVENT(kvm_arm_set_regset,
|
||||||
TP_PROTO(const char *type, int len, __u64 *control, __u64 *value),
|
TP_PROTO(const char *type, int len, __u64 *control, __u64 *value),
|
||||||
TP_ARGS(type, len, control, 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_ip; /* holds the IP to write to */
|
||||||
static void *mod_code_newcode; /* holds the text to write to the IP */
|
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)
|
static void clear_mod_flag(void)
|
||||||
{
|
{
|
||||||
int old = atomic_read(&nmi_running);
|
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) {
|
if (atomic_inc_return(&nmi_running) & MOD_CODE_WRITE_FLAG) {
|
||||||
smp_rmb();
|
smp_rmb();
|
||||||
ftrace_mod_code();
|
ftrace_mod_code();
|
||||||
atomic_inc(&nmi_update_count);
|
|
||||||
}
|
}
|
||||||
/* Must have previous changes seen before executions */
|
/* Must have previous changes seen before executions */
|
||||||
smp_mb();
|
smp_mb();
|
||||||
|
@ -165,8 +151,6 @@ static void wait_for_nmi_and_set_mod_flag(void)
|
||||||
do {
|
do {
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
} while (atomic_cmpxchg(&nmi_running, 0, MOD_CODE_WRITE_FLAG));
|
} while (atomic_cmpxchg(&nmi_running, 0, MOD_CODE_WRITE_FLAG));
|
||||||
|
|
||||||
nmi_wait_count++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wait_for_nmi(void)
|
static void wait_for_nmi(void)
|
||||||
|
@ -177,8 +161,6 @@ static void wait_for_nmi(void)
|
||||||
do {
|
do {
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
} while (atomic_read(&nmi_running));
|
} while (atomic_read(&nmi_running));
|
||||||
|
|
||||||
nmi_wait_count++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -125,9 +125,9 @@
|
||||||
VMLINUX_SYMBOL(__start_ftrace_events) = .; \
|
VMLINUX_SYMBOL(__start_ftrace_events) = .; \
|
||||||
KEEP(*(_ftrace_events)) \
|
KEEP(*(_ftrace_events)) \
|
||||||
VMLINUX_SYMBOL(__stop_ftrace_events) = .; \
|
VMLINUX_SYMBOL(__stop_ftrace_events) = .; \
|
||||||
VMLINUX_SYMBOL(__start_ftrace_enum_maps) = .; \
|
VMLINUX_SYMBOL(__start_ftrace_eval_maps) = .; \
|
||||||
KEEP(*(_ftrace_enum_map)) \
|
KEEP(*(_ftrace_eval_map)) \
|
||||||
VMLINUX_SYMBOL(__stop_ftrace_enum_maps) = .;
|
VMLINUX_SYMBOL(__stop_ftrace_eval_maps) = .;
|
||||||
#else
|
#else
|
||||||
#define FTRACE_EVENTS()
|
#define FTRACE_EVENTS()
|
||||||
#endif
|
#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
|
* for any of the functions that this ops will be registered for, then
|
||||||
* this ops will fail to register or set_filter_ip.
|
* this ops will fail to register or set_filter_ip.
|
||||||
* PID - Is affected by set_ftrace_pid (allows filtering on those pids)
|
* 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 {
|
enum {
|
||||||
FTRACE_OPS_FL_ENABLED = 1 << 0,
|
FTRACE_OPS_FL_ENABLED = 1 << 0,
|
||||||
|
@ -137,6 +139,7 @@ enum {
|
||||||
FTRACE_OPS_FL_IPMODIFY = 1 << 13,
|
FTRACE_OPS_FL_IPMODIFY = 1 << 13,
|
||||||
FTRACE_OPS_FL_PID = 1 << 14,
|
FTRACE_OPS_FL_PID = 1 << 14,
|
||||||
FTRACE_OPS_FL_RCU = 1 << 15,
|
FTRACE_OPS_FL_RCU = 1 << 15,
|
||||||
|
FTRACE_OPS_FL_TRACE_ARRAY = 1 << 16,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||||
|
@ -445,7 +448,8 @@ enum {
|
||||||
FTRACE_ITER_PRINTALL = (1 << 2),
|
FTRACE_ITER_PRINTALL = (1 << 2),
|
||||||
FTRACE_ITER_DO_PROBES = (1 << 3),
|
FTRACE_ITER_DO_PROBES = (1 << 3),
|
||||||
FTRACE_ITER_PROBE = (1 << 4),
|
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);
|
void arch_ftrace_update_code(int command);
|
||||||
|
|
|
@ -442,8 +442,8 @@ struct module {
|
||||||
#ifdef CONFIG_EVENT_TRACING
|
#ifdef CONFIG_EVENT_TRACING
|
||||||
struct trace_event_call **trace_events;
|
struct trace_event_call **trace_events;
|
||||||
unsigned int num_trace_events;
|
unsigned int num_trace_events;
|
||||||
struct trace_enum_map **trace_enums;
|
struct trace_eval_map **trace_evals;
|
||||||
unsigned int num_trace_enums;
|
unsigned int num_trace_evals;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_FTRACE_MCOUNT_RECORD
|
#ifdef CONFIG_FTRACE_MCOUNT_RECORD
|
||||||
unsigned int num_ftrace_callsites;
|
unsigned int num_ftrace_callsites;
|
||||||
|
|
|
@ -151,7 +151,15 @@ trace_event_buffer_lock_reserve(struct ring_buffer **current_buffer,
|
||||||
int type, unsigned long len,
|
int type, unsigned long len,
|
||||||
unsigned long flags, int pc);
|
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, ...);
|
int trace_output_call(struct trace_iterator *iter, char *name, char *fmt, ...);
|
||||||
|
|
||||||
|
@ -290,6 +298,7 @@ struct trace_subsystem_dir;
|
||||||
enum {
|
enum {
|
||||||
EVENT_FILE_FL_ENABLED_BIT,
|
EVENT_FILE_FL_ENABLED_BIT,
|
||||||
EVENT_FILE_FL_RECORDED_CMD_BIT,
|
EVENT_FILE_FL_RECORDED_CMD_BIT,
|
||||||
|
EVENT_FILE_FL_RECORDED_TGID_BIT,
|
||||||
EVENT_FILE_FL_FILTERED_BIT,
|
EVENT_FILE_FL_FILTERED_BIT,
|
||||||
EVENT_FILE_FL_NO_SET_FILTER_BIT,
|
EVENT_FILE_FL_NO_SET_FILTER_BIT,
|
||||||
EVENT_FILE_FL_SOFT_MODE_BIT,
|
EVENT_FILE_FL_SOFT_MODE_BIT,
|
||||||
|
@ -303,6 +312,7 @@ enum {
|
||||||
* Event file flags:
|
* Event file flags:
|
||||||
* ENABLED - The event is enabled
|
* ENABLED - The event is enabled
|
||||||
* RECORDED_CMD - The comms should be recorded at sched_switch
|
* 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
|
* FILTERED - The event has a filter attached
|
||||||
* NO_SET_FILTER - Set when filter has error and is to be ignored
|
* NO_SET_FILTER - Set when filter has error and is to be ignored
|
||||||
* SOFT_MODE - The event is enabled/disabled by SOFT_DISABLED
|
* SOFT_MODE - The event is enabled/disabled by SOFT_DISABLED
|
||||||
|
@ -315,6 +325,7 @@ enum {
|
||||||
enum {
|
enum {
|
||||||
EVENT_FILE_FL_ENABLED = (1 << EVENT_FILE_FL_ENABLED_BIT),
|
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_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_FILTERED = (1 << EVENT_FILE_FL_FILTERED_BIT),
|
||||||
EVENT_FILE_FL_NO_SET_FILTER = (1 << EVENT_FILE_FL_NO_SET_FILTER_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),
|
EVENT_FILE_FL_SOFT_MODE = (1 << EVENT_FILE_FL_SOFT_MODE_BIT),
|
||||||
|
|
|
@ -25,10 +25,10 @@ struct module;
|
||||||
struct tracepoint;
|
struct tracepoint;
|
||||||
struct notifier_block;
|
struct notifier_block;
|
||||||
|
|
||||||
struct trace_enum_map {
|
struct trace_eval_map {
|
||||||
const char *system;
|
const char *system;
|
||||||
const char *enum_string;
|
const char *eval_string;
|
||||||
unsigned long enum_value;
|
unsigned long eval_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TRACEPOINT_DEFAULT_PRIO 10
|
#define TRACEPOINT_DEFAULT_PRIO 10
|
||||||
|
@ -88,6 +88,7 @@ extern void syscall_unregfunc(void);
|
||||||
#define PARAMS(args...) args
|
#define PARAMS(args...) args
|
||||||
|
|
||||||
#define TRACE_DEFINE_ENUM(x)
|
#define TRACE_DEFINE_ENUM(x)
|
||||||
|
#define TRACE_DEFINE_SIZEOF(x)
|
||||||
|
|
||||||
#endif /* _LINUX_TRACEPOINT_H */
|
#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_batch);
|
||||||
DEFINE_XEN_MC_BATCH(xen_mc_issue);
|
DEFINE_XEN_MC_BATCH(xen_mc_issue);
|
||||||
|
|
||||||
|
TRACE_DEFINE_SIZEOF(ulong);
|
||||||
|
|
||||||
TRACE_EVENT(xen_mc_entry,
|
TRACE_EVENT(xen_mc_entry,
|
||||||
TP_PROTO(struct multicall_entry *mc, unsigned nargs),
|
TP_PROTO(struct multicall_entry *mc, unsigned nargs),
|
||||||
TP_ARGS(mc, nargs),
|
TP_ARGS(mc, nargs),
|
||||||
|
@ -40,8 +42,8 @@ TRACE_EVENT(xen_mc_entry,
|
||||||
),
|
),
|
||||||
TP_fast_assign(__entry->op = mc->op;
|
TP_fast_assign(__entry->op = mc->op;
|
||||||
__entry->nargs = nargs;
|
__entry->nargs = nargs;
|
||||||
memcpy(__entry->args, mc->args, sizeof(unsigned long) * nargs);
|
memcpy(__entry->args, mc->args, sizeof(ulong) * nargs);
|
||||||
memset(__entry->args + nargs, 0, sizeof(unsigned long) * (6 - nargs));
|
memset(__entry->args + nargs, 0, sizeof(ulong) * (6 - nargs));
|
||||||
),
|
),
|
||||||
TP_printk("op %u%s args [%lx, %lx, %lx, %lx, %lx, %lx]",
|
TP_printk("op %u%s args [%lx, %lx, %lx, %lx, %lx, %lx]",
|
||||||
__entry->op, xen_hypercall_name(__entry->op),
|
__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" : "???")
|
__entry->res == XEN_MC_XE_NO_SPACE ? "NO_SPACE" : "???")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_DEFINE_SIZEOF(pteval_t);
|
||||||
/* mmu */
|
/* mmu */
|
||||||
DECLARE_EVENT_CLASS(xen_mmu__set_pte,
|
DECLARE_EVENT_CLASS(xen_mmu__set_pte,
|
||||||
TP_PROTO(pte_t *ptep, pte_t pteval),
|
TP_PROTO(pte_t *ptep, pte_t pteval),
|
||||||
|
@ -199,6 +202,8 @@ TRACE_EVENT(xen_mmu_pte_clear,
|
||||||
__entry->mm, __entry->addr, __entry->ptep)
|
__entry->mm, __entry->addr, __entry->ptep)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_DEFINE_SIZEOF(pmdval_t);
|
||||||
|
|
||||||
TRACE_EVENT(xen_mmu_set_pmd,
|
TRACE_EVENT(xen_mmu_set_pmd,
|
||||||
TP_PROTO(pmd_t *pmdp, pmd_t pmdval),
|
TP_PROTO(pmd_t *pmdp, pmd_t pmdval),
|
||||||
TP_ARGS(pmdp, pmdval),
|
TP_ARGS(pmdp, pmdval),
|
||||||
|
@ -226,6 +231,8 @@ TRACE_EVENT(xen_mmu_pmd_clear,
|
||||||
|
|
||||||
#if CONFIG_PGTABLE_LEVELS >= 4
|
#if CONFIG_PGTABLE_LEVELS >= 4
|
||||||
|
|
||||||
|
TRACE_DEFINE_SIZEOF(pudval_t);
|
||||||
|
|
||||||
TRACE_EVENT(xen_mmu_set_pud,
|
TRACE_EVENT(xen_mmu_set_pud,
|
||||||
TP_PROTO(pud_t *pudp, pud_t pudval),
|
TP_PROTO(pud_t *pudp, pud_t pudval),
|
||||||
TP_ARGS(pudp, 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)
|
(int)sizeof(pudval_t) * 2, (unsigned long long)__entry->pudval)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_DEFINE_SIZEOF(p4dval_t);
|
||||||
|
|
||||||
TRACE_EVENT(xen_mmu_set_p4d,
|
TRACE_EVENT(xen_mmu_set_p4d,
|
||||||
TP_PROTO(p4d_t *p4dp, p4d_t *user_p4dp, p4d_t p4dval),
|
TP_PROTO(p4d_t *p4dp, p4d_t *user_p4dp, p4d_t p4dval),
|
||||||
TP_ARGS(p4dp, user_p4dp, p4dval),
|
TP_ARGS(p4dp, user_p4dp, p4dval),
|
||||||
|
|
|
@ -35,15 +35,28 @@ TRACE_MAKE_SYSTEM_STR();
|
||||||
|
|
||||||
#undef TRACE_DEFINE_ENUM
|
#undef TRACE_DEFINE_ENUM
|
||||||
#define TRACE_DEFINE_ENUM(a) \
|
#define TRACE_DEFINE_ENUM(a) \
|
||||||
static struct trace_enum_map __used __initdata \
|
static struct trace_eval_map __used __initdata \
|
||||||
__##TRACE_SYSTEM##_##a = \
|
__##TRACE_SYSTEM##_##a = \
|
||||||
{ \
|
{ \
|
||||||
.system = TRACE_SYSTEM_STRING, \
|
.system = TRACE_SYSTEM_STRING, \
|
||||||
.enum_string = #a, \
|
.eval_string = #a, \
|
||||||
.enum_value = a \
|
.eval_value = a \
|
||||||
}; \
|
}; \
|
||||||
static struct trace_enum_map __used \
|
static struct trace_eval_map __used \
|
||||||
__attribute__((section("_ftrace_enum_map"))) \
|
__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
|
*TRACE_SYSTEM##_##a = &__##TRACE_SYSTEM##_##a
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -158,6 +171,9 @@ TRACE_MAKE_SYSTEM_STR();
|
||||||
#undef TRACE_DEFINE_ENUM
|
#undef TRACE_DEFINE_ENUM
|
||||||
#define TRACE_DEFINE_ENUM(a)
|
#define TRACE_DEFINE_ENUM(a)
|
||||||
|
|
||||||
|
#undef TRACE_DEFINE_SIZEOF
|
||||||
|
#define TRACE_DEFINE_SIZEOF(a)
|
||||||
|
|
||||||
#undef __field
|
#undef __field
|
||||||
#define __field(type, item)
|
#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",
|
mod->trace_events = section_objs(info, "_ftrace_events",
|
||||||
sizeof(*mod->trace_events),
|
sizeof(*mod->trace_events),
|
||||||
&mod->num_trace_events);
|
&mod->num_trace_events);
|
||||||
mod->trace_enums = section_objs(info, "_ftrace_enum_map",
|
mod->trace_evals = section_objs(info, "_ftrace_eval_map",
|
||||||
sizeof(*mod->trace_enums),
|
sizeof(*mod->trace_evals),
|
||||||
&mod->num_trace_enums);
|
&mod->num_trace_evals);
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_TRACING
|
#ifdef CONFIG_TRACING
|
||||||
mod->trace_bprintk_fmt_start = section_objs(info, "__trace_printk_fmt",
|
mod->trace_bprintk_fmt_start = section_objs(info, "__trace_printk_fmt",
|
||||||
|
|
|
@ -667,30 +667,30 @@ config RING_BUFFER_STARTUP_TEST
|
||||||
|
|
||||||
If unsure, say N
|
If unsure, say N
|
||||||
|
|
||||||
config TRACE_ENUM_MAP_FILE
|
config TRACE_EVAL_MAP_FILE
|
||||||
bool "Show enum mappings for trace events"
|
bool "Show eval mappings for trace events"
|
||||||
depends on TRACING
|
depends on TRACING
|
||||||
help
|
help
|
||||||
The "print fmt" of the trace events will show the enum names instead
|
The "print fmt" of the trace events will show the enum/sizeof names
|
||||||
of their values. This can cause problems for user space tools that
|
instead of their values. This can cause problems for user space tools
|
||||||
use this string to parse the raw data as user space does not know
|
that use this string to parse the raw data as user space does not know
|
||||||
how to convert the string to its value.
|
how to convert the string to its value.
|
||||||
|
|
||||||
To fix this, there's a special macro in the kernel that can be used
|
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
|
to convert an enum/sizeof into its value. If this macro is used, then
|
||||||
print fmt strings will have the enums converted to their values.
|
the print fmt strings will be converted to their values.
|
||||||
|
|
||||||
If something does not get converted properly, this option can be
|
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
|
This option is for debugging the conversions. A file is created
|
||||||
in the tracing directory called "enum_map" that will show the enum
|
in the tracing directory called "eval_map" that will show the
|
||||||
names matched with their values and what trace event system they
|
names matched with their values and what trace event system they
|
||||||
belong too.
|
belong too.
|
||||||
|
|
||||||
Normally, the mapping of the strings to values will be freed after
|
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
|
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.
|
increase the memory footprint of the running kernel.
|
||||||
|
|
||||||
If unsure, say N
|
If unsure, say N
|
||||||
|
|
|
@ -1293,6 +1293,28 @@ static void ftrace_hash_clear(struct ftrace_hash *hash)
|
||||||
FTRACE_WARN_ON(hash->count);
|
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)
|
static void free_ftrace_hash(struct ftrace_hash *hash)
|
||||||
{
|
{
|
||||||
if (!hash || hash == EMPTY_HASH)
|
if (!hash || hash == EMPTY_HASH)
|
||||||
|
@ -1346,6 +1368,35 @@ static struct ftrace_hash *alloc_ftrace_hash(int size_bits)
|
||||||
return hash;
|
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 *
|
static struct ftrace_hash *
|
||||||
alloc_and_copy_ftrace_hash(int size_bits, struct ftrace_hash *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)
|
if (!new_hash)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
if (hash)
|
||||||
|
new_hash->flags = hash->flags;
|
||||||
|
|
||||||
/* Empty hash? */
|
/* Empty hash? */
|
||||||
if (ftrace_hash_empty(hash))
|
if (ftrace_hash_empty(hash))
|
||||||
return new_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 the new source is empty, just return the empty_hash.
|
||||||
*/
|
*/
|
||||||
if (!src->count)
|
if (ftrace_hash_empty(src))
|
||||||
return EMPTY_HASH;
|
return EMPTY_HASH;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1420,6 +1474,8 @@ __ftrace_hash_move(struct ftrace_hash *src)
|
||||||
if (!new_hash)
|
if (!new_hash)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
new_hash->flags = src->flags;
|
||||||
|
|
||||||
size = 1 << src->size_bits;
|
size = 1 << src->size_bits;
|
||||||
for (i = 0; i < size; i++) {
|
for (i = 0; i < size; i++) {
|
||||||
hhd = &src->buckets[i];
|
hhd = &src->buckets[i];
|
||||||
|
@ -1650,7 +1706,7 @@ static bool __ftrace_hash_rec_update(struct ftrace_ops *ops,
|
||||||
struct dyn_ftrace *rec;
|
struct dyn_ftrace *rec;
|
||||||
bool update = false;
|
bool update = false;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int all = 0;
|
int all = false;
|
||||||
|
|
||||||
/* Only update if the ops has been registered */
|
/* Only update if the ops has been registered */
|
||||||
if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
|
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;
|
hash = ops->func_hash->filter_hash;
|
||||||
other_hash = ops->func_hash->notrace_hash;
|
other_hash = ops->func_hash->notrace_hash;
|
||||||
if (ftrace_hash_empty(hash))
|
if (ftrace_hash_empty(hash))
|
||||||
all = 1;
|
all = true;
|
||||||
} else {
|
} else {
|
||||||
inc = !inc;
|
inc = !inc;
|
||||||
hash = ops->func_hash->notrace_hash;
|
hash = ops->func_hash->notrace_hash;
|
||||||
|
@ -3061,6 +3117,7 @@ ftrace_allocate_pages(unsigned long num_to_init)
|
||||||
struct ftrace_iterator {
|
struct ftrace_iterator {
|
||||||
loff_t pos;
|
loff_t pos;
|
||||||
loff_t func_pos;
|
loff_t func_pos;
|
||||||
|
loff_t mod_pos;
|
||||||
struct ftrace_page *pg;
|
struct ftrace_page *pg;
|
||||||
struct dyn_ftrace *func;
|
struct dyn_ftrace *func;
|
||||||
struct ftrace_func_probe *probe;
|
struct ftrace_func_probe *probe;
|
||||||
|
@ -3068,6 +3125,8 @@ struct ftrace_iterator {
|
||||||
struct trace_parser parser;
|
struct trace_parser parser;
|
||||||
struct ftrace_hash *hash;
|
struct ftrace_hash *hash;
|
||||||
struct ftrace_ops *ops;
|
struct ftrace_ops *ops;
|
||||||
|
struct trace_array *tr;
|
||||||
|
struct list_head *mod_list;
|
||||||
int pidx;
|
int pidx;
|
||||||
int idx;
|
int idx;
|
||||||
unsigned flags;
|
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))
|
if (!(iter->flags & FTRACE_ITER_DO_PROBES))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (iter->func_pos > *pos)
|
if (iter->mod_pos > *pos)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
iter->probe = NULL;
|
iter->probe = NULL;
|
||||||
iter->probe_entry = NULL;
|
iter->probe_entry = NULL;
|
||||||
iter->pidx = 0;
|
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);
|
p = t_probe_next(m, &l);
|
||||||
if (!p)
|
if (!p)
|
||||||
break;
|
break;
|
||||||
|
@ -3196,6 +3255,82 @@ t_probe_show(struct seq_file *m, struct ftrace_iterator *iter)
|
||||||
return 0;
|
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 *
|
static void *
|
||||||
t_func_next(struct seq_file *m, loff_t *pos)
|
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)
|
t_next(struct seq_file *m, void *v, loff_t *pos)
|
||||||
{
|
{
|
||||||
struct ftrace_iterator *iter = m->private;
|
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;
|
void *ret;
|
||||||
|
|
||||||
if (unlikely(ftrace_disabled))
|
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)
|
if (iter->flags & FTRACE_ITER_PROBE)
|
||||||
return t_probe_next(m, pos);
|
return t_probe_next(m, pos);
|
||||||
|
|
||||||
|
if (iter->flags & FTRACE_ITER_MOD)
|
||||||
|
return t_mod_next(m, pos);
|
||||||
|
|
||||||
if (iter->flags & FTRACE_ITER_PRINTALL) {
|
if (iter->flags & FTRACE_ITER_PRINTALL) {
|
||||||
/* next must increment pos, and t_probe_start does not */
|
/* next must increment pos, and t_probe_start does not */
|
||||||
(*pos)++;
|
(*pos)++;
|
||||||
return t_probe_start(m, &l);
|
return t_mod_start(m, &l);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = t_func_next(m, pos);
|
ret = t_func_next(m, pos);
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return t_probe_start(m, &l);
|
return t_mod_start(m, &l);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -3264,7 +3402,7 @@ static void reset_iter_read(struct ftrace_iterator *iter)
|
||||||
{
|
{
|
||||||
iter->pos = 0;
|
iter->pos = 0;
|
||||||
iter->func_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)
|
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)) {
|
ftrace_hash_empty(iter->hash)) {
|
||||||
iter->func_pos = 1; /* Account for the message */
|
iter->func_pos = 1; /* Account for the message */
|
||||||
if (*pos > 0)
|
if (*pos > 0)
|
||||||
return t_probe_start(m, pos);
|
return t_mod_start(m, pos);
|
||||||
iter->flags |= FTRACE_ITER_PRINTALL;
|
iter->flags |= FTRACE_ITER_PRINTALL;
|
||||||
/* reset in case of seek/pread */
|
/* reset in case of seek/pread */
|
||||||
iter->flags &= ~FTRACE_ITER_PROBE;
|
iter->flags &= ~FTRACE_ITER_PROBE;
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter->flags & FTRACE_ITER_PROBE)
|
if (iter->flags & FTRACE_ITER_MOD)
|
||||||
return t_probe_start(m, pos);
|
return t_mod_start(m, pos);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unfortunately, we need to restart at ftrace_pages_start
|
* 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)
|
if (!p)
|
||||||
return t_probe_start(m, pos);
|
return t_mod_start(m, pos);
|
||||||
|
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
@ -3351,6 +3489,9 @@ static int t_show(struct seq_file *m, void *v)
|
||||||
if (iter->flags & FTRACE_ITER_PROBE)
|
if (iter->flags & FTRACE_ITER_PROBE)
|
||||||
return t_probe_show(m, iter);
|
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_PRINTALL) {
|
||||||
if (iter->flags & FTRACE_ITER_NOTRACE)
|
if (iter->flags & FTRACE_ITER_NOTRACE)
|
||||||
seq_puts(m, "#### no functions disabled ####\n");
|
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_iterator *iter;
|
||||||
struct ftrace_hash *hash;
|
struct ftrace_hash *hash;
|
||||||
|
struct list_head *mod_head;
|
||||||
|
struct trace_array *tr = ops->private;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
ftrace_ops_init(ops);
|
ftrace_ops_init(ops);
|
||||||
|
@ -3475,21 +3618,29 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag,
|
||||||
|
|
||||||
iter->ops = ops;
|
iter->ops = ops;
|
||||||
iter->flags = flag;
|
iter->flags = flag;
|
||||||
|
iter->tr = tr;
|
||||||
|
|
||||||
mutex_lock(&ops->func_hash->regex_lock);
|
mutex_lock(&ops->func_hash->regex_lock);
|
||||||
|
|
||||||
if (flag & FTRACE_ITER_NOTRACE)
|
if (flag & FTRACE_ITER_NOTRACE) {
|
||||||
hash = ops->func_hash->notrace_hash;
|
hash = ops->func_hash->notrace_hash;
|
||||||
else
|
mod_head = tr ? &tr->mod_notrace : NULL;
|
||||||
|
} else {
|
||||||
hash = ops->func_hash->filter_hash;
|
hash = ops->func_hash->filter_hash;
|
||||||
|
mod_head = tr ? &tr->mod_trace : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter->mod_list = mod_head;
|
||||||
|
|
||||||
if (file->f_mode & FMODE_WRITE) {
|
if (file->f_mode & FMODE_WRITE) {
|
||||||
const int size_bits = FTRACE_HASH_DEFAULT_BITS;
|
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);
|
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);
|
iter->hash = alloc_and_copy_ftrace_hash(size_bits, hash);
|
||||||
|
}
|
||||||
|
|
||||||
if (!iter->hash) {
|
if (!iter->hash) {
|
||||||
trace_parser_put(&iter->parser);
|
trace_parser_put(&iter->parser);
|
||||||
|
@ -3761,6 +3912,163 @@ static int ftrace_hash_move_and_update_ops(struct ftrace_ops *ops,
|
||||||
return ret;
|
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
|
* We register the module command as a template to show others how
|
||||||
* to register the a command as well.
|
* 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
|
static int
|
||||||
ftrace_mod_callback(struct trace_array *tr, struct ftrace_hash *hash,
|
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;
|
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
|
* cmd == 'mod' because we only registered this func
|
||||||
* for the 'mod' ftrace_func_command.
|
* for the 'mod' ftrace_func_command.
|
||||||
|
@ -3780,8 +4094,10 @@ ftrace_mod_callback(struct trace_array *tr, struct ftrace_hash *hash,
|
||||||
* parameter.
|
* parameter.
|
||||||
*/
|
*/
|
||||||
ret = match_records(hash, func, strlen(func), module);
|
ret = match_records(hash, func, strlen(func), module);
|
||||||
|
kfree(func);
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return -EINVAL;
|
return cache_mod(tr, func_orig, module, enable);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -4725,9 +5041,11 @@ int ftrace_regex_release(struct inode *inode, struct file *file)
|
||||||
if (file->f_mode & FMODE_WRITE) {
|
if (file->f_mode & FMODE_WRITE) {
|
||||||
filter_hash = !!(iter->flags & FTRACE_ITER_FILTER);
|
filter_hash = !!(iter->flags & FTRACE_ITER_FILTER);
|
||||||
|
|
||||||
if (filter_hash)
|
if (filter_hash) {
|
||||||
orig_hash = &iter->ops->func_hash->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;
|
orig_hash = &iter->ops->func_hash->notrace_hash;
|
||||||
|
|
||||||
mutex_lock(&ftrace_lock);
|
mutex_lock(&ftrace_lock);
|
||||||
|
@ -5385,6 +5703,7 @@ void ftrace_release_mod(struct module *mod)
|
||||||
if (pg == ftrace_pages)
|
if (pg == ftrace_pages)
|
||||||
ftrace_pages = next_to_ftrace_page(last_pg);
|
ftrace_pages = next_to_ftrace_page(last_pg);
|
||||||
|
|
||||||
|
ftrace_update_tot_cnt -= pg->index;
|
||||||
*last_pg = pg->next;
|
*last_pg = pg->next;
|
||||||
order = get_count_order(pg->size / ENTRIES_PER_PAGE);
|
order = get_count_order(pg->size / ENTRIES_PER_PAGE);
|
||||||
free_pages((unsigned long)pg->records, order);
|
free_pages((unsigned long)pg->records, order);
|
||||||
|
@ -5463,6 +5782,8 @@ void ftrace_module_enable(struct module *mod)
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
mutex_unlock(&ftrace_lock);
|
mutex_unlock(&ftrace_lock);
|
||||||
|
|
||||||
|
process_cached_mods(mod->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ftrace_module_init(struct module *mod)
|
void ftrace_module_init(struct module *mod)
|
||||||
|
@ -5501,6 +5822,7 @@ void __init ftrace_free_init_mem(void)
|
||||||
if (!rec)
|
if (!rec)
|
||||||
continue;
|
continue;
|
||||||
pg->index--;
|
pg->index--;
|
||||||
|
ftrace_update_tot_cnt--;
|
||||||
if (!pg->index) {
|
if (!pg->index) {
|
||||||
*last_pg = pg->next;
|
*last_pg = pg->next;
|
||||||
order = get_count_order(pg->size / ENTRIES_PER_PAGE);
|
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)
|
void ftrace_init_trace_array(struct trace_array *tr)
|
||||||
{
|
{
|
||||||
INIT_LIST_HEAD(&tr->func_probes);
|
INIT_LIST_HEAD(&tr->func_probes);
|
||||||
|
INIT_LIST_HEAD(&tr->mod_trace);
|
||||||
|
INIT_LIST_HEAD(&tr->mod_notrace);
|
||||||
}
|
}
|
||||||
#else
|
#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
|
* tracing is active, only save the comm when a trace event
|
||||||
* occurred.
|
* 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).
|
* 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 */
|
/* When set, tracing will stop when a WARN*() is hit */
|
||||||
int __disable_trace_on_warning;
|
int __disable_trace_on_warning;
|
||||||
|
|
||||||
#ifdef CONFIG_TRACE_ENUM_MAP_FILE
|
#ifdef CONFIG_TRACE_EVAL_MAP_FILE
|
||||||
/* Map of enums to their values, for "enum_map" file */
|
/* Map of enums to their values, for "eval_map" file */
|
||||||
struct trace_enum_map_head {
|
struct trace_eval_map_head {
|
||||||
struct module *mod;
|
struct module *mod;
|
||||||
unsigned long length;
|
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
|
* "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 */
|
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
|
* 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
|
* 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
|
* 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 {
|
union trace_eval_map_item {
|
||||||
struct trace_enum_map map;
|
struct trace_eval_map map;
|
||||||
struct trace_enum_map_head head;
|
struct trace_eval_map_head head;
|
||||||
struct trace_enum_map_tail tail;
|
struct trace_eval_map_tail tail;
|
||||||
};
|
};
|
||||||
|
|
||||||
static union trace_enum_map_item *trace_enum_maps;
|
static union trace_eval_map_item *trace_eval_maps;
|
||||||
#endif /* CONFIG_TRACE_ENUM_MAP_FILE */
|
#endif /* CONFIG_TRACE_EVAL_MAP_FILE */
|
||||||
|
|
||||||
static int tracing_set_tracer(struct trace_array *tr, const char *buf);
|
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
|
static __always_inline void
|
||||||
__buffer_unlock_commit(struct ring_buffer *buffer, struct ring_buffer_event *event)
|
__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 is the temp buffer, we need to commit fully */
|
||||||
if (this_cpu_read(trace_buffered_event) == event) {
|
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.
|
* 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
|
* 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
|
#undef C
|
||||||
#define C(a, b) b
|
#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 SAVED_CMDLINES_DEFAULT 128
|
||||||
#define NO_CMDLINE_MAP UINT_MAX
|
#define NO_CMDLINE_MAP UINT_MAX
|
||||||
static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED;
|
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;
|
static struct saved_cmdlines_buffer *savedcmd;
|
||||||
|
|
||||||
/* temporary disable recording */
|
/* 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)
|
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);
|
raw_spin_unlock_irqrestore(&tr->start_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void trace_stop_cmdline_recording(void);
|
|
||||||
|
|
||||||
static int trace_save_cmdline(struct task_struct *tsk)
|
static int trace_save_cmdline(struct task_struct *tsk)
|
||||||
{
|
{
|
||||||
unsigned pid, idx;
|
unsigned pid, idx;
|
||||||
|
@ -1992,16 +1992,87 @@ void trace_find_cmdline(int pid, char comm[])
|
||||||
preempt_enable();
|
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;
|
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;
|
return;
|
||||||
|
|
||||||
if (trace_save_cmdline(tsk))
|
if ((flags & TRACE_RECORD_CMDLINE) &&
|
||||||
__this_cpu_write(trace_cmdline_save, false);
|
(!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
|
#endif
|
||||||
|
|
||||||
if (!iter->snapshot)
|
if (!iter->snapshot)
|
||||||
atomic_inc(&trace_record_cmdline_disabled);
|
atomic_inc(&trace_record_taskinfo_disabled);
|
||||||
|
|
||||||
if (*pos != iter->pos) {
|
if (*pos != iter->pos) {
|
||||||
iter->ent = NULL;
|
iter->ent = NULL;
|
||||||
|
@ -3191,7 +3262,7 @@ static void s_stop(struct seq_file *m, void *p)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!iter->snapshot)
|
if (!iter->snapshot)
|
||||||
atomic_dec(&trace_record_cmdline_disabled);
|
atomic_dec(&trace_record_taskinfo_disabled);
|
||||||
|
|
||||||
trace_access_unlock(iter->cpu_file);
|
trace_access_unlock(iter->cpu_file);
|
||||||
trace_event_read_unlock();
|
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");
|
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);
|
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);
|
bool tgid = flags & TRACE_ITER_RECORD_TGID;
|
||||||
seq_puts(m, "# _-----=> irqs-off\n"
|
|
||||||
"# / _----=> need-resched\n"
|
seq_printf(m, "# %s _-----=> irqs-off\n", tgid ? " " : "");
|
||||||
"# | / _---=> hardirq/softirq\n"
|
seq_printf(m, "# %s / _----=> need-resched\n", tgid ? " " : "");
|
||||||
"# || / _--=> preempt-depth\n"
|
seq_printf(m, "# %s| / _---=> hardirq/softirq\n", tgid ? " " : "");
|
||||||
"# ||| / delay\n"
|
seq_printf(m, "# %s|| / _--=> preempt-depth\n", tgid ? " " : "");
|
||||||
"# TASK-PID CPU# |||| TIMESTAMP FUNCTION\n"
|
seq_printf(m, "# %s||| / delay\n", tgid ? " " : "");
|
||||||
"# | | | |||| | |\n");
|
seq_printf(m, "# TASK-PID CPU#%s|||| TIMESTAMP FUNCTION\n", tgid ? " TGID " : "");
|
||||||
|
seq_printf(m, "# | | | %s|||| | |\n", tgid ? " | " : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -3580,9 +3657,11 @@ void trace_default_header(struct seq_file *m)
|
||||||
} else {
|
} else {
|
||||||
if (!(trace_flags & TRACE_ITER_VERBOSE)) {
|
if (!(trace_flags & TRACE_ITER_VERBOSE)) {
|
||||||
if (trace_flags & TRACE_ITER_IRQ_INFO)
|
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
|
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)
|
if (mask == TRACE_ITER_RECORD_CMD)
|
||||||
trace_event_enable_cmd_record(enabled);
|
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)
|
if (mask == TRACE_ITER_EVENT_FORK)
|
||||||
trace_event_follow_fork(tr, enabled);
|
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,
|
.write = tracing_saved_cmdlines_size_write,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_TRACE_ENUM_MAP_FILE
|
#ifdef CONFIG_TRACE_EVAL_MAP_FILE
|
||||||
static union trace_enum_map_item *
|
static union trace_eval_map_item *
|
||||||
update_enum_map(union trace_enum_map_item *ptr)
|
update_eval_map(union trace_eval_map_item *ptr)
|
||||||
{
|
{
|
||||||
if (!ptr->map.enum_string) {
|
if (!ptr->map.eval_string) {
|
||||||
if (ptr->tail.next) {
|
if (ptr->tail.next) {
|
||||||
ptr = ptr->tail.next;
|
ptr = ptr->tail.next;
|
||||||
/* Set ptr to the next real item (skip head) */
|
/* Set ptr to the next real item (skip head) */
|
||||||
|
@ -4761,15 +4852,15 @@ update_enum_map(union trace_enum_map_item *ptr)
|
||||||
return 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.
|
* Paranoid! If ptr points to end, we don't want to increment past it.
|
||||||
* This really should never happen.
|
* This really should never happen.
|
||||||
*/
|
*/
|
||||||
ptr = update_enum_map(ptr);
|
ptr = update_eval_map(ptr);
|
||||||
if (WARN_ON_ONCE(!ptr))
|
if (WARN_ON_ONCE(!ptr))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -4777,104 +4868,104 @@ static void *enum_map_next(struct seq_file *m, void *v, loff_t *pos)
|
||||||
|
|
||||||
(*pos)++;
|
(*pos)++;
|
||||||
|
|
||||||
ptr = update_enum_map(ptr);
|
ptr = update_eval_map(ptr);
|
||||||
|
|
||||||
return 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;
|
loff_t l = 0;
|
||||||
|
|
||||||
mutex_lock(&trace_enum_mutex);
|
mutex_lock(&trace_eval_mutex);
|
||||||
|
|
||||||
v = trace_enum_maps;
|
v = trace_eval_maps;
|
||||||
if (v)
|
if (v)
|
||||||
v++;
|
v++;
|
||||||
|
|
||||||
while (v && l < *pos) {
|
while (v && l < *pos) {
|
||||||
v = enum_map_next(m, v, &l);
|
v = eval_map_next(m, v, &l);
|
||||||
}
|
}
|
||||||
|
|
||||||
return v;
|
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",
|
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);
|
ptr->map.system);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct seq_operations tracing_enum_map_seq_ops = {
|
static const struct seq_operations tracing_eval_map_seq_ops = {
|
||||||
.start = enum_map_start,
|
.start = eval_map_start,
|
||||||
.next = enum_map_next,
|
.next = eval_map_next,
|
||||||
.stop = enum_map_stop,
|
.stop = eval_map_stop,
|
||||||
.show = enum_map_show,
|
.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)
|
if (tracing_disabled)
|
||||||
return -ENODEV;
|
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 = {
|
static const struct file_operations tracing_eval_map_fops = {
|
||||||
.open = tracing_enum_map_open,
|
.open = tracing_eval_map_open,
|
||||||
.read = seq_read,
|
.read = seq_read,
|
||||||
.llseek = seq_lseek,
|
.llseek = seq_lseek,
|
||||||
.release = seq_release,
|
.release = seq_release,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline union trace_enum_map_item *
|
static inline union trace_eval_map_item *
|
||||||
trace_enum_jmp_to_tail(union trace_enum_map_item *ptr)
|
trace_eval_jmp_to_tail(union trace_eval_map_item *ptr)
|
||||||
{
|
{
|
||||||
/* Return tail of array given the head */
|
/* Return tail of array given the head */
|
||||||
return ptr + ptr->head.length + 1;
|
return ptr + ptr->head.length + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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)
|
int len)
|
||||||
{
|
{
|
||||||
struct trace_enum_map **stop;
|
struct trace_eval_map **stop;
|
||||||
struct trace_enum_map **map;
|
struct trace_eval_map **map;
|
||||||
union trace_enum_map_item *map_array;
|
union trace_eval_map_item *map_array;
|
||||||
union trace_enum_map_item *ptr;
|
union trace_eval_map_item *ptr;
|
||||||
|
|
||||||
stop = start + len;
|
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
|
* where the head holds the module and length of array, and the
|
||||||
* tail holds a pointer to the next list.
|
* tail holds a pointer to the next list.
|
||||||
*/
|
*/
|
||||||
map_array = kmalloc(sizeof(*map_array) * (len + 2), GFP_KERNEL);
|
map_array = kmalloc(sizeof(*map_array) * (len + 2), GFP_KERNEL);
|
||||||
if (!map_array) {
|
if (!map_array) {
|
||||||
pr_warn("Unable to allocate trace enum mapping\n");
|
pr_warn("Unable to allocate trace eval mapping\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&trace_enum_mutex);
|
mutex_lock(&trace_eval_mutex);
|
||||||
|
|
||||||
if (!trace_enum_maps)
|
if (!trace_eval_maps)
|
||||||
trace_enum_maps = map_array;
|
trace_eval_maps = map_array;
|
||||||
else {
|
else {
|
||||||
ptr = trace_enum_maps;
|
ptr = trace_eval_maps;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ptr = trace_enum_jmp_to_tail(ptr);
|
ptr = trace_eval_jmp_to_tail(ptr);
|
||||||
if (!ptr->tail.next)
|
if (!ptr->tail.next)
|
||||||
break;
|
break;
|
||||||
ptr = ptr->tail.next;
|
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));
|
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,
|
trace_create_file("eval_map", 0444, d_tracer,
|
||||||
NULL, &tracing_enum_map_fops);
|
NULL, &tracing_eval_map_fops);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /* CONFIG_TRACE_ENUM_MAP_FILE */
|
#else /* CONFIG_TRACE_EVAL_MAP_FILE */
|
||||||
static inline void trace_create_enum_file(struct dentry *d_tracer) { }
|
static inline void trace_create_eval_file(struct dentry *d_tracer) { }
|
||||||
static inline void trace_insert_enum_map_file(struct module *mod,
|
static inline void trace_insert_eval_map_file(struct module *mod,
|
||||||
struct trace_enum_map **start, int len) { }
|
struct trace_eval_map **start, int len) { }
|
||||||
#endif /* !CONFIG_TRACE_ENUM_MAP_FILE */
|
#endif /* !CONFIG_TRACE_EVAL_MAP_FILE */
|
||||||
|
|
||||||
static void trace_insert_enum_map(struct module *mod,
|
static void trace_insert_eval_map(struct module *mod,
|
||||||
struct trace_enum_map **start, int len)
|
struct trace_eval_map **start, int len)
|
||||||
{
|
{
|
||||||
struct trace_enum_map **map;
|
struct trace_eval_map **map;
|
||||||
|
|
||||||
if (len <= 0)
|
if (len <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
map = start;
|
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
|
static ssize_t
|
||||||
|
@ -6739,33 +6830,18 @@ static const struct file_operations tracing_stats_fops = {
|
||||||
|
|
||||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||||
|
|
||||||
int __weak ftrace_arch_read_dyn_info(char *buf, int size)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
tracing_read_dyn_info(struct file *filp, char __user *ubuf,
|
tracing_read_dyn_info(struct file *filp, char __user *ubuf,
|
||||||
size_t cnt, loff_t *ppos)
|
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;
|
unsigned long *p = filp->private_data;
|
||||||
char *buf = ftrace_dyn_info_buffer;
|
char buf[64]; /* Not too big for a shallow stack */
|
||||||
int size = ARRAY_SIZE(ftrace_dyn_info_buffer);
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
mutex_lock(&dyn_info_mutex);
|
r = scnprintf(buf, 63, "%ld", *p);
|
||||||
r = sprintf(buf, "%ld ", *p);
|
|
||||||
|
|
||||||
r += ftrace_arch_read_dyn_info(buf+r, (size-1)-r);
|
|
||||||
buf[r++] = '\n';
|
buf[r++] = '\n';
|
||||||
|
|
||||||
r = simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
|
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
|
||||||
|
|
||||||
mutex_unlock(&dyn_info_mutex);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct file_operations tracing_dyn_info_fops = {
|
static const struct file_operations tracing_dyn_info_fops = {
|
||||||
|
@ -7737,21 +7813,21 @@ struct dentry *tracing_init_dentry(void)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern struct trace_enum_map *__start_ftrace_enum_maps[];
|
extern struct trace_eval_map *__start_ftrace_eval_maps[];
|
||||||
extern struct trace_enum_map *__stop_ftrace_enum_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;
|
int len;
|
||||||
|
|
||||||
len = __stop_ftrace_enum_maps - __start_ftrace_enum_maps;
|
len = __stop_ftrace_eval_maps - __start_ftrace_eval_maps;
|
||||||
trace_insert_enum_map(NULL, __start_ftrace_enum_maps, len);
|
trace_insert_eval_map(NULL, __start_ftrace_eval_maps, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_MODULES
|
#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;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -7761,40 +7837,40 @@ static void trace_module_add_enums(struct module *mod)
|
||||||
if (trace_module_has_bad_taint(mod))
|
if (trace_module_has_bad_taint(mod))
|
||||||
return;
|
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
|
#ifdef CONFIG_TRACE_EVAL_MAP_FILE
|
||||||
static void trace_module_remove_enums(struct module *mod)
|
static void trace_module_remove_evals(struct module *mod)
|
||||||
{
|
{
|
||||||
union trace_enum_map_item *map;
|
union trace_eval_map_item *map;
|
||||||
union trace_enum_map_item **last = &trace_enum_maps;
|
union trace_eval_map_item **last = &trace_eval_maps;
|
||||||
|
|
||||||
if (!mod->num_trace_enums)
|
if (!mod->num_trace_evals)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mutex_lock(&trace_enum_mutex);
|
mutex_lock(&trace_eval_mutex);
|
||||||
|
|
||||||
map = trace_enum_maps;
|
map = trace_eval_maps;
|
||||||
|
|
||||||
while (map) {
|
while (map) {
|
||||||
if (map->head.mod == mod)
|
if (map->head.mod == mod)
|
||||||
break;
|
break;
|
||||||
map = trace_enum_jmp_to_tail(map);
|
map = trace_eval_jmp_to_tail(map);
|
||||||
last = &map->tail.next;
|
last = &map->tail.next;
|
||||||
map = map->tail.next;
|
map = map->tail.next;
|
||||||
}
|
}
|
||||||
if (!map)
|
if (!map)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
*last = trace_enum_jmp_to_tail(map)->tail.next;
|
*last = trace_eval_jmp_to_tail(map)->tail.next;
|
||||||
kfree(map);
|
kfree(map);
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&trace_enum_mutex);
|
mutex_unlock(&trace_eval_mutex);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static inline void trace_module_remove_enums(struct module *mod) { }
|
static inline void trace_module_remove_evals(struct module *mod) { }
|
||||||
#endif /* CONFIG_TRACE_ENUM_MAP_FILE */
|
#endif /* CONFIG_TRACE_EVAL_MAP_FILE */
|
||||||
|
|
||||||
static int trace_module_notify(struct notifier_block *self,
|
static int trace_module_notify(struct notifier_block *self,
|
||||||
unsigned long val, void *data)
|
unsigned long val, void *data)
|
||||||
|
@ -7803,10 +7879,10 @@ static int trace_module_notify(struct notifier_block *self,
|
||||||
|
|
||||||
switch (val) {
|
switch (val) {
|
||||||
case MODULE_STATE_COMING:
|
case MODULE_STATE_COMING:
|
||||||
trace_module_add_enums(mod);
|
trace_module_add_evals(mod);
|
||||||
break;
|
break;
|
||||||
case MODULE_STATE_GOING:
|
case MODULE_STATE_GOING:
|
||||||
trace_module_remove_enums(mod);
|
trace_module_remove_evals(mod);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7844,9 +7920,9 @@ static __init int tracer_init_tracefs(void)
|
||||||
trace_create_file("saved_cmdlines_size", 0644, d_tracer,
|
trace_create_file("saved_cmdlines_size", 0644, d_tracer,
|
||||||
NULL, &tracing_saved_cmdlines_size_fops);
|
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
|
#ifdef CONFIG_MODULES
|
||||||
register_module_notifier(&trace_module_nb);
|
register_module_notifier(&trace_module_nb);
|
||||||
|
|
|
@ -263,7 +263,10 @@ struct trace_array {
|
||||||
struct ftrace_ops *ops;
|
struct ftrace_ops *ops;
|
||||||
struct trace_pid_list __rcu *function_pids;
|
struct trace_pid_list __rcu *function_pids;
|
||||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||||
|
/* All of these are protected by the ftrace_lock */
|
||||||
struct list_head func_probes;
|
struct list_head func_probes;
|
||||||
|
struct list_head mod_trace;
|
||||||
|
struct list_head mod_notrace;
|
||||||
#endif
|
#endif
|
||||||
/* function tracing enabled */
|
/* function tracing enabled */
|
||||||
int function_enabled;
|
int function_enabled;
|
||||||
|
@ -637,6 +640,9 @@ void set_graph_array(struct trace_array *tr);
|
||||||
|
|
||||||
void tracing_start_cmdline_record(void);
|
void tracing_start_cmdline_record(void);
|
||||||
void tracing_stop_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 register_tracer(struct tracer *type);
|
||||||
int is_tracing_stopped(void);
|
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 u64 ftrace_now(int cpu);
|
||||||
|
|
||||||
extern void trace_find_cmdline(int pid, char comm[]);
|
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);
|
extern void trace_event_follow_fork(struct trace_array *tr, bool enable);
|
||||||
|
|
||||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
#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);
|
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 {
|
struct ftrace_hash {
|
||||||
unsigned long size_bits;
|
unsigned long size_bits;
|
||||||
struct hlist_head *buckets;
|
struct hlist_head *buckets;
|
||||||
unsigned long count;
|
unsigned long count;
|
||||||
|
unsigned long flags;
|
||||||
struct rcu_head rcu;
|
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)
|
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 */
|
/* 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(CONTEXT_INFO, "context-info"), /* Print pid/cpu/time */ \
|
||||||
C(LATENCY_FMT, "latency-format"), \
|
C(LATENCY_FMT, "latency-format"), \
|
||||||
C(RECORD_CMD, "record-cmd"), \
|
C(RECORD_CMD, "record-cmd"), \
|
||||||
|
C(RECORD_TGID, "record-tgid"), \
|
||||||
C(OVERWRITE, "overwrite"), \
|
C(OVERWRITE, "overwrite"), \
|
||||||
C(STOP_ON_FREE, "disable_on_free"), \
|
C(STOP_ON_FREE, "disable_on_free"), \
|
||||||
C(IRQ_INFO, "irq-info"), \
|
C(IRQ_INFO, "irq-info"), \
|
||||||
|
@ -1423,6 +1445,8 @@ struct ftrace_event_field *
|
||||||
trace_find_event_field(struct trace_event_call *call, char *name);
|
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_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_add_tracer(struct dentry *parent, struct trace_array *tr);
|
||||||
extern int event_trace_del_tracer(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
|
#ifdef CONFIG_EVENT_TRACING
|
||||||
void trace_event_init(void);
|
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
|
#else
|
||||||
static inline void __init trace_event_init(void) { }
|
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
|
#endif
|
||||||
|
|
||||||
extern struct trace_iterator *tracepoint_print_iter;
|
extern struct trace_iterator *tracepoint_print_iter;
|
||||||
|
|
|
@ -343,6 +343,28 @@ void trace_event_enable_cmd_record(bool enable)
|
||||||
mutex_unlock(&event_mutex);
|
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,
|
static int __ftrace_event_enable_disable(struct trace_event_file *file,
|
||||||
int enable, int soft_disable)
|
int enable, int soft_disable)
|
||||||
{
|
{
|
||||||
|
@ -381,6 +403,12 @@ static int __ftrace_event_enable_disable(struct trace_event_file *file,
|
||||||
tracing_stop_cmdline_record();
|
tracing_stop_cmdline_record();
|
||||||
clear_bit(EVENT_FILE_FL_RECORDED_CMD_BIT, &file->flags);
|
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);
|
call->class->reg(call, TRACE_REG_UNREGISTER, file);
|
||||||
}
|
}
|
||||||
/* If in SOFT_MODE, just set the SOFT_DISABLE_BIT, else clear it */
|
/* 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)) {
|
if (!(file->flags & EVENT_FILE_FL_ENABLED)) {
|
||||||
|
bool cmd = false, tgid = false;
|
||||||
|
|
||||||
/* Keep the event disabled, when going to SOFT_MODE. */
|
/* Keep the event disabled, when going to SOFT_MODE. */
|
||||||
if (soft_disable)
|
if (soft_disable)
|
||||||
set_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags);
|
set_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags);
|
||||||
|
|
||||||
if (tr->trace_flags & TRACE_ITER_RECORD_CMD) {
|
if (tr->trace_flags & TRACE_ITER_RECORD_CMD) {
|
||||||
|
cmd = true;
|
||||||
tracing_start_cmdline_record();
|
tracing_start_cmdline_record();
|
||||||
set_bit(EVENT_FILE_FL_RECORDED_CMD_BIT, &file->flags);
|
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);
|
ret = call->class->reg(call, TRACE_REG_REGISTER, file);
|
||||||
if (ret) {
|
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 "
|
pr_info("event trace: Could not enable event "
|
||||||
"%s\n", trace_event_name(call));
|
"%s\n", trace_event_name(call));
|
||||||
break;
|
break;
|
||||||
|
@ -2067,18 +2107,18 @@ __register_event(struct trace_event_call *call, struct module *mod)
|
||||||
return 0;
|
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 rlen;
|
||||||
int elen;
|
int elen;
|
||||||
|
|
||||||
/* Find the length of the enum value as a string */
|
/* Find the length of the eval value as a string */
|
||||||
elen = snprintf(ptr, 0, "%ld", map->enum_value);
|
elen = snprintf(ptr, 0, "%ld", map->eval_value);
|
||||||
/* Make sure there's enough room to replace the string with the value */
|
/* Make sure there's enough room to replace the string with the value */
|
||||||
if (len < elen)
|
if (len < elen)
|
||||||
return NULL;
|
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 */
|
/* Get the rest of the string of ptr */
|
||||||
rlen = strlen(ptr + len);
|
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,
|
static void update_event_printk(struct trace_event_call *call,
|
||||||
struct trace_enum_map *map)
|
struct trace_eval_map *map)
|
||||||
{
|
{
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int quote = 0;
|
int quote = 0;
|
||||||
int len = strlen(map->enum_string);
|
int len = strlen(map->eval_string);
|
||||||
|
|
||||||
for (ptr = call->print_fmt; *ptr; ptr++) {
|
for (ptr = call->print_fmt; *ptr; ptr++) {
|
||||||
if (*ptr == '\\') {
|
if (*ptr == '\\') {
|
||||||
|
@ -2125,16 +2165,16 @@ static void update_event_printk(struct trace_event_call *call,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (isalpha(*ptr) || *ptr == '_') {
|
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] != '_') {
|
!isalnum(ptr[len]) && ptr[len] != '_') {
|
||||||
ptr = enum_replace(ptr, map, len);
|
ptr = eval_replace(ptr, map, len);
|
||||||
/* Hmm, enum string smaller than value */
|
/* enum/sizeof string smaller than value */
|
||||||
if (WARN_ON_ONCE(!ptr))
|
if (WARN_ON_ONCE(!ptr))
|
||||||
return;
|
return;
|
||||||
/*
|
/*
|
||||||
* No need to decrement here, as enum_replace()
|
* No need to decrement here, as eval_replace()
|
||||||
* returns the pointer to the character passed
|
* 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.
|
* back to back without something in between.
|
||||||
* We can skip that 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;
|
struct trace_event_call *call, *p;
|
||||||
const char *last_system = NULL;
|
const char *last_system = NULL;
|
||||||
|
|
|
@ -340,31 +340,41 @@ static inline const char *kretprobed(const char *name)
|
||||||
static void
|
static void
|
||||||
seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address)
|
seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KALLSYMS
|
|
||||||
char str[KSYM_SYMBOL_LEN];
|
char str[KSYM_SYMBOL_LEN];
|
||||||
|
#ifdef CONFIG_KALLSYMS
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
kallsyms_lookup(address, NULL, NULL, NULL, str);
|
kallsyms_lookup(address, NULL, NULL, NULL, str);
|
||||||
|
|
||||||
name = kretprobed(str);
|
name = kretprobed(str);
|
||||||
|
|
||||||
trace_seq_printf(s, fmt, name);
|
if (name && strlen(name)) {
|
||||||
|
trace_seq_printf(s, fmt, name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
snprintf(str, KSYM_SYMBOL_LEN, "0x%08lx", address);
|
||||||
|
trace_seq_printf(s, fmt, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
seq_print_sym_offset(struct trace_seq *s, const char *fmt,
|
seq_print_sym_offset(struct trace_seq *s, const char *fmt,
|
||||||
unsigned long address)
|
unsigned long address)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KALLSYMS
|
|
||||||
char str[KSYM_SYMBOL_LEN];
|
char str[KSYM_SYMBOL_LEN];
|
||||||
|
#ifdef CONFIG_KALLSYMS
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
sprint_symbol(str, address);
|
sprint_symbol(str, address);
|
||||||
name = kretprobed(str);
|
name = kretprobed(str);
|
||||||
|
|
||||||
trace_seq_printf(s, fmt, name);
|
if (name && strlen(name)) {
|
||||||
|
trace_seq_printf(s, fmt, name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
snprintf(str, KSYM_SYMBOL_LEN, "0x%08lx", address);
|
||||||
|
trace_seq_printf(s, fmt, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef CONFIG_64BIT
|
#ifndef CONFIG_64BIT
|
||||||
|
@ -587,6 +597,15 @@ int trace_print_context(struct trace_iterator *iter)
|
||||||
trace_seq_printf(s, "%16s-%-5d [%03d] ",
|
trace_seq_printf(s, "%16s-%-5d [%03d] ",
|
||||||
comm, entry->pid, iter->cpu);
|
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)
|
if (tr->trace_flags & TRACE_ITER_IRQ_INFO)
|
||||||
trace_print_lat_fmt(s, entry);
|
trace_print_lat_fmt(s, entry);
|
||||||
|
|
||||||
|
|
|
@ -12,27 +12,38 @@
|
||||||
|
|
||||||
#include "trace.h"
|
#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 DEFINE_MUTEX(sched_register_mutex);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
probe_sched_switch(void *ignore, bool preempt,
|
probe_sched_switch(void *ignore, bool preempt,
|
||||||
struct task_struct *prev, struct task_struct *next)
|
struct task_struct *prev, struct task_struct *next)
|
||||||
{
|
{
|
||||||
if (unlikely(!sched_ref))
|
int flags;
|
||||||
return;
|
|
||||||
|
|
||||||
tracing_record_cmdline(prev);
|
flags = (RECORD_TGID * !!sched_tgid_ref) +
|
||||||
tracing_record_cmdline(next);
|
(RECORD_CMDLINE * !!sched_cmdline_ref);
|
||||||
|
|
||||||
|
if (!flags)
|
||||||
|
return;
|
||||||
|
tracing_record_taskinfo_sched_switch(prev, next, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
probe_sched_wakeup(void *ignore, struct task_struct *wakee)
|
probe_sched_wakeup(void *ignore, struct task_struct *wakee)
|
||||||
{
|
{
|
||||||
if (unlikely(!sched_ref))
|
int flags;
|
||||||
return;
|
|
||||||
|
|
||||||
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)
|
static int tracing_sched_register(void)
|
||||||
|
@ -75,28 +86,61 @@ static void tracing_sched_unregister(void)
|
||||||
unregister_trace_sched_wakeup(probe_sched_wakeup, NULL);
|
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);
|
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();
|
tracing_sched_register();
|
||||||
mutex_unlock(&sched_register_mutex);
|
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);
|
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();
|
tracing_sched_unregister();
|
||||||
mutex_unlock(&sched_register_mutex);
|
mutex_unlock(&sched_register_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tracing_start_cmdline_record(void)
|
void tracing_start_cmdline_record(void)
|
||||||
{
|
{
|
||||||
tracing_start_sched_switch();
|
tracing_start_sched_switch(RECORD_CMDLINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tracing_stop_cmdline_record(void)
|
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