Commit 8b6ddd10 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'trace-v5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace

Pull tracing fixes from Steven Rostedt:

 - Have recordmcount work with > 64K sections (to support LTO)

 - kprobe RCU fixes

 - Correct a kprobe critical section with missing mutex

 - Remove redundant arch_disarm_kprobe() call

 - Fix lockup when kretprobe triggers within kprobe_flush_task()

 - Fix memory leak in fetch_op_data operations

 - Fix sleep in atomic in ftrace trace array sample code

 - Free up memory on failure in sample trace array code

 - Fix incorrect reporting of function_graph fields in format file

 - Fix quote within quote parsing in bootconfig

 - Fix return value of bootconfig tool

 - Add testcases for bootconfig tool

 - Fix maybe uninitialized warning in ftrace pid file code

 - Remove unused variable in tracing_iter_reset()

 - Fix some typos

* tag 'trace-v5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace:
  ftrace: Fix maybe-uninitialized compiler warning
  tools/bootconfig: Add testcase for show-command and quotes test
  tools/bootconfig: Fix to return 0 if succeeded to show the bootconfig
  tools/bootconfig: Fix to use correct quotes for value
  proc/bootconfig: Fix to use correct quotes for value
  tracing: Remove unused event variable in tracing_iter_reset
  tracing/probe: Fix memleak in fetch_op_data operations
  trace: Fix typo in allocate_ftrace_ops()'s comment
  tracing: Make ftrace packed events have align of 1
  sample-trace-array: Remove trace_array 'sample-instance'
  sample-trace-array: Fix sleeping function called from invalid context
  kretprobe: Prevent triggering kretprobe from within kprobe_flush_task
  kprobes: Remove redundant arch_disarm_kprobe() call
  kprobes: Fix to protect kick_kprobe_optimizer() by kprobe_mutex
  kprobes: Use non RCU traversal APIs on kprobe_tables if possible
  kprobes: Suppress the suspicious RCU warning on kprobes
  recordmcount: support >64k sections
