You can subscribe to this list here.
| 2009 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(32) |
Jun
(66) |
Jul
(102) |
Aug
(78) |
Sep
(106) |
Oct
(137) |
Nov
(147) |
Dec
(147) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2010 |
Jan
(71) |
Feb
(139) |
Mar
(86) |
Apr
(76) |
May
(57) |
Jun
(10) |
Jul
(12) |
Aug
(6) |
Sep
(8) |
Oct
(12) |
Nov
(12) |
Dec
(18) |
| 2011 |
Jan
(16) |
Feb
(19) |
Mar
(3) |
Apr
(1) |
May
(16) |
Jun
(17) |
Jul
(74) |
Aug
(22) |
Sep
(18) |
Oct
(24) |
Nov
(21) |
Dec
(30) |
| 2012 |
Jan
(31) |
Feb
(16) |
Mar
(22) |
Apr
(25) |
May
(18) |
Jun
(13) |
Jul
(83) |
Aug
(49) |
Sep
(20) |
Oct
(60) |
Nov
(35) |
Dec
(28) |
| 2013 |
Jan
(39) |
Feb
(61) |
Mar
(35) |
Apr
(21) |
May
(45) |
Jun
(56) |
Jul
(20) |
Aug
(9) |
Sep
(10) |
Oct
(31) |
Nov
(8) |
Dec
(4) |
| 2014 |
Jan
(6) |
Feb
(7) |
Mar
(7) |
Apr
(6) |
May
(4) |
Jun
(8) |
Jul
(5) |
Aug
(2) |
Sep
(4) |
Oct
(4) |
Nov
(11) |
Dec
(5) |
| 2015 |
Jan
(4) |
Feb
(4) |
Mar
(3) |
Apr
(4) |
May
(9) |
Jun
(4) |
Jul
(15) |
Aug
(8) |
Sep
(16) |
Oct
(18) |
Nov
(15) |
Dec
(7) |
| 2016 |
Jan
(20) |
Feb
(9) |
Mar
(15) |
Apr
(24) |
May
(16) |
Jun
(28) |
Jul
(22) |
Aug
(23) |
Sep
(18) |
Oct
(30) |
Nov
(40) |
Dec
(9) |
| 2017 |
Jan
(1) |
Feb
(8) |
Mar
(37) |
Apr
(26) |
May
(25) |
Jun
(46) |
Jul
(24) |
Aug
(9) |
Sep
|
Oct
|
Nov
|
Dec
|
|
From: Masami H. <mhi...@re...> - 2009-12-18 18:11:50
|
Add /proc/sys/debug/kprobes-optimization sysctl which enables and disables
kprobes jump optimization on the fly for debugging.
Changes in v7:
- Remove ctl_name = CTL_UNNUMBERED for upstream compatibility.
Changes in v6:
- Update comments and coding style.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ananth N Mavinakayanahalli <an...@in...>
Cc: Ingo Molnar <mi...@el...>
Cc: Jim Keniston <jke...@us...>
Cc: Srikar Dronamraju <sr...@li...>
Cc: Christoph Hellwig <hc...@in...>
Cc: Steven Rostedt <ro...@go...>
Cc: Frederic Weisbecker <fwe...@gm...>
Cc: H. Peter Anvin <hp...@zy...>
Cc: Anders Kaseorg <an...@ks...>
Cc: Tim Abbott <ta...@ks...>
Cc: Andi Kleen <an...@fi...>
Cc: Jason Baron <jb...@re...>
Cc: Mathieu Desnoyers <co...@kr...>
---
include/linux/kprobes.h | 8 ++++
kernel/kprobes.c | 89 +++++++++++++++++++++++++++++++++++++++++++++--
kernel/sysctl.c | 12 ++++++
3 files changed, 106 insertions(+), 3 deletions(-)
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index aed1f95..e7d1b2e 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -283,6 +283,14 @@ extern int arch_within_optimized_kprobe(struct optimized_kprobe *op,
unsigned long addr);
extern void opt_pre_handler(struct kprobe *p, struct pt_regs *regs);
+
+#ifdef CONFIG_SYSCTL
+extern int sysctl_kprobes_optimization;
+extern int proc_kprobes_optimization_handler(struct ctl_table *table,
+ int write, void __user *buffer,
+ size_t *length, loff_t *ppos);
+#endif
+
#endif /* CONFIG_OPTPROBES */
/* Get the kprobe at this addr (if any) - called with preemption disabled */
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 4b1da1e..079e116 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -42,6 +42,7 @@
#include <linux/freezer.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
+#include <linux/sysctl.h>
#include <linux/kdebug.h>
#include <linux/memory.h>
#include <linux/cpu.h>
@@ -383,6 +384,9 @@ static inline void copy_kprobe(struct kprobe *old_p, struct kprobe *p)
}
#ifdef CONFIG_OPTPROBES
+/* NOTE: change this value only with kprobe_mutex held */
+static bool kprobes_allow_optimization;
+
/*
* Call all pre_handler on the list, but ignores its return value.
* This must be called from arch-dep optimized caller.
@@ -447,7 +451,7 @@ static __kprobes void kprobe_optimizer(struct work_struct *work)
/* Lock modules while optimizing kprobes */
mutex_lock(&module_mutex);
mutex_lock(&kprobe_mutex);
- if (kprobes_all_disarmed)
+ if (kprobes_all_disarmed || !kprobes_allow_optimization)
goto end;
/*
@@ -490,7 +494,7 @@ static __kprobes void optimize_kprobe(struct kprobe *p)
struct optimized_kprobe *op;
/* Check if the kprobe is disabled or not ready for optimization. */
- if (!kprobe_optready(p) ||
+ if (!kprobe_optready(p) || !kprobes_allow_optimization ||
(kprobe_disabled(p) || kprobes_all_disarmed))
return;
@@ -606,6 +610,81 @@ static __kprobes void try_to_optimize_kprobe(struct kprobe *p)
init_aggr_kprobe(ap, p);
optimize_kprobe(ap);
}
+
+#ifdef CONFIG_SYSCTL
+static void __kprobes optimize_all_kprobes(void)
+{
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct kprobe *p;
+ unsigned int i;
+
+ /* If optimization is already allowed, just return */
+ if (kprobes_allow_optimization)
+ return;
+
+ kprobes_allow_optimization = true;
+ mutex_lock(&text_mutex);
+ for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
+ head = &kprobe_table[i];
+ hlist_for_each_entry_rcu(p, node, head, hlist)
+ if (!kprobe_disabled(p))
+ optimize_kprobe(p);
+ }
+ mutex_unlock(&text_mutex);
+ printk(KERN_INFO "Kprobes globally optimized\n");
+}
+
+static void __kprobes unoptimize_all_kprobes(void)
+{
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct kprobe *p;
+ unsigned int i;
+
+ /* If optimization is already prohibited, just return */
+ if (!kprobes_allow_optimization)
+ return;
+
+ kprobes_allow_optimization = false;
+ printk(KERN_INFO "Kprobes globally unoptimized\n");
+ get_online_cpus(); /* For avoiding text_mutex deadlock */
+ mutex_lock(&text_mutex);
+ for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
+ head = &kprobe_table[i];
+ hlist_for_each_entry_rcu(p, node, head, hlist) {
+ if (!kprobe_disabled(p))
+ unoptimize_kprobe(p);
+ }
+ }
+
+ mutex_unlock(&text_mutex);
+ put_online_cpus();
+ /* Allow all currently running kprobes to complete */
+ synchronize_sched();
+}
+
+int sysctl_kprobes_optimization;
+int proc_kprobes_optimization_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *length,
+ loff_t *ppos)
+{
+ int ret;
+
+ mutex_lock(&kprobe_mutex);
+ sysctl_kprobes_optimization = kprobes_allow_optimization ? 1 : 0;
+ ret = proc_dointvec_minmax(table, write, buffer, length, ppos);
+
+ if (sysctl_kprobes_optimization)
+ optimize_all_kprobes();
+ else
+ unoptimize_all_kprobes();
+ mutex_unlock(&kprobe_mutex);
+
+ return ret;
+}
+#endif /* CONFIG_SYSCTL */
+
#else /* !CONFIG_OPTPROBES */
#define get_optimized_kprobe(addr) (NULL)
#define optimize_kprobe(p) do {} while (0)
@@ -1621,10 +1700,14 @@ static int __init init_kprobes(void)
}
}
-#if defined(CONFIG_OPTPROBES) && defined(__ARCH_WANT_KPROBES_INSN_SLOT)
+#if defined(CONFIG_OPTPROBES)
+#if defined(__ARCH_WANT_KPROBES_INSN_SLOT)
/* Init kprobe_optinsn_slots */
kprobe_optinsn_slots.insn_size = MAX_OPTINSN_SIZE;
#endif
+ /* By default, kprobes can be optimized */
+ kprobes_allow_optimization = true;
+#endif
/* By default, kprobes are armed */
kprobes_all_disarmed = false;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 9b459f0..7480e80 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -50,6 +50,7 @@
#include <linux/ftrace.h>
#include <linux/slow-work.h>
#include <linux/perf_event.h>
+#include <linux/kprobes.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
@@ -1459,6 +1460,17 @@ static struct ctl_table debug_table[] = {
.proc_handler = proc_dointvec
},
#endif
+#if defined(CONFIG_OPTPROBES)
+ {
+ .procname = "kprobes-optimization",
+ .data = &sysctl_kprobes_optimization,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_kprobes_optimization_handler,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+#endif
{ }
};
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2009-12-18 18:11:45
|
Make insn_slot framework support various size slots.
Current insn_slot just supports one-size instruction buffer slot. However,
kprobes jump optimization needs larger size buffers.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ananth N Mavinakayanahalli <an...@in...>
Cc: Ingo Molnar <mi...@el...>
Cc: Jim Keniston <jke...@us...>
Cc: Srikar Dronamraju <sr...@li...>
Cc: Christoph Hellwig <hc...@in...>
Cc: Steven Rostedt <ro...@go...>
Cc: Frederic Weisbecker <fwe...@gm...>
Cc: H. Peter Anvin <hp...@zy...>
Cc: Anders Kaseorg <an...@ks...>
Cc: Tim Abbott <ta...@ks...>
Cc: Andi Kleen <an...@fi...>
Cc: Jason Baron <jb...@re...>
Cc: Mathieu Desnoyers <co...@kr...>
---
kernel/kprobes.c | 104 ++++++++++++++++++++++++++++++++++--------------------
1 files changed, 65 insertions(+), 39 deletions(-)
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index e5342a3..1a2fcf9 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -103,26 +103,42 @@ static struct kprobe_blackpoint kprobe_blacklist[] = {
* stepping on the instruction on a vmalloced/kmalloced/data page
* is a recipe for disaster
*/
-#define INSNS_PER_PAGE (PAGE_SIZE/(MAX_INSN_SIZE * sizeof(kprobe_opcode_t)))
-
struct kprobe_insn_page {
struct list_head list;
kprobe_opcode_t *insns; /* Page of instruction slots */
- char slot_used[INSNS_PER_PAGE];
int nused;
int ngarbage;
+ char slot_used[];
+};
+
+#define KPROBE_INSN_PAGE_SIZE(slots) \
+ (offsetof(struct kprobe_insn_page, slot_used) + \
+ (sizeof(char) * (slots)))
+
+struct kprobe_insn_cache {
+ struct list_head pages; /* list of kprobe_insn_page */
+ size_t insn_size; /* size of instruction slot */
+ int nr_garbage;
};
+static int slots_per_page(struct kprobe_insn_cache *c)
+{
+ return PAGE_SIZE/(c->insn_size * sizeof(kprobe_opcode_t));
+}
+
enum kprobe_slot_state {
SLOT_CLEAN = 0,
SLOT_DIRTY = 1,
SLOT_USED = 2,
};
-static DEFINE_MUTEX(kprobe_insn_mutex); /* Protects kprobe_insn_pages */
-static LIST_HEAD(kprobe_insn_pages);
-static int kprobe_garbage_slots;
-static int collect_garbage_slots(void);
+static DEFINE_MUTEX(kprobe_insn_mutex); /* Protects kprobe_insn_slots */
+static struct kprobe_insn_cache kprobe_insn_slots = {
+ .pages = LIST_HEAD_INIT(kprobe_insn_slots.pages),
+ .insn_size = MAX_INSN_SIZE,
+ .nr_garbage = 0,
+};
+static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c);
static int __kprobes check_safety(void)
{
@@ -152,32 +168,33 @@ loop_end:
* __get_insn_slot() - Find a slot on an executable page for an instruction.
* We allocate an executable page if there's no room on existing ones.
*/
-static kprobe_opcode_t __kprobes *__get_insn_slot(void)
+static kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
{
struct kprobe_insn_page *kip;
retry:
- list_for_each_entry(kip, &kprobe_insn_pages, list) {
- if (kip->nused < INSNS_PER_PAGE) {
+ list_for_each_entry(kip, &c->pages, list) {
+ if (kip->nused < slots_per_page(c)) {
int i;
- for (i = 0; i < INSNS_PER_PAGE; i++) {
+ for (i = 0; i < slots_per_page(c); i++) {
if (kip->slot_used[i] == SLOT_CLEAN) {
kip->slot_used[i] = SLOT_USED;
kip->nused++;
- return kip->insns + (i * MAX_INSN_SIZE);
+ return kip->insns + (i * c->insn_size);
}
}
- /* Surprise! No unused slots. Fix kip->nused. */
- kip->nused = INSNS_PER_PAGE;
+ /* kip->nused is broken. Fix it. */
+ kip->nused = slots_per_page(c);
+ WARN_ON(1);
}
}
/* If there are any garbage slots, collect it and try again. */
- if (kprobe_garbage_slots && collect_garbage_slots() == 0) {
+ if (c->nr_garbage && collect_garbage_slots(c) == 0)
goto retry;
- }
- /* All out of space. Need to allocate a new page. Use slot 0. */
- kip = kmalloc(sizeof(struct kprobe_insn_page), GFP_KERNEL);
+
+ /* All out of space. Need to allocate a new page. */
+ kip = kmalloc(KPROBE_INSN_PAGE_SIZE(slots_per_page(c)), GFP_KERNEL);
if (!kip)
return NULL;
@@ -192,20 +209,23 @@ static kprobe_opcode_t __kprobes *__get_insn_slot(void)
return NULL;
}
INIT_LIST_HEAD(&kip->list);
- list_add(&kip->list, &kprobe_insn_pages);
- memset(kip->slot_used, SLOT_CLEAN, INSNS_PER_PAGE);
+ memset(kip->slot_used, SLOT_CLEAN, slots_per_page(c));
kip->slot_used[0] = SLOT_USED;
kip->nused = 1;
kip->ngarbage = 0;
+ list_add(&kip->list, &c->pages);
return kip->insns;
}
+
kprobe_opcode_t __kprobes *get_insn_slot(void)
{
- kprobe_opcode_t *ret;
+ kprobe_opcode_t *ret = NULL;
+
mutex_lock(&kprobe_insn_mutex);
- ret = __get_insn_slot();
+ ret = __get_insn_slot(&kprobe_insn_slots);
mutex_unlock(&kprobe_insn_mutex);
+
return ret;
}
@@ -221,7 +241,7 @@ static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
* so as not to have to set it up again the
* next time somebody inserts a probe.
*/
- if (!list_is_singular(&kprobe_insn_pages)) {
+ if (!list_is_singular(&kip->list)) {
list_del(&kip->list);
module_free(NULL, kip->insns);
kfree(kip);
@@ -231,7 +251,7 @@ static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
return 0;
}
-static int __kprobes collect_garbage_slots(void)
+static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c)
{
struct kprobe_insn_page *kip, *next;
@@ -239,42 +259,48 @@ static int __kprobes collect_garbage_slots(void)
if (check_safety())
return -EAGAIN;
- list_for_each_entry_safe(kip, next, &kprobe_insn_pages, list) {
+ list_for_each_entry_safe(kip, next, &c->pages, list) {
int i;
if (kip->ngarbage == 0)
continue;
kip->ngarbage = 0; /* we will collect all garbages */
- for (i = 0; i < INSNS_PER_PAGE; i++) {
+ for (i = 0; i < slots_per_page(c); i++) {
if (kip->slot_used[i] == SLOT_DIRTY &&
collect_one_slot(kip, i))
break;
}
}
- kprobe_garbage_slots = 0;
+ c->nr_garbage = 0;
return 0;
}
-void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
+static void __kprobes __free_insn_slot(struct kprobe_insn_cache *c,
+ kprobe_opcode_t *slot, int dirty)
{
struct kprobe_insn_page *kip;
- mutex_lock(&kprobe_insn_mutex);
- list_for_each_entry(kip, &kprobe_insn_pages, list) {
- if (kip->insns <= slot &&
- slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)) {
- int i = (slot - kip->insns) / MAX_INSN_SIZE;
+ list_for_each_entry(kip, &c->pages, list) {
+ long idx = ((long)slot - (long)kip->insns) / c->insn_size;
+ if (idx >= 0 && idx < slots_per_page(c)) {
+ WARN_ON(kip->slot_used[idx] != SLOT_USED);
if (dirty) {
- kip->slot_used[i] = SLOT_DIRTY;
+ kip->slot_used[idx] = SLOT_DIRTY;
kip->ngarbage++;
+ if (++c->nr_garbage > slots_per_page(c))
+ collect_garbage_slots(c);
} else
- collect_one_slot(kip, i);
- break;
+ collect_one_slot(kip, idx);
+ return;
}
}
+ /* Could not free this slot. */
+ WARN_ON(1);
+}
- if (dirty && ++kprobe_garbage_slots > INSNS_PER_PAGE)
- collect_garbage_slots();
-
+void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
+{
+ mutex_lock(&kprobe_insn_mutex);
+ __free_insn_slot(&kprobe_insn_slots, slot, dirty);
mutex_unlock(&kprobe_insn_mutex);
}
#endif
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2009-12-18 18:11:40
|
Introduce kprobes jump optimization arch-independent parts.
Kprobes uses breakpoint instruction for interrupting execution flow, on
some architectures, it can be replaced by a jump instruction and
interruption emulation code. This gains kprobs' performance drastically.
To enable this feature, set CONFIG_OPTPROBES=y (default y if the arch
supports OPTPROBE).
Changes in v6:
- Cleanup coding style for readability.
- Add comments around get/put_online_cpus().
Changes in v5:
- Use get_online_cpus()/put_online_cpus() for avoiding text_mutex
deadlock.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ananth N Mavinakayanahalli <an...@in...>
Cc: Ingo Molnar <mi...@el...>
Cc: Jim Keniston <jke...@us...>
Cc: Srikar Dronamraju <sr...@li...>
Cc: Christoph Hellwig <hc...@in...>
Cc: Steven Rostedt <ro...@go...>
Cc: Frederic Weisbecker <fwe...@gm...>
Cc: H. Peter Anvin <hp...@zy...>
Cc: Anders Kaseorg <an...@ks...>
Cc: Tim Abbott <ta...@ks...>
Cc: Andi Kleen <an...@fi...>
Cc: Jason Baron <jb...@re...>
Cc: Mathieu Desnoyers <co...@kr...>
---
arch/Kconfig | 13 +
include/linux/kprobes.h | 36 ++++
kernel/kprobes.c | 439 ++++++++++++++++++++++++++++++++++++++++++-----
3 files changed, 442 insertions(+), 46 deletions(-)
diff --git a/arch/Kconfig b/arch/Kconfig
index 32e8b68..2622a9d 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -57,6 +57,17 @@ config KPROBES
for kernel debugging, non-intrusive instrumentation and testing.
If in doubt, say "N".
+config OPTPROBES
+ bool "Kprobes jump optimization support (EXPERIMENTAL)"
+ default y
+ depends on KPROBES
+ depends on !PREEMPT
+ depends on HAVE_OPTPROBES
+ select KALLSYMS_ALL
+ help
+ This option will allow kprobes to optimize breakpoint to
+ a jump for reducing its overhead.
+
config HAVE_EFFICIENT_UNALIGNED_ACCESS
bool
help
@@ -99,6 +110,8 @@ config HAVE_KPROBES
config HAVE_KRETPROBES
bool
+config HAVE_OPTPROBES
+ bool
#
# An arch should select this if it provides all these things:
#
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index 1b672f7..aed1f95 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -122,6 +122,11 @@ struct kprobe {
/* Kprobe status flags */
#define KPROBE_FLAG_GONE 1 /* breakpoint has already gone */
#define KPROBE_FLAG_DISABLED 2 /* probe is temporarily disabled */
+#define KPROBE_FLAG_OPTIMIZED 4 /*
+ * probe is really optimized.
+ * NOTE:
+ * this flag is only for optimized_kprobe.
+ */
/* Has this kprobe gone ? */
static inline int kprobe_gone(struct kprobe *p)
@@ -134,6 +139,12 @@ static inline int kprobe_disabled(struct kprobe *p)
{
return p->flags & (KPROBE_FLAG_DISABLED | KPROBE_FLAG_GONE);
}
+
+/* Is this kprobe really running optimized path ? */
+static inline int kprobe_optimized(struct kprobe *p)
+{
+ return p->flags & KPROBE_FLAG_OPTIMIZED;
+}
/*
* Special probe type that uses setjmp-longjmp type tricks to resume
* execution at a specified entry with a matching prototype corresponding
@@ -249,6 +260,31 @@ extern kprobe_opcode_t *get_insn_slot(void);
extern void free_insn_slot(kprobe_opcode_t *slot, int dirty);
extern void kprobes_inc_nmissed_count(struct kprobe *p);
+#ifdef CONFIG_OPTPROBES
+/*
+ * Internal structure for direct jump optimized probe
+ */
+struct optimized_kprobe {
+ struct kprobe kp;
+ struct list_head list; /* list for optimizing queue */
+ struct arch_optimized_insn optinsn;
+};
+
+/* Architecture dependent functions for direct jump optimization */
+extern int arch_prepared_optinsn(struct arch_optimized_insn *optinsn);
+extern int arch_check_optimized_kprobe(struct optimized_kprobe *op);
+extern int arch_prepare_optimized_kprobe(struct optimized_kprobe *op);
+extern void arch_remove_optimized_kprobe(struct optimized_kprobe *op);
+extern int arch_optimize_kprobe(struct optimized_kprobe *op);
+extern void arch_unoptimize_kprobe(struct optimized_kprobe *op);
+extern kprobe_opcode_t *get_optinsn_slot(void);
+extern void free_optinsn_slot(kprobe_opcode_t *slot, int dirty);
+extern int arch_within_optimized_kprobe(struct optimized_kprobe *op,
+ unsigned long addr);
+
+extern void opt_pre_handler(struct kprobe *p, struct pt_regs *regs);
+#endif /* CONFIG_OPTPROBES */
+
/* Get the kprobe at this addr (if any) - called with preemption disabled */
struct kprobe *get_kprobe(void *addr);
void kretprobe_hash_lock(struct task_struct *tsk,
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 1a2fcf9..4b1da1e 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -44,6 +44,7 @@
#include <linux/debugfs.h>
#include <linux/kdebug.h>
#include <linux/memory.h>
+#include <linux/cpu.h>
#include <asm-generic/sections.h>
#include <asm/cacheflush.h>
@@ -303,6 +304,33 @@ void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
__free_insn_slot(&kprobe_insn_slots, slot, dirty);
mutex_unlock(&kprobe_insn_mutex);
}
+#ifdef CONFIG_OPTPROBES
+/* For optimized_kprobe buffer */
+static DEFINE_MUTEX(kprobe_optinsn_mutex); /* Protects kprobe_optinsn_slots */
+static struct kprobe_insn_cache kprobe_optinsn_slots = {
+ .pages = LIST_HEAD_INIT(kprobe_optinsn_slots.pages),
+ /* .insn_size is initialized later */
+ .nr_garbage = 0,
+};
+/* Get a slot for optimized_kprobe buffer */
+kprobe_opcode_t __kprobes *get_optinsn_slot(void)
+{
+ kprobe_opcode_t *ret = NULL;
+
+ mutex_lock(&kprobe_optinsn_mutex);
+ ret = __get_insn_slot(&kprobe_optinsn_slots);
+ mutex_unlock(&kprobe_optinsn_mutex);
+
+ return ret;
+}
+
+void __kprobes free_optinsn_slot(kprobe_opcode_t * slot, int dirty)
+{
+ mutex_lock(&kprobe_optinsn_mutex);
+ __free_insn_slot(&kprobe_optinsn_slots, slot, dirty);
+ mutex_unlock(&kprobe_optinsn_mutex);
+}
+#endif
#endif
/* We have preemption disabled.. so it is safe to use __ versions */
@@ -333,23 +361,303 @@ struct kprobe __kprobes *get_kprobe(void *addr)
if (p->addr == addr)
return p;
}
+
+ return NULL;
+}
+
+static int __kprobes aggr_pre_handler(struct kprobe *p, struct pt_regs *regs);
+
+/* Return true if the kprobe is an aggregator */
+static inline int kprobe_aggrprobe(struct kprobe *p)
+{
+ return p->pre_handler == aggr_pre_handler;
+}
+
+/*
+ * Keep all fields in the kprobe consistent
+ */
+static inline void copy_kprobe(struct kprobe *old_p, struct kprobe *p)
+{
+ memcpy(&p->opcode, &old_p->opcode, sizeof(kprobe_opcode_t));
+ memcpy(&p->ainsn, &old_p->ainsn, sizeof(struct arch_specific_insn));
+}
+
+#ifdef CONFIG_OPTPROBES
+/*
+ * Call all pre_handler on the list, but ignores its return value.
+ * This must be called from arch-dep optimized caller.
+ */
+void __kprobes opt_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct kprobe *kp;
+
+ list_for_each_entry_rcu(kp, &p->list, list) {
+ if (kp->pre_handler && likely(!kprobe_disabled(kp))) {
+ set_kprobe_instance(kp);
+ kp->pre_handler(kp, regs);
+ }
+ reset_kprobe_instance();
+ }
+}
+
+/* Return true(!0) if the kprobe is ready for optimization. */
+static inline int kprobe_optready(struct kprobe *p)
+{
+ struct optimized_kprobe *op;
+
+ if (kprobe_aggrprobe(p)) {
+ op = container_of(p, struct optimized_kprobe, kp);
+ return arch_prepared_optinsn(&op->optinsn);
+ }
+
+ return 0;
+}
+
+/* Return an optimized kprobe which replaces instructions including addr. */
+struct kprobe *__kprobes get_optimized_kprobe(unsigned long addr)
+{
+ int i;
+ struct kprobe *p = NULL;
+ struct optimized_kprobe *op;
+
+ for (i = 0; !p && i < MAX_OPTIMIZED_LENGTH; i++)
+ p = get_kprobe((void *)(addr - i));
+
+ if (p && kprobe_optready(p)) {
+ op = container_of(p, struct optimized_kprobe, kp);
+ if (arch_within_optimized_kprobe(op, addr))
+ return p;
+ }
+
return NULL;
}
+/* Optimization staging list, protected by kprobe_mutex */
+static LIST_HEAD(optimizing_list);
+
+static void kprobe_optimizer(struct work_struct *work);
+static DECLARE_DELAYED_WORK(optimizing_work, kprobe_optimizer);
+#define OPTIMIZE_DELAY 5
+
+/* Kprobe jump optimizer */
+static __kprobes void kprobe_optimizer(struct work_struct *work)
+{
+ struct optimized_kprobe *op, *tmp;
+
+ /* Lock modules while optimizing kprobes */
+ mutex_lock(&module_mutex);
+ mutex_lock(&kprobe_mutex);
+ if (kprobes_all_disarmed)
+ goto end;
+
+ /*
+ * Wait for quiesence period to ensure all running interrupts
+ * are done. Because optprobe may modify multiple instructions
+ * there is a chance that Nth instruction is interrupted. In that
+ * case, running interrupt can return to 2nd-Nth byte of jump
+ * instruction. This wait is for avoiding it.
+ */
+ synchronize_sched();
+
+ /*
+ * The optimization/unoptimization refers online_cpus via
+ * stop_machine() and cpu-hotplug modifies online_cpus.
+ * And same time, text_mutex will be held in cpu-hotplug and here.
+ * This combination can cause a deadlock (cpu-hotplug try to lock
+ * text_mutex but stop_machine can not be done because online_cpus
+ * has been changed)
+ * To avoid this deadlock, we need to call get_online_cpus()
+ * for preventing cpu-hotplug outside of text_mutex locking.
+ */
+ get_online_cpus();
+ mutex_lock(&text_mutex);
+ list_for_each_entry_safe(op, tmp, &optimizing_list, list) {
+ WARN_ON(kprobe_disabled(&op->kp));
+ if (arch_optimize_kprobe(op) < 0)
+ op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
+ list_del_init(&op->list);
+ }
+ mutex_unlock(&text_mutex);
+ put_online_cpus();
+end:
+ mutex_unlock(&kprobe_mutex);
+ mutex_unlock(&module_mutex);
+}
+
+/* Optimize kprobe if p is ready to be optimized */
+static __kprobes void optimize_kprobe(struct kprobe *p)
+{
+ struct optimized_kprobe *op;
+
+ /* Check if the kprobe is disabled or not ready for optimization. */
+ if (!kprobe_optready(p) ||
+ (kprobe_disabled(p) || kprobes_all_disarmed))
+ return;
+
+ /* Both of break_handler and post_handler are not supported. */
+ if (p->break_handler || p->post_handler)
+ return;
+
+ op = container_of(p, struct optimized_kprobe, kp);
+
+ /* Check there is no other kprobes at the optimized instructions */
+ if (arch_check_optimized_kprobe(op) < 0)
+ return;
+
+ /* Check if it is already optimized. */
+ if (op->kp.flags & KPROBE_FLAG_OPTIMIZED)
+ return;
+
+ op->kp.flags |= KPROBE_FLAG_OPTIMIZED;
+ list_add(&op->list, &optimizing_list);
+ if (!delayed_work_pending(&optimizing_work))
+ schedule_delayed_work(&optimizing_work, OPTIMIZE_DELAY);
+}
+
+/* Unoptimize a kprobe if p is optimized */
+static __kprobes void unoptimize_kprobe(struct kprobe *p)
+{
+ struct optimized_kprobe *op;
+
+ if ((p->flags & KPROBE_FLAG_OPTIMIZED) && kprobe_aggrprobe(p)) {
+ op = container_of(p, struct optimized_kprobe, kp);
+ if (!list_empty(&op->list))
+ /* Dequeue from the optimization queue */
+ list_del_init(&op->list);
+ else
+ /* Replace jump with break */
+ arch_unoptimize_kprobe(op);
+ op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
+ }
+}
+
+/* Remove optimized instructions */
+static void __kprobes kill_optimized_kprobe(struct kprobe *p)
+{
+ struct optimized_kprobe *op;
+
+ op = container_of(p, struct optimized_kprobe, kp);
+ if (!list_empty(&op->list)) {
+ /* Dequeue from the optimization queue */
+ list_del_init(&op->list);
+ op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
+ }
+ /* Don't unoptimize, because the target code will be freed. */
+ arch_remove_optimized_kprobe(op);
+}
+
+/* Try to prepare optimized instructions */
+static __kprobes void prepare_optimized_kprobe(struct kprobe *p)
+{
+ struct optimized_kprobe *op;
+
+ op = container_of(p, struct optimized_kprobe, kp);
+ arch_prepare_optimized_kprobe(op);
+}
+
+/* Free optimized instructions and optimized_kprobe */
+static __kprobes void free_aggr_kprobe(struct kprobe *p)
+{
+ struct optimized_kprobe *op;
+
+ op = container_of(p, struct optimized_kprobe, kp);
+ arch_remove_optimized_kprobe(op);
+ kfree(op);
+}
+
+/* Allocate new optimized_kprobe and try to prepare optimized instructions */
+static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
+{
+ struct optimized_kprobe *op;
+
+ op = kzalloc(sizeof(struct optimized_kprobe), GFP_KERNEL);
+ if (!op)
+ return NULL;
+
+ INIT_LIST_HEAD(&op->list);
+ op->kp.addr = p->addr;
+ arch_prepare_optimized_kprobe(op);
+
+ return &op->kp;
+}
+
+static void __kprobes init_aggr_kprobe(struct kprobe *ap, struct kprobe *p);
+
+/*
+ * Prepare an optimized_kprobe and optimize it
+ * NOTE: p must be a normal registered kprobe
+ */
+static __kprobes void try_to_optimize_kprobe(struct kprobe *p)
+{
+ struct kprobe *ap;
+ struct optimized_kprobe *op;
+
+ ap = alloc_aggr_kprobe(p);
+ if (!ap)
+ return;
+
+ op = container_of(ap, struct optimized_kprobe, kp);
+ if (!arch_prepared_optinsn(&op->optinsn)) {
+ /* If failed to setup optimizing, fallback to kprobe */
+ free_aggr_kprobe(ap);
+ return;
+ }
+
+ init_aggr_kprobe(ap, p);
+ optimize_kprobe(ap);
+}
+#else /* !CONFIG_OPTPROBES */
+#define get_optimized_kprobe(addr) (NULL)
+#define optimize_kprobe(p) do {} while (0)
+#define unoptimize_kprobe(p) do {} while (0)
+#define kill_optimized_kprobe(p) do {} while (0)
+#define prepare_optimized_kprobe(p) do {} while (0)
+#define try_to_optimize_kprobe(p) do {} while (0)
+
+static __kprobes void free_aggr_kprobe(struct kprobe *p)
+{
+ kfree(p);
+}
+
+static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
+{
+ return kzalloc(sizeof(struct kprobe), GFP_KERNEL);
+}
+#endif /* CONFIG_OPTPROBES */
+
+static void __kprobes __arm_kprobe(struct kprobe *kp)
+{
+ arch_arm_kprobe(kp);
+ optimize_kprobe(kp); /* Try to optimize (add kprobe to a list) */
+}
+
+static void __kprobes __disarm_kprobe(struct kprobe *kp)
+{
+ unoptimize_kprobe(kp); /* Try to unoptimize */
+ arch_disarm_kprobe(kp);
+}
+
/* Arm a kprobe with text_mutex */
static void __kprobes arm_kprobe(struct kprobe *kp)
{
+ /*
+ * Here, since __arm_kprobe() doesn't use stop_machine(),
+ * this doesn't cause deadlock on text_mutex. So, we don't
+ * need get_online_cpus().
+ */
mutex_lock(&text_mutex);
- arch_arm_kprobe(kp);
+ __arm_kprobe(kp);
mutex_unlock(&text_mutex);
}
/* Disarm a kprobe with text_mutex */
static void __kprobes disarm_kprobe(struct kprobe *kp)
{
+ get_online_cpus(); /* For avoiding text_mutex deadlock */
mutex_lock(&text_mutex);
- arch_disarm_kprobe(kp);
+ __disarm_kprobe(kp);
mutex_unlock(&text_mutex);
+ put_online_cpus();
}
/*
@@ -418,7 +726,7 @@ static int __kprobes aggr_break_handler(struct kprobe *p, struct pt_regs *regs)
void __kprobes kprobes_inc_nmissed_count(struct kprobe *p)
{
struct kprobe *kp;
- if (p->pre_handler != aggr_pre_handler) {
+ if (!kprobe_aggrprobe(p)) {
p->nmissed++;
} else {
list_for_each_entry_rcu(kp, &p->list, list)
@@ -542,21 +850,16 @@ static void __kprobes cleanup_rp_inst(struct kretprobe *rp)
}
/*
- * Keep all fields in the kprobe consistent
- */
-static inline void copy_kprobe(struct kprobe *old_p, struct kprobe *p)
-{
- memcpy(&p->opcode, &old_p->opcode, sizeof(kprobe_opcode_t));
- memcpy(&p->ainsn, &old_p->ainsn, sizeof(struct arch_specific_insn));
-}
-
-/*
* Add the new probe to ap->list. Fail if this is the
* second jprobe at the address - two jprobes can't coexist
*/
static int __kprobes add_new_kprobe(struct kprobe *ap, struct kprobe *p)
{
BUG_ON(kprobe_gone(ap) || kprobe_gone(p));
+
+ if (p->break_handler || p->post_handler)
+ unoptimize_kprobe(ap); /* Fall back to normal kprobe */
+
if (p->break_handler) {
if (ap->break_handler)
return -EEXIST;
@@ -571,7 +874,7 @@ static int __kprobes add_new_kprobe(struct kprobe *ap, struct kprobe *p)
ap->flags &= ~KPROBE_FLAG_DISABLED;
if (!kprobes_all_disarmed)
/* Arm the breakpoint again. */
- arm_kprobe(ap);
+ __arm_kprobe(ap);
}
return 0;
}
@@ -580,12 +883,13 @@ static int __kprobes add_new_kprobe(struct kprobe *ap, struct kprobe *p)
* Fill in the required fields of the "manager kprobe". Replace the
* earlier kprobe in the hlist with the manager kprobe
*/
-static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
+static void __kprobes init_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
{
+ /* Copy p's insn slot to ap */
copy_kprobe(p, ap);
flush_insn_slot(ap);
ap->addr = p->addr;
- ap->flags = p->flags;
+ ap->flags = p->flags & ~KPROBE_FLAG_OPTIMIZED;
ap->pre_handler = aggr_pre_handler;
ap->fault_handler = aggr_fault_handler;
/* We don't care the kprobe which has gone. */
@@ -595,8 +899,9 @@ static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
ap->break_handler = aggr_break_handler;
INIT_LIST_HEAD(&ap->list);
- list_add_rcu(&p->list, &ap->list);
+ INIT_HLIST_NODE(&ap->hlist);
+ list_add_rcu(&p->list, &ap->list);
hlist_replace_rcu(&p->hlist, &ap->hlist);
}
@@ -610,12 +915,12 @@ static int __kprobes register_aggr_kprobe(struct kprobe *old_p,
int ret = 0;
struct kprobe *ap = old_p;
- if (old_p->pre_handler != aggr_pre_handler) {
- /* If old_p is not an aggr_probe, create new aggr_kprobe. */
- ap = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
+ if (!kprobe_aggrprobe(old_p)) {
+ /* If old_p is not an aggr_kprobe, create new aggr_kprobe. */
+ ap = alloc_aggr_kprobe(old_p);
if (!ap)
return -ENOMEM;
- add_aggr_kprobe(ap, old_p);
+ init_aggr_kprobe(ap, old_p);
}
if (kprobe_gone(ap)) {
@@ -634,6 +939,9 @@ static int __kprobes register_aggr_kprobe(struct kprobe *old_p,
*/
return ret;
+ /* Prepare optimized instructions if possible. */
+ prepare_optimized_kprobe(ap);
+
/*
* Clear gone flag to prevent allocating new slot again, and
* set disabled flag because it is not armed yet.
@@ -642,6 +950,7 @@ static int __kprobes register_aggr_kprobe(struct kprobe *old_p,
| KPROBE_FLAG_DISABLED;
}
+ /* Copy ap's insn slot to p */
copy_kprobe(ap, p);
return add_new_kprobe(ap, p);
}
@@ -791,16 +1100,25 @@ int __kprobes register_kprobe(struct kprobe *p)
p->nmissed = 0;
INIT_LIST_HEAD(&p->list);
mutex_lock(&kprobe_mutex);
+
+ get_online_cpus(); /* For avoiding text_mutex deadlock. */
+ mutex_lock(&text_mutex);
+
old_p = get_kprobe(p->addr);
if (old_p) {
+ /* Since this may unoptimize old_p, locking text_mutex. */
ret = register_aggr_kprobe(old_p, p);
goto out;
}
- mutex_lock(&text_mutex);
+ /* Check collision with other optimized kprobes */
+ old_p = get_optimized_kprobe((unsigned long)p->addr);
+ if (unlikely(old_p))
+ unoptimize_kprobe(old_p); /* Fallback to unoptimized kprobe */
+
ret = arch_prepare_kprobe(p);
if (ret)
- goto out_unlock_text;
+ goto out;
INIT_HLIST_NODE(&p->hlist);
hlist_add_head_rcu(&p->hlist,
@@ -809,9 +1127,12 @@ int __kprobes register_kprobe(struct kprobe *p)
if (!kprobes_all_disarmed && !kprobe_disabled(p))
arch_arm_kprobe(p);
-out_unlock_text:
- mutex_unlock(&text_mutex);
+ /* Try to optimize kprobe */
+ try_to_optimize_kprobe(p);
+
out:
+ mutex_unlock(&text_mutex);
+ put_online_cpus();
mutex_unlock(&kprobe_mutex);
if (probed_mod)
@@ -833,7 +1154,7 @@ static int __kprobes __unregister_kprobe_top(struct kprobe *p)
return -EINVAL;
if (old_p == p ||
- (old_p->pre_handler == aggr_pre_handler &&
+ (kprobe_aggrprobe(old_p) &&
list_is_singular(&old_p->list))) {
/*
* Only probe on the hash list. Disarm only if kprobes are
@@ -841,8 +1162,13 @@ static int __kprobes __unregister_kprobe_top(struct kprobe *p)
* already have been removed. We save on flushing icache.
*/
if (!kprobes_all_disarmed && !kprobe_disabled(old_p))
- disarm_kprobe(p);
+ disarm_kprobe(old_p);
hlist_del_rcu(&old_p->hlist);
+
+ /* If another kprobe was blocked, optimize it. */
+ old_p = get_optimized_kprobe((unsigned long)p->addr);
+ if (unlikely(old_p))
+ optimize_kprobe(old_p);
} else {
if (p->break_handler && !kprobe_gone(p))
old_p->break_handler = NULL;
@@ -857,8 +1183,13 @@ noclean:
list_del_rcu(&p->list);
if (!kprobe_disabled(old_p)) {
try_to_disable_aggr_kprobe(old_p);
- if (!kprobes_all_disarmed && kprobe_disabled(old_p))
- disarm_kprobe(old_p);
+ if (!kprobes_all_disarmed) {
+ if (kprobe_disabled(old_p))
+ disarm_kprobe(old_p);
+ else
+ /* Try to optimize this probe again */
+ optimize_kprobe(old_p);
+ }
}
}
return 0;
@@ -875,7 +1206,7 @@ static void __kprobes __unregister_kprobe_bottom(struct kprobe *p)
old_p = list_entry(p->list.next, struct kprobe, list);
list_del(&p->list);
arch_remove_kprobe(old_p);
- kfree(old_p);
+ free_aggr_kprobe(old_p);
}
}
@@ -1171,7 +1502,7 @@ static void __kprobes kill_kprobe(struct kprobe *p)
struct kprobe *kp;
p->flags |= KPROBE_FLAG_GONE;
- if (p->pre_handler == aggr_pre_handler) {
+ if (kprobe_aggrprobe(p)) {
/*
* If this is an aggr_kprobe, we have to list all the
* chained probes and mark them GONE.
@@ -1180,6 +1511,7 @@ static void __kprobes kill_kprobe(struct kprobe *p)
kp->flags |= KPROBE_FLAG_GONE;
p->post_handler = NULL;
p->break_handler = NULL;
+ kill_optimized_kprobe(p);
}
/*
* Here, we can remove insn_slot safely, because no thread calls
@@ -1289,6 +1621,11 @@ static int __init init_kprobes(void)
}
}
+#if defined(CONFIG_OPTPROBES) && defined(__ARCH_WANT_KPROBES_INSN_SLOT)
+ /* Init kprobe_optinsn_slots */
+ kprobe_optinsn_slots.insn_size = MAX_OPTINSN_SIZE;
+#endif
+
/* By default, kprobes are armed */
kprobes_all_disarmed = false;
@@ -1307,7 +1644,7 @@ static int __init init_kprobes(void)
#ifdef CONFIG_DEBUG_FS
static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p,
- const char *sym, int offset,char *modname)
+ const char *sym, int offset, char *modname, struct kprobe *pp)
{
char *kprobe_type;
@@ -1317,19 +1654,21 @@ static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p,
kprobe_type = "j";
else
kprobe_type = "k";
+
if (sym)
- seq_printf(pi, "%p %s %s+0x%x %s %s%s\n",
+ seq_printf(pi, "%p %s %s+0x%x %s ",
p->addr, kprobe_type, sym, offset,
- (modname ? modname : " "),
- (kprobe_gone(p) ? "[GONE]" : ""),
- ((kprobe_disabled(p) && !kprobe_gone(p)) ?
- "[DISABLED]" : ""));
+ (modname ? modname : " "));
else
- seq_printf(pi, "%p %s %p %s%s\n",
- p->addr, kprobe_type, p->addr,
- (kprobe_gone(p) ? "[GONE]" : ""),
- ((kprobe_disabled(p) && !kprobe_gone(p)) ?
- "[DISABLED]" : ""));
+ seq_printf(pi, "%p %s %p ",
+ p->addr, kprobe_type, p->addr);
+
+ if (!pp)
+ pp = p;
+ seq_printf(pi, "%s%s%s\n",
+ (kprobe_gone(p) ? "[GONE]" : ""),
+ ((kprobe_disabled(p) && !kprobe_gone(p)) ? "[DISABLED]" : ""),
+ (kprobe_optimized(pp) ? "[OPTIMIZED]" : ""));
}
static void __kprobes *kprobe_seq_start(struct seq_file *f, loff_t *pos)
@@ -1365,11 +1704,11 @@ static int __kprobes show_kprobe_addr(struct seq_file *pi, void *v)
hlist_for_each_entry_rcu(p, node, head, hlist) {
sym = kallsyms_lookup((unsigned long)p->addr, NULL,
&offset, &modname, namebuf);
- if (p->pre_handler == aggr_pre_handler) {
+ if (kprobe_aggrprobe(p)) {
list_for_each_entry_rcu(kp, &p->list, list)
- report_probe(pi, kp, sym, offset, modname);
+ report_probe(pi, kp, sym, offset, modname, p);
} else
- report_probe(pi, p, sym, offset, modname);
+ report_probe(pi, p, sym, offset, modname, NULL);
}
preempt_enable();
return 0;
@@ -1472,12 +1811,13 @@ static void __kprobes arm_all_kprobes(void)
if (!kprobes_all_disarmed)
goto already_enabled;
+ /* Arming kprobes doesn't optimize kprobe itself */
mutex_lock(&text_mutex);
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
head = &kprobe_table[i];
hlist_for_each_entry_rcu(p, node, head, hlist)
if (!kprobe_disabled(p))
- arch_arm_kprobe(p);
+ __arm_kprobe(p);
}
mutex_unlock(&text_mutex);
@@ -1504,16 +1844,23 @@ static void __kprobes disarm_all_kprobes(void)
kprobes_all_disarmed = true;
printk(KERN_INFO "Kprobes globally disabled\n");
+
+ /*
+ * Here we call get_online_cpus() for avoiding text_mutex deadlock,
+ * because disarming may also unoptimize kprobes.
+ */
+ get_online_cpus();
mutex_lock(&text_mutex);
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
head = &kprobe_table[i];
hlist_for_each_entry_rcu(p, node, head, hlist) {
if (!arch_trampoline_kprobe(p) && !kprobe_disabled(p))
- arch_disarm_kprobe(p);
+ __disarm_kprobe(p);
}
}
mutex_unlock(&text_mutex);
+ put_online_cpus();
mutex_unlock(&kprobe_mutex);
/* Allow all currently running kprobes to complete */
synchronize_sched();
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2009-12-18 18:11:40
|
Change RELATIVEJUMP_INSTRUCTION macro to RELATIVEJUMP_OPCODE since it represents just the opcode byte. Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: Ananth N Mavinakayanahalli <an...@in...> Cc: Ingo Molnar <mi...@el...> Cc: Jim Keniston <jke...@us...> Cc: Srikar Dronamraju <sr...@li...> Cc: Christoph Hellwig <hc...@in...> Cc: Steven Rostedt <ro...@go...> Cc: Frederic Weisbecker <fwe...@gm...> Cc: H. Peter Anvin <hp...@zy...> Cc: Anders Kaseorg <an...@ks...> Cc: Tim Abbott <ta...@ks...> Cc: Andi Kleen <an...@fi...> Cc: Jason Baron <jb...@re...> Cc: Mathieu Desnoyers <co...@kr...> --- arch/x86/include/asm/kprobes.h | 2 +- arch/x86/kernel/kprobes.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/asm/kprobes.h b/arch/x86/include/asm/kprobes.h index 4fe681d..eaec8ea 100644 --- a/arch/x86/include/asm/kprobes.h +++ b/arch/x86/include/asm/kprobes.h @@ -32,7 +32,7 @@ struct kprobe; typedef u8 kprobe_opcode_t; #define BREAKPOINT_INSTRUCTION 0xcc -#define RELATIVEJUMP_INSTRUCTION 0xe9 +#define RELATIVEJUMP_OPCODE 0xe9 #define MAX_INSN_SIZE 16 #define MAX_STACK_SIZE 64 #define MIN_STACK_SIZE(ADDR) \ diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index 5b8c750..7039e6e 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c @@ -115,7 +115,7 @@ static void __kprobes set_jmp_op(void *from, void *to) } __attribute__((packed)) * jop; jop = (struct __arch_jmp_op *)from; jop->raddr = (s32)((long)(to) - ((long)(from) + 5)); - jop->op = RELATIVEJUMP_INSTRUCTION; + jop->op = RELATIVEJUMP_OPCODE; } /* -- Masami Hiramatsu Software Engineer Hitachi Computer Products (America), Inc. Software Solutions Division e-mail: mhi...@re... |
|
From: Masami H. <mhi...@re...> - 2009-12-18 18:11:19
|
Hi,
Here are the patchset of the kprobes jump optimization v7
(a.k.a. Djprobe). This version is just moving onto
2.6.33-rc1-tip. Ingo, I assume its a good timing to
push this code onto -tip tree (maybe developing branch?),
since people can test it with perf-probe.
I made a separated patch of generic text_poke_smp()
for cross modifying code(XMC) by using stop_machine(),
which originally comes from Mathieu's immediate value
code. Thanks Mathieu, for helping me to implement it
and organizing discussion points about int3-base XMC!
These patches can be applied on the latest -tip.
Changes in v7:
- Remove ctl_name = CTL_UNNUMBERED for upstream compatibility.
And kprobe stress test didn't found any regressions - from kprobes,
under kvm/x86.
TODO:
- Support preemptive kernel (by stack unwinding and checking address).
Jump Optimized Kprobes
======================
o Concept
Kprobes uses the int3 breakpoint instruction on x86 for instrumenting
probes into running kernel. Jump optimization allows kprobes to replace
breakpoint with a jump instruction for reducing probing overhead drastically.
o Performance
An optimized kprobe 5 times faster than a kprobe.
Optimizing probes gains its performance. Usually, a kprobe hit takes
0.5 to 1.0 microseconds to process. On the other hand, a jump optimized
probe hit takes less than 0.1 microseconds (actual number depends on the
processor). Here is a sample overheads.
Intel(R) Xeon(R) CPU E5410 @ 2.33GHz (without debugging options)
x86-32 x86-64
kprobe: 0.68us 0.91us
kprobe+booster: 0.27us 0.40us
kprobe+optimized: 0.06us 0.06us
kretprobe : 0.95us 1.21us
kretprobe+booster: 0.53us 0.71us
kretprobe+optimized: 0.30us 0.35us
(booster skips single-stepping)
Note that jump optimization also consumes more memory, but not so much.
It just uses ~200 bytes, so, even if you use ~10,000 probes, it just
consumes a few MB.
o Usage
Set CONFIG_OPTPROBES=y when building a kernel, then all *probes will be
optimized if possible.
Kprobes decodes probed function and checks whether the target instructions
can be optimized(replaced with a jump) safely. If it can't be, Kprobes just
doesn't optimize it.
o Optimization
Before preparing optimization, Kprobes inserts original(user-defined)
kprobe on the specified address. So, even if the kprobe is not
possible to be optimized, it just uses a normal kprobe.
- Safety check
First, Kprobes gets the address of probed function and checks whether the
optimized region, which will be replaced by a jump instruction, does NOT
straddle the function boundary, because if the optimized region reaches the
next function, its caller causes unexpected results.
Next, Kprobes decodes whole body of probed function and checks there is
NO indirect jump, NO instruction which will cause exception by checking
exception_tables (this will jump to fixup code and fixup code jumps into
same function body) and NO near jump which jumps into the optimized region
(except the 1st byte of jump), because if some jump instruction jumps
into the middle of another instruction, it causes unexpected results too.
Kprobes also measures the length of instructions which will be replaced
by a jump instruction, because a jump instruction is longer than 1 byte,
it may replaces multiple instructions, and it checks whether those
instructions can be executed out-of-line.
- Preparing detour code
Then, Kprobes prepares "detour" buffer, which contains exception emulating
code (push/pop registers, call handler), copied instructions(Kprobes copies
instructions which will be replaced by a jump, to the detour buffer), and
a jump which jumps back to the original execution path.
- Pre-optimization
After preparing detour code, Kprobes enqueues the kprobe to optimizing list
and kicks kprobe-optimizer workqueue to optimize it. To wait other optimized
probes, kprobe-optimizer will delay to work.
When the optimized-kprobe is hit before optimization, its handler
changes IP(instruction pointer) to copied code and exits. So, the
instructions which were copied to detour buffer are executed on the detour
buffer.
- Optimization
Kprobe-optimizer doesn't start instruction-replacing soon, it waits
synchronize_sched for safety, because some processors are possible to be
interrupted on the middle of instruction series (2nd or Nth instruction)
which will be replaced by a jump instruction(*).
As you know, synchronize_sched() can ensure that all interruptions which were
executed when synchronize_sched() was called are done, only if
CONFIG_PREEMPT=n. So, this version supports only the kernel with
CONFIG_PREEMPT=n.(**)
After that, kprobe-optimizer replaces the 4 bytes right after int3 breakpoint
with relative-jump destination, and synchronize caches on all processors. Next,
it replaces int3 with relative-jump opcode, and synchronize caches again.
- Unoptimization
When unregistering, disabling kprobe or being blocked by other kprobe,
an optimized-kprobe will be unoptimized. Before kprobe-optimizer runs,
the kprobe just be dequeued from the optimized list. When the optimization
has been done, it replaces a jump with int3 breakpoint and original code.
First it puts int3 at the first byte of the jump, synchronize caches
on all processors, and replaces the 4 bytes right after int3 with the
original code.
(*)Please imagine that 2nd instruction is interrupted and
stop_machine() replaces the 2nd instruction with jump
*address* while running interrupt handler. When the interrupt
returns to original address, there is no valid instructions
and it causes unexpected result.
(**)This optimization-safety checking may be replaced with stop-machine
method which ksplice is done for supporting CONFIG_PREEMPT=y kernel.
Thank you,
---
Masami Hiramatsu (11):
[RFC] kprobes/x86: Use text_poke_fixup() for jump optimization
[RFC] x86: Introduce generic jump patching without stop_machine
kprobes: Add documents of jump optimization
kprobes/x86: Support kprobes jump optimization on x86
x86: Add text_poke_smp for SMP cross modifying code
kprobes/x86: Cleanup save/restore registers
kprobes/x86: Boost probes when reentering
kprobes: Jump optimization sysctl interface
kprobes: Introduce kprobes jump optimization
kprobes: Introduce generic insn_slot framework
kprobes/x86: Cleanup RELATIVEJUMP_INSTRUCTION to RELATIVEJUMP_OPCODE
Documentation/kprobes.txt | 192 ++++++++++-
arch/Kconfig | 13 +
arch/x86/Kconfig | 1
arch/x86/include/asm/alternative.h | 15 +
arch/x86/include/asm/kprobes.h | 31 ++
arch/x86/kernel/alternative.c | 162 +++++++++
arch/x86/kernel/kprobes.c | 592 ++++++++++++++++++++++++++++------
include/linux/kprobes.h | 44 +++
kernel/kprobes.c | 628 +++++++++++++++++++++++++++++++-----
kernel/sysctl.c | 12 +
10 files changed, 1484 insertions(+), 206 deletions(-)
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2009-12-18 17:02:59
|
Fix resource (write-pipe file) leak in call_usermodehelper_pipe().
When call_usermodehelper_exec() is failed, write-pipe file is opened
and call_usermodehelper_pipe() just returns an error. Since it is
hard for caller to classify the error is occured when opening pipe
or executing helper, caller can't close the pipe by themselves.
I've found this resoruce leak when testing coredump.
You can check how the resource leaks as below;
$ echo "|nocommand" > /proc/sys/kernel/core_pattern
$ ulimit -c unlimited
$ while [ 1 ]; do ./segv; done &> /dev/null &
$ cat /proc/meminfo (<- repeat it)
where segv.c is;
//-----
int main () {
char *p = 0;
*p = 1;
}
//-----
This patch closes write-pipe file if call_usermodehelper_exec()
is failed.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Rusty Russell <ru...@ru...>
Cc: Andrew Morton <ak...@li...>
---
kernel/kmod.c | 12 +++++++-----
1 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/kernel/kmod.c b/kernel/kmod.c
index 25b1031..bf0e231 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -520,13 +520,15 @@ int call_usermodehelper_pipe(char *path, char **argv, char **envp,
return -ENOMEM;
ret = call_usermodehelper_stdinpipe(sub_info, filp);
- if (ret < 0)
- goto out;
+ if (ret < 0) {
+ call_usermodehelper_freeinfo(sub_info);
+ return ret;
+ }
- return call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
+ ret = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
+ if (ret < 0) /* Failed to execute helper, close pipe */
+ filp_close(*filp, NULL);
- out:
- call_usermodehelper_freeinfo(sub_info);
return ret;
}
EXPORT_SYMBOL(call_usermodehelper_pipe);
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2009-12-18 16:46:19
|
Pass mm->flags as a coredump parameter for consistency.
---
1787 if (mm->core_state || !get_dumpable(mm)) { <- (1)
1788 up_write(&mm->mmap_sem);
1789 put_cred(cred);
1790 goto fail;
1791 }
1792
[...]
1798 if (get_dumpable(mm) == 2) { /* Setuid core dump mode */ <-(2)
1799 flag = O_EXCL; /* Stop rewrite attacks */
1800 cred->fsuid = 0; /* Dump root private */
1801 }
---
Since dumpable bits are not protected by lock, there is a
chance to change these bits between (1) and (2).
To solve this issue, this patch copies mm->flags to
coredump_params.mm_flags at the beginning of do_coredump() and uses it instead of get_dumpable() while dumping core.
This copy is also passed to binfmt->core_dump, since
elf*_core_dump() uses dump_filter bits in mm->flags.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Roland McGrath <ro...@re...>
Cc: Hidehiro Kawai <hid...@hi...>
Cc: Andrew Morton <ak...@li...>
Cc: Oleg Nesterov <ol...@re...>
Cc: Ingo Molnar <mi...@el...>
Cc: KOSAKI Motohiro <kos...@jp...>
---
fs/binfmt_elf.c | 12 ++----------
fs/binfmt_elf_fdpic.c | 12 ++----------
fs/exec.c | 20 ++++++++++++++++----
include/linux/binfmts.h | 1 +
4 files changed, 21 insertions(+), 24 deletions(-)
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index edd90c4..3ebb534 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1911,7 +1911,6 @@ static int elf_core_dump(struct coredump_params *cprm)
struct vm_area_struct *vma, *gate_vma;
struct elfhdr *elf = NULL;
loff_t offset = 0, dataoff, foffset;
- unsigned long mm_flags;
struct elf_note_info info;
/*
@@ -1976,13 +1975,6 @@ static int elf_core_dump(struct coredump_params *cprm)
dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
- /*
- * We must use the same mm->flags while dumping core to avoid
- * inconsistency between the program headers and bodies, otherwise an
- * unusable core file can be generated.
- */
- mm_flags = current->mm->flags;
-
/* Write program headers for segments dump */
for (vma = first_vma(current, gate_vma); vma != NULL;
vma = next_vma(vma, gate_vma)) {
@@ -1992,7 +1984,7 @@ static int elf_core_dump(struct coredump_params *cprm)
phdr.p_offset = offset;
phdr.p_vaddr = vma->vm_start;
phdr.p_paddr = 0;
- phdr.p_filesz = vma_dump_size(vma, mm_flags);
+ phdr.p_filesz = vma_dump_size(vma, cprm->mm_flags);
phdr.p_memsz = vma->vm_end - vma->vm_start;
offset += phdr.p_filesz;
phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;
@@ -2025,7 +2017,7 @@ static int elf_core_dump(struct coredump_params *cprm)
unsigned long addr;
unsigned long end;
- end = vma->vm_start + vma_dump_size(vma, mm_flags);
+ end = vma->vm_start + vma_dump_size(vma, cprm->mm_flags);
for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) {
struct page *page;
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index c25256a..aadd7a3 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -1606,7 +1606,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
#endif
int thread_status_size = 0;
elf_addr_t *auxv;
- unsigned long mm_flags;
/*
* We no longer stop all VM operations.
@@ -1737,13 +1736,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
/* Page-align dumped data */
dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
- /*
- * We must use the same mm->flags while dumping core to avoid
- * inconsistency between the program headers and bodies, otherwise an
- * unusable core file can be generated.
- */
- mm_flags = current->mm->flags;
-
/* write program headers for segments dump */
for (vma = current->mm->mmap; vma; vma = vma->vm_next) {
struct elf_phdr phdr;
@@ -1755,7 +1747,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
phdr.p_offset = offset;
phdr.p_vaddr = vma->vm_start;
phdr.p_paddr = 0;
- phdr.p_filesz = maydump(vma, mm_flags) ? sz : 0;
+ phdr.p_filesz = maydump(vma, cprm->mm_flags) ? sz : 0;
phdr.p_memsz = sz;
offset += phdr.p_filesz;
phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;
@@ -1791,7 +1783,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
goto end_coredump;
if (elf_fdpic_dump_segments(cprm->file, &size, &cprm->limit,
- mm_flags) < 0)
+ cprm->mm_flags) < 0)
goto end_coredump;
#ifdef ELF_CORE_WRITE_EXTRA_DATA
diff --git a/fs/exec.c b/fs/exec.c
index 632b02e..a7685f6 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1725,14 +1725,19 @@ void set_dumpable(struct mm_struct *mm, int value)
}
}
-int get_dumpable(struct mm_struct *mm)
+static int __get_dumpable(unsigned long mm_flags)
{
int ret;
- ret = mm->flags & 0x3;
+ ret = mm_flags & MMF_DUMPABLE_MASK;
return (ret >= 2) ? 2 : ret;
}
+int get_dumpable(struct mm_struct *mm)
+{
+ return __get_dumpable(mm->flags);
+}
+
static void wait_for_dump_helpers(struct file *file)
{
struct pipe_inode_info *pipe;
@@ -1776,6 +1781,12 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
.signr = signr,
.regs = regs,
.limit = current->signal->rlim[RLIMIT_CORE].rlim_cur,
+ /*
+ * We must use the same mm->flags while dumping core to avoid
+ * inconsistency of bit flags, since this flag is not protected
+ * by any locks.
+ */
+ .mm_flags = mm->flags,
};
audit_core_dumps(signr);
@@ -1794,7 +1805,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
/*
* If another thread got here first, or we are not dumpable, bail out.
*/
- if (mm->core_state || !get_dumpable(mm)) {
+ if (mm->core_state || !__get_dumpable(cprm.mm_flags)) {
up_write(&mm->mmap_sem);
put_cred(cred);
goto fail;
@@ -1805,7 +1816,8 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
* process nor do we know its entire history. We only know it
* was tainted so we dump it as root in mode 2.
*/
- if (get_dumpable(mm) == 2) { /* Setuid core dump mode */
+ if (__get_dumpable(cprm.mm_flags) == 2) {
+ /* Setuid core dump mode */
flag = O_EXCL; /* Stop rewrite attacks */
cred->fsuid = 0; /* Dump root private */
}
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index cd4349b..99e529b 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -74,6 +74,7 @@ struct coredump_params {
struct pt_regs *regs;
struct file *file;
unsigned long limit;
+ unsigned long mm_flags;
};
/*
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2009-12-18 15:36:43
|
Different version of objdump says its version in different way;
GNU objdump 2.16.1
or
GNU objdump version 2.19.51.0.14-1.fc11 20090722
This patch uses the first argument which starts with a number
as version string.
Changes in v2:
- Remove unneeded increment.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Suggested-by: H. Peter Anvin <hp...@zy...>
Cc: Thomas Gleixner <tg...@li...>
Cc: Ingo Molnar <mi...@el...>
Cc: Andrew Morton <ak...@li...>
---
arch/x86/tools/chkobjdump.awk | 16 +++++++++++++---
1 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/arch/x86/tools/chkobjdump.awk b/arch/x86/tools/chkobjdump.awk
index 5bbb5a3..fd1ab80 100644
--- a/arch/x86/tools/chkobjdump.awk
+++ b/arch/x86/tools/chkobjdump.awk
@@ -8,14 +8,24 @@ BEGIN {
od_sver = 19;
}
-/^GNU/ {
- split($3, ver, ".");
+/^GNU objdump/ {
+ verstr = ""
+ for (i = 3; i <= NF; i++)
+ if (match($(i), "^[0-9]")) {
+ verstr = $(i);
+ break;
+ }
+ if (verstr == "") {
+ printf("Warning: Failed to find objdump version number.\n");
+ exit 0;
+ }
+ split(verstr, ver, ".");
if (ver[1] > od_ver ||
(ver[1] == od_ver && ver[2] >= od_sver)) {
exit 1;
} else {
printf("Warning: objdump version %s is older than %d.%d\n",
- $4, od_ver, od_sver);
+ verstr, od_ver, od_sver);
print("Warning: Skipping posttest.");
# Logic is inverted, because we just skip test without error.
exit 0;
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2009-12-18 15:33:25
|
Masami Hiramatsu wrote:
> Different version of objdump says its version in different way;
>
> GNU objdump 2.16.1
>
> or
>
> GNU objdump version 2.19.51.0.14-1.fc11 20090722
>
> This patch uses the first argument which starts with a number
> as version string.
>
> Signed-off-by: Masami Hiramatsu <mhi...@re...>
> Suggested-by: H. Peter Anvin <hp...@zy...>
> Cc: Thomas Gleixner <tg...@li...>
> Cc: Ingo Molnar <mi...@el...>
> Cc: Andrew Morton <ak...@li...>
> ---
>
> arch/x86/tools/chkobjdump.awk | 16 +++++++++++++---
> 1 files changed, 13 insertions(+), 3 deletions(-)
>
> diff --git a/arch/x86/tools/chkobjdump.awk b/arch/x86/tools/chkobjdump.awk
> index 5bbb5a3..250dc79 100644
> --- a/arch/x86/tools/chkobjdump.awk
> +++ b/arch/x86/tools/chkobjdump.awk
> @@ -8,14 +8,24 @@ BEGIN {
> od_sver = 19;
> }
>
> -/^GNU/ {
> - split($3, ver, ".");
> +/^GNU objdump/ {
> + verstr = ""
> + for (i = 3; i <= NF; i++)
> + if (match($(i), "^[0-9]")) {
> + verstr = $(i++);
^^^^oops, it's not needed!
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2009-12-18 15:30:53
|
Different version of objdump says its version in different way;
GNU objdump 2.16.1
or
GNU objdump version 2.19.51.0.14-1.fc11 20090722
This patch uses the first argument which starts with a number
as version string.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Suggested-by: H. Peter Anvin <hp...@zy...>
Cc: Thomas Gleixner <tg...@li...>
Cc: Ingo Molnar <mi...@el...>
Cc: Andrew Morton <ak...@li...>
---
arch/x86/tools/chkobjdump.awk | 16 +++++++++++++---
1 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/arch/x86/tools/chkobjdump.awk b/arch/x86/tools/chkobjdump.awk
index 5bbb5a3..250dc79 100644
--- a/arch/x86/tools/chkobjdump.awk
+++ b/arch/x86/tools/chkobjdump.awk
@@ -8,14 +8,24 @@ BEGIN {
od_sver = 19;
}
-/^GNU/ {
- split($3, ver, ".");
+/^GNU objdump/ {
+ verstr = ""
+ for (i = 3; i <= NF; i++)
+ if (match($(i), "^[0-9]")) {
+ verstr = $(i++);
+ break;
+ }
+ if (verstr == "") {
+ printf("Warning: Failed to find objdump version number.\n");
+ exit 0;
+ }
+ split(verstr, ver, ".");
if (ver[1] > od_ver ||
(ver[1] == od_ver && ver[2] >= od_sver)) {
exit 1;
} else {
printf("Warning: objdump version %s is older than %d.%d\n",
- $4, od_ver, od_sver);
+ verstr, od_ver, od_sver);
print("Warning: Skipping posttest.");
# Logic is inverted, because we just skip test without error.
exit 0;
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: tip-bot f. M. H. <mhi...@re...> - 2009-12-17 10:53:46
|
Commit-ID: b7702a2136b5f8e0e186e22cae91aaecf98b418c Gitweb: http://git.kernel.org/tip/b7702a2136b5f8e0e186e22cae91aaecf98b418c Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Wed, 16 Dec 2009 17:24:15 -0500 Committer: Ingo Molnar <mi...@el...> CommitDate: Thu, 17 Dec 2009 09:42:44 +0100 perf probe: Check new event name Check new event name is same syntax as a C symbol in perf command. In other words, checking the name is as like as other tracepoint events. This can prevent user to create an event with useless name (e.g. foo|bar, foo*bar). Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: Frederic Weisbecker <fwe...@gm...> Cc: Paul Mackerras <pa...@sa...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Steven Rostedt <ro...@go...> Cc: Jim Keniston <jke...@us...> Cc: Ananth N Mavinakayanahalli <an...@in...> Cc: Christoph Hellwig <hc...@in...> Cc: Jason Baron <jb...@re...> Cc: K.Prasad <pr...@li...> Cc: Peter Zijlstra <pe...@in...> Cc: Srikar Dronamraju <sr...@li...> Cc: Frederic Weisbecker <fwe...@gm...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> LKML-Reference: <200...@dh...> [ v2: minor cleanups ] Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/util/probe-event.c | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 2ca6215..29465d4 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -62,6 +62,18 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) return ret; } +/* Check the name is good for event/group */ +static bool check_event_name(const char *name) +{ + if (!isalpha(*name) && *name != '_') + return false; + while (*++name != '\0') { + if (!isalpha(*name) && !isdigit(*name) && *name != '_') + return false; + } + return true; +} + /* Parse probepoint definition. */ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) { @@ -82,6 +94,9 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) ptr = strchr(arg, ':'); if (ptr) /* Group name is not supported yet. */ semantic_error("Group name is not supported yet."); + if (!check_event_name(arg)) + semantic_error("%s is bad for event name -it must " + "follow C symbol-naming rule.", arg); pp->event = strdup(arg); arg = tmp; } |
|
From: tip-bot f. M. H. <mhi...@re...> - 2009-12-17 10:53:40
|
Commit-ID: 6f3cf440470650b3841d325acacd0c5ea9504c68 Gitweb: http://git.kernel.org/tip/6f3cf440470650b3841d325acacd0c5ea9504c68 Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Wed, 16 Dec 2009 17:24:08 -0500 Committer: Ingo Molnar <mi...@el...> CommitDate: Thu, 17 Dec 2009 09:42:44 +0100 kprobe-tracer: Check new event/group name Check new event/group name is same syntax as a C symbol. In other words, checking the name is as like as other tracepoint events. This can prevent user to create an event with useless name (e.g. foo|bar, foo*bar). Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: Frederic Weisbecker <fwe...@gm...> Cc: Paul Mackerras <pa...@sa...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Steven Rostedt <ro...@go...> Cc: Jim Keniston <jke...@us...> Cc: Ananth N Mavinakayanahalli <an...@in...> Cc: Christoph Hellwig <hc...@in...> Cc: Jason Baron <jb...@re...> Cc: K.Prasad <pr...@li...> Cc: Peter Zijlstra <pe...@in...> Cc: Srikar Dronamraju <sr...@li...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> LKML-Reference: <200...@dh...> [ v2: minor cleanups ] Signed-off-by: Ingo Molnar <mi...@el...> --- kernel/trace/trace_kprobe.c | 31 +++++++++++++++++++++++++------ 1 files changed, 25 insertions(+), 6 deletions(-) diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 7ecab06..375f81a 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -282,6 +282,18 @@ static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs); static int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs); +/* Check the name is good for event/group */ +static int check_event_name(const char *name) +{ + if (!isalpha(*name) && *name != '_') + return 0; + while (*++name != '\0') { + if (!isalpha(*name) && !isdigit(*name) && *name != '_') + return 0; + } + return 1; +} + /* * Allocate new trace_probe and initialize it (including kprobes). */ @@ -293,10 +305,11 @@ static struct trace_probe *alloc_trace_probe(const char *group, int nargs, int is_return) { struct trace_probe *tp; + int ret = -ENOMEM; tp = kzalloc(SIZEOF_TRACE_PROBE(nargs), GFP_KERNEL); if (!tp) - return ERR_PTR(-ENOMEM); + return ERR_PTR(ret); if (symbol) { tp->symbol = kstrdup(symbol, GFP_KERNEL); @@ -312,14 +325,20 @@ static struct trace_probe *alloc_trace_probe(const char *group, else tp->rp.kp.pre_handler = kprobe_dispatcher; - if (!event) + if (!event || !check_event_name(event)) { + ret = -EINVAL; goto error; + } + tp->call.name = kstrdup(event, GFP_KERNEL); if (!tp->call.name) goto error; - if (!group) + if (!group || !check_event_name(group)) { + ret = -EINVAL; goto error; + } + tp->call.system = kstrdup(group, GFP_KERNEL); if (!tp->call.system) goto error; @@ -330,7 +349,7 @@ error: kfree(tp->call.name); kfree(tp->symbol); kfree(tp); - return ERR_PTR(-ENOMEM); + return ERR_PTR(ret); } static void free_probe_arg(struct probe_arg *arg) @@ -695,10 +714,10 @@ static int create_trace_probe(int argc, char **argv) if (!event) { /* Make a new event name */ if (symbol) - snprintf(buf, MAX_EVENT_NAME_LEN, "%c@%s%+ld", + snprintf(buf, MAX_EVENT_NAME_LEN, "%c_%s_%ld", is_return ? 'r' : 'p', symbol, offset); else - snprintf(buf, MAX_EVENT_NAME_LEN, "%c@0x%p", + snprintf(buf, MAX_EVENT_NAME_LEN, "%c_0x%p", is_return ? 'r' : 'p', addr); event = buf; } |
|
From: tip-bot f. M. H. <mhi...@re...> - 2009-12-17 10:53:26
|
Commit-ID: 96c96612e952f63cc0055db9df7d8b5b1ada02be Gitweb: http://git.kernel.org/tip/96c96612e952f63cc0055db9df7d8b5b1ada02be Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Wed, 16 Dec 2009 17:24:00 -0500 Committer: Ingo Molnar <mi...@el...> CommitDate: Thu, 17 Dec 2009 09:42:43 +0100 perf probe: Check whether debugfs path is correct Check whether the debugfs path is correct before executing a command, because perf-probe depends on debugfs. Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: Frederic Weisbecker <fwe...@gm...> Cc: Paul Mackerras <pa...@sa...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Steven Rostedt <ro...@go...> Cc: Jim Keniston <jke...@us...> Cc: Ananth N Mavinakayanahalli <an...@in...> Cc: Christoph Hellwig <hc...@in...> Cc: Jason Baron <jb...@re...> Cc: K.Prasad <pr...@li...> Cc: Peter Zijlstra <pe...@in...> Cc: Srikar Dronamraju <sr...@li...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> LKML-Reference: <200...@dh...> Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/builtin-probe.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 7e741f5..c1e6774 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -38,6 +38,7 @@ #include "util/strlist.h" #include "util/event.h" #include "util/debug.h" +#include "util/debugfs.h" #include "util/symbol.h" #include "util/thread.h" #include "util/session.h" @@ -205,6 +206,9 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) if ((!session.nr_probe && !session.dellist && !session.list_events)) usage_with_options(probe_usage, options); + if (debugfs_valid_mountpoint(debugfs_path) < 0) + die("Failed to find debugfs path."); + if (session.list_events) { if (session.nr_probe != 0 || session.dellist) { pr_warning(" Error: Don't use --list with" |
|
From: tip-bot f. M. H. <mhi...@re...> - 2009-12-17 07:56:32
|
Commit-ID: 27f3b24de03fc7cec6f2406f8525ad18086c2121 Gitweb: http://git.kernel.org/tip/27f3b24de03fc7cec6f2406f8525ad18086c2121 Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Wed, 16 Dec 2009 17:16:19 -0500 Committer: Ingo Molnar <mi...@el...> CommitDate: Thu, 17 Dec 2009 08:06:41 +0100 perf probe: Fix libdwarf include path for Debian Fix libdwarf include path to fit debian-like systems too. Borislav Petkov reported: > even after installing libdwarf-dev on my debian box here, > make in tools/perf/ still complains that it cannot find libdwarf: > > Makefile:491: No libdwarf.h found or old libdwarf.h found, disables dwarf > support. Please install libdwarf-dev/libdwarf-devel >= 20081231 > > The problem is that the include path on debian is not > /usr/include/libdwarf/ but simply /usr/include because the debian > package libdwarf-dev puts the headers straight into > /usr/include. This patch adds -I/usr/include/libdwarf to BASIC_CFLAGS and fix probe-finder.h to include just libdwarf.h/dwarf.h. This patch also adds a workaround for the undefined _MIPS_SZLONG bug in libdwarf.h. Reported-by: Borislav Petkov <bor...@am...> Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: Frederic Weisbecker <fwe...@gm...> Cc: Paul Mackerras <pa...@sa...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Peter Zijlstra <pe...@in...> Cc: Srikar Dronamraju <sr...@li...> Cc: Gabor Gombas <go...@sz...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> LKML-Reference: <200...@dh...> [ v2: small stylistic fixlets to probe-finder.h ] Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/Makefile | 3 +- tools/perf/util/probe-finder.h | 59 +++++++++++++++++++++------------------ 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 7814dbb..4390d22 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -487,10 +487,11 @@ else msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel and glibc-dev[el]); endif -ifneq ($(shell sh -c "(echo '\#include <libdwarf/dwarf.h>'; echo '\#include <libdwarf/libdwarf.h>'; echo 'int main(void) { Dwarf_Debug dbg; Dwarf_Error err; Dwarf_Ranges *rng; dwarf_init(0, DW_DLC_READ, 0, 0, &dbg, &err); dwarf_get_ranges(dbg, 0, &rng, 0, 0, &err); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -ldwarf -lelf -o /dev/null $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) +ifneq ($(shell sh -c "(echo '\#ifndef _MIPS_SZLONG'; echo '\#define _MIPS_SZLONG 0'; echo '\#endif'; echo '\#include <dwarf.h>'; echo '\#include <libdwarf.h>'; echo 'int main(void) { Dwarf_Debug dbg; Dwarf_Error err; Dwarf_Ranges *rng; dwarf_init(0, DW_DLC_READ, 0, 0, &dbg, &err); dwarf_get_ranges(dbg, 0, &rng, 0, 0, &err); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/libdwarf -ldwarf -lelf -o /dev/null $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) msg := $(warning No libdwarf.h found or old libdwarf.h found, disables dwarf support. Please install libdwarf-dev/libdwarf-devel >= 20081231); BASIC_CFLAGS += -DNO_LIBDWARF else + BASIC_CFLAGS += -I/usr/include/libdwarf EXTLIBS += -lelf -ldwarf LIB_OBJS += util/probe-finder.o endif diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 5e4050c..a4086aa 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -1,9 +1,9 @@ #ifndef _PROBE_FINDER_H #define _PROBE_FINDER_H -#define MAX_PATH_LEN 256 -#define MAX_PROBE_BUFFER 1024 -#define MAX_PROBES 128 +#define MAX_PATH_LEN 256 +#define MAX_PROBE_BUFFER 1024 +#define MAX_PROBES 128 static inline int is_c_varname(const char *name) { @@ -12,48 +12,53 @@ static inline int is_c_varname(const char *name) } struct probe_point { - char *event; /* Event name */ - char *group; /* Event group */ + char *event; /* Event name */ + char *group; /* Event group */ /* Inputs */ - char *file; /* File name */ - int line; /* Line number */ + char *file; /* File name */ + int line; /* Line number */ - char *function; /* Function name */ - int offset; /* Offset bytes */ + char *function; /* Function name */ + int offset; /* Offset bytes */ - int nr_args; /* Number of arguments */ - char **args; /* Arguments */ + int nr_args; /* Number of arguments */ + char **args; /* Arguments */ - int retprobe; /* Return probe */ + int retprobe; /* Return probe */ /* Output */ - int found; /* Number of found probe points */ - char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ + int found; /* Number of found probe points */ + char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ }; #ifndef NO_LIBDWARF extern int find_probepoint(int fd, struct probe_point *pp); -#include <libdwarf/dwarf.h> -#include <libdwarf/libdwarf.h> +/* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */ +#ifndef _MIPS_SZLONG +# define _MIPS_SZLONG 0 +#endif + +#include <dwarf.h> +#include <libdwarf.h> struct probe_finder { - struct probe_point *pp; /* Target probe point */ + struct probe_point *pp; /* Target probe point */ /* For function searching */ - Dwarf_Addr addr; /* Address */ - Dwarf_Unsigned fno; /* File number */ - Dwarf_Unsigned lno; /* Line number */ - Dwarf_Off inl_offs; /* Inline offset */ - Dwarf_Die cu_die; /* Current CU */ + Dwarf_Addr addr; /* Address */ + Dwarf_Unsigned fno; /* File number */ + Dwarf_Unsigned lno; /* Line number */ + Dwarf_Off inl_offs; /* Inline offset */ + Dwarf_Die cu_die; /* Current CU */ /* For variable searching */ - Dwarf_Addr cu_base; /* Current CU base address */ - Dwarf_Locdesc fbloc; /* Location of Current Frame Base */ - const char *var; /* Current variable name */ - char *buf; /* Current output buffer */ - int len; /* Length of output buffer */ + Dwarf_Addr cu_base; /* Current CU base address */ + Dwarf_Locdesc fbloc; /* Location of Current Frame Base */ + const char *var; /* Current variable name */ + char *buf; /* Current output buffer */ + int len; /* Length of output buffer */ }; #endif /* NO_LIBDWARF */ |
|
From: Masami H. <mhi...@re...> - 2009-12-16 22:20:41
|
Check new event name is same syntax as c symbol in perf command.
In other words, checking the name is as like as other tracepoint
events.
This can prevent user to create an event with useless name (e.g.
foo|bar, foo*bar).
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ingo Molnar <mi...@el...>
Cc: Frederic Weisbecker <fwe...@gm...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Steven Rostedt <ro...@go...>
Cc: Jim Keniston <jke...@us...>
Cc: Ananth N Mavinakayanahalli <an...@in...>
Cc: Christoph Hellwig <hc...@in...>
Cc: Frank Ch. Eigler <fc...@re...>
Cc: Jason Baron <jb...@re...>
Cc: K.Prasad <pr...@li...>
Cc: Peter Zijlstra <pe...@in...>
Cc: Srikar Dronamraju <sr...@li...>
---
tools/perf/util/probe-event.c | 14 ++++++++++++++
1 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 2ca6215..6fca3b6 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -62,6 +62,17 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
return ret;
}
+/* Check the name is good for event/group */
+static bool good_event_name(const char *name)
+{
+ if (!isalpha(*name) && *name != '_')
+ return false;
+ while (*++name != '\0')
+ if (!isalpha(*name) && !isdigit(*name) && *name != '_')
+ return false;
+ return true;
+}
+
/* Parse probepoint definition. */
static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
{
@@ -82,6 +93,9 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
ptr = strchr(arg, ':');
if (ptr) /* Group name is not supported yet. */
semantic_error("Group name is not supported yet.");
+ if (!good_event_name(arg))
+ semantic_error("%s is bad for event name -it must "
+ "follow C symbol-naming rule.", arg);
pp->event = strdup(arg);
arg = tmp;
}
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2009-12-16 22:20:14
|
Check new event/group name is same syntax as c symbol. In other
words, checking the name is as like as other tracepoint events.
This can prevent user to create an event with useless name (e.g.
foo|bar, foo*bar).
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ingo Molnar <mi...@el...>
Cc: Frederic Weisbecker <fwe...@gm...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Steven Rostedt <ro...@go...>
Cc: Jim Keniston <jke...@us...>
Cc: Ananth N Mavinakayanahalli <an...@in...>
Cc: Christoph Hellwig <hc...@in...>
Cc: Frank Ch. Eigler <fc...@re...>
Cc: Jason Baron <jb...@re...>
Cc: K.Prasad <pr...@li...>
Cc: Peter Zijlstra <pe...@in...>
Cc: Srikar Dronamraju <sr...@li...>
---
kernel/trace/trace_kprobe.c | 30 ++++++++++++++++++++++++------
1 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 7ecab06..167c189 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -282,6 +282,17 @@ static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs);
static int kretprobe_dispatcher(struct kretprobe_instance *ri,
struct pt_regs *regs);
+/* Check the name is good for event/group */
+static int good_event_name(const char *name)
+{
+ if (!isalpha(*name) && *name != '_')
+ return 0;
+ while (*++name != '\0')
+ if (!isalpha(*name) && !isdigit(*name) && *name != '_')
+ return 0;
+ return 1;
+}
+
/*
* Allocate new trace_probe and initialize it (including kprobes).
*/
@@ -293,10 +304,11 @@ static struct trace_probe *alloc_trace_probe(const char *group,
int nargs, int is_return)
{
struct trace_probe *tp;
+ int ret = -ENOMEM;
tp = kzalloc(SIZEOF_TRACE_PROBE(nargs), GFP_KERNEL);
if (!tp)
- return ERR_PTR(-ENOMEM);
+ return ERR_PTR(ret);
if (symbol) {
tp->symbol = kstrdup(symbol, GFP_KERNEL);
@@ -312,14 +324,20 @@ static struct trace_probe *alloc_trace_probe(const char *group,
else
tp->rp.kp.pre_handler = kprobe_dispatcher;
- if (!event)
+ if (!event || !good_event_name(event)) {
+ ret = -EINVAL;
goto error;
+ }
+
tp->call.name = kstrdup(event, GFP_KERNEL);
if (!tp->call.name)
goto error;
- if (!group)
+ if (!group || !good_event_name(group)) {
+ ret = -EINVAL;
goto error;
+ }
+
tp->call.system = kstrdup(group, GFP_KERNEL);
if (!tp->call.system)
goto error;
@@ -330,7 +348,7 @@ error:
kfree(tp->call.name);
kfree(tp->symbol);
kfree(tp);
- return ERR_PTR(-ENOMEM);
+ return ERR_PTR(ret);
}
static void free_probe_arg(struct probe_arg *arg)
@@ -695,10 +713,10 @@ static int create_trace_probe(int argc, char **argv)
if (!event) {
/* Make a new event name */
if (symbol)
- snprintf(buf, MAX_EVENT_NAME_LEN, "%c@%s%+ld",
+ snprintf(buf, MAX_EVENT_NAME_LEN, "%c_%s_%ld",
is_return ? 'r' : 'p', symbol, offset);
else
- snprintf(buf, MAX_EVENT_NAME_LEN, "%c@0x%p",
+ snprintf(buf, MAX_EVENT_NAME_LEN, "%c_0x%p",
is_return ? 'r' : 'p', addr);
event = buf;
}
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2009-12-16 22:19:51
|
Check whether debugpath is correct before executing command,
because perf-probe depends on the debugpath.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ingo Molnar <mi...@el...>
Cc: Frederic Weisbecker <fwe...@gm...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Steven Rostedt <ro...@go...>
Cc: Jim Keniston <jke...@us...>
Cc: Ananth N Mavinakayanahalli <an...@in...>
Cc: Christoph Hellwig <hc...@in...>
Cc: Frank Ch. Eigler <fc...@re...>
Cc: Jason Baron <jb...@re...>
Cc: K.Prasad <pr...@li...>
Cc: Peter Zijlstra <pe...@in...>
Cc: Srikar Dronamraju <sr...@li...>
---
tools/perf/builtin-probe.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 7e741f5..c1e6774 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -38,6 +38,7 @@
#include "util/strlist.h"
#include "util/event.h"
#include "util/debug.h"
+#include "util/debugfs.h"
#include "util/symbol.h"
#include "util/thread.h"
#include "util/session.h"
@@ -205,6 +206,9 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
if ((!session.nr_probe && !session.dellist && !session.list_events))
usage_with_options(probe_usage, options);
+ if (debugfs_valid_mountpoint(debugfs_path) < 0)
+ die("Failed to find debugfs path.");
+
if (session.list_events) {
if (session.nr_probe != 0 || session.dellist) {
pr_warning(" Error: Don't use --list with"
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2009-12-16 22:19:32
|
Hi,
Here are several bugfixes for perf-probe and kprobe tracer.
Thank you,
---
Masami Hiramatsu (3):
perf probe: Check new event name
kprobe-tracer: Check new event/group name
perf probe: Check debugpath is correct
kernel/trace/trace_kprobe.c | 30 ++++++++++++++++++++++++------
tools/perf/builtin-probe.c | 4 ++++
tools/perf/util/probe-event.c | 14 ++++++++++++++
3 files changed, 42 insertions(+), 6 deletions(-)
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2009-12-16 22:14:40
|
Fix a typo of "character" in gen-insn-attr-x86.awk.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ingo Molnar <mi...@el...>
Cc: Jim Keniston <jke...@us...>
---
arch/x86/tools/gen-insn-attr-x86.awk | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/x86/tools/gen-insn-attr-x86.awk b/arch/x86/tools/gen-insn-attr-x86.awk
index 7a68506..6d1dc09 100644
--- a/arch/x86/tools/gen-insn-attr-x86.awk
+++ b/arch/x86/tools/gen-insn-attr-x86.awk
@@ -7,7 +7,7 @@
# Awk implementation sanity check
function check_awk_implement() {
if (!match("abc", "[[:lower:]]+"))
- return "Your awk doesn't support charactor-class."
+ return "Your awk doesn't support character-class."
if (sprintf("%x", 0) != "0")
return "Your awk has a printf-format problem."
return ""
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2009-12-16 22:12:38
|
Fix libdwarf include path to fit debian-like systems too.
Borislav Petkov said:
> even after installing libdwarf-dev on my debian box here, make in
> tools/perf/
> still complains that it cannot find libdwarf:
>
> Makefile:491: No libdwarf.h found or old libdwarf.h found, disables dwarf
> support. Please install libdwarf-dev/libdwarf-devel >= 20081231
>
> The problem is that the include path on debian is not
> /usr/include/libdwarf/ but simply /usr/include because the debian
> package libdwarf-dev puts the headers straight into /usr/include.
This patch adds -I/usr/include/libdwarf to BASIC_CFLAGS
and fix probe-finder.h to include just libdwarf.h/dwarf.h.
This patch also add a workaround for undefined _MIPS_SZLONG bug
in libdwarf.h.
Reported-by: Borislav Petkov <bor...@am...>
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ingo Molnar <mi...@el...>
Cc: Frederic Weisbecker <fwe...@gm...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Peter Zijlstra <pe...@in...>
Cc: Srikar Dronamraju <sr...@li...>
Cc: Gabor Gombas <go...@sz...>
Cc: Borislav Petkov <bor...@am...>
---
tools/perf/Makefile | 3 ++-
tools/perf/util/probe-finder.h | 9 +++++++--
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 7814dbb..4390d22 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -487,10 +487,11 @@ else
msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel and glibc-dev[el]);
endif
-ifneq ($(shell sh -c "(echo '\#include <libdwarf/dwarf.h>'; echo '\#include <libdwarf/libdwarf.h>'; echo 'int main(void) { Dwarf_Debug dbg; Dwarf_Error err; Dwarf_Ranges *rng; dwarf_init(0, DW_DLC_READ, 0, 0, &dbg, &err); dwarf_get_ranges(dbg, 0, &rng, 0, 0, &err); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -ldwarf -lelf -o /dev/null $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
+ifneq ($(shell sh -c "(echo '\#ifndef _MIPS_SZLONG'; echo '\#define _MIPS_SZLONG 0'; echo '\#endif'; echo '\#include <dwarf.h>'; echo '\#include <libdwarf.h>'; echo 'int main(void) { Dwarf_Debug dbg; Dwarf_Error err; Dwarf_Ranges *rng; dwarf_init(0, DW_DLC_READ, 0, 0, &dbg, &err); dwarf_get_ranges(dbg, 0, &rng, 0, 0, &err); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/libdwarf -ldwarf -lelf -o /dev/null $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
msg := $(warning No libdwarf.h found or old libdwarf.h found, disables dwarf support. Please install libdwarf-dev/libdwarf-devel >= 20081231);
BASIC_CFLAGS += -DNO_LIBDWARF
else
+ BASIC_CFLAGS += -I/usr/include/libdwarf
EXTLIBS += -lelf -ldwarf
LIB_OBJS += util/probe-finder.o
endif
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 5e4050c..b6858f4 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -35,8 +35,13 @@ struct probe_point {
#ifndef NO_LIBDWARF
extern int find_probepoint(int fd, struct probe_point *pp);
-#include <libdwarf/dwarf.h>
-#include <libdwarf/libdwarf.h>
+/* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h */
+#ifndef _MIPS_SZLONG
+#define _MIPS_SZLONG 0
+#endif
+
+#include <dwarf.h>
+#include <libdwarf.h>
struct probe_finder {
struct probe_point *pp; /* Target probe point */
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: tip-bot f. M. H. <mhi...@re...> - 2009-12-15 19:29:11
|
Commit-ID: d761b08bff0a9b653f6bd248cea50322e7eccb14 Gitweb: http://git.kernel.org/tip/d761b08bff0a9b653f6bd248cea50322e7eccb14 Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Tue, 15 Dec 2009 10:32:25 -0500 Committer: Ingo Molnar <mi...@el...> CommitDate: Tue, 15 Dec 2009 20:22:04 +0100 perf probe: Reject second attempt of adding same-name event Reject second attempt of adding same-name event. This patch also provides --force option which allows user to add additional probe events on the same-name event. e.g. (the first attempt : success) ./perf probe schedule Added new event: probe:schedule (on schedule+0) (the second attempt : failure) ./perf probe schedule:11 Error: event "schedule" already exists. (Use -f to force duplicates.) Fatal: Can't add new event. (the second attempt with -f : successfully added) ./perf probe -f schedule:11 Added new event: probe:schedule_1 (on schedule+45) Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: Paul Mackerras <pa...@sa...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Steven Rostedt <ro...@go...> Cc: Jim Keniston <jke...@us...> Cc: Ananth N Mavinakayanahalli <an...@in...> Cc: Christoph Hellwig <hc...@in...> Cc: Frank Ch. Eigler <fc...@re...> Cc: Jason Baron <jb...@re...> Cc: K.Prasad <pr...@li...> Cc: Peter Zijlstra <pe...@in...> Cc: Srikar Dronamraju <sr...@li...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> Cc: Frederic Weisbecker <fwe...@gm...> LKML-Reference: <200...@dh...> Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/builtin-probe.c | 6 +++++- tools/perf/util/probe-event.c | 24 +++++++++++++++++++++--- tools/perf/util/probe-event.h | 3 ++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index adc0a55..8b4fdae 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -61,6 +61,7 @@ static struct { char *release; bool need_dwarf; bool list_events; + bool force_add; int nr_probe; struct probe_point probes[MAX_PROBES]; struct strlist *dellist; @@ -192,6 +193,8 @@ static const struct option options[] = { #endif "\t\t\tkprobe-tracer argument format.)\n", opt_add_probe_event), + OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events" + " with existing name"), OPT_END() }; @@ -294,7 +297,8 @@ end_dwarf: } /* Settng up probe points */ - add_trace_kprobe_events(session.probes, session.nr_probe); + add_trace_kprobe_events(session.probes, session.nr_probe, + session.force_add); return 0; } diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 3b4cf45..b05d532 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -482,7 +482,7 @@ static void write_trace_kprobe_event(int fd, const char *buf) } static void get_new_event_name(char *buf, size_t len, const char *base, - struct strlist *namelist) + struct strlist *namelist, bool allow_suffix) { int i, ret; @@ -493,6 +493,12 @@ static void get_new_event_name(char *buf, size_t len, const char *base, if (!strlist__has_entry(namelist, buf)) return; + if (!allow_suffix) { + pr_warning("Error: event \"%s\" already exists. " + "(Use -f to force duplicates.)\n", base); + die("Can't add new event."); + } + /* Try to add suffix */ for (i = 1; i < MAX_EVENT_INDEX; i++) { ret = e_snprintf(buf, len, "%s_%d", base, i); @@ -505,13 +511,15 @@ static void get_new_event_name(char *buf, size_t len, const char *base, die("Too many events are on the same function."); } -void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) +void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, + bool force_add) { int i, j, fd; struct probe_point *pp; char buf[MAX_CMDLEN]; char event[64]; struct strlist *namelist; + bool allow_suffix; fd = open_kprobe_events(O_RDWR, O_APPEND); /* Get current event names */ @@ -524,9 +532,12 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) if (!pp->group) pp->group = strdup(PERFPROBE_GROUP); DIE_IF(!pp->event || !pp->group); + /* If force_add is true, suffix search is allowed */ + allow_suffix = force_add; for (i = 0; i < pp->found; i++) { /* Get an unused new event name */ - get_new_event_name(event, 64, pp->event, namelist); + get_new_event_name(event, 64, pp->event, namelist, + allow_suffix); snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", pp->retprobe ? 'r' : 'p', pp->group, event, @@ -538,6 +549,13 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) show_perf_probe_event(event, buf, pp); /* Add added event name to namelist */ strlist__add(namelist, event); + /* + * Probes after the first probe which comes from same + * user input are always allowed to add suffix, because + * there might be several addresses corresponding to + * one code line. + */ + allow_suffix = true; } } /* Show how to use the event. */ diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 8bb22f5..8fd3052 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -10,7 +10,8 @@ extern void parse_perf_probe_event(const char *str, struct probe_point *pp, extern int synthesize_perf_probe_event(struct probe_point *pp); extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp); extern int synthesize_trace_kprobe_event(struct probe_point *pp); -extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes); +extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, + bool force_add); extern void del_trace_kprobe_events(struct strlist *dellist); extern void show_perf_probe_events(void); |
|
From: tip-bot f. M. H. <mhi...@re...> - 2009-12-15 19:29:11
|
Commit-ID: a128168d1e79e537d6666655e7771d973e9230e3 Gitweb: http://git.kernel.org/tip/a128168d1e79e537d6666655e7771d973e9230e3 Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Tue, 15 Dec 2009 10:32:33 -0500 Committer: Ingo Molnar <mi...@el...> CommitDate: Tue, 15 Dec 2009 20:22:04 +0100 perf probe: Check build-id of vmlinux Check build-id of vmlinux by using functions in symbol.c. This also exposes map__load() for getting vmlinux path, and removes vmlinux path list in builtin-probe.c, because symbol.c already has that. Checking build-id prevents users to open old or different debuginfo from current running kernel. Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: Paul Mackerras <pa...@sa...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Steven Rostedt <ro...@go...> Cc: Jim Keniston <jke...@us...> Cc: Ananth N Mavinakayanahalli <an...@in...> Cc: Christoph Hellwig <hc...@in...> Cc: Frank Ch. Eigler <fc...@re...> Cc: Jason Baron <jb...@re...> Cc: K.Prasad <pr...@li...> Cc: Peter Zijlstra <pe...@in...> Cc: Srikar Dronamraju <sr...@li...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> Cc: Frederic Weisbecker <fwe...@gm...> LKML-Reference: <200...@dh...> Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/builtin-probe.c | 72 ++++++++++++++++++------------------------- tools/perf/util/event.h | 2 + tools/perf/util/map.c | 14 +++++--- 3 files changed, 41 insertions(+), 47 deletions(-) diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 8b4fdae..0584b7a 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -38,33 +38,27 @@ #include "util/strlist.h" #include "util/event.h" #include "util/debug.h" +#include "util/symbol.h" +#include "util/thread.h" +#include "util/session.h" #include "util/parse-options.h" #include "util/parse-events.h" /* For debugfs_path */ #include "util/probe-finder.h" #include "util/probe-event.h" -/* Default vmlinux search paths */ -#define NR_SEARCH_PATH 4 -const char *default_search_path[NR_SEARCH_PATH] = { -"/lib/modules/%s/build/vmlinux", /* Custom build kernel */ -"/usr/lib/debug/lib/modules/%s/vmlinux", /* Red Hat debuginfo */ -"/boot/vmlinux-debug-%s", /* Ubuntu */ -"./vmlinux", /* CWD */ -}; - #define MAX_PATH_LEN 256 #define MAX_PROBES 128 /* Session management structure */ static struct { - char *vmlinux; - char *release; bool need_dwarf; bool list_events; bool force_add; int nr_probe; struct probe_point probes[MAX_PROBES]; struct strlist *dellist; + struct symbol_conf conf; + struct perf_session *psession; } session; @@ -122,33 +116,21 @@ static int opt_del_probe_event(const struct option *opt __used, } #ifndef NO_LIBDWARF -static int open_default_vmlinux(void) +static int open_vmlinux(void) { - struct utsname uts; - char fname[MAX_PATH_LEN]; - int fd, ret, i; - - ret = uname(&uts); - if (ret) { - pr_debug("uname() failed.\n"); - return -errno; + struct map *kmap; + kmap = map_groups__find_by_name(&session.psession->kmaps, + MAP__FUNCTION, "[kernel.kallsyms]"); + if (!kmap) { + pr_debug("Could not find kernel map.\n"); + return -ENOENT; } - session.release = uts.release; - for (i = 0; i < NR_SEARCH_PATH; i++) { - ret = snprintf(fname, MAX_PATH_LEN, - default_search_path[i], session.release); - if (ret >= MAX_PATH_LEN || ret < 0) { - pr_debug("Filename(%d,%s) is too long.\n", i, - uts.release); - errno = E2BIG; - return -E2BIG; - } - pr_debug("try to open %s\n", fname); - fd = open(fname, O_RDONLY); - if (fd >= 0) - break; + if (map__load(kmap, session.psession, NULL) < 0) { + pr_debug("Failed to load kernel map.\n"); + return -EINVAL; } - return fd; + pr_debug("Try to open %s\n", kmap->dso->long_name); + return open(kmap->dso->long_name, O_RDONLY); } #endif @@ -164,8 +146,8 @@ static const struct option options[] = { OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show parsed arguments, etc)"), #ifndef NO_LIBDWARF - OPT_STRING('k', "vmlinux", &session.vmlinux, "file", - "vmlinux/module pathname"), + OPT_STRING('k', "vmlinux", &session.conf.vmlinux_name, + "file", "vmlinux pathname"), #endif OPT_BOOLEAN('l', "list", &session.list_events, "list up current probe events"), @@ -236,17 +218,23 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) return 0; } + /* Initialize symbol maps for vmlinux */ + if (session.conf.vmlinux_name == NULL) + session.conf.try_vmlinux_path = true; + if (symbol__init(&session.conf) < 0) + die("Failed to init symbol map."); + session.psession = perf_session__new(NULL, O_WRONLY, false, + &session.conf); + if (session.psession == NULL) + die("Failed to init perf_session."); + if (session.need_dwarf) #ifdef NO_LIBDWARF die("Debuginfo-analysis is not supported"); #else /* !NO_LIBDWARF */ pr_debug("Some probes require debuginfo.\n"); - if (session.vmlinux) { - pr_debug("Try to open %s.", session.vmlinux); - fd = open(session.vmlinux, O_RDONLY); - } else - fd = open_default_vmlinux(); + fd = open_vmlinux(); if (fd < 0) { if (session.need_dwarf) die("Could not open debuginfo file."); diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index a92e0b0..8027309 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -152,6 +152,8 @@ size_t map__fprintf(struct map *self, FILE *fp); struct perf_session; +int map__load(struct map *self, struct perf_session *session, + symbol_filter_t filter); struct symbol *map__find_symbol(struct map *self, struct perf_session *session, u64 addr, symbol_filter_t filter); struct symbol *map__find_symbol_by_name(struct map *self, const char *name, diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 8b3dd46..c4d55a0 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -104,12 +104,16 @@ void map__fixup_end(struct map *self) #define DSO__DELETED "(deleted)" -static int map__load(struct map *self, struct perf_session *session, - symbol_filter_t filter) +int map__load(struct map *self, struct perf_session *session, + symbol_filter_t filter) { const char *name = self->dso->long_name; - int nr = dso__load(self->dso, self, session, filter); + int nr; + if (dso__loaded(self->dso, self->type)) + return 0; + + nr = dso__load(self->dso, self, session, filter); if (nr < 0) { if (self->dso->has_build_id) { char sbuild_id[BUILD_ID_SIZE * 2 + 1]; @@ -147,7 +151,7 @@ static int map__load(struct map *self, struct perf_session *session, struct symbol *map__find_symbol(struct map *self, struct perf_session *session, u64 addr, symbol_filter_t filter) { - if (!dso__loaded(self->dso, self->type) && map__load(self, session, filter) < 0) + if (map__load(self, session, filter) < 0) return NULL; return dso__find_symbol(self->dso, self->type, addr); @@ -157,7 +161,7 @@ struct symbol *map__find_symbol_by_name(struct map *self, const char *name, struct perf_session *session, symbol_filter_t filter) { - if (!dso__loaded(self->dso, self->type) && map__load(self, session, filter) < 0) + if (map__load(self, session, filter) < 0) return NULL; if (!dso__sorted_by_name(self->dso, self->type)) |
|
From: tip-bot f. M. H. <mhi...@re...> - 2009-12-15 19:28:55
|
Commit-ID: f6bbff77252cf12c82d480eaf6d9189129d1040a Gitweb: http://git.kernel.org/tip/f6bbff77252cf12c82d480eaf6d9189129d1040a Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Tue, 15 Dec 2009 10:31:42 -0500 Committer: Ingo Molnar <mi...@el...> CommitDate: Tue, 15 Dec 2009 20:22:01 +0100 perf probe: Fix --del to show info instead of warning Fix --del option to show info message instead of warning if failing to find specified event. Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: Paul Mackerras <pa...@sa...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Steven Rostedt <ro...@go...> Cc: Jim Keniston <jke...@us...> Cc: Ananth N Mavinakayanahalli <an...@in...> Cc: Christoph Hellwig <hc...@in...> Cc: Frank Ch. Eigler <fc...@re...> Cc: Jason Baron <jb...@re...> Cc: K.Prasad <pr...@li...> Cc: Peter Zijlstra <pe...@in...> Cc: Srikar Dronamraju <sr...@li...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> Cc: Frederic Weisbecker <fwe...@gm...> LKML-Reference: <200...@dh...> Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/util/probe-event.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 1653a62..1eacee6 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -544,7 +544,7 @@ static void del_trace_kprobe_event(int fd, const char *group, if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) die("Failed to copy event."); if (!strlist__has_entry(namelist, buf)) { - pr_warning("Warning: event \"%s\" is not found.\n", buf); + pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); return; } /* Convert from perf-probe event to trace-kprobe event */ |
|
From: tip-bot f. M. H. <mhi...@re...> - 2009-12-15 19:28:53
|
Commit-ID: af663d75a64d2cc3f18bdb8a29ff4650b9417c16 Gitweb: http://git.kernel.org/tip/af663d75a64d2cc3f18bdb8a29ff4650b9417c16 Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Tue, 15 Dec 2009 10:32:18 -0500 Committer: Ingo Molnar <mi...@el...> CommitDate: Tue, 15 Dec 2009 20:22:03 +0100 perf probe: Support event name for --add option Support event name syntax for --add option. This allows users to specify event name for each new event. The --add syntax is: perf probe --add '[EVENT=]SRC:LINE ARGS' or perf probe --add '[EVENT=]FUNC[+OFFS|%return|:RLN][@SRC] ARGS' e.g. ./perf probe --add myprobe1=schedule Note: currently group name is not supported yet, because it can cause name-space confliction with other tracepoint/ hw-breakpoint events. Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: Paul Mackerras <pa...@sa...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Steven Rostedt <ro...@go...> Cc: Jim Keniston <jke...@us...> Cc: Ananth N Mavinakayanahalli <an...@in...> Cc: Christoph Hellwig <hc...@in...> Cc: Frank Ch. Eigler <fc...@re...> Cc: Jason Baron <jb...@re...> Cc: K.Prasad <pr...@li...> Cc: Peter Zijlstra <pe...@in...> Cc: Srikar Dronamraju <sr...@li...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> Cc: Frederic Weisbecker <fwe...@gm...> LKML-Reference: <200...@dh...> Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/Documentation/perf-probe.txt | 3 +- tools/perf/builtin-probe.c | 8 ++-- tools/perf/util/probe-event.c | 74 +++++++++++++++++++------------ tools/perf/util/probe-event.h | 3 +- tools/perf/util/probe-finder.h | 3 + 5 files changed, 55 insertions(+), 36 deletions(-) diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 8fa6bf9..250e391 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -49,8 +49,9 @@ PROBE SYNTAX ------------ Probe points are defined by following syntax. - "FUNC[+OFFS|:RLN|%return][@SRC]|SRC:ALN [ARG ...]" + "[EVENT=]FUNC[+OFFS|:RLN|%return][@SRC]|SRC:ALN [ARG ...]" +'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'. 'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, 'RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. In addition, 'SRC' specifies a source file which has that function. It is also possible to specify a probe point by the source line number by using 'SRC:ALN' syntax, where 'SRC' is the source file path and 'ALN' is the line number. 'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc). diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 438a7bb..adc0a55 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -172,13 +172,13 @@ static const struct option options[] = { opt_del_probe_event), OPT_CALLBACK('a', "add", NULL, #ifdef NO_LIBDWARF - "FUNC[+OFFS|%return] [ARG ...]", + "[EVENT=]FUNC[+OFFS|%return] [ARG ...]", #else - "FUNC[+OFFS|%return|:RLN][@SRC]|SRC:ALN [ARG ...]", + "[EVENT=]FUNC[+OFFS|%return|:RLN][@SRC]|SRC:ALN [ARG ...]", #endif "probe point definition, where\n" - "\t\tGRP:\tGroup name (optional)\n" - "\t\tNAME:\tEvent name\n" + "\t\tGROUP:\tGroup name (optional)\n" + "\t\tEVENT:\tEvent name\n" "\t\tFUNC:\tFunction name\n" "\t\tOFFS:\tOffset from function entry (in byte)\n" "\t\t%return:\tPut the probe at function return\n" diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 5e99e52..3b4cf45 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -69,10 +69,23 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) char c, nc = 0; /* * <Syntax> - * perf probe SRC:LN - * perf probe FUNC[+OFFS|%return][@SRC] + * perf probe [EVENT=]SRC:LN + * perf probe [EVENT=]FUNC[+OFFS|%return][@SRC] + * + * TODO:Group name support */ + ptr = strchr(arg, '='); + if (ptr) { /* Event name */ + *ptr = '\0'; + tmp = ptr + 1; + ptr = strchr(arg, ':'); + if (ptr) /* Group name is not supported yet. */ + semantic_error("Group name is not supported yet."); + pp->event = strdup(arg); + arg = tmp; + } + ptr = strpbrk(arg, ":+@%"); if (ptr) { nc = *ptr; @@ -188,8 +201,7 @@ void parse_perf_probe_event(const char *str, struct probe_point *pp, } /* Parse kprobe_events event into struct probe_point */ -void parse_trace_kprobe_event(const char *str, char **group, char **event, - struct probe_point *pp) +void parse_trace_kprobe_event(const char *str, struct probe_point *pp) { char pr; char *p; @@ -205,18 +217,17 @@ void parse_trace_kprobe_event(const char *str, char **group, char **event, /* Scan event and group name. */ ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", - &pr, (float *)(void *)group, (float *)(void *)event); + &pr, (float *)(void *)&pp->group, + (float *)(void *)&pp->event); if (ret != 3) semantic_error("Failed to parse event name: %s", argv[0]); - pr_debug("Group:%s Event:%s probe:%c\n", *group, *event, pr); - - if (!pp) - goto end; + pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); pp->retprobe = (pr == 'r'); /* Scan function name and offset */ - ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, &pp->offset); + ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, + &pp->offset); if (ret == 1) pp->offset = 0; @@ -235,7 +246,6 @@ void parse_trace_kprobe_event(const char *str, char **group, char **event, die("Failed to copy argument."); } -end: argv_free(argv); } @@ -368,6 +378,10 @@ static void clear_probe_point(struct probe_point *pp) { int i; + if (pp->event) + free(pp->event); + if (pp->group) + free(pp->group); if (pp->function) free(pp->function); if (pp->file) @@ -382,13 +396,13 @@ static void clear_probe_point(struct probe_point *pp) } /* Show an event */ -static void show_perf_probe_event(const char *group, const char *event, - const char *place, struct probe_point *pp) +static void show_perf_probe_event(const char *event, const char *place, + struct probe_point *pp) { int i, ret; char buf[128]; - ret = e_snprintf(buf, 128, "%s:%s", group, event); + ret = e_snprintf(buf, 128, "%s:%s", pp->group, event); if (ret < 0) die("Failed to copy event: %s", strerror(-ret)); printf(" %-40s (on %s", buf, place); @@ -405,7 +419,6 @@ static void show_perf_probe_event(const char *group, const char *event, void show_perf_probe_events(void) { int fd, nr; - char *group, *event; struct probe_point pp; struct strlist *rawlist; struct str_node *ent; @@ -415,16 +428,14 @@ void show_perf_probe_events(void) close(fd); strlist__for_each(ent, rawlist) { - parse_trace_kprobe_event(ent->s, &group, &event, &pp); + parse_trace_kprobe_event(ent->s, &pp); /* Synthesize only event probe point */ nr = pp.nr_args; pp.nr_args = 0; synthesize_perf_probe_event(&pp); pp.nr_args = nr; /* Show an event */ - show_perf_probe_event(group, event, pp.probes[0], &pp); - free(group); - free(event); + show_perf_probe_event(pp.event, pp.probes[0], &pp); clear_probe_point(&pp); } @@ -434,24 +445,25 @@ void show_perf_probe_events(void) /* Get current perf-probe event names */ static struct strlist *get_perf_event_names(int fd, bool include_group) { - char *group, *event; char buf[128]; struct strlist *sl, *rawlist; struct str_node *ent; + struct probe_point pp; + memset(&pp, 0, sizeof(pp)); rawlist = get_trace_kprobe_event_rawlist(fd); sl = strlist__new(true, NULL); strlist__for_each(ent, rawlist) { - parse_trace_kprobe_event(ent->s, &group, &event, NULL); + parse_trace_kprobe_event(ent->s, &pp); if (include_group) { - if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) + if (e_snprintf(buf, 128, "%s:%s", pp.group, + pp.event) < 0) die("Failed to copy group:event name."); strlist__add(sl, buf); } else - strlist__add(sl, event); - free(group); - free(event); + strlist__add(sl, pp.event); + clear_probe_point(&pp); } strlist__delete(rawlist); @@ -507,19 +519,23 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) for (j = 0; j < nr_probes; j++) { pp = probes + j; + if (!pp->event) + pp->event = strdup(pp->function); + if (!pp->group) + pp->group = strdup(PERFPROBE_GROUP); + DIE_IF(!pp->event || !pp->group); for (i = 0; i < pp->found; i++) { /* Get an unused new event name */ - get_new_event_name(event, 64, pp->function, namelist); + get_new_event_name(event, 64, pp->event, namelist); snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", pp->retprobe ? 'r' : 'p', - PERFPROBE_GROUP, event, + pp->group, event, pp->probes[i]); write_trace_kprobe_event(fd, buf); printf("Added new event:\n"); /* Get the first parameter (probe-point) */ sscanf(pp->probes[i], "%s", buf); - show_perf_probe_event(PERFPROBE_GROUP, event, - buf, pp); + show_perf_probe_event(event, buf, pp); /* Add added event name to namelist */ strlist__add(namelist, event); } diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 028575b..8bb22f5 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -8,8 +8,7 @@ extern void parse_perf_probe_event(const char *str, struct probe_point *pp, bool *need_dwarf); extern int synthesize_perf_probe_event(struct probe_point *pp); -extern void parse_trace_kprobe_event(const char *str, char **group, - char **event, struct probe_point *pp); +extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp); extern int synthesize_trace_kprobe_event(struct probe_point *pp); extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes); extern void del_trace_kprobe_events(struct strlist *dellist); diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index bdebca6..5e4050c 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -12,6 +12,9 @@ static inline int is_c_varname(const char *name) } struct probe_point { + char *event; /* Event name */ + char *group; /* Event group */ + /* Inputs */ char *file; /* File name */ int line; /* Line number */ |
|
From: tip-bot f. M. H. <mhi...@re...> - 2009-12-15 19:28:51
|
Commit-ID: 3e3405906dab00afecd5a16871850a088eba4626 Gitweb: http://git.kernel.org/tip/3e3405906dab00afecd5a16871850a088eba4626 Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Tue, 15 Dec 2009 10:31:49 -0500 Committer: Ingo Molnar <mi...@el...> CommitDate: Tue, 15 Dec 2009 20:22:01 +0100 perf probe: Fix --del to update current event list Fix --del option to update current existing event list after perf probe deleted an event. Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: Paul Mackerras <pa...@sa...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Steven Rostedt <ro...@go...> Cc: Jim Keniston <jke...@us...> Cc: Ananth N Mavinakayanahalli <an...@in...> Cc: Christoph Hellwig <hc...@in...> Cc: Frank Ch. Eigler <fc...@re...> Cc: Jason Baron <jb...@re...> Cc: K.Prasad <pr...@li...> Cc: Peter Zijlstra <pe...@in...> Cc: Srikar Dronamraju <sr...@li...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> Cc: Frederic Weisbecker <fwe...@gm...> LKML-Reference: <200...@dh...> Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/util/probe-event.c | 5 ++++- tools/perf/util/strlist.c | 6 +++--- tools/perf/util/strlist.h | 7 ++++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 1eacee6..eab25d6 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -540,10 +540,12 @@ static void del_trace_kprobe_event(int fd, const char *group, const char *event, struct strlist *namelist) { char buf[128]; + struct str_node *ent; if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) die("Failed to copy event."); - if (!strlist__has_entry(namelist, buf)) { + ent = strlist__find(namelist, buf); + if (!ent) { pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); return; } @@ -553,6 +555,7 @@ static void del_trace_kprobe_event(int fd, const char *group, write_trace_kprobe_event(fd, buf); printf("Remove event: %s:%s\n", group, event); + strlist__remove(namelist, ent); } void del_trace_kprobe_events(struct strlist *dellist) diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 7ad3817..6783a20 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c @@ -102,7 +102,7 @@ void strlist__remove(struct strlist *self, struct str_node *sn) str_node__delete(sn, self->dupstr); } -bool strlist__has_entry(struct strlist *self, const char *entry) +struct str_node *strlist__find(struct strlist *self, const char *entry) { struct rb_node **p = &self->entries.rb_node; struct rb_node *parent = NULL; @@ -120,10 +120,10 @@ bool strlist__has_entry(struct strlist *self, const char *entry) else if (rc < 0) p = &(*p)->rb_right; else - return true; + return sn; } - return false; + return NULL; } static int strlist__parse_list_entry(struct strlist *self, const char *s) diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index cb46593..59091c7 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h @@ -23,7 +23,12 @@ int strlist__load(struct strlist *self, const char *filename); int strlist__add(struct strlist *self, const char *str); struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); -bool strlist__has_entry(struct strlist *self, const char *entry); +struct str_node *strlist__find(struct strlist *self, const char *entry); + +static inline bool strlist__has_entry(struct strlist *self, const char *entry) +{ + return strlist__find(self, entry) != NULL; +} static inline bool strlist__empty(const struct strlist *self) { |