forked from Minki/linux
A few fixes and small cleanups for tracing:
- 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 -----BEGIN PGP SIGNATURE----- iIoEABYIADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCXu1jrRQccm9zdGVkdEBn b29kbWlzLm9yZwAKCRAp5XQQmuv6qoCMAP91nOccE3X+Nvc3zET3isDWnl1tWJxk icsBgN/JwBRuTAD/dnWTHIWM2/5lTiagvyVsmINdJHP6JLr8T7dpN9tlxAQ= =Cuo7 -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
8b6ddd10d6
@ -754,16 +754,11 @@ asm(
|
||||
NOKPROBE_SYMBOL(kretprobe_trampoline);
|
||||
STACK_FRAME_NON_STANDARD(kretprobe_trampoline);
|
||||
|
||||
static struct kprobe kretprobe_kprobe = {
|
||||
.addr = (void *)kretprobe_trampoline,
|
||||
};
|
||||
|
||||
/*
|
||||
* Called from kretprobe_trampoline
|
||||
*/
|
||||
__used __visible void *trampoline_handler(struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_ctlblk *kcb;
|
||||
struct kretprobe_instance *ri = NULL;
|
||||
struct hlist_head *head, empty_rp;
|
||||
struct hlist_node *tmp;
|
||||
@ -773,16 +768,12 @@ __used __visible void *trampoline_handler(struct pt_regs *regs)
|
||||
void *frame_pointer;
|
||||
bool skipped = false;
|
||||
|
||||
preempt_disable();
|
||||
|
||||
/*
|
||||
* Set a dummy kprobe for avoiding kretprobe recursion.
|
||||
* Since kretprobe never run in kprobe handler, kprobe must not
|
||||
* be running at this point.
|
||||
*/
|
||||
kcb = get_kprobe_ctlblk();
|
||||
__this_cpu_write(current_kprobe, &kretprobe_kprobe);
|
||||
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
|
||||
kprobe_busy_begin();
|
||||
|
||||
INIT_HLIST_HEAD(&empty_rp);
|
||||
kretprobe_hash_lock(current, &head, &flags);
|
||||
@ -858,7 +849,7 @@ __used __visible void *trampoline_handler(struct pt_regs *regs)
|
||||
__this_cpu_write(current_kprobe, &ri->rp->kp);
|
||||
ri->ret_addr = correct_ret_addr;
|
||||
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);
|
||||
@ -874,8 +865,7 @@ __used __visible void *trampoline_handler(struct pt_regs *regs)
|
||||
|
||||
kretprobe_hash_unlock(current, &flags);
|
||||
|
||||
__this_cpu_write(current_kprobe, NULL);
|
||||
preempt_enable();
|
||||
kprobe_busy_end();
|
||||
|
||||
hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
|
||||
hlist_del(&ri->hlist);
|
||||
|
@ -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)
|
||||
{
|
||||
struct xbc_node *leaf, *vnode;
|
||||
const char *val;
|
||||
char *key, *end = dst + size;
|
||||
const char *val;
|
||||
char q;
|
||||
int ret = 0;
|
||||
|
||||
key = kzalloc(XBC_KEYLEN_MAX, GFP_KERNEL);
|
||||
@ -41,16 +42,20 @@ static int __init copy_xbc_key_value_list(char *dst, size_t size)
|
||||
break;
|
||||
dst += ret;
|
||||
vnode = xbc_node_get_child(leaf);
|
||||
if (vnode && xbc_node_is_array(vnode)) {
|
||||
if (vnode) {
|
||||
xbc_array_for_each_value(vnode, val) {
|
||||
ret = snprintf(dst, rest(dst, end), "\"%s\"%s",
|
||||
val, vnode->next ? ", " : "\n");
|
||||
if (strchr(val, '"'))
|
||||
q = '\'';
|
||||
else
|
||||
q = '"';
|
||||
ret = snprintf(dst, rest(dst, end), "%c%s%c%s",
|
||||
q, val, q, vnode->next ? ", " : "\n");
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
dst += ret;
|
||||
}
|
||||
} else {
|
||||
ret = snprintf(dst, rest(dst, end), "\"%s\"\n", val);
|
||||
ret = snprintf(dst, rest(dst, end), "\"\"\n");
|
||||
if (ret < 0)
|
||||
break;
|
||||
dst += ret;
|
||||
|
@ -350,6 +350,10 @@ static inline struct kprobe_ctlblk *get_kprobe_ctlblk(void)
|
||||
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);
|
||||
int register_kprobe(struct kprobe *p);
|
||||
void unregister_kprobe(struct kprobe *p);
|
||||
|
@ -46,6 +46,11 @@
|
||||
|
||||
|
||||
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 kretprobe_inst_table[KPROBE_TABLE_SIZE];
|
||||
|
||||
@ -326,7 +331,8 @@ struct kprobe *get_kprobe(void *addr)
|
||||
struct kprobe *p;
|
||||
|
||||
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)
|
||||
return p;
|
||||
}
|
||||
@ -586,11 +592,12 @@ static void kprobe_optimizer(struct work_struct *work)
|
||||
mutex_unlock(&module_mutex);
|
||||
mutex_unlock(&text_mutex);
|
||||
cpus_read_unlock();
|
||||
mutex_unlock(&kprobe_mutex);
|
||||
|
||||
/* Step 5: Kick optimizer again if needed */
|
||||
if (!list_empty(&optimizing_list) || !list_empty(&unoptimizing_list))
|
||||
kick_kprobe_optimizer();
|
||||
|
||||
mutex_unlock(&kprobe_mutex);
|
||||
}
|
||||
|
||||
/* Wait for completing optimization and unoptimization */
|
||||
@ -668,8 +675,6 @@ static void force_unoptimize_kprobe(struct optimized_kprobe *op)
|
||||
lockdep_assert_cpus_held();
|
||||
arch_unoptimize_kprobe(op);
|
||||
op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
|
||||
if (kprobe_disabled(&op->kp))
|
||||
arch_disarm_kprobe(&op->kp);
|
||||
}
|
||||
|
||||
/* Unoptimize a kprobe if p is optimized */
|
||||
@ -849,7 +854,7 @@ static void optimize_all_kprobes(void)
|
||||
kprobes_allow_optimization = true;
|
||||
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
|
||||
head = &kprobe_table[i];
|
||||
hlist_for_each_entry_rcu(p, head, hlist)
|
||||
hlist_for_each_entry(p, head, hlist)
|
||||
if (!kprobe_disabled(p))
|
||||
optimize_kprobe(p);
|
||||
}
|
||||
@ -876,7 +881,7 @@ static void unoptimize_all_kprobes(void)
|
||||
kprobes_allow_optimization = false;
|
||||
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
|
||||
head = &kprobe_table[i];
|
||||
hlist_for_each_entry_rcu(p, head, hlist) {
|
||||
hlist_for_each_entry(p, head, hlist) {
|
||||
if (!kprobe_disabled(p))
|
||||
unoptimize_kprobe(p, false);
|
||||
}
|
||||
@ -1236,6 +1241,26 @@ __releases(hlist_lock)
|
||||
}
|
||||
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,
|
||||
* so that we can recycle any function-return probe instances associated
|
||||
@ -1253,6 +1278,8 @@ void kprobe_flush_task(struct task_struct *tk)
|
||||
/* Early boot. kretprobe_table_locks not yet initialized. */
|
||||
return;
|
||||
|
||||
kprobe_busy_begin();
|
||||
|
||||
INIT_HLIST_HEAD(&empty_rp);
|
||||
hash = hash_ptr(tk, KPROBE_HASH_BITS);
|
||||
head = &kretprobe_inst_table[hash];
|
||||
@ -1266,6 +1293,8 @@ void kprobe_flush_task(struct task_struct *tk)
|
||||
hlist_del(&ri->hlist);
|
||||
kfree(ri);
|
||||
}
|
||||
|
||||
kprobe_busy_end();
|
||||
}
|
||||
NOKPROBE_SYMBOL(kprobe_flush_task);
|
||||
|
||||
@ -1499,12 +1528,14 @@ static struct kprobe *__get_valid_kprobe(struct kprobe *p)
|
||||
{
|
||||
struct kprobe *ap, *list_p;
|
||||
|
||||
lockdep_assert_held(&kprobe_mutex);
|
||||
|
||||
ap = get_kprobe(p->addr);
|
||||
if (unlikely(!ap))
|
||||
return NULL;
|
||||
|
||||
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)
|
||||
/* kprobe p is a valid probe */
|
||||
goto valid;
|
||||
@ -1669,7 +1700,9 @@ static int aggr_kprobe_disabled(struct kprobe *ap)
|
||||
{
|
||||
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))
|
||||
/*
|
||||
* There is an active probe on the list.
|
||||
@ -1748,7 +1781,7 @@ static int __unregister_kprobe_top(struct kprobe *p)
|
||||
else {
|
||||
/* If disabling probe has special handlers, update aggrprobe */
|
||||
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))
|
||||
goto noclean;
|
||||
}
|
||||
@ -2062,13 +2095,15 @@ static void kill_kprobe(struct kprobe *p)
|
||||
{
|
||||
struct kprobe *kp;
|
||||
|
||||
lockdep_assert_held(&kprobe_mutex);
|
||||
|
||||
p->flags |= KPROBE_FLAG_GONE;
|
||||
if (kprobe_aggrprobe(p)) {
|
||||
/*
|
||||
* If this is an aggr_kprobe, we have to list all the
|
||||
* 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;
|
||||
p->post_handler = NULL;
|
||||
kill_optimized_kprobe(p);
|
||||
@ -2312,7 +2347,7 @@ static int kprobes_module_callback(struct notifier_block *nb,
|
||||
mutex_lock(&kprobe_mutex);
|
||||
for (i = 0; i < KPROBE_TABLE_SIZE; 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) ||
|
||||
(checkcore &&
|
||||
within_module_core((unsigned long)p->addr, mod))) {
|
||||
@ -2550,7 +2585,7 @@ static int arm_all_kprobes(void)
|
||||
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
|
||||
head = &kprobe_table[i];
|
||||
/* 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)) {
|
||||
err = arm_kprobe(p);
|
||||
if (err) {
|
||||
@ -2593,7 +2628,7 @@ static int disarm_all_kprobes(void)
|
||||
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
|
||||
head = &kprobe_table[i];
|
||||
/* 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)) {
|
||||
err = disarm_kprobe(p, false);
|
||||
if (err) {
|
||||
|
@ -2260,7 +2260,7 @@ ftrace_find_tramp_ops_next(struct dyn_ftrace *rec,
|
||||
|
||||
if (hash_contains_ip(ip, op->func_hash))
|
||||
return op;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -3599,7 +3599,7 @@ static int t_show(struct seq_file *m, void *v)
|
||||
if (direct)
|
||||
seq_printf(m, "\n\tdirect-->%pS", (void *)direct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
seq_putc(m, '\n');
|
||||
|
||||
@ -7151,6 +7151,10 @@ static int pid_open(struct inode *inode, struct file *file, int type)
|
||||
case TRACE_NO_PIDS:
|
||||
seq_ops = &ftrace_no_pid_sops;
|
||||
break;
|
||||
default:
|
||||
trace_array_put(tr);
|
||||
WARN_ON_ONCE(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = seq_open(file, seq_ops);
|
||||
@ -7229,6 +7233,10 @@ pid_write(struct file *filp, const char __user *ubuf,
|
||||
other_pids = rcu_dereference_protected(tr->function_pids,
|
||||
lockdep_is_held(&ftrace_lock));
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
WARN_ON_ONCE(1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
void tracing_iter_reset(struct trace_iterator *iter, int cpu)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer_iter *buf_iter;
|
||||
unsigned long entries = 0;
|
||||
u64 ts;
|
||||
@ -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
|
||||
* 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)
|
||||
break;
|
||||
entries++;
|
||||
|
@ -61,6 +61,9 @@ enum trace_type {
|
||||
#undef __field_desc
|
||||
#define __field_desc(type, container, item)
|
||||
|
||||
#undef __field_packed
|
||||
#define __field_packed(type, container, item)
|
||||
|
||||
#undef __array
|
||||
#define __array(type, item, size) type item[size];
|
||||
|
||||
|
@ -78,8 +78,8 @@ FTRACE_ENTRY_PACKED(funcgraph_entry, ftrace_graph_ent_entry,
|
||||
|
||||
F_STRUCT(
|
||||
__field_struct( struct ftrace_graph_ent, graph_ent )
|
||||
__field_desc( unsigned long, graph_ent, func )
|
||||
__field_desc( int, graph_ent, depth )
|
||||
__field_packed( unsigned long, graph_ent, func )
|
||||
__field_packed( int, graph_ent, depth )
|
||||
),
|
||||
|
||||
F_printk("--> %ps (%d)", (void *)__entry->func, __entry->depth)
|
||||
@ -92,11 +92,11 @@ FTRACE_ENTRY_PACKED(funcgraph_exit, ftrace_graph_ret_entry,
|
||||
|
||||
F_STRUCT(
|
||||
__field_struct( struct ftrace_graph_ret, ret )
|
||||
__field_desc( unsigned long, ret, func )
|
||||
__field_desc( unsigned long, ret, overrun )
|
||||
__field_desc( unsigned long long, ret, calltime)
|
||||
__field_desc( unsigned long long, ret, rettime )
|
||||
__field_desc( int, ret, depth )
|
||||
__field_packed( unsigned long, ret, func )
|
||||
__field_packed( unsigned long, ret, overrun )
|
||||
__field_packed( unsigned long long, ret, calltime)
|
||||
__field_packed( unsigned long long, ret, rettime )
|
||||
__field_packed( int, ret, depth )
|
||||
),
|
||||
|
||||
F_printk("<-- %ps (%d) (start: %llx end: %llx) over: %d",
|
||||
|
@ -45,6 +45,9 @@ static int ftrace_event_register(struct trace_event_call *call,
|
||||
#undef __field_desc
|
||||
#define __field_desc(type, container, item) type item;
|
||||
|
||||
#undef __field_packed
|
||||
#define __field_packed(type, container, item) type item;
|
||||
|
||||
#undef __array
|
||||
#define __array(type, item, size) type item[size];
|
||||
|
||||
@ -85,6 +88,13 @@ static void __always_unused ____ftrace_check_##name(void) \
|
||||
.size = sizeof(_type), .align = __alignof__(_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
|
||||
#define __field(_type, _item) __field_ext(_type, _item, FILTER_OTHER)
|
||||
|
||||
@ -94,6 +104,9 @@ static void __always_unused ____ftrace_check_##name(void) \
|
||||
#undef __field_desc
|
||||
#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
|
||||
#define __array(_type, _item, _len) { \
|
||||
.type = #_type"["__stringify(_len)"]", .name = #_item, \
|
||||
@ -129,6 +142,9 @@ static struct trace_event_fields ftrace_event_fields_##name[] = { \
|
||||
#undef __field_desc
|
||||
#define __field_desc(type, container, item)
|
||||
|
||||
#undef __field_packed
|
||||
#define __field_packed(type, container, item)
|
||||
|
||||
#undef __array
|
||||
#define __array(type, item, len)
|
||||
|
||||
|
@ -42,7 +42,7 @@ static int allocate_ftrace_ops(struct trace_array *tr)
|
||||
if (!ops)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Currently only the non stack verision is supported */
|
||||
/* Currently only the non stack version is supported */
|
||||
ops->func = function_trace_call;
|
||||
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,
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM) ||
|
||||
parg->count) {
|
||||
if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM ||
|
||||
code->op == FETCH_OP_DATA) || parg->count) {
|
||||
/*
|
||||
* IMM, DATA and COMM is pointing actual address, those
|
||||
* must be kept, and if parg->count != 0, this is an
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <linux/timer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
/*
|
||||
* Any file that uses trace points, must include the header.
|
||||
@ -20,6 +21,16 @@ struct trace_array *tr;
|
||||
static void mytimer_handler(struct timer_list *unused);
|
||||
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
|
||||
* timer is only for the purposes of the sample module to demonstrate access of
|
||||
@ -29,11 +40,7 @@ static DEFINE_TIMER(mytimer, mytimer_handler);
|
||||
|
||||
static void mytimer_handler(struct timer_list *unused)
|
||||
{
|
||||
/*
|
||||
* Disable tracing for event "sample_event".
|
||||
*/
|
||||
trace_array_set_clr_event(tr, "sample-subsystem", "sample_event",
|
||||
false);
|
||||
schedule_work(&trace_work);
|
||||
}
|
||||
|
||||
static void simple_thread_func(int count)
|
||||
@ -76,6 +83,7 @@ static int simple_thread(void *arg)
|
||||
simple_thread_func(count++);
|
||||
|
||||
del_timer(&mytimer);
|
||||
cancel_work_sync(&trace_work);
|
||||
|
||||
/*
|
||||
* trace_array_put() decrements the reference counter associated with
|
||||
@ -107,8 +115,12 @@ static int __init sample_trace_array_init(void)
|
||||
trace_printk_init_buffers();
|
||||
|
||||
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 0;
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,11 @@
|
||||
#undef has_rel_mcount
|
||||
#undef tot_relsize
|
||||
#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 do_func
|
||||
#undef Elf_Addr
|
||||
@ -58,6 +63,11 @@
|
||||
# define __has_rel_mcount __has64_rel_mcount
|
||||
# define has_rel_mcount has64_rel_mcount
|
||||
# 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 do_func do64
|
||||
# define get_mcountsym get_mcountsym_64
|
||||
@ -91,6 +101,11 @@
|
||||
# define __has_rel_mcount __has32_rel_mcount
|
||||
# define has_rel_mcount has32_rel_mcount
|
||||
# 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 do_func do32
|
||||
# define get_mcountsym get_mcountsym_32
|
||||
@ -173,6 +188,67 @@ static int MIPS_is_fake_mcount(Elf_Rel const *rp)
|
||||
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. */
|
||||
static int append_func(Elf_Ehdr *const ehdr,
|
||||
Elf_Shdr *const shstr,
|
||||
@ -188,10 +264,12 @@ static int append_func(Elf_Ehdr *const ehdr,
|
||||
char const *mc_name = (sizeof(Elf_Rela) == rel_entsize)
|
||||
? ".rela__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_shstr_sh_size = _w(shstr->sh_size);
|
||||
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 new_e_shoff;
|
||||
|
||||
@ -201,6 +279,8 @@ static int append_func(Elf_Ehdr *const ehdr,
|
||||
t += (_align & -t); /* word-byte align */
|
||||
new_e_shoff = t;
|
||||
|
||||
set_shnum(ehdr, shdr0, new_shnum);
|
||||
|
||||
/* body for new shstrtab */
|
||||
if (ulseek(sb.st_size, SEEK_SET) < 0)
|
||||
return -1;
|
||||
@ -255,7 +335,6 @@ static int append_func(Elf_Ehdr *const ehdr,
|
||||
return -1;
|
||||
|
||||
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)
|
||||
return -1;
|
||||
if (uwrite(ehdr, sizeof(*ehdr)) < 0)
|
||||
@ -434,6 +513,8 @@ static int find_secsym_ndx(unsigned const txtndx,
|
||||
uint_t *const recvalp,
|
||||
unsigned int *sym_index,
|
||||
Elf_Shdr const *const symhdr,
|
||||
Elf32_Word const *symtab,
|
||||
Elf32_Word const *symtab_shndx,
|
||||
Elf_Ehdr const *const ehdr)
|
||||
{
|
||||
Elf_Sym const *const sym0 = (Elf_Sym const *)(_w(symhdr->sh_offset)
|
||||
@ -445,7 +526,7 @@ static int find_secsym_ndx(unsigned const txtndx,
|
||||
for (symp = sym0, t = nsym; t; --t, ++symp) {
|
||||
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 */
|
||||
&& (STB_LOCAL == st_bind || STB_GLOBAL == st_bind)) {
|
||||
/* function symbols on ARM have quirks, avoid them */
|
||||
@ -516,21 +597,23 @@ static unsigned tot_relsize(Elf_Shdr const *const shdr0,
|
||||
return totrelsz;
|
||||
}
|
||||
|
||||
|
||||
/* Overall supervision for Elf32 ET_REL file. */
|
||||
static int do_func(Elf_Ehdr *const ehdr, char const *const fname,
|
||||
unsigned const reltype)
|
||||
{
|
||||
Elf_Shdr *const shdr0 = (Elf_Shdr *)(_w(ehdr->e_shoff)
|
||||
+ (void *)ehdr);
|
||||
unsigned const nhdr = w2(ehdr->e_shnum);
|
||||
Elf_Shdr *const shstr = &shdr0[w2(ehdr->e_shstrndx)];
|
||||
unsigned const nhdr = get_shnum(ehdr, shdr0);
|
||||
Elf_Shdr *const shstr = &shdr0[get_shstrndx(ehdr, shdr0)];
|
||||
char const *const shstrtab = (char const *)(_w(shstr->sh_offset)
|
||||
+ (void *)ehdr);
|
||||
|
||||
Elf_Shdr const *relhdr;
|
||||
unsigned k;
|
||||
|
||||
Elf32_Word *symtab;
|
||||
Elf32_Word *symtab_shndx;
|
||||
|
||||
/* Upper bound on space: assume all relevant relocs are for mcount. */
|
||||
unsigned totrelsz;
|
||||
|
||||
@ -561,6 +644,8 @@ static int do_func(Elf_Ehdr *const ehdr, char const *const fname,
|
||||
return -1;
|
||||
}
|
||||
|
||||
find_symtab(ehdr, shdr0, nhdr, &symtab, &symtab_shndx);
|
||||
|
||||
for (relhdr = shdr0, k = nhdr; k; --k, ++relhdr) {
|
||||
char const *const txtname = has_rel_mcount(relhdr, shdr0,
|
||||
shstrtab, 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,
|
||||
&recval, &recsym,
|
||||
&shdr0[symsec_sh_link],
|
||||
symtab, symtab_shndx,
|
||||
ehdr);
|
||||
if (result)
|
||||
goto out;
|
||||
|
@ -14,13 +14,18 @@
|
||||
#include <linux/kernel.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;
|
||||
char q;
|
||||
int i = 0;
|
||||
|
||||
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++;
|
||||
}
|
||||
return i;
|
||||
@ -48,10 +53,7 @@ static void xbc_show_compact_tree(void)
|
||||
continue;
|
||||
} else if (cnode && xbc_node_is_value(cnode)) {
|
||||
printf("%s = ", xbc_node_get_data(node));
|
||||
if (cnode->next)
|
||||
xbc_show_array(cnode);
|
||||
else
|
||||
printf("\"%s\";\n", xbc_node_get_data(cnode));
|
||||
xbc_show_value(cnode);
|
||||
} else {
|
||||
printf("%s;\n", xbc_node_get_data(node));
|
||||
}
|
||||
@ -205,11 +207,13 @@ int show_xbc(const char *path)
|
||||
}
|
||||
|
||||
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);
|
||||
else
|
||||
xbc_show_compact_tree();
|
||||
|
||||
goto out;
|
||||
}
|
||||
xbc_show_compact_tree();
|
||||
ret = 0;
|
||||
out:
|
||||
close(fd);
|
||||
free(buf);
|
||||
|
||||
|
@ -55,6 +55,9 @@ echo "Apply command test"
|
||||
xpass $BOOTCONF -a $TEMPCONF $INITRD
|
||||
new_size=$(stat -c %s $INITRD)
|
||||
|
||||
echo "Show command test"
|
||||
xpass $BOOTCONF $INITRD
|
||||
|
||||
echo "File size check"
|
||||
xpass test $new_size -eq $(expr $bconf_size + $initrd_size + 9 + 12)
|
||||
|
||||
@ -114,6 +117,13 @@ xpass grep -q "bar" $OUTFILE
|
||||
xpass grep -q "baz" $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 ==="
|
||||
for i in samples/bad-* ; do
|
||||
xfail $BOOTCONF -a $i $INITRD
|
||||
|
Loading…
Reference in New Issue
Block a user