parents eede2b9b 026bb845
...@@ -754,16 +754,11 @@ asm( ...@@ -754,16 +754,11 @@ asm(
NOKPROBE_SYMBOL(kretprobe_trampoline); NOKPROBE_SYMBOL(kretprobe_trampoline);
STACK_FRAME_NON_STANDARD(kretprobe_trampoline); STACK_FRAME_NON_STANDARD(kretprobe_trampoline);
static struct kprobe kretprobe_kprobe = {
.addr = (void *)kretprobe_trampoline,
};
/* /*
* Called from kretprobe_trampoline * Called from kretprobe_trampoline
*/ */
__used __visible void *trampoline_handler(struct pt_regs *regs) __used __visible void *trampoline_handler(struct pt_regs *regs)
{ {
struct kprobe_ctlblk *kcb;
struct kretprobe_instance *ri = NULL; struct kretprobe_instance *ri = NULL;
struct hlist_head *head, empty_rp; struct hlist_head *head, empty_rp;
struct hlist_node *tmp; struct hlist_node *tmp;
...@@ -773,16 +768,12 @@ __used __visible void *trampoline_handler(struct pt_regs *regs) ...@@ -773,16 +768,12 @@ __used __visible void *trampoline_handler(struct pt_regs *regs)
void *frame_pointer; void *frame_pointer;
bool skipped = false; bool skipped = false;
preempt_disable();
/* /*
* Set a dummy kprobe for avoiding kretprobe recursion. * Set a dummy kprobe for avoiding kretprobe recursion.
* Since kretprobe never run in kprobe handler, kprobe must not * Since kretprobe never run in kprobe handler, kprobe must not
* be running at this point. * be running at this point.
*/ */
kcb = get_kprobe_ctlblk(); kprobe_busy_begin();
__this_cpu_write(current_kprobe, &kretprobe_kprobe);
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
INIT_HLIST_HEAD(&empty_rp); INIT_HLIST_HEAD(&empty_rp);
kretprobe_hash_lock(current, &head, &flags); kretprobe_hash_lock(current, &head, &flags);
...@@ -858,7 +849,7 @@ __used __visible void *trampoline_handler(struct pt_regs *regs) ...@@ -858,7 +849,7 @@ __used __visible void *trampoline_handler(struct pt_regs *regs)
__this_cpu_write(current_kprobe, &ri->rp->kp); __this_cpu_write(current_kprobe, &ri->rp->kp);
ri->ret_addr = correct_ret_addr; ri->ret_addr = correct_ret_addr;
ri->rp->handler(ri, regs); ri->rp->handler(ri, regs);
__this_cpu_write(current_kprobe, &kretprobe_kprobe); __this_cpu_write(current_kprobe, &kprobe_busy);
} }
recycle_rp_inst(ri, &empty_rp); recycle_rp_inst(ri, &empty_rp);
...@@ -874,8 +865,7 @@ __used __visible void *trampoline_handler(struct pt_regs *regs) ...@@ -874,8 +865,7 @@ __used __visible void *trampoline_handler(struct pt_regs *regs)
kretprobe_hash_unlock(current, &flags); kretprobe_hash_unlock(current, &flags);
__this_cpu_write(current_kprobe, NULL); kprobe_busy_end();
preempt_enable();
hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
hlist_del(&ri->hlist); hlist_del(&ri->hlist);
......
...@@ -26,8 +26,9 @@ static int boot_config_proc_show(struct seq_file *m, void *v) ...@@ -26,8 +26,9 @@ static int boot_config_proc_show(struct seq_file *m, void *v)
static int __init copy_xbc_key_value_list(char *dst, size_t size) static int __init copy_xbc_key_value_list(char *dst, size_t size)
{ {
struct xbc_node *leaf, *vnode; struct xbc_node *leaf, *vnode;
const char *val;
char *key, *end = dst + size; char *key, *end = dst + size;
const char *val;
char q;
int ret = 0; int ret = 0;
key = kzalloc(XBC_KEYLEN_MAX, GFP_KERNEL); key = kzalloc(XBC_KEYLEN_MAX, GFP_KERNEL);
...@@ -41,16 +42,20 @@ static int __init copy_xbc_key_value_list(char *dst, size_t size) ...@@ -41,16 +42,20 @@ static int __init copy_xbc_key_value_list(char *dst, size_t size)
break; break;
dst += ret; dst += ret;
vnode = xbc_node_get_child(leaf); vnode = xbc_node_get_child(leaf);
if (vnode && xbc_node_is_array(vnode)) { if (vnode) {
xbc_array_for_each_value(vnode, val) { xbc_array_for_each_value(vnode, val) {
ret = snprintf(dst, rest(dst, end), "\"%s\"%s", if (strchr(val, '"'))
val, vnode->next ? ", " : "\n"); q = '\'';
else
q = '"';
ret = snprintf(dst, rest(dst, end), "%c%s%c%s",
q, val, q, vnode->next ? ", " : "\n");
if (ret < 0) if (ret < 0)
goto out; goto out;
dst += ret; dst += ret;
} }
} else { } else {
ret = snprintf(dst, rest(dst, end), "\"%s\"\n", val); ret = snprintf(dst, rest(dst, end), "\"\"\n");
if (ret < 0) if (ret < 0)
break; break;
dst += ret; dst += ret;
......
...@@ -350,6 +350,10 @@ static inline struct kprobe_ctlblk *get_kprobe_ctlblk(void) ...@@ -350,6 +350,10 @@ static inline struct kprobe_ctlblk *get_kprobe_ctlblk(void)
return this_cpu_ptr(&kprobe_ctlblk); return this_cpu_ptr(&kprobe_ctlblk);
} }
extern struct kprobe kprobe_busy;
void kprobe_busy_begin(void);
void kprobe_busy_end(void);
kprobe_opcode_t *kprobe_lookup_name(const char *name, unsigned int offset); kprobe_opcode_t *kprobe_lookup_name(const char *name, unsigned int offset);
int register_kprobe(struct kprobe *p); int register_kprobe(struct kprobe *p);
void unregister_kprobe(struct kprobe *p); void unregister_kprobe(struct kprobe *p);
......
...@@ -46,6 +46,11 @@ ...@@ -46,6 +46,11 @@
static int kprobes_initialized; static int kprobes_initialized;
/* kprobe_table can be accessed by
* - Normal hlist traversal and RCU add/del under kprobe_mutex is held.
* Or
* - RCU hlist traversal under disabling preempt (breakpoint handlers)
*/
static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE]; static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE];
static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE];
...@@ -326,7 +331,8 @@ struct kprobe *get_kprobe(void *addr) ...@@ -326,7 +331,8 @@ struct kprobe *get_kprobe(void *addr)
struct kprobe *p; struct kprobe *p;
head = &kprobe_table[hash_ptr(addr, KPROBE_HASH_BITS)]; head = &kprobe_table[hash_ptr(addr, KPROBE_HASH_BITS)];
hlist_for_each_entry_rcu(p, head, hlist) { hlist_for_each_entry_rcu(p, head, hlist,
lockdep_is_held(&kprobe_mutex)) {
if (p->addr == addr) if (p->addr == addr)
return p; return p;
} }
...@@ -586,11 +592,12 @@ static void kprobe_optimizer(struct work_struct *work) ...@@ -586,11 +592,12 @@ static void kprobe_optimizer(struct work_struct *work)
mutex_unlock(&module_mutex); mutex_unlock(&module_mutex);
mutex_unlock(&text_mutex); mutex_unlock(&text_mutex);
cpus_read_unlock(); cpus_read_unlock();
mutex_unlock(&kprobe_mutex);
/* Step 5: Kick optimizer again if needed */ /* Step 5: Kick optimizer again if needed */
if (!list_empty(&optimizing_list) || !list_empty(&unoptimizing_list)) if (!list_empty(&optimizing_list) || !list_empty(&unoptimizing_list))
kick_kprobe_optimizer(); kick_kprobe_optimizer();
mutex_unlock(&kprobe_mutex);
} }
/* Wait for completing optimization and unoptimization */ /* Wait for completing optimization and unoptimization */
...@@ -668,8 +675,6 @@ static void force_unoptimize_kprobe(struct optimized_kprobe *op) ...@@ -668,8 +675,6 @@ static void force_unoptimize_kprobe(struct optimized_kprobe *op)
lockdep_assert_cpus_held(); lockdep_assert_cpus_held();
arch_unoptimize_kprobe(op); arch_unoptimize_kprobe(op);
op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED; op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
if (kprobe_disabled(&op->kp))
arch_disarm_kprobe(&op->kp);
} }
/* Unoptimize a kprobe if p is optimized */ /* Unoptimize a kprobe if p is optimized */
...@@ -849,7 +854,7 @@ static void optimize_all_kprobes(void) ...@@ -849,7 +854,7 @@ static void optimize_all_kprobes(void)
kprobes_allow_optimization = true; kprobes_allow_optimization = true;
for (i = 0; i < KPROBE_TABLE_SIZE; i++) { for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
head = &kprobe_table[i]; head = &kprobe_table[i];
hlist_for_each_entry_rcu(p, head, hlist) hlist_for_each_entry(p, head, hlist)
if (!kprobe_disabled(p)) if (!kprobe_disabled(p))
optimize_kprobe(p); optimize_kprobe(p);
} }
...@@ -876,7 +881,7 @@ static void unoptimize_all_kprobes(void) ...@@ -876,7 +881,7 @@ static void unoptimize_all_kprobes(void)
kprobes_allow_optimization = false; kprobes_allow_optimization = false;
for (i = 0; i < KPROBE_TABLE_SIZE; i++) { for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
head = &kprobe_table[i]; head = &kprobe_table[i];
hlist_for_each_entry_rcu(p, head, hlist) { hlist_for_each_entry(p, head, hlist) {
if (!kprobe_disabled(p)) if (!kprobe_disabled(p))
unoptimize_kprobe(p, false); unoptimize_kprobe(p, false);
} }
...@@ -1236,6 +1241,26 @@ __releases(hlist_lock) ...@@ -1236,6 +1241,26 @@ __releases(hlist_lock)
} }
NOKPROBE_SYMBOL(kretprobe_table_unlock); NOKPROBE_SYMBOL(kretprobe_table_unlock);
struct kprobe kprobe_busy = {
.addr = (void *) get_kprobe,
};
void kprobe_busy_begin(void)
{
struct kprobe_ctlblk *kcb;
preempt_disable();
__this_cpu_write(current_kprobe, &kprobe_busy);
kcb = get_kprobe_ctlblk();
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
}
void kprobe_busy_end(void)
{
__this_cpu_write(current_kprobe, NULL);
preempt_enable();
}
/* /*
* This function is called from finish_task_switch when task tk becomes dead, * This function is called from finish_task_switch when task tk becomes dead,
* so that we can recycle any function-return probe instances associated * so that we can recycle any function-return probe instances associated
...@@ -1253,6 +1278,8 @@ void kprobe_flush_task(struct task_struct *tk) ...@@ -1253,6 +1278,8 @@ void kprobe_flush_task(struct task_struct *tk)
/* Early boot. kretprobe_table_locks not yet initialized. */ /* Early boot. kretprobe_table_locks not yet initialized. */
return; return;
kprobe_busy_begin();
INIT_HLIST_HEAD(&empty_rp); INIT_HLIST_HEAD(&empty_rp);
hash = hash_ptr(tk, KPROBE_HASH_BITS); hash = hash_ptr(tk, KPROBE_HASH_BITS);
head = &kretprobe_inst_table[hash]; head = &kretprobe_inst_table[hash];
...@@ -1266,6 +1293,8 @@ void kprobe_flush_task(struct task_struct *tk) ...@@ -1266,6 +1293,8 @@ void kprobe_flush_task(struct task_struct *tk)
hlist_del(&ri->hlist); hlist_del(&ri->hlist);
kfree(ri); kfree(ri);
} }
kprobe_busy_end();
} }
NOKPROBE_SYMBOL(kprobe_flush_task); NOKPROBE_SYMBOL(kprobe_flush_task);
...@@ -1499,12 +1528,14 @@ static struct kprobe *__get_valid_kprobe(struct kprobe *p) ...@@ -1499,12 +1528,14 @@ static struct kprobe *__get_valid_kprobe(struct kprobe *p)
{ {
struct kprobe *ap, *list_p; struct kprobe *ap, *list_p;
lockdep_assert_held(&kprobe_mutex);
ap = get_kprobe(p->addr); ap = get_kprobe(p->addr);
if (unlikely(!ap)) if (unlikely(!ap))
return NULL; return NULL;
if (p != ap) { if (p != ap) {
list_for_each_entry_rcu(list_p, &ap->list, list) list_for_each_entry(list_p, &ap->list, list)
if (list_p == p) if (list_p == p)
/* kprobe p is a valid probe */ /* kprobe p is a valid probe */
goto valid; goto valid;
...@@ -1669,7 +1700,9 @@ static int aggr_kprobe_disabled(struct kprobe *ap) ...@@ -1669,7 +1700,9 @@ static int aggr_kprobe_disabled(struct kprobe *ap)
{ {
struct kprobe *kp; struct kprobe *kp;
list_for_each_entry_rcu(kp, &ap->list, list) lockdep_assert_held(&kprobe_mutex);
list_for_each_entry(kp, &ap->list, list)
if (!kprobe_disabled(kp)) if (!kprobe_disabled(kp))
/* /*
* There is an active probe on the list. * There is an active probe on the list.
...@@ -1748,7 +1781,7 @@ static int __unregister_kprobe_top(struct kprobe *p) ...@@ -1748,7 +1781,7 @@ static int __unregister_kprobe_top(struct kprobe *p)
else { else {
/* If disabling probe has special handlers, update aggrprobe */ /* If disabling probe has special handlers, update aggrprobe */
if (p->post_handler && !kprobe_gone(p)) { if (p->post_handler && !kprobe_gone(p)) {
list_for_each_entry_rcu(list_p, &ap->list, list) { list_for_each_entry(list_p, &ap->list, list) {
if ((list_p != p) && (list_p->post_handler)) if ((list_p != p) && (list_p->post_handler))
goto noclean; goto noclean;
} }
...@@ -2062,13 +2095,15 @@ static void kill_kprobe(struct kprobe *p) ...@@ -2062,13 +2095,15 @@ static void kill_kprobe(struct kprobe *p)
{ {
struct kprobe *kp; struct kprobe *kp;
lockdep_assert_held(&kprobe_mutex);
p->flags |= KPROBE_FLAG_GONE; p->flags |= KPROBE_FLAG_GONE;
if (kprobe_aggrprobe(p)) { if (kprobe_aggrprobe(p)) {
/* /*
* If this is an aggr_kprobe, we have to list all the * If this is an aggr_kprobe, we have to list all the
* chained probes and mark them GONE. * chained probes and mark them GONE.
*/ */
list_for_each_entry_rcu(kp, &p->list, list) list_for_each_entry(kp, &p->list, list)
kp->flags |= KPROBE_FLAG_GONE; kp->flags |= KPROBE_FLAG_GONE;
p->post_handler = NULL; p->post_handler = NULL;
kill_optimized_kprobe(p); kill_optimized_kprobe(p);
...@@ -2312,7 +2347,7 @@ static int kprobes_module_callback(struct notifier_block *nb, ...@@ -2312,7 +2347,7 @@ static int kprobes_module_callback(struct notifier_block *nb,
mutex_lock(&kprobe_mutex); mutex_lock(&kprobe_mutex);
for (i = 0; i < KPROBE_TABLE_SIZE; i++) { for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
head = &kprobe_table[i]; head = &kprobe_table[i];
hlist_for_each_entry_rcu(p, head, hlist) hlist_for_each_entry(p, head, hlist)
if (within_module_init((unsigned long)p->addr, mod) || if (within_module_init((unsigned long)p->addr, mod) ||
(checkcore && (checkcore &&
within_module_core((unsigned long)p->addr, mod))) { within_module_core((unsigned long)p->addr, mod))) {
...@@ -2550,7 +2585,7 @@ static int arm_all_kprobes(void) ...@@ -2550,7 +2585,7 @@ static int arm_all_kprobes(void)
for (i = 0; i < KPROBE_TABLE_SIZE; i++) { for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
head = &kprobe_table[i]; head = &kprobe_table[i];
/* Arm all kprobes on a best-effort basis */ /* Arm all kprobes on a best-effort basis */
hlist_for_each_entry_rcu(p, head, hlist) { hlist_for_each_entry(p, head, hlist) {
if (!kprobe_disabled(p)) { if (!kprobe_disabled(p)) {
err = arm_kprobe(p); err = arm_kprobe(p);
if (err) { if (err) {
...@@ -2593,7 +2628,7 @@ static int disarm_all_kprobes(void) ...@@ -2593,7 +2628,7 @@ static int disarm_all_kprobes(void)
for (i = 0; i < KPROBE_TABLE_SIZE; i++) { for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
head = &kprobe_table[i]; head = &kprobe_table[i];
/* Disarm all kprobes on a best-effort basis */ /* Disarm all kprobes on a best-effort basis */
hlist_for_each_entry_rcu(p, head, hlist) { hlist_for_each_entry(p, head, hlist) {
if (!arch_trampoline_kprobe(p) && !kprobe_disabled(p)) { if (!arch_trampoline_kprobe(p) && !kprobe_disabled(p)) {
err = disarm_kprobe(p, false); err = disarm_kprobe(p, false);
if (err) { if (err) {
......
...@@ -2260,7 +2260,7 @@ ftrace_find_tramp_ops_next(struct dyn_ftrace *rec, ...@@ -2260,7 +2260,7 @@ ftrace_find_tramp_ops_next(struct dyn_ftrace *rec,
if (hash_contains_ip(ip, op->func_hash)) if (hash_contains_ip(ip, op->func_hash))
return op; return op;
} }
return NULL; return NULL;
} }
...@@ -3599,7 +3599,7 @@ static int t_show(struct seq_file *m, void *v) ...@@ -3599,7 +3599,7 @@ static int t_show(struct seq_file *m, void *v)
if (direct) if (direct)
seq_printf(m, "\n\tdirect-->%pS", (void *)direct); seq_printf(m, "\n\tdirect-->%pS", (void *)direct);
} }
} }
seq_putc(m, '\n'); seq_putc(m, '\n');
...@@ -7151,6 +7151,10 @@ static int pid_open(struct inode *inode, struct file *file, int type) ...@@ -7151,6 +7151,10 @@ static int pid_open(struct inode *inode, struct file *file, int type)
case TRACE_NO_PIDS: case TRACE_NO_PIDS:
seq_ops = &ftrace_no_pid_sops; seq_ops = &ftrace_no_pid_sops;
break; break;
default:
trace_array_put(tr);
WARN_ON_ONCE(1);
return -EINVAL;
} }
ret = seq_open(file, seq_ops); ret = seq_open(file, seq_ops);
...@@ -7229,6 +7233,10 @@ pid_write(struct file *filp, const char __user *ubuf, ...@@ -7229,6 +7233,10 @@ pid_write(struct file *filp, const char __user *ubuf,
other_pids = rcu_dereference_protected(tr->function_pids, other_pids = rcu_dereference_protected(tr->function_pids,
lockdep_is_held(&ftrace_lock)); lockdep_is_held(&ftrace_lock));
break; break;
default:
ret = -EINVAL;
WARN_ON_ONCE(1);
goto out;
} }
ret = trace_pid_write(filtered_pids, &pid_list, ubuf, cnt); ret = trace_pid_write(filtered_pids, &pid_list, ubuf, cnt);
......
...@@ -3570,7 +3570,6 @@ static void *s_next(struct seq_file *m, void *v, loff_t *pos) ...@@ -3570,7 +3570,6 @@ static void *s_next(struct seq_file *m, void *v, loff_t *pos)
void tracing_iter_reset(struct trace_iterator *iter, int cpu) void tracing_iter_reset(struct trace_iterator *iter, int cpu)
{ {
struct ring_buffer_event *event;
struct ring_buffer_iter *buf_iter; struct ring_buffer_iter *buf_iter;
unsigned long entries = 0; unsigned long entries = 0;
u64 ts; u64 ts;
...@@ -3588,7 +3587,7 @@ void tracing_iter_reset(struct trace_iterator *iter, int cpu) ...@@ -3588,7 +3587,7 @@ void tracing_iter_reset(struct trace_iterator *iter, int cpu)
* that a reset never took place on a cpu. This is evident * that a reset never took place on a cpu. This is evident
* by the timestamp being before the start of the buffer. * by the timestamp being before the start of the buffer.
*/ */
while ((event = ring_buffer_iter_peek(buf_iter, &ts))) { while (ring_buffer_iter_peek(buf_iter, &ts)) {
if (ts >= iter->array_buffer->time_start) if (ts >= iter->array_buffer->time_start)
break; break;
entries++; entries++;
......
...@@ -61,6 +61,9 @@ enum trace_type { ...@@ -61,6 +61,9 @@ enum trace_type {
#undef __field_desc #undef __field_desc
#define __field_desc(type, container, item) #define __field_desc(type, container, item)
#undef __field_packed
#define __field_packed(type, container, item)
#undef __array #undef __array
#define __array(type, item, size) type item[size]; #define __array(type, item, size) type item[size];
......
...@@ -78,8 +78,8 @@ FTRACE_ENTRY_PACKED(funcgraph_entry, ftrace_graph_ent_entry, ...@@ -78,8 +78,8 @@ FTRACE_ENTRY_PACKED(funcgraph_entry, ftrace_graph_ent_entry,
F_STRUCT( F_STRUCT(
__field_struct( struct ftrace_graph_ent, graph_ent ) __field_struct( struct ftrace_graph_ent, graph_ent )
__field_desc( unsigned long, graph_ent, func ) __field_packed( unsigned long, graph_ent, func )
__field_desc( int, graph_ent, depth ) __field_packed( int, graph_ent, depth )
), ),
F_printk("--> %ps (%d)", (void *)__entry->func, __entry->depth) F_printk("--> %ps (%d)", (void *)__entry->func, __entry->depth)
...@@ -92,11 +92,11 @@ FTRACE_ENTRY_PACKED(funcgraph_exit, ftrace_graph_ret_entry, ...@@ -92,11 +92,11 @@ FTRACE_ENTRY_PACKED(funcgraph_exit, ftrace_graph_ret_entry,
F_STRUCT( F_STRUCT(
__field_struct( struct ftrace_graph_ret, ret ) __field_struct( struct ftrace_graph_ret, ret )
__field_desc( unsigned long, ret, func ) __field_packed( unsigned long, ret, func )
__field_desc( unsigned long, ret, overrun ) __field_packed( unsigned long, ret, overrun )
__field_desc( unsigned long long, ret, calltime) __field_packed( unsigned long long, ret, calltime)
__field_desc( unsigned long long, ret, rettime ) __field_packed( unsigned long long, ret, rettime )
__field_desc( int, ret, depth ) __field_packed( int, ret, depth )
), ),
F_printk("<-- %ps (%d) (start: %llx end: %llx) over: %d", F_printk("<-- %ps (%d) (start: %llx end: %llx) over: %d",
......
...@@ -45,6 +45,9 @@ static int ftrace_event_register(struct trace_event_call *call, ...@@ -45,6 +45,9 @@ static int ftrace_event_register(struct trace_event_call *call,
#undef __field_desc #undef __field_desc
#define __field_desc(type, container, item) type item; #define __field_desc(type, container, item) type item;
#undef __field_packed
#define __field_packed(type, container, item) type item;
#undef __array #undef __array
#define __array(type, item, size) type item[size]; #define __array(type, item, size) type item[size];
...@@ -85,6 +88,13 @@ static void __always_unused ____ftrace_check_##name(void) \ ...@@ -85,6 +88,13 @@ static void __always_unused ____ftrace_check_##name(void) \
.size = sizeof(_type), .align = __alignof__(_type), \ .size = sizeof(_type), .align = __alignof__(_type), \
is_signed_type(_type), .filter_type = _filter_type }, is_signed_type(_type), .filter_type = _filter_type },
#undef __field_ext_packed
#define __field_ext_packed(_type, _item, _filter_type) { \
.type = #_type, .name = #_item, \
.size = sizeof(_type), .align = 1, \
is_signed_type(_type), .filter_type = _filter_type },
#undef __field #undef __field
#define __field(_type, _item) __field_ext(_type, _item, FILTER_OTHER) #define __field(_type, _item) __field_ext(_type, _item, FILTER_OTHER)
...@@ -94,6 +104,9 @@ static void __always_unused ____ftrace_check_##name(void) \ ...@@ -94,6 +104,9 @@ static void __always_unused ____ftrace_check_##name(void) \
#undef __field_desc #undef __field_desc
#define __field_desc(_type, _container, _item) __field_ext(_type, _item, FILTER_OTHER) #define __field_desc(_type, _container, _item) __field_ext(_type, _item, FILTER_OTHER)
#undef __field_packed
#define __field_packed(_type, _container, _item) __field_ext_packed(_type, _item, FILTER_OTHER)
#undef __array #undef __array
#define __array(_type, _item, _len) { \ #define __array(_type, _item, _len) { \
.type = #_type"["__stringify(_len)"]", .name = #_item, \ .type = #_type"["__stringify(_len)"]", .name = #_item, \
...@@ -129,6 +142,9 @@ static struct trace_event_fields ftrace_event_fields_##name[] = { \ ...@@ -129,6 +142,9 @@ static struct trace_event_fields ftrace_event_fields_##name[] = { \
#undef __field_desc #undef __field_desc
#define __field_desc(type, container, item) #define __field_desc(type, container, item)
#undef __field_packed
#define __field_packed(type, container, item)
#undef __array #undef __array
#define __array(type, item, len) #define __array(type, item, len)
......
...@@ -42,7 +42,7 @@ static int allocate_ftrace_ops(struct trace_array *tr) ...@@ -42,7 +42,7 @@ static int allocate_ftrace_ops(struct trace_array *tr)
if (!ops) if (!ops)
return -ENOMEM; return -ENOMEM;
/* Currently only the non stack verision is supported */ /* Currently only the non stack version is supported */
ops->func = function_trace_call; ops->func = function_trace_call;
ops->flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_PID; ops->flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_PID;
......
...@@ -639,8 +639,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size, ...@@ -639,8 +639,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM) || if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM ||
parg->count) { code->op == FETCH_OP_DATA) || parg->count) {
/* /*
* IMM, DATA and COMM is pointing actual address, those * IMM, DATA and COMM is pointing actual address, those
* must be kept, and if parg->count != 0, this is an * must be kept, and if parg->count != 0, this is an
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/workqueue.h>
/* /*
* Any file that uses trace points, must include the header. * Any file that uses trace points, must include the header.
...@@ -20,6 +21,16 @@ struct trace_array *tr; ...@@ -20,6 +21,16 @@ struct trace_array *tr;
static void mytimer_handler(struct timer_list *unused); static void mytimer_handler(struct timer_list *unused);
static struct task_struct *simple_tsk; static struct task_struct *simple_tsk;
static void trace_work_fn(struct work_struct *work)
{
/*
* Disable tracing for event "sample_event".
*/
trace_array_set_clr_event(tr, "sample-subsystem", "sample_event",
false);
}
static DECLARE_WORK(trace_work, trace_work_fn);
/* /*
* mytimer: Timer setup to disable tracing for event "sample_event". This * mytimer: Timer setup to disable tracing for event "sample_event". This
* timer is only for the purposes of the sample module to demonstrate access of * timer is only for the purposes of the sample module to demonstrate access of
...@@ -29,11 +40,7 @@ static DEFINE_TIMER(mytimer, mytimer_handler); ...@@ -29,11 +40,7 @@ static DEFINE_TIMER(mytimer, mytimer_handler);
static void mytimer_handler(struct timer_list *unused) static void mytimer_handler(struct timer_list *unused)
{ {
/* schedule_work(&trace_work);
* Disable tracing for event "sample_event".
*/
trace_array_set_clr_event(tr, "sample-subsystem", "sample_event",
false);
} }
static void simple_thread_func(int count) static void simple_thread_func(int count)
...@@ -76,6 +83,7 @@ static int simple_thread(void *arg) ...@@ -76,6 +83,7 @@ static int simple_thread(void *arg)
simple_thread_func(count++); simple_thread_func(count++);
del_timer(&mytimer); del_timer(&mytimer);
cancel_work_sync(&trace_work);
/* /*
* trace_array_put() decrements the reference counter associated with * trace_array_put() decrements the reference counter associated with
...@@ -107,8 +115,12 @@ static int __init sample_trace_array_init(void) ...@@ -107,8 +115,12 @@ static int __init sample_trace_array_init(void)
trace_printk_init_buffers(); trace_printk_init_buffers();
simple_tsk = kthread_run(simple_thread, NULL, "sample-instance"); simple_tsk = kthread_run(simple_thread, NULL, "sample-instance");
if (IS_ERR(simple_tsk)) if (IS_ERR(simple_tsk)) {
trace_array_put(tr);
trace_array_destroy(tr);
return -1; return -1;
}
return 0; return 0;
} }
......
...@@ -29,6 +29,11 @@ ...@@ -29,6 +29,11 @@
#undef has_rel_mcount #undef has_rel_mcount
#undef tot_relsize #undef tot_relsize
#undef get_mcountsym #undef get_mcountsym
#undef find_symtab
#undef get_shnum
#undef set_shnum
#undef get_shstrndx
#undef get_symindex
#undef get_sym_str_and_relp #undef get_sym_str_and_relp
#undef do_func #undef do_func
#undef Elf_Addr #undef Elf_Addr
...@@ -58,6 +63,11 @@ ...@@ -58,6 +63,11 @@
# define __has_rel_mcount __has64_rel_mcount # define __has_rel_mcount __has64_rel_mcount
# define has_rel_mcount has64_rel_mcount # define has_rel_mcount has64_rel_mcount
# define tot_relsize tot64_relsize # define tot_relsize tot64_relsize
# define find_symtab find_symtab64
# define get_shnum get_shnum64
# define set_shnum set_shnum64
# define get_shstrndx get_shstrndx64
# define get_symindex get_symindex64
# define get_sym_str_and_relp get_sym_str_and_relp_64 # define get_sym_str_and_relp get_sym_str_and_relp_64
# define do_func do64 # define do_func do64
# define get_mcountsym get_mcountsym_64 # define get_mcountsym get_mcountsym_64
...@@ -91,6 +101,11 @@ ...@@ -91,6 +101,11 @@
# define __has_rel_mcount __has32_rel_mcount # define __has_rel_mcount __has32_rel_mcount
# define has_rel_mcount has32_rel_mcount # define has_rel_mcount has32_rel_mcount
# define tot_relsize tot32_relsize # define tot_relsize tot32_relsize
# define find_symtab find_symtab32
# define get_shnum get_shnum32
# define set_shnum set_shnum32
# define get_shstrndx get_shstrndx32
# define get_symindex get_symindex32
# define get_sym_str_and_relp get_sym_str_and_relp_32 # define get_sym_str_and_relp get_sym_str_and_relp_32
# define do_func do32 # define do_func do32
# define get_mcountsym get_mcountsym_32 # define get_mcountsym get_mcountsym_32
...@@ -173,6 +188,67 @@ static int MIPS_is_fake_mcount(Elf_Rel const *rp) ...@@ -173,6 +188,67 @@ static int MIPS_is_fake_mcount(Elf_Rel const *rp)
return is_fake; return is_fake;
} }
static unsigned int get_symindex(Elf_Sym const *sym, Elf32_Word const *symtab,
Elf32_Word const *symtab_shndx)
{
unsigned long offset;
int index;
if (sym->st_shndx != SHN_XINDEX)
return w2(sym->st_shndx);
offset = (unsigned long)sym - (unsigned long)symtab;
index = offset / sizeof(*sym);
return w(symtab_shndx[index]);
}
static unsigned int get_shnum(Elf_Ehdr const *ehdr, Elf_Shdr const *shdr0)
{
if (shdr0 && !ehdr->e_shnum)
return w(shdr0->sh_size);
return w2(ehdr->e_shnum);
}
static void set_shnum(Elf_Ehdr *ehdr, Elf_Shdr *shdr0, unsigned int new_shnum)
{
if (new_shnum >= SHN_LORESERVE) {
ehdr->e_shnum = 0;
shdr0->sh_size = w(new_shnum);
} else
ehdr->e_shnum = w2(new_shnum);
}
static int get_shstrndx(Elf_Ehdr const *ehdr, Elf_Shdr const *shdr0)
{
if (ehdr->e_shstrndx != SHN_XINDEX)
return w2(ehdr->e_shstrndx);
return w(shdr0->sh_link);
}
static void find_symtab(Elf_Ehdr *const ehdr, Elf_Shdr const *shdr0,
unsigned const nhdr, Elf32_Word **symtab,
Elf32_Word **symtab_shndx)
{
Elf_Shdr const *relhdr;
unsigned k;
*symtab = NULL;
*symtab_shndx = NULL;
for (relhdr = shdr0, k = nhdr; k; --k, ++relhdr) {
if (relhdr->sh_type == SHT_SYMTAB)
*symtab = (void *)ehdr + relhdr->sh_offset;
else if (relhdr->sh_type == SHT_SYMTAB_SHNDX)
*symtab_shndx = (void *)ehdr + relhdr->sh_offset;
if (*symtab && *symtab_shndx)
break;
}
}
/* Append the new shstrtab, Elf_Shdr[], __mcount_loc and its relocations. */ /* Append the new shstrtab, Elf_Shdr[], __mcount_loc and its relocations. */
static int append_func(Elf_Ehdr *const ehdr, static int append_func(Elf_Ehdr *const ehdr,
Elf_Shdr *const shstr, Elf_Shdr *const shstr,
...@@ -188,10 +264,12 @@ static int append_func(Elf_Ehdr *const ehdr, ...@@ -188,10 +264,12 @@ static int append_func(Elf_Ehdr *const ehdr,
char const *mc_name = (sizeof(Elf_Rela) == rel_entsize) char const *mc_name = (sizeof(Elf_Rela) == rel_entsize)
? ".rela__mcount_loc" ? ".rela__mcount_loc"
: ".rel__mcount_loc"; : ".rel__mcount_loc";
unsigned const old_shnum = w2(ehdr->e_shnum);
uint_t const old_shoff = _w(ehdr->e_shoff); uint_t const old_shoff = _w(ehdr->e_shoff);
uint_t const old_shstr_sh_size = _w(shstr->sh_size); uint_t const old_shstr_sh_size = _w(shstr->sh_size);
uint_t const old_shstr_sh_offset = _w(shstr->sh_offset); uint_t const old_shstr_sh_offset = _w(shstr->sh_offset);
Elf_Shdr *const shdr0 = (Elf_Shdr *)(old_shoff + (void *)ehdr);
unsigned int const old_shnum = get_shnum(ehdr, shdr0);
unsigned int const new_shnum = 2 + old_shnum; /* {.rel,}__mcount_loc */
uint_t t = 1 + strlen(mc_name) + _w(shstr->sh_size); uint_t t = 1 + strlen(mc_name) + _w(shstr->sh_size);
uint_t new_e_shoff; uint_t new_e_shoff;
...@@ -201,6 +279,8 @@ static int append_func(Elf_Ehdr *const ehdr, ...@@ -201,6 +279,8 @@ static int append_func(Elf_Ehdr *const ehdr,
t += (_align & -t); /* word-byte align */ t += (_align & -t); /* word-byte align */
new_e_shoff = t; new_e_shoff = t;
set_shnum(ehdr, shdr0, new_shnum);
/* body for new shstrtab */ /* body for new shstrtab */
if (ulseek(sb.st_size, SEEK_SET) < 0) if (ulseek(sb.st_size, SEEK_SET) < 0)
return -1; return -1;
...@@ -255,7 +335,6 @@ static int append_func(Elf_Ehdr *const ehdr, ...@@ -255,7 +335,6 @@ static int append_func(Elf_Ehdr *const ehdr,
return -1; return -1;
ehdr->e_shoff = _w(new_e_shoff); ehdr->e_shoff = _w(new_e_shoff);
ehdr->e_shnum = w2(2 + w2(ehdr->e_shnum)); /* {.rel,}__mcount_loc */
if (ulseek(0, SEEK_SET) < 0) if (ulseek(0, SEEK_SET) < 0)
return -1; return -1;
if (uwrite(ehdr, sizeof(*ehdr)) < 0) if (uwrite(ehdr, sizeof(*ehdr)) < 0)
...@@ -434,6 +513,8 @@ static int find_secsym_ndx(unsigned const txtndx, ...@@ -434,6 +513,8 @@ static int find_secsym_ndx(unsigned const txtndx,
uint_t *const recvalp, uint_t *const recvalp,
unsigned int *sym_index, unsigned int *sym_index,
Elf_Shdr const *const symhdr, Elf_Shdr const *const symhdr,
Elf32_Word const *symtab,
Elf32_Word const *symtab_shndx,
Elf_Ehdr const *const ehdr) Elf_Ehdr const *const ehdr)
{ {
Elf_Sym const *const sym0 = (Elf_Sym const *)(_w(symhdr->sh_offset) Elf_Sym const *const sym0 = (Elf_Sym const *)(_w(symhdr->sh_offset)
...@@ -445,7 +526,7 @@ static int find_secsym_ndx(unsigned const txtndx, ...@@ -445,7 +526,7 @@ static int find_secsym_ndx(unsigned const txtndx,
for (symp = sym0, t = nsym; t; --t, ++symp) { for (symp = sym0, t = nsym; t; --t, ++symp) {
unsigned int const st_bind = ELF_ST_BIND(symp->st_info); unsigned int const st_bind = ELF_ST_BIND(symp->st_info);
if (txtndx == w2(symp->st_shndx) if (txtndx == get_symindex(symp, symtab, symtab_shndx)
/* avoid STB_WEAK */ /* avoid STB_WEAK */
&& (STB_LOCAL == st_bind || STB_GLOBAL == st_bind)) { && (STB_LOCAL == st_bind || STB_GLOBAL == st_bind)) {
/* function symbols on ARM have quirks, avoid them */ /* function symbols on ARM have quirks, avoid them */
...@@ -516,21 +597,23 @@ static unsigned tot_relsize(Elf_Shdr const *const shdr0, ...@@ -516,21 +597,23 @@ static unsigned tot_relsize(Elf_Shdr const *const shdr0,
return totrelsz; return totrelsz;
} }
/* Overall supervision for Elf32 ET_REL file. */ /* Overall supervision for Elf32 ET_REL file. */
static int do_func(Elf_Ehdr *const ehdr, char const *const fname, static int do_func(Elf_Ehdr *const ehdr, char const *const fname,
unsigned const reltype) unsigned const reltype)
{ {
Elf_Shdr *const shdr0 = (Elf_Shdr *)(_w(ehdr->e_shoff) Elf_Shdr *const shdr0 = (Elf_Shdr *)(_w(ehdr->e_shoff)
+ (void *)ehdr); + (void *)ehdr);
unsigned const nhdr = w2(ehdr->e_shnum); unsigned const nhdr = get_shnum(ehdr, shdr0);
Elf_Shdr *const shstr = &shdr0[w2(ehdr->e_shstrndx)]; Elf_Shdr *const shstr = &shdr0[get_shstrndx(ehdr, shdr0)];
char const *const shstrtab = (char const *)(_w(shstr->sh_offset) char const *const shstrtab = (char const *)(_w(shstr->sh_offset)
+ (void *)ehdr); + (void *)ehdr);
Elf_Shdr const *relhdr; Elf_Shdr const *relhdr;
unsigned k; unsigned k;
Elf32_Word *symtab;
Elf32_Word *symtab_shndx;
/* Upper bound on space: assume all relevant relocs are for mcount. */ /* Upper bound on space: assume all relevant relocs are for mcount. */
unsigned totrelsz; unsigned totrelsz;
...@@ -561,6 +644,8 @@ static int do_func(Elf_Ehdr *const ehdr, char const *const fname, ...@@ -561,6 +644,8 @@ static int do_func(Elf_Ehdr *const ehdr, char const *const fname,
return -1; return -1;
} }
find_symtab(ehdr, shdr0, nhdr, &symtab, &symtab_shndx);
for (relhdr = shdr0, k = nhdr; k; --k, ++relhdr) { for (relhdr = shdr0, k = nhdr; k; --k, ++relhdr) {
char const *const txtname = has_rel_mcount(relhdr, shdr0, char const *const txtname = has_rel_mcount(relhdr, shdr0,
shstrtab, fname); shstrtab, fname);
...@@ -577,6 +662,7 @@ static int do_func(Elf_Ehdr *const ehdr, char const *const fname, ...@@ -577,6 +662,7 @@ static int do_func(Elf_Ehdr *const ehdr, char const *const fname,
result = find_secsym_ndx(w(relhdr->sh_info), txtname, result = find_secsym_ndx(w(relhdr->sh_info), txtname,
&recval, &recsym, &recval, &recsym,
&shdr0[symsec_sh_link], &shdr0[symsec_sh_link],
symtab, symtab_shndx,
ehdr); ehdr);
if (result) if (result)
goto out; goto out;
......
...@@ -14,13 +14,18 @@ ...@@ -14,13 +14,18 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/bootconfig.h> #include <linux/bootconfig.h>
static int xbc_show_array(struct xbc_node *node) static int xbc_show_value(struct xbc_node *node)
{ {
const char *val; const char *val;
char q;
int i = 0; int i = 0;
xbc_array_for_each_value(node, val) { xbc_array_for_each_value(node, val) {
printf("\"%s\"%s", val, node->next ? ", " : ";\n"); if (strchr(val, '"'))
q = '\'';
else
q = '"';
printf("%c%s%c%s", q, val, q, node->next ? ", " : ";\n");
i++; i++;
} }
return i; return i;
...@@ -48,10 +53,7 @@ static void xbc_show_compact_tree(void) ...@@ -48,10 +53,7 @@ static void xbc_show_compact_tree(void)
continue; continue;
} else if (cnode && xbc_node_is_value(cnode)) { } else if (cnode && xbc_node_is_value(cnode)) {
printf("%s = ", xbc_node_get_data(node)); printf("%s = ", xbc_node_get_data(node));
if (cnode->next) xbc_show_value(cnode);
xbc_show_array(cnode);
else
printf("\"%s\";\n", xbc_node_get_data(cnode));
} else { } else {
printf("%s;\n", xbc_node_get_data(node)); printf("%s;\n", xbc_node_get_data(node));
} }
...@@ -205,11 +207,13 @@ int show_xbc(const char *path) ...@@ -205,11 +207,13 @@ int show_xbc(const char *path)
} }
ret = load_xbc_from_initrd(fd, &buf); ret = load_xbc_from_initrd(fd, &buf);
if (ret < 0) if (ret < 0) {
pr_err("Failed to load a boot config from initrd: %d\n", ret); pr_err("Failed to load a boot config from initrd: %d\n", ret);
else goto out;
xbc_show_compact_tree(); }
xbc_show_compact_tree();
ret = 0;
out:
close(fd); close(fd);
free(buf); free(buf);
......
...@@ -55,6 +55,9 @@ echo "Apply command test" ...@@ -55,6 +55,9 @@ echo "Apply command test"
xpass $BOOTCONF -a $TEMPCONF $INITRD xpass $BOOTCONF -a $TEMPCONF $INITRD
new_size=$(stat -c %s $INITRD) new_size=$(stat -c %s $INITRD)
echo "Show command test"
xpass $BOOTCONF $INITRD
echo "File size check" echo "File size check"
xpass test $new_size -eq $(expr $bconf_size + $initrd_size + 9 + 12) xpass test $new_size -eq $(expr $bconf_size + $initrd_size + 9 + 12)
...@@ -114,6 +117,13 @@ xpass grep -q "bar" $OUTFILE ...@@ -114,6 +117,13 @@ xpass grep -q "bar" $OUTFILE
xpass grep -q "baz" $OUTFILE xpass grep -q "baz" $OUTFILE
xpass grep -q "qux" $OUTFILE xpass grep -q "qux" $OUTFILE
echo "Double/single quotes test"
echo "key = '\"string\"';" > $TEMPCONF
$BOOTCONF -a $TEMPCONF $INITRD
$BOOTCONF $INITRD > $TEMPCONF
cat $TEMPCONF
xpass grep \'\"string\"\' $TEMPCONF
echo "=== expected failure cases ===" echo "=== expected failure cases ==="
for i in samples/bad-* ; do for i in samples/bad-* ; do
xfail $BOOTCONF -a $i $INITRD xfail $BOOTCONF -a $i $INITRD
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment