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: <ebi...@xm...> - 2010-09-17 00:12:42
|
Seiji Aguchi <sei...@hd...> writes: > Hi, > > I'm Seiji Aguchi. > I work for Hitachi Data Systems. > It's a first time to send a patch to lkml. > Nice to meet you. > > I found an issue in kexec. > Please give me your comments and suggestions. > > Kexec abort when two cpus panic at the same time. > An example scenario: > 1. Two cpus panic at the same time . > 2. One cpu ,cpu0, get kexec_mutex in crash_kexec(). > 3. The other cpu ,cpu1, can't get kexec_mutex and return from crash_kexec(). > 4. Cpu0 runs kmsg_dump(KMSG_DUMP_KEXEC). > 5. Cpu1 can't get dump_list_lock and return from kmsg_dump(KMSG_DUMP_PANIC). > 6. Cpu1 runs smp_send_stop() in panic() and sends IPI to other cpus. > 7. Cpu0 may receive IPI from cpu1 while running kmsg_dump(KMSG_DUMP_KEXEC), > crash_setup_regs(), or crash_save_vmcore(). > > We can solve this issue by disabling external interrupt while getting kexec_mutex > in crash_kexec(). Disabling interrupts is fine, I thought we did that already at some point. However that call to kmsg_dump(KMSG_DUMP_KEXEC) is a bug as it introduces locks into a path that should not be taking locks. Please remove that broken kmsg_dump call as well. Nothing in the crash_kexec path should even have the option of blocking. Eric |
|
From: Seiji A. <sei...@hd...> - 2010-09-16 20:17:06
|
Hi,
I'm Seiji Aguchi.
I work for Hitachi Data Systems.
It's a first time to send a patch to lkml.
Nice to meet you.
I found an issue in kexec.
Please give me your comments and suggestions.
Kexec abort when two cpus panic at the same time.
An example scenario:
1. Two cpus panic at the same time .
2. One cpu ,cpu0, get kexec_mutex in crash_kexec().
3. The other cpu ,cpu1, can't get kexec_mutex and return from crash_kexec().
4. Cpu0 runs kmsg_dump(KMSG_DUMP_KEXEC).
5. Cpu1 can't get dump_list_lock and return from kmsg_dump(KMSG_DUMP_PANIC).
6. Cpu1 runs smp_send_stop() in panic() and sends IPI to other cpus.
7. Cpu0 may receive IPI from cpu1 while running kmsg_dump(KMSG_DUMP_KEXEC),
crash_setup_regs(), or crash_save_vmcore().
We can solve this issue by disabling external interrupt while getting kexec_mutex
in crash_kexec().
Signed-off-by: Seiji Aguchi <sei...@hd...>
---
kernel/kexec.c | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/kernel/kexec.c b/kernel/kexec.c
index c0613f7..9e9f159 100644
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -1075,6 +1075,10 @@ void crash_kexec(struct pt_regs *regs)
* sufficient. But since I reuse the memory...
*/
if (mutex_trylock(&kexec_mutex)) {
+ unsigned long flags;
+
+ local_irq_save(flags);
+
if (kexec_crash_image) {
struct pt_regs fixed_regs;
@@ -1085,6 +1089,9 @@ void crash_kexec(struct pt_regs *regs)
machine_crash_shutdown(&fixed_regs);
machine_kexec(kexec_crash_image);
}
+
+ local_irq_restore(flags);
+
mutex_unlock(&kexec_mutex);
}
}
--
1.7.2.2
Regards,
Seiji
|
|
From: Pctk <oi...@fc...> - 2010-08-31 07:47:36
|
Rar! |
|
From: Laure P. <res...@rv...> - 2010-08-05 20:27:16
|
Your private photo attached |
|
From: Vanessa B. <va...@ki...> - 2010-07-08 18:54:16
|
Hey There, Last year was a roller coaster year for many of us due to the economy. So I wanted to take a moment today to introduce you to an Author friend of mine that shares our mission of educating and empowering the world. That Author is “THE” nationally recognized Business Credit Expert, Thomas Kish. And his recent writing addresses one of the key elements essential to your success in ANY business - having access to business lines of credit. *Did you know that mon*y is available RIGHT NOW, even in today’s tough economy if you know how to get it? Seriously - NOW is one of the greatest times to be able to get MONEY. And one of the coolest ways to start investing or to start or expand a business is with the kind of money that most people aren’t aware of - Business Lines of Credit. If you know the techniques, you can quickly get $300,000 in unsecured business credit that can be used for ANY purpose. And this is money that never reports on your personal credit reports... If you want to get this kind of business credit FAST, don’t miss the special Saturday webinar we just found out about. It’s hosted by “THE” National Business Credit Expert himself, Thomas Kish. You’ll need to register for this webinar quickly before all the lines fill up (like last time!) Register right now at: http://www.kishbusinesscredit.com/ The training webinar will be held – Sat. July 10th 9am pac./11am central/Noon eastern In this upcoming class, you'll learn how to: *Register a business name in any state. *Separate your personal credit from your business. *Remove debt that is your personal name. *Save big on your taxes. *Get HUGE cash lines of business credit. *Plus more! Access to business credit is a valuable privilege in today’s tough market conditions. And you can quickly enjoy that privilege if you know the secret shortcuts to building business credit and getting money that you can use for any purpose. Tom is the creator of a trademarked 4 step recipe to getting new unsecured business lines of credit for any purpose. The system about to be revealed on this training is an amazingly simple way to get $200,000 to $300,000 in cash and business lines of credit no matter what kind of income, assets or score you currently have. And it gets EVEN BETTER – you’re also going to learn how to double or triple your lines of credit and build them to a million dollars. So please join Tom on this special LIVE call to learn all about his proven shortcut to getting business lines of credit in today’s economy. Register right now and receive the webinar access codes at: http://www.kishbusinesscredit.com/ Seriously; DON’T MISS THIS – I won’t! Sincerely, Vanessa Baumer Kish Business Credit LLC 3753 Howard Hughes Parkway, Suite 200 Las Vegas, NV 89169 P.S. Register right away before all the lines fill up and you’re LEFT OUT in the cold! http://www.kishbusinesscredit.com/ http://app.streamsend.com/private/w4D0/NeX/ZjAY7DR/unsubscribe/12078621 |
|
From: Arnaldo C. de M. <ac...@in...> - 2010-07-06 01:13:31
|
Em Mon, Jul 05, 2010 at 02:44:50PM -0300, Arnaldo Carvalho de Melo escreveu: > Em Mon, Jul 05, 2010 at 10:30:47AM -0400, Chase Douglas escreveu: > > On May 19, 2010, at 3:57 PM, Masami Hiramatsu wrote: > > > Here is a series of patches which improves perf probe to > > > add below features. I cleared some coding style issues. > > > - String support, which enables dynamic events to trace string > > > variables instead of its address. > > > - Fixed-index array entry support, which allows user to trace > > > an array entry with digit index, e.g. array[0]. > > > - Global/static variable support, which allows user to trace > > > global or static variables, as same as local one. > > What's the status of these patches? They seem very useful to me, but I > > don't see them in Arnaldo's tree at git.kernel.org. > Thanks for reminding me about these ones, will look at them to get them > in perf/core. Masami, I fixed up a minor conflict and while trying it this happened: [root@emilia ~]# perf probe -a 'bio_split bi->bi_io_vec[0]' kprobe_events file does not exist - please rebuild kernel with CONFIG_KPROBE_EVENT. Error: Failed to add events. (-1) But on a second try: [root@emilia ~]# l /sys/kernel/debug/tracing/kprobe_events -rw-r--r-- 1 root root 0 Jul 5 18:37 /sys/kernel/debug/tracing/kprobe_events [root@emilia ~]# cat /sys/kernel/debug/tracing/kprobe_events [root@emilia ~]# [root@emilia ~]# perf probe -a 'bio_split bi->bi_io_vec[0]' Add new event: probe:bio_split (on bio_split with bi_io_vec=bi->bi_io_vec[0]) You can now use it on all perf tools, such as: perf record -e probe:bio_split -aR sleep 1 [root@emilia ~]# cat /sys/kernel/debug/tracing/kprobe_events p:probe/bio_split bio_split bi_io_vec=+0(+72(%di)):u64 [root@emilia ~]# I'll push it as is, posting the pull request with the patches today, please check if everything is ok and send fixups if needed, Thanks, - Arnaldo |
|
From: Trmv <tz...@wx...> - 2010-07-05 18:00:05
|
您好!专业供应高仿香_-_烟。上好烟丝制作,包装无分别,口味不逊色真烟。仿真度90%以上。常年自抽,送礼皆可!QQ:107_938_9189 135_3012_0553王小姐 Ppbbrq Chfxr Yaflbq (Xcaf Igxides Hhfflas Loent Kbskqg Yqscemi Tcl (Ntudcnqe Udljwe Ninwvhk Gvt |
|
From: Arnaldo C. de M. <ac...@re...> - 2010-07-05 17:45:20
|
Em Mon, Jul 05, 2010 at 10:30:47AM -0400, Chase Douglas escreveu: > On May 19, 2010, at 3:57 PM, Masami Hiramatsu wrote: > > Hi, > > > > Here is a series of patches which improves perf probe to > > add below features. I cleared some coding style issues. > > > > - String support, which enables dynamic events to trace string > > variables instead of its address. > > > > - Fixed-index array entry support, which allows user to trace > > an array entry with digit index, e.g. array[0]. > > > > - Global/static variable support, which allows user to trace > > global or static variables, as same as local one. > > > > > > TODOs (possible features): > > - Support dynamic array-indexing (var[var2]) > > - Support force type-casting ((type)var) > > - Show what deta-structure member is assigned to each argument. > > - Better support for probes on modules > > - More debugger like enhancements(%next, --disasm, etc.) > > > > Thank you, > > > > --- > > > > Masami Hiramatsu (4): > > perf probe: Support static and global variables > > perf probe: Support tracing an entry of array > > perf probe: Support "string" type > > tracing/kprobes: Support "string" type > > What's the status of these patches? They seem very useful to me, but I > don't see them in Arnaldo's tree at git.kernel.org. Thanks for reminding me about these ones, will look at them to get them in perf/core. - Arnaldo |
|
From: Chase D. <cha...@ca...> - 2010-07-05 14:31:16
|
On May 19, 2010, at 3:57 PM, Masami Hiramatsu wrote: > Hi, > > Here is a series of patches which improves perf probe to > add below features. I cleared some coding style issues. > > - String support, which enables dynamic events to trace string > variables instead of its address. > > - Fixed-index array entry support, which allows user to trace > an array entry with digit index, e.g. array[0]. > > - Global/static variable support, which allows user to trace > global or static variables, as same as local one. > > > TODOs (possible features): > - Support dynamic array-indexing (var[var2]) > - Support force type-casting ((type)var) > - Show what deta-structure member is assigned to each argument. > - Better support for probes on modules > - More debugger like enhancements(%next, --disasm, etc.) > > Thank you, > > --- > > Masami Hiramatsu (4): > perf probe: Support static and global variables > perf probe: Support tracing an entry of array > perf probe: Support "string" type > tracing/kprobes: Support "string" type What's the status of these patches? They seem very useful to me, but I don't see them in Arnaldo's tree at git.kernel.org. Thanks, -- Chase |
|
From: J. R. J. <ry...@sh...> - 2010-06-22 10:17:44
|
Hello! Yesterday, I sent you a couple emails letting you know about D.C. Fawcett’s FR*EE virtual short sale investing videos. I’m sure you’ll agree that when you watched the videos you realized how extremely powerful D.C’s strategies are for today’s market. They are unlike anything else out there. I have never seen an investment strategy where you can get an unlimited number of leads for absolutely ZERO marketing cost. Well, I have even better news for you. I just got word from D.C. that he is covering the cost for you for his $495 Virtual Short Sale Investing Blueprint and DVD Course. The only catch is that he only has a few left so go here to enter your mailing address to claim your copy: http://www.shortsalereia.com You'll discover the secrets to investing virtually in short sales without using your own money or credit. ALL of the work is done for you by your network. You never have to leave the comfort of your own home. You don't need any experience because you are leveraging the experience of everyone in your network. This system includes... *** D.C.’s Virtual Short Sale Investing Blueprint and DVD with BONUS DVD on How to get as many leads as you can handle from 3 different Referral networks for no costs whatsoever ($495 Value) *** 30-day access into the Virtual Short Sale Investing (VSSI) VIP Coaching Club. ($97 Value) Your system is already paid for, but this offer is extremely limited and you must confirm your request or risk missing out forever. Get the details here: http://www.shortsalereia.com Committed to Your Extreme Success, J. Ryan Jermyn Transactional Funding Leader The National Short Sale REIA 401 Congress Ave., Suite 1540 Austin, TX 78701 P.S. There are only 1000 of the courses available and I know that 967 are already spoken for. Don't miss out on this. Get it here now before they’re all gone: http://www.shortsalereia.com http://app.streamsend.com/private/w4D0/NeX/ZjAY7DR/unsubscribe/11725421 |
|
From: DR M. J. <fmf...@ya...> - 2010-06-17 09:46:47
|
新しいメールアドレスをお知らせします新しいメールアドレス: fmf...@ya... My name is Dr MICHAEL JOHNSON would advise you to respond to this mail through fed...@ya... for a fund transfer to your account valued $10.5M only,Call me 234-802-616-6168 upon receipt of this mail as the fund is ready to be transfered to your account Regards - DR MICHAEL JOHNSON |
|
From: Masami H. <mas...@hi...> - 2010-06-16 09:25:11
|
Frederic Weisbecker wrote:
> On Wed, May 19, 2010 at 12:53:26PM -0400, Masami Hiramatsu wrote:
>> Introduce text_poke_smp_batch(). This function modifies several
>> text areas with one stop_machine() on SMPr. Because calling
>> stop_machine() is heavy task, it is better to aggregate text_poke
>> requests.
>>
>> Signed-off-by: Masami Hiramatsu <mhi...@re...>
>> Cc: Ananth N Mavinakayanahalli <an...@in...>
>> Cc: Ingo Molnar <mi...@el...>
>> Cc: Jim Keniston <jke...@us...>
>> Cc: Jason Baron <jb...@re...>
>> Cc: Mathieu Desnoyers <mat...@ef...>
> <snip>
>> +/**
>> + * text_poke_smp_batch - Update instructions on a live kernel on SMP
>> + * @params: an array of text_poke parameters
>> + * @n: the number of elements in params.
>> + *
>> + * Modify multi-byte instruction by using stop_machine() on SMP. Since the
>> + * stop_machine() is heavy task, it is better to aggregate text_poke requests
>> + * and do it once if possible.
>> + *
>> + * Note: Must be called under get_online_cpus() and text_mutex.
>> + */
>> +void __kprobes text_poke_smp_batch(struct text_poke_param *params, int n)
>> +{
>> + struct text_poke_params tpp = {.params = params, .nparams = n};
>> +
>> + atomic_set(&stop_machine_first, 1);
>> + wrote_text = 0;
>> + stop_machine(stop_machine_text_poke, (void *)&tpp, NULL);
>> +}
>
>
> Looks good. But wouldn't it be even better to get stop_machine()
> be able to support batches itself?
>
> We could have stop_machine_queue() and stop_machine_flush(),
> stop_machine() would be a shortcut for both, to execute single jobs,
> may be that could simplify some code here and there.
>
Ah, that's nice too :) But I don't know other people who need
that interfaces. Would you know there are any other potential users?
Thank you,
|
|
From: Frederic W. <fwe...@gm...> - 2010-06-15 12:27:06
|
On Wed, May 19, 2010 at 12:53:26PM -0400, Masami Hiramatsu wrote:
> Introduce text_poke_smp_batch(). This function modifies several
> text areas with one stop_machine() on SMPr. Because calling
> stop_machine() is heavy task, it is better to aggregate text_poke
> requests.
>
> Signed-off-by: Masami Hiramatsu <mhi...@re...>
> Cc: Ananth N Mavinakayanahalli <an...@in...>
> Cc: Ingo Molnar <mi...@el...>
> Cc: Jim Keniston <jke...@us...>
> Cc: Jason Baron <jb...@re...>
> Cc: Mathieu Desnoyers <mat...@ef...>
<snip>
> +/**
> + * text_poke_smp_batch - Update instructions on a live kernel on SMP
> + * @params: an array of text_poke parameters
> + * @n: the number of elements in params.
> + *
> + * Modify multi-byte instruction by using stop_machine() on SMP. Since the
> + * stop_machine() is heavy task, it is better to aggregate text_poke requests
> + * and do it once if possible.
> + *
> + * Note: Must be called under get_online_cpus() and text_mutex.
> + */
> +void __kprobes text_poke_smp_batch(struct text_poke_param *params, int n)
> +{
> + struct text_poke_params tpp = {.params = params, .nparams = n};
> +
> + atomic_set(&stop_machine_first, 1);
> + wrote_text = 0;
> + stop_machine(stop_machine_text_poke, (void *)&tpp, NULL);
> +}
Looks good. But wouldn't it be even better to get stop_machine()
be able to support batches itself?
We could have stop_machine_queue() and stop_machine_flush(),
stop_machine() would be a shortcut for both, to execute single jobs,
may be that could simplify some code here and there.
|
|
From: REAL A. E. <ac...@qi...> - 2010-05-22 02:29:48
|
REAL ASSESORIA ESPAÑA Tel: +34 688 236 252 Fax: +34 911 881 377 Email:rea...@ai... 22-05-2010 ******************* Dear email user (dle...@li...), Real Assesoria España, help you to change the way you live. Arranging a loan with us is simple and straight forward, loans from 5,000 to 2,000,000.00 over 5 to 30 years. We make the process convenient and fast. Our interest rate is 3% APR Typical variable and this means that all of our clients pay this rate. All loan applicant must be able to prove trustworthy and demonstraes ability to repay the loan under the calculated period of time. For Loan and mortgage application, you have to send us the below information to this email rea...@ai...: 1. Your Fulnames 2. Telephone and fax Numbers 3. Country 4. Age 5. Amount Needed. You will be contacted by email or Phone as soon as we receive the above details. Best Regards, Dr. Vicente Sancho. REAL ASSESORIA ESPAÑA Tel: +34 688 236 252 Fax: +34 911 881 377 Email:rea...@ai... |
|
From: Masami H. <mhi...@re...> - 2010-05-19 19:52:51
|
Add static and global variables support to perf probe.
This allows user to trace non-local variables (and
structure members) at probe points.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Peter Zijlstra <pe...@in...>
Cc: Mike Galbraith <ef...@gm...>
Cc: Frederic Weisbecker <fwe...@gm...>
---
tools/perf/util/probe-event.c | 15 ++++++-
tools/perf/util/probe-finder.c | 85 +++++++++++++++++++++++++++++-----------
2 files changed, 73 insertions(+), 27 deletions(-)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 351baa9..09cf546 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -926,6 +926,7 @@ out:
static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg,
char *buf, size_t buflen)
{
+ struct kprobe_trace_arg_ref *ref = arg->ref;
int ret, depth = 0;
char *tmp = buf;
@@ -939,16 +940,24 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg,
buf += ret;
buflen -= ret;
+ /* Special case: @XXX */
+ if (arg->value[0] == '@' && arg->ref)
+ ref = ref->next;
+
/* Dereferencing arguments */
- if (arg->ref) {
- depth = __synthesize_kprobe_trace_arg_ref(arg->ref, &buf,
+ if (ref) {
+ depth = __synthesize_kprobe_trace_arg_ref(ref, &buf,
&buflen, 1);
if (depth < 0)
return depth;
}
/* Print argument value */
- ret = e_snprintf(buf, buflen, "%s", arg->value);
+ if (arg->value[0] == '@' && arg->ref)
+ ret = e_snprintf(buf, buflen, "%s%+ld", arg->value,
+ arg->ref->offset);
+ else
+ ret = e_snprintf(buf, buflen, "%s", arg->value);
if (ret < 0)
return ret;
buf += ret;
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 75d6e6c..294a437 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -356,14 +356,50 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
* Probe finder related functions
*/
+static struct kprobe_trace_arg_ref *alloc_trace_arg_ref(long offs)
+{
+ struct kprobe_trace_arg_ref *ref;
+ ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
+ if (ref != NULL)
+ ref->offset = offs;
+ return ref;
+}
+
/* Show a location */
-static int convert_location(Dwarf_Op *op, struct probe_finder *pf)
+static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
{
+ Dwarf_Attribute attr;
+ Dwarf_Op *op;
+ size_t nops;
unsigned int regn;
Dwarf_Word offs = 0;
bool ref = false;
const char *regs;
struct kprobe_trace_arg *tvar = pf->tvar;
+ int ret;
+
+ /* TODO: handle more than 1 exprs */
+ if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
+ dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 ||
+ nops == 0) {
+ /* TODO: Support const_value */
+ pr_err("Failed to find the location of %s at this address.\n"
+ " Perhaps, it has been optimized out.\n", pf->pvar->var);
+ return -ENOENT;
+ }
+
+ if (op->atom == DW_OP_addr) {
+ /* Static variables on memory (not stack), make @varname */
+ ret = strlen(dwarf_diename(vr_die));
+ tvar->value = zalloc(ret + 2);
+ if (tvar->value == NULL)
+ return -ENOMEM;
+ snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die));
+ tvar->ref = alloc_trace_arg_ref((long)offs);
+ if (tvar->ref == NULL)
+ return -ENOMEM;
+ return 0;
+ }
/* If this is based on frame buffer, set the offset */
if (op->atom == DW_OP_fbreg) {
@@ -405,10 +441,9 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf)
return -ENOMEM;
if (ref) {
- tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
+ tvar->ref = alloc_trace_arg_ref((long)offs);
if (tvar->ref == NULL)
return -ENOMEM;
- tvar->ref->offset = (long)offs;
}
return 0;
}
@@ -616,20 +651,13 @@ next:
/* Show a variables in kprobe event format */
static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
{
- Dwarf_Attribute attr;
Dwarf_Die die_mem;
- Dwarf_Op *expr;
- size_t nexpr;
int ret;
- if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL)
- goto error;
- /* TODO: handle more than 1 exprs */
- ret = dwarf_getlocation_addr(&attr, pf->addr, &expr, &nexpr, 1);
- if (ret <= 0 || nexpr == 0)
- goto error;
+ pr_debug("Converting variable %s into trace event.\n",
+ dwarf_diename(vr_die));
- ret = convert_location(expr, pf);
+ ret = convert_variable_location(vr_die, pf);
if (ret == 0 && pf->pvar->field) {
ret = convert_variable_fields(vr_die, pf->pvar->var,
pf->pvar->field, &pf->tvar->ref,
@@ -640,19 +668,14 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type);
/* *expr will be cached in libdw. Don't free it. */
return ret;
-error:
- /* TODO: Support const_value */
- pr_err("Failed to find the location of %s at this address.\n"
- " Perhaps, it has been optimized out.\n", pf->pvar->var);
- return -ENOENT;
}
/* Find a variable in a subprogram die */
static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
{
- Dwarf_Die vr_die;
+ Dwarf_Die vr_die, *scopes;
char buf[32], *ptr;
- int ret;
+ int ret, nscopes;
if (pf->pvar->name)
pf->tvar->name = strdup(pf->pvar->name);
@@ -680,12 +703,26 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
pr_debug("Searching '%s' variable in context.\n",
pf->pvar->var);
/* Search child die for local variables and parameters. */
- if (!die_find_variable(sp_die, pf->pvar->var, &vr_die)) {
+ if (die_find_variable(sp_die, pf->pvar->var, &vr_die))
+ ret = convert_variable(&vr_die, pf);
+ else {
+ /* Search upper class */
+ nscopes = dwarf_getscopes_die(sp_die, &scopes);
+ if (nscopes > 0) {
+ ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var,
+ 0, NULL, 0, 0, &vr_die);
+ if (ret >= 0)
+ ret = convert_variable(&vr_die, pf);
+ else
+ ret = -ENOENT;
+ free(scopes);
+ } else
+ ret = -ENOENT;
+ }
+ if (ret < 0)
pr_warning("Failed to find '%s' in this function.\n",
pf->pvar->var);
- return -ENOENT;
- }
- return convert_variable(&vr_die, pf);
+ return ret;
}
/* Show a probe point to output buffer */
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-05-19 19:52:45
|
Add array-entry tracing support to perf probe. This enables to trace
an entry of array which is indexed by constant value, e.g. array[0].
For example:
$ perf probe -a 'bio_split bi->bi_io_vec[0]'
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Peter Zijlstra <pe...@in...>
Cc: Mike Galbraith <ef...@gm...>
Cc: Frederic Weisbecker <fwe...@gm...>
---
tools/perf/Documentation/perf-probe.txt | 2 +
tools/perf/util/probe-event.c | 56 ++++++++++++++++++++++---------
tools/perf/util/probe-event.h | 1 +
tools/perf/util/probe-finder.c | 48 +++++++++++++++++++++++----
4 files changed, 83 insertions(+), 24 deletions(-)
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index e5cf3ff..ddc3999 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -90,7 +90,7 @@ Each probe argument follows below syntax.
[NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]
-'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
+'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), local array with fixed index (e.g. array[1], var->array[0], var->pointer[2]), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. You can specify 'string' type only for the local variable or structure member which is an array of or a pointer to 'char' or 'unsigned char' type.
LINE SYNTAX
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 914c670..351baa9 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -557,7 +557,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
/* Parse perf-probe event argument */
static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
{
- char *tmp;
+ char *tmp, *goodname;
struct perf_probe_arg_field **fieldp;
pr_debug("parsing arg: %s into ", str);
@@ -580,7 +580,7 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
pr_debug("type:%s ", arg->type);
}
- tmp = strpbrk(str, "-.");
+ tmp = strpbrk(str, "-.[");
if (!is_c_varname(str) || !tmp) {
/* A variable, register, symbol or special value */
arg->var = strdup(str);
@@ -590,10 +590,11 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
return 0;
}
- /* Structure fields */
+ /* Structure fields or array element */
arg->var = strndup(str, tmp - str);
if (arg->var == NULL)
return -ENOMEM;
+ goodname = arg->var;
pr_debug("%s, ", arg->var);
fieldp = &arg->field;
@@ -601,22 +602,38 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
*fieldp = zalloc(sizeof(struct perf_probe_arg_field));
if (*fieldp == NULL)
return -ENOMEM;
- if (*tmp == '.') {
- str = tmp + 1;
- (*fieldp)->ref = false;
- } else if (tmp[1] == '>') {
- str = tmp + 2;
+ if (*tmp == '[') { /* Array */
+ str = tmp;
+ (*fieldp)->index = strtol(str + 1, &tmp, 0);
(*fieldp)->ref = true;
- } else {
- semantic_error("Argument parse error: %s\n", str);
- return -EINVAL;
+ if (*tmp != ']' || tmp == str + 1) {
+ semantic_error("Array index must be a"
+ " number.\n");
+ return -EINVAL;
+ }
+ tmp++;
+ if (*tmp == '\0')
+ tmp = NULL;
+ } else { /* Structure */
+ if (*tmp == '.') {
+ str = tmp + 1;
+ (*fieldp)->ref = false;
+ } else if (tmp[1] == '>') {
+ str = tmp + 2;
+ (*fieldp)->ref = true;
+ } else {
+ semantic_error("Argument parse error: %s\n",
+ str);
+ return -EINVAL;
+ }
+ tmp = strpbrk(str, "-.[");
}
-
- tmp = strpbrk(str, "-.");
if (tmp) {
(*fieldp)->name = strndup(str, tmp - str);
if ((*fieldp)->name == NULL)
return -ENOMEM;
+ if (*str != '[')
+ goodname = (*fieldp)->name;
pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref);
fieldp = &(*fieldp)->next;
}
@@ -624,11 +641,13 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
(*fieldp)->name = strdup(str);
if ((*fieldp)->name == NULL)
return -ENOMEM;
+ if (*str != '[')
+ goodname = (*fieldp)->name;
pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref);
- /* If no name is specified, set the last field name */
+ /* If no name is specified, set the last field name (not array index)*/
if (!arg->name) {
- arg->name = strdup((*fieldp)->name);
+ arg->name = strdup(goodname);
if (arg->name == NULL)
return -ENOMEM;
}
@@ -776,8 +795,11 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
len -= ret;
while (field) {
- ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".",
- field->name);
+ if (field->name[0] == '[')
+ ret = e_snprintf(tmp, len, "%s", field->name);
+ else
+ ret = e_snprintf(tmp, len, "%s%s",
+ field->ref ? "->" : ".", field->name);
if (ret <= 0)
goto error;
tmp += ret;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index e9db1a2..bc06d3e 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -50,6 +50,7 @@ struct perf_probe_point {
struct perf_probe_arg_field {
struct perf_probe_arg_field *next; /* Next field */
char *name; /* Name of the field */
+ long index; /* Array index number */
bool ref; /* Referencing flag */
};
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index af3868b..75d6e6c 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -435,6 +435,9 @@ static int convert_variable_type(Dwarf_Die *vr_die,
return -ENOENT;
}
+ pr_debug("%s type is %s.\n",
+ dwarf_diename(vr_die), dwarf_diename(&type));
+
if (cast && strcmp(cast, "string") == 0) { /* String type */
ret = dwarf_tag(&type);
if (ret != DW_TAG_pointer_type &&
@@ -503,16 +506,44 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
struct kprobe_trace_arg_ref *ref = *ref_ptr;
Dwarf_Die type;
Dwarf_Word offs;
- int ret;
+ int ret, tag;
pr_debug("converting %s in %s\n", field->name, varname);
if (die_get_real_type(vr_die, &type) == NULL) {
pr_warning("Failed to get the type of %s.\n", varname);
return -ENOENT;
}
-
- /* Check the pointer and dereference */
- if (dwarf_tag(&type) == DW_TAG_pointer_type) {
+ pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type));
+ tag = dwarf_tag(&type);
+
+ if (field->name[0] == '[' &&
+ (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) {
+ if (field->next)
+ /* Save original type for next field */
+ memcpy(die_mem, &type, sizeof(*die_mem));
+ /* Get the type of this array */
+ if (die_get_real_type(&type, &type) == NULL) {
+ pr_warning("Failed to get the type of %s.\n", varname);
+ return -ENOENT;
+ }
+ pr_debug2("Array real type: (%x)\n",
+ (unsigned)dwarf_dieoffset(&type));
+ if (tag == DW_TAG_pointer_type) {
+ ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
+ if (ref == NULL)
+ return -ENOMEM;
+ if (*ref_ptr)
+ (*ref_ptr)->next = ref;
+ else
+ *ref_ptr = ref;
+ }
+ ref->offset += die_get_byte_size(&type) * field->index;
+ if (!field->next)
+ /* Save vr_die for converting types */
+ memcpy(die_mem, vr_die, sizeof(*die_mem));
+ goto next;
+ } else if (tag == DW_TAG_pointer_type) {
+ /* Check the pointer and dereference */
if (!field->ref) {
pr_err("Semantic error: %s must be referred by '->'\n",
field->name);
@@ -538,10 +569,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
*ref_ptr = ref;
} else {
/* Verify it is a data structure */
- if (dwarf_tag(&type) != DW_TAG_structure_type) {
+ if (tag != DW_TAG_structure_type) {
pr_warning("%s is not a data structure.\n", varname);
return -EINVAL;
}
+ if (field->name[0] == '[') {
+ pr_err("Semantic error: %s is not a pointor nor array.",
+ varname);
+ return -EINVAL;
+ }
if (field->ref) {
pr_err("Semantic error: %s must be referred by '.'\n",
field->name);
@@ -568,6 +604,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
}
ref->offset += (long)offs;
+next:
/* Converting next field */
if (field->next)
return convert_variable_fields(die_mem, field->name,
@@ -617,7 +654,6 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
char buf[32], *ptr;
int ret;
- /* TODO: Support arrays */
if (pf->pvar->name)
pf->tvar->name = strdup(pf->pvar->name);
else {
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-05-19 19:52:28
|
Support string type casting to event argument. If perf-probe
finds an argument casted as string, it ensures the target
variable is "(unsigned/signed) char *(or []). perf-probe also
adds dereference if the target is a pointer.
So, both of 'char buf[10];' and 'char *buf;' can be accessed
by 'buf:string'
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Peter Zijlstra <pe...@in...>
Cc: Mike Galbraith <ef...@gm...>
Cc: Frederic Weisbecker <fwe...@gm...>
---
tools/perf/Documentation/perf-probe.txt | 2 +
tools/perf/util/probe-finder.c | 59 +++++++++++++++++++++++++------
2 files changed, 49 insertions(+), 12 deletions(-)
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 94a258c..e5cf3ff 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -91,7 +91,7 @@ Each probe argument follows below syntax.
[NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]
'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
-'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo.
+'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. You can specify 'string' type only for the local variable or structure member which is an array of or a pointer to 'char' or 'unsigned char' type.
LINE SYNTAX
-----------
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index d964cb1..af3868b 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -414,18 +414,61 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf)
}
static int convert_variable_type(Dwarf_Die *vr_die,
- struct kprobe_trace_arg *targ)
+ struct kprobe_trace_arg *tvar,
+ const char *cast)
{
+ struct kprobe_trace_arg_ref **ref_ptr = &tvar->ref;
Dwarf_Die type;
char buf[16];
int ret;
+ /* TODO: check all types */
+ if (cast && strcmp(cast, "string") != 0) {
+ /* Non string type is OK */
+ tvar->type = strdup(cast);
+ return (tvar->type == NULL) ? -ENOMEM : 0;
+ }
+
if (die_get_real_type(vr_die, &type) == NULL) {
pr_warning("Failed to get a type information of %s.\n",
dwarf_diename(vr_die));
return -ENOENT;
}
+ if (cast && strcmp(cast, "string") == 0) { /* String type */
+ ret = dwarf_tag(&type);
+ if (ret != DW_TAG_pointer_type &&
+ ret != DW_TAG_array_type) {
+ pr_warning("Failed to cast into string: "
+ "%s(%s) is not a pointer nor array.",
+ dwarf_diename(vr_die), dwarf_diename(&type));
+ return -EINVAL;
+ }
+ if (ret == DW_TAG_pointer_type) {
+ if (die_get_real_type(&type, &type) == NULL) {
+ pr_warning("Failed to get a type information.");
+ return -ENOENT;
+ }
+ while (*ref_ptr)
+ ref_ptr = &(*ref_ptr)->next;
+ /* Add new reference with offset +0 */
+ *ref_ptr = zalloc(sizeof(struct kprobe_trace_arg_ref));
+ if (*ref_ptr == NULL) {
+ pr_warning("Out of memory error\n");
+ return -ENOMEM;
+ }
+ }
+ if (die_compare_name(&type, "char") != 0 &&
+ die_compare_name(&type, "unsigned char") != 0) {
+ pr_warning("Failed to cast into string: "
+ "%s is not (unsigned) char *.",
+ dwarf_diename(vr_die));
+ return -EINVAL;
+ }
+ tvar->type = strdup(cast);
+ return (tvar->type == NULL) ? -ENOMEM : 0;
+ }
+
ret = die_get_byte_size(&type) * 8;
if (ret) {
/* Check the bitwidth */
@@ -445,8 +488,8 @@ static int convert_variable_type(Dwarf_Die *vr_die,
strerror(-ret));
return ret;
}
- targ->type = strdup(buf);
- if (targ->type == NULL)
+ tvar->type = strdup(buf);
+ if (tvar->type == NULL)
return -ENOMEM;
}
return 0;
@@ -556,14 +599,8 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
&die_mem);
vr_die = &die_mem;
}
- if (ret == 0) {
- if (pf->pvar->type) {
- pf->tvar->type = strdup(pf->pvar->type);
- if (pf->tvar->type == NULL)
- ret = -ENOMEM;
- } else
- ret = convert_variable_type(vr_die, pf->tvar);
- }
+ if (ret == 0)
+ ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type);
/* *expr will be cached in libdw. Don't free it. */
return ret;
error:
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-05-19 19:52:24
|
Support string type tracing and printing in kprobe-tracer.
This allows user to trace string data in kernel including
__user data. Note that sometimes __user data may not be
accessed if it is paged-out (sorry, but kprobes operation
should be done in atomic, we can not wait for page-in).
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Peter Zijlstra <pe...@in...>
Cc: Mike Galbraith <ef...@gm...>
Cc: Frederic Weisbecker <fwe...@gm...>
---
Documentation/trace/kprobetrace.txt | 2
kernel/trace/trace_kprobe.c | 370 ++++++++++++++++++++++++++++-------
2 files changed, 293 insertions(+), 79 deletions(-)
diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt
index ec94748..5f77d94 100644
--- a/Documentation/trace/kprobetrace.txt
+++ b/Documentation/trace/kprobetrace.txt
@@ -42,7 +42,7 @@ Synopsis of kprobe_events
+|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**)
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
- (u8/u16/u32/u64/s8/s16/s32/s64) are supported.
+ (u8/u16/u32/u64/s8/s16/s32/s64) and string are supported.
(*) only for return probe.
(**) this is useful for fetching a field of data structures.
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 2d7bf41..10fcc4e 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -30,6 +30,8 @@
#include <linux/ptrace.h>
#include <linux/perf_event.h>
#include <linux/stringify.h>
+#include <linux/limits.h>
+#include <linux/uaccess.h>
#include <asm/bitsperlong.h>
#include "trace.h"
@@ -38,6 +40,7 @@
#define MAX_TRACE_ARGS 128
#define MAX_ARGSTR_LEN 63
#define MAX_EVENT_NAME_LEN 64
+#define MAX_STRING_SIZE PATH_MAX
#define KPROBE_EVENT_SYSTEM "kprobes"
/* Reserved field names */
@@ -58,14 +61,16 @@ const char *reserved_field_names[] = {
};
/* Printing function type */
-typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *);
+typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *,
+ void *);
#define PRINT_TYPE_FUNC_NAME(type) print_type_##type
#define PRINT_TYPE_FMT_NAME(type) print_type_format_##type
/* Printing in basic type function template */
#define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast) \
static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, \
- const char *name, void *data)\
+ const char *name, \
+ void *data, void *ent)\
{ \
return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\
} \
@@ -80,6 +85,49 @@ DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int)
DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long)
DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long)
+/* data_rloc: data relative location, compatible with u32 */
+#define make_data_rloc(len, roffs) \
+ (((u32)(len) << 16) | ((u32)(roffs) & 0xffff))
+#define get_rloc_len(dl) ((u32)(dl) >> 16)
+#define get_rloc_offs(dl) ((u32)(dl) & 0xffff)
+
+static inline void *get_rloc_data(u32 *dl)
+{
+ return (u8 *)dl + get_rloc_offs(*dl);
+}
+
+/* For data_loc conversion */
+static inline void *get_loc_data(u32 *dl, void *ent)
+{
+ return (u8 *)ent + get_rloc_offs(*dl);
+}
+
+/*
+ * Convert data_rloc to data_loc:
+ * data_rloc stores the offset from data_rloc itself, but data_loc
+ * stores the offset from event entry.
+ */
+#define convert_rloc_to_loc(dl, offs) ((u32)(dl) + (offs))
+
+/* For defining macros, define string/string_size types */
+typedef u32 string;
+typedef u32 string_size;
+
+/* Print type function for string type */
+static __kprobes int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s,
+ const char *name,
+ void *data, void *ent)
+{
+ int len = *(u32 *)data >> 16;
+
+ if (!len)
+ return trace_seq_printf(s, " %s=(fault)", name);
+ else
+ return trace_seq_printf(s, " %s=\"%s\"", name,
+ (const char *)get_loc_data(data, ent));
+}
+static const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\"";
+
/* Data fetch function type */
typedef void (*fetch_func_t)(struct pt_regs *, void *, void *);
@@ -94,32 +142,38 @@ static __kprobes void call_fetch(struct fetch_param *fprm,
return fprm->fn(regs, fprm->data, dest);
}
-#define FETCH_FUNC_NAME(kind, type) fetch_##kind##_##type
+#define FETCH_FUNC_NAME(method, type) fetch_##method##_##type
/*
* Define macro for basic types - we don't need to define s* types, because
* we have to care only about bitwidth at recording time.
*/
-#define DEFINE_BASIC_FETCH_FUNCS(kind) \
-DEFINE_FETCH_##kind(u8) \
-DEFINE_FETCH_##kind(u16) \
-DEFINE_FETCH_##kind(u32) \
-DEFINE_FETCH_##kind(u64)
-
-#define CHECK_BASIC_FETCH_FUNCS(kind, fn) \
- ((FETCH_FUNC_NAME(kind, u8) == fn) || \
- (FETCH_FUNC_NAME(kind, u16) == fn) || \
- (FETCH_FUNC_NAME(kind, u32) == fn) || \
- (FETCH_FUNC_NAME(kind, u64) == fn))
+#define DEFINE_BASIC_FETCH_FUNCS(method) \
+DEFINE_FETCH_##method(u8) \
+DEFINE_FETCH_##method(u16) \
+DEFINE_FETCH_##method(u32) \
+DEFINE_FETCH_##method(u64)
+
+#define CHECK_FETCH_FUNCS(method, fn) \
+ (((FETCH_FUNC_NAME(method, u8) == fn) || \
+ (FETCH_FUNC_NAME(method, u16) == fn) || \
+ (FETCH_FUNC_NAME(method, u32) == fn) || \
+ (FETCH_FUNC_NAME(method, u64) == fn) || \
+ (FETCH_FUNC_NAME(method, string) == fn) || \
+ (FETCH_FUNC_NAME(method, string_size) == fn)) \
+ && (fn != NULL))
/* Data fetch function templates */
#define DEFINE_FETCH_reg(type) \
static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, \
- void *offset, void *dest) \
+ void *offset, void *dest) \
{ \
*(type *)dest = (type)regs_get_register(regs, \
(unsigned int)((unsigned long)offset)); \
}
DEFINE_BASIC_FETCH_FUNCS(reg)
+/* No string on the register */
+#define fetch_reg_string NULL
+#define fetch_reg_string_size NULL
#define DEFINE_FETCH_stack(type) \
static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\
@@ -129,6 +183,9 @@ static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\
(unsigned int)((unsigned long)offset)); \
}
DEFINE_BASIC_FETCH_FUNCS(stack)
+/* No string on the stack entry */
+#define fetch_stack_string NULL
+#define fetch_stack_string_size NULL
#define DEFINE_FETCH_retval(type) \
static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\
@@ -137,6 +194,9 @@ static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\
*(type *)dest = (type)regs_return_value(regs); \
}
DEFINE_BASIC_FETCH_FUNCS(retval)
+/* No string on the retval */
+#define fetch_retval_string NULL
+#define fetch_retval_string_size NULL
#define DEFINE_FETCH_memory(type) \
static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\
@@ -149,6 +209,62 @@ static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\
*(type *)dest = retval; \
}
DEFINE_BASIC_FETCH_FUNCS(memory)
+/*
+ * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max
+ * length and relative data location.
+ */
+static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
+ void *addr, void *dest)
+{
+ long ret;
+ int maxlen = get_rloc_len(*(u32 *)dest);
+ u8 *dst = get_rloc_data(dest);
+ u8 *src = addr;
+ mm_segment_t old_fs = get_fs();
+ if (!maxlen)
+ return;
+ /*
+ * Try to get string again, since the string can be changed while
+ * probing.
+ */
+ set_fs(KERNEL_DS);
+ pagefault_disable();
+ do
+ ret = __copy_from_user_inatomic(dst++, src++, 1);
+ while (dst[-1] && ret == 0 && src - (u8 *)addr < maxlen);
+ dst[-1] = '\0';
+ pagefault_enable();
+ set_fs(old_fs);
+
+ if (ret < 0) { /* Failed to fetch string */
+ ((u8 *)get_rloc_data(dest))[0] = '\0';
+ *(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest));
+ } else
+ *(u32 *)dest = make_data_rloc(src - (u8 *)addr,
+ get_rloc_offs(*(u32 *)dest));
+}
+/* Return the length of string -- including null terminal byte */
+static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
+ void *addr, void *dest)
+{
+ int ret, len = 0;
+ u8 c;
+ mm_segment_t old_fs = get_fs();
+
+ set_fs(KERNEL_DS);
+ pagefault_disable();
+ do {
+ ret = __copy_from_user_inatomic(&c, (u8 *)addr + len, 1);
+ len++;
+ } while (c && ret == 0 && len < MAX_STRING_SIZE);
+ pagefault_enable();
+ set_fs(old_fs);
+
+ if (ret < 0) /* Failed to check the length */
+ *(u32 *)dest = 0;
+ else
+ *(u32 *)dest = len;
+}
/* Memory fetching by symbol */
struct symbol_cache {
@@ -203,6 +319,8 @@ static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\
*(type *)dest = 0; \
}
DEFINE_BASIC_FETCH_FUNCS(symbol)
+DEFINE_FETCH_symbol(string)
+DEFINE_FETCH_symbol(string_size)
/* Dereference memory access function */
struct deref_fetch_param {
@@ -224,12 +342,14 @@ static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\
*(type *)dest = 0; \
}
DEFINE_BASIC_FETCH_FUNCS(deref)
+DEFINE_FETCH_deref(string)
+DEFINE_FETCH_deref(string_size)
static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
{
- if (CHECK_BASIC_FETCH_FUNCS(deref, data->orig.fn))
+ if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
free_deref_fetch_param(data->orig.data);
- else if (CHECK_BASIC_FETCH_FUNCS(symbol, data->orig.fn))
+ else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn))
free_symbol_cache(data->orig.data);
kfree(data);
}
@@ -240,23 +360,43 @@ static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG)
#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE)
-#define ASSIGN_FETCH_FUNC(kind, type) \
- .kind = FETCH_FUNC_NAME(kind, type)
-
-#define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \
- {.name = #ptype, \
- .size = sizeof(ftype), \
- .is_signed = sign, \
- .print = PRINT_TYPE_FUNC_NAME(ptype), \
- .fmt = PRINT_TYPE_FMT_NAME(ptype), \
-ASSIGN_FETCH_FUNC(reg, ftype), \
-ASSIGN_FETCH_FUNC(stack, ftype), \
-ASSIGN_FETCH_FUNC(retval, ftype), \
-ASSIGN_FETCH_FUNC(memory, ftype), \
-ASSIGN_FETCH_FUNC(symbol, ftype), \
-ASSIGN_FETCH_FUNC(deref, ftype), \
+/* Fetch types */
+enum {
+ FETCH_MTD_reg = 0,
+ FETCH_MTD_stack,
+ FETCH_MTD_retval,
+ FETCH_MTD_memory,
+ FETCH_MTD_symbol,
+ FETCH_MTD_deref,
+ FETCH_MTD_END,
+};
+
+#define ASSIGN_FETCH_FUNC(method, type) \
+ [FETCH_MTD_##method] = FETCH_FUNC_NAME(method, type)
+
+#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \
+ {.name = _name, \
+ .size = _size, \
+ .is_signed = sign, \
+ .print = PRINT_TYPE_FUNC_NAME(ptype), \
+ .fmt = PRINT_TYPE_FMT_NAME(ptype), \
+ .fmttype = _fmttype, \
+ .fetch = { \
+ASSIGN_FETCH_FUNC(reg, ftype), \
+ASSIGN_FETCH_FUNC(stack, ftype), \
+ASSIGN_FETCH_FUNC(retval, ftype), \
+ASSIGN_FETCH_FUNC(memory, ftype), \
+ASSIGN_FETCH_FUNC(symbol, ftype), \
+ASSIGN_FETCH_FUNC(deref, ftype), \
+ } \
}
+#define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \
+ __ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, #ptype)
+
+#define FETCH_TYPE_STRING 0
+#define FETCH_TYPE_STRSIZE 1
+
/* Fetch type information table */
static const struct fetch_type {
const char *name; /* Name of type */
@@ -264,14 +404,16 @@ static const struct fetch_type {
int is_signed; /* Signed flag */
print_type_func_t print; /* Print functions */
const char *fmt; /* Fromat string */
+ const char *fmttype; /* Name in format file */
/* Fetch functions */
- fetch_func_t reg;
- fetch_func_t stack;
- fetch_func_t retval;
- fetch_func_t memory;
- fetch_func_t symbol;
- fetch_func_t deref;
+ fetch_func_t fetch[FETCH_MTD_END];
} fetch_type_table[] = {
+ /* Special types */
+ [FETCH_TYPE_STRING] = __ASSIGN_FETCH_TYPE("string", string, string,
+ sizeof(u32), 1, "__data_loc char[]"),
+ [FETCH_TYPE_STRSIZE] = __ASSIGN_FETCH_TYPE("string_size", u32,
+ string_size, sizeof(u32), 0, "u32"),
+ /* Basic types */
ASSIGN_FETCH_TYPE(u8, u8, 0),
ASSIGN_FETCH_TYPE(u16, u16, 0),
ASSIGN_FETCH_TYPE(u32, u32, 0),
@@ -302,12 +444,28 @@ static __kprobes void fetch_stack_address(struct pt_regs *regs,
*(unsigned long *)dest = kernel_stack_pointer(regs);
}
+static fetch_func_t get_fetch_size_function(const struct fetch_type *type,
+ fetch_func_t orig_fn)
+{
+ int i;
+
+ if (type != &fetch_type_table[FETCH_TYPE_STRING])
+ return NULL; /* Only string type needs size function */
+ for (i = 0; i < FETCH_MTD_END; i++)
+ if (type->fetch[i] == orig_fn)
+ return fetch_type_table[FETCH_TYPE_STRSIZE].fetch[i];
+
+ WARN_ON(1); /* This should not happen */
+ return NULL;
+}
+
/**
* Kprobe event core functions
*/
struct probe_arg {
struct fetch_param fetch;
+ struct fetch_param fetch_size;
unsigned int offset; /* Offset from argument entry */
const char *name; /* Name of this argument */
const char *comm; /* Command of this argument */
@@ -428,9 +586,9 @@ error:
static void free_probe_arg(struct probe_arg *arg)
{
- if (CHECK_BASIC_FETCH_FUNCS(deref, arg->fetch.fn))
+ if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn))
free_deref_fetch_param(arg->fetch.data);
- else if (CHECK_BASIC_FETCH_FUNCS(symbol, arg->fetch.fn))
+ else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn))
free_symbol_cache(arg->fetch.data);
kfree(arg->name);
kfree(arg->comm);
@@ -547,7 +705,7 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
if (strcmp(arg, "retval") == 0) {
if (is_return)
- f->fn = t->retval;
+ f->fn = t->fetch[FETCH_MTD_retval];
else
ret = -EINVAL;
} else if (strncmp(arg, "stack", 5) == 0) {
@@ -561,7 +719,7 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
if (ret || param > PARAM_MAX_STACK)
ret = -EINVAL;
else {
- f->fn = t->stack;
+ f->fn = t->fetch[FETCH_MTD_stack];
f->data = (void *)param;
}
} else
@@ -587,7 +745,7 @@ static int __parse_probe_arg(char *arg, const struct fetch_type *t,
case '%': /* named register */
ret = regs_query_register_offset(arg + 1);
if (ret >= 0) {
- f->fn = t->reg;
+ f->fn = t->fetch[FETCH_MTD_reg];
f->data = (void *)(unsigned long)ret;
ret = 0;
}
@@ -597,7 +755,7 @@ static int __parse_probe_arg(char *arg, const struct fetch_type *t,
ret = strict_strtoul(arg + 1, 0, ¶m);
if (ret)
break;
- f->fn = t->memory;
+ f->fn = t->fetch[FETCH_MTD_memory];
f->data = (void *)param;
} else {
ret = split_symbol_offset(arg + 1, &offset);
@@ -605,7 +763,7 @@ static int __parse_probe_arg(char *arg, const struct fetch_type *t,
break;
f->data = alloc_symbol_cache(arg + 1, offset);
if (f->data)
- f->fn = t->symbol;
+ f->fn = t->fetch[FETCH_MTD_symbol];
}
break;
case '+': /* deref memory */
@@ -635,14 +793,17 @@ static int __parse_probe_arg(char *arg, const struct fetch_type *t,
if (ret)
kfree(dprm);
else {
- f->fn = t->deref;
+ f->fn = t->fetch[FETCH_MTD_deref];
f->data = (void *)dprm;
}
}
break;
}
- if (!ret && !f->fn)
+ if (!ret && !f->fn) { /* Parsed, but do not find fetch method */
+ pr_info("%s type has no corresponding fetch method.\n",
+ t->name);
ret = -EINVAL;
+ }
return ret;
}
@@ -651,6 +812,7 @@ static int parse_probe_arg(char *arg, struct trace_probe *tp,
struct probe_arg *parg, int is_return)
{
const char *t;
+ int ret;
if (strlen(arg) > MAX_ARGSTR_LEN) {
pr_info("Argument is too long.: %s\n", arg);
@@ -673,7 +835,13 @@ static int parse_probe_arg(char *arg, struct trace_probe *tp,
}
parg->offset = tp->size;
tp->size += parg->type->size;
- return __parse_probe_arg(arg, parg->type, &parg->fetch, is_return);
+ ret = __parse_probe_arg(arg, parg->type, &parg->fetch, is_return);
+ if (ret >= 0) {
+ parg->fetch_size.fn = get_fetch_size_function(parg->type,
+ parg->fetch.fn);
+ parg->fetch_size.data = parg->fetch.data;
+ }
+ return ret;
}
/* Return 1 if name is reserved or already used by another argument */
@@ -1042,6 +1210,54 @@ static const struct file_operations kprobe_profile_ops = {
.release = seq_release,
};
+/* Sum up total data length for dynamic arraies (strings) */
+static __kprobes int __get_data_size(struct trace_probe *tp,
+ struct pt_regs *regs)
+{
+ int i, ret = 0;
+ u32 len;
+
+ for (i = 0; i < tp->nr_args; i++)
+ if (unlikely(tp->args[i].fetch_size.fn)) {
+ call_fetch(&tp->args[i].fetch_size, regs, &len);
+ ret += len;
+ }
+
+ return ret;
+}
+
+/* Store the value of each argument */
+static __kprobes void store_trace_args(int ent_size, struct trace_probe *tp,
+ struct pt_regs *regs,
+ u8 *data, int maxlen)
+{
+ int i;
+ u32 end = tp->size;
+ u32 *dl; /* Data (relative) location */
+
+ for (i = 0; i < tp->nr_args; i++) {
+ if (unlikely(tp->args[i].fetch_size.fn)) {
+ /*
+ * First, we set the relative location and
+ * maximum data length to *dl
+ */
+ dl = (u32 *)(data + tp->args[i].offset);
+ *dl = make_data_rloc(maxlen, end - tp->args[i].offset);
+ /* Then try to fetch string or dynamic array data */
+ call_fetch(&tp->args[i].fetch, regs, dl);
+ /* Reduce maximum length */
+ end += get_rloc_len(*dl);
+ maxlen -= get_rloc_len(*dl);
+ /* Trick here, convert data_rloc to data_loc */
+ *dl = convert_rloc_to_loc(*dl,
+ ent_size + tp->args[i].offset);
+ } else
+ /* Just fetching data normally */
+ call_fetch(&tp->args[i].fetch, regs,
+ data + tp->args[i].offset);
+ }
+}
+
/* Kprobe handler */
static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
{
@@ -1049,8 +1265,7 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
struct kprobe_trace_entry_head *entry;
struct ring_buffer_event *event;
struct ring_buffer *buffer;
- u8 *data;
- int size, i, pc;
+ int size, dsize, pc;
unsigned long irq_flags;
struct ftrace_event_call *call = &tp->call;
@@ -1059,7 +1274,8 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
local_save_flags(irq_flags);
pc = preempt_count();
- size = sizeof(*entry) + tp->size;
+ dsize = __get_data_size(tp, regs);
+ size = sizeof(*entry) + tp->size + dsize;
event = trace_current_buffer_lock_reserve(&buffer, call->id, size,
irq_flags, pc);
@@ -1068,9 +1284,7 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
entry = ring_buffer_event_data(event);
entry->ip = (unsigned long)kp->addr;
- data = (u8 *)&entry[1];
- for (i = 0; i < tp->nr_args; i++)
- call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);
+ store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
if (!filter_current_check_discard(buffer, call, entry, event))
trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc);
@@ -1084,15 +1298,15 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
struct kretprobe_trace_entry_head *entry;
struct ring_buffer_event *event;
struct ring_buffer *buffer;
- u8 *data;
- int size, i, pc;
+ int size, pc, dsize;
unsigned long irq_flags;
struct ftrace_event_call *call = &tp->call;
local_save_flags(irq_flags);
pc = preempt_count();
- size = sizeof(*entry) + tp->size;
+ dsize = __get_data_size(tp, regs);
+ size = sizeof(*entry) + tp->size + dsize;
event = trace_current_buffer_lock_reserve(&buffer, call->id, size,
irq_flags, pc);
@@ -1102,9 +1316,7 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
entry = ring_buffer_event_data(event);
entry->func = (unsigned long)tp->rp.kp.addr;
entry->ret_ip = (unsigned long)ri->ret_addr;
- data = (u8 *)&entry[1];
- for (i = 0; i < tp->nr_args; i++)
- call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);
+ store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
if (!filter_current_check_discard(buffer, call, entry, event))
trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc);
@@ -1137,7 +1349,7 @@ print_kprobe_event(struct trace_iterator *iter, int flags)
data = (u8 *)&field[1];
for (i = 0; i < tp->nr_args; i++)
if (!tp->args[i].type->print(s, tp->args[i].name,
- data + tp->args[i].offset))
+ data + tp->args[i].offset, field))
goto partial;
if (!trace_seq_puts(s, "\n"))
@@ -1180,7 +1392,7 @@ print_kretprobe_event(struct trace_iterator *iter, int flags)
data = (u8 *)&field[1];
for (i = 0; i < tp->nr_args; i++)
if (!tp->args[i].type->print(s, tp->args[i].name,
- data + tp->args[i].offset))
+ data + tp->args[i].offset, field))
goto partial;
if (!trace_seq_puts(s, "\n"))
@@ -1242,7 +1454,7 @@ static int kprobe_event_define_fields(struct ftrace_event_call *event_call)
DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0);
/* Set argument names as fields */
for (i = 0; i < tp->nr_args; i++) {
- ret = trace_define_field(event_call, tp->args[i].type->name,
+ ret = trace_define_field(event_call, tp->args[i].type->fmttype,
tp->args[i].name,
sizeof(field) + tp->args[i].offset,
tp->args[i].type->size,
@@ -1264,7 +1476,7 @@ static int kretprobe_event_define_fields(struct ftrace_event_call *event_call)
DEFINE_FIELD(unsigned long, ret_ip, FIELD_STRING_RETIP, 0);
/* Set argument names as fields */
for (i = 0; i < tp->nr_args; i++) {
- ret = trace_define_field(event_call, tp->args[i].type->name,
+ ret = trace_define_field(event_call, tp->args[i].type->fmttype,
tp->args[i].name,
sizeof(field) + tp->args[i].offset,
tp->args[i].type->size,
@@ -1304,8 +1516,13 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len)
pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg);
for (i = 0; i < tp->nr_args; i++) {
- pos += snprintf(buf + pos, LEN_OR_ZERO, ", REC->%s",
- tp->args[i].name);
+ if (strcmp(tp->args[i].type->name, "string") == 0)
+ pos += snprintf(buf + pos, LEN_OR_ZERO,
+ ", __get_str(%s)",
+ tp->args[i].name);
+ else
+ pos += snprintf(buf + pos, LEN_OR_ZERO, ", REC->%s",
+ tp->args[i].name);
}
#undef LEN_OR_ZERO
@@ -1341,12 +1558,12 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp,
struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp);
struct ftrace_event_call *call = &tp->call;
struct kprobe_trace_entry_head *entry;
- u8 *data;
- int size, __size, i;
+ int size, __size, dsize;
unsigned long irq_flags;
int rctx;
- __size = sizeof(*entry) + tp->size;
+ dsize = __get_data_size(tp, regs);
+ __size = sizeof(*entry) + tp->size + dsize;
size = ALIGN(__size + sizeof(u32), sizeof(u64));
size -= sizeof(u32);
if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
@@ -1358,9 +1575,8 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp,
return;
entry->ip = (unsigned long)kp->addr;
- data = (u8 *)&entry[1];
- for (i = 0; i < tp->nr_args; i++)
- call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);
+ memset(&entry[1], 0, dsize);
+ store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, irq_flags, regs, call->perf_data);
}
@@ -1372,12 +1588,12 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp);
struct ftrace_event_call *call = &tp->call;
struct kretprobe_trace_entry_head *entry;
- u8 *data;
- int size, __size, i;
+ int size, __size, dsize;
unsigned long irq_flags;
int rctx;
- __size = sizeof(*entry) + tp->size;
+ dsize = __get_data_size(tp, regs);
+ __size = sizeof(*entry) + tp->size + dsize;
size = ALIGN(__size + sizeof(u32), sizeof(u64));
size -= sizeof(u32);
if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
@@ -1390,9 +1606,7 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
entry->func = (unsigned long)tp->rp.kp.addr;
entry->ret_ip = (unsigned long)ri->ret_addr;
- data = (u8 *)&entry[1];
- for (i = 0; i < tp->nr_args; i++)
- call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);
+ store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1,
irq_flags, regs, call->perf_data);
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-05-19 19:52:16
|
Hi,
Here is a series of patches which improves perf probe to
add below features. I cleared some coding style issues.
- String support, which enables dynamic events to trace string
variables instead of its address.
- Fixed-index array entry support, which allows user to trace
an array entry with digit index, e.g. array[0].
- Global/static variable support, which allows user to trace
global or static variables, as same as local one.
TODOs (possible features):
- Support dynamic array-indexing (var[var2])
- Support force type-casting ((type)var)
- Show what deta-structure member is assigned to each argument.
- Better support for probes on modules
- More debugger like enhancements(%next, --disasm, etc.)
Thank you,
---
Masami Hiramatsu (4):
perf probe: Support static and global variables
perf probe: Support tracing an entry of array
perf probe: Support "string" type
tracing/kprobes: Support "string" type
Documentation/trace/kprobetrace.txt | 2
kernel/trace/trace_kprobe.c | 370 ++++++++++++++++++++++++-------
tools/perf/Documentation/perf-probe.txt | 4
tools/perf/util/probe-event.c | 71 ++++--
tools/perf/util/probe-event.h | 1
tools/perf/util/probe-finder.c | 192 +++++++++++++---
6 files changed, 498 insertions(+), 142 deletions(-)
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-05-19 16:57:04
|
Ananth N Mavinakayanahalli wrote:
> On Tue, May 18, 2010 at 04:12:51PM -0400, Masami Hiramatsu wrote:
>
>> kernel/test_kprobes.c | 12 +++++++++---
>> 1 files changed, 9 insertions(+), 3 deletions(-)
>>
>> diff --git a/kernel/test_kprobes.c b/kernel/test_kprobes.c
>> index 4f10451..ede20f4 100644
>> --- a/kernel/test_kprobes.c
>> +++ b/kernel/test_kprobes.c
>> @@ -115,7 +115,9 @@ static int test_kprobes(void)
>> int ret;
>> struct kprobe *kps[2] = {&kp, &kp2};
>>
>> - kp.addr = 0; /* addr should be cleard for reusing kprobe. */
>> + /* addr and flags should be cleard for reusing kprobe. */
>> + kp.addr = 0;
>
> kp.addr = NULL;
OK, I'll fix that (and below too).
Thank you!
>
>> + kp.flags = 0;
>> ret = register_kprobes(kps, 2);
>> if (ret < 0) {
>> printk(KERN_ERR "Kprobe smoke test failed: "
>> @@ -210,7 +212,9 @@ static int test_jprobes(void)
>> int ret;
>> struct jprobe *jps[2] = {&jp, &jp2};
>>
>> - jp.kp.addr = 0; /* addr should be cleard for reusing kprobe. */
>> + /* addr and flags should be cleard for reusing kprobe. */
>> + jp.kp.addr = 0;
>
> Same here...
>
>> + jp.kp.flags = 0;
>> ret = register_jprobes(jps, 2);
>> if (ret < 0) {
>> printk(KERN_ERR "Kprobe smoke test failed: "
>> @@ -323,7 +327,9 @@ static int test_kretprobes(void)
>> int ret;
>> struct kretprobe *rps[2] = {&rp, &rp2};
>>
>> - rp.kp.addr = 0; /* addr should be cleard for reusing kprobe. */
>> + /* addr and flags should be cleard for reusing kprobe. */
>> + rp.kp.addr = 0;
>
> Here too.
>
> Ananth
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to maj...@vg...
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-05-19 16:48:42
|
Unoptimization occurs when a probe is unregistered or disabled, and
is heavy because it recovers instructions by using stop_machine().
This patch delays unoptimization operations and unoptimize several
probes at once by using text_poke_smp_batch().
This can avoid unexpected system slowdown coming from stop_machine().
Changes in v2:
- Use dynamic allocated buffers and params.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ananth N Mavinakayanahalli <an...@in...>
Cc: Ingo Molnar <mi...@el...>
Cc: Jim Keniston <jke...@us...>
Cc: Jason Baron <jb...@re...>
Cc: Mathieu Desnoyers <mat...@ef...>
---
arch/x86/include/asm/kprobes.h | 4
arch/x86/kernel/kprobes.c | 45 ++++
include/linux/kprobes.h | 2
kernel/kprobes.c | 434 +++++++++++++++++++++++++++-------------
4 files changed, 344 insertions(+), 141 deletions(-)
diff --git a/arch/x86/include/asm/kprobes.h b/arch/x86/include/asm/kprobes.h
index 5478825..e8a864c 100644
--- a/arch/x86/include/asm/kprobes.h
+++ b/arch/x86/include/asm/kprobes.h
@@ -79,12 +79,12 @@ struct arch_specific_insn {
};
struct arch_optimized_insn {
- /* copy of the original instructions */
- kprobe_opcode_t copied_insn[RELATIVE_ADDR_SIZE];
/* detour code buffer */
kprobe_opcode_t *insn;
/* the size of instructions copied to detour code buffer */
size_t size;
+ /* copy of the original instructions */
+ kprobe_opcode_t copied_insn[RELATIVE_ADDR_SIZE];
};
/* Return true (!0) if optinsn is prepared for optimization. */
diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c
index 50cf5d4..92a7114 100644
--- a/arch/x86/kernel/kprobes.c
+++ b/arch/x86/kernel/kprobes.c
@@ -1167,6 +1167,10 @@ static void __kprobes optimized_callback(struct optimized_kprobe *op,
{
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ /* This is possible if op is under delayed unoptimizing */
+ if (kprobe_disabled(&op->kp))
+ return;
+
preempt_disable();
if (kprobe_running()) {
kprobes_inc_nmissed_count(&op->kp);
@@ -1437,6 +1441,47 @@ void __kprobes arch_optimize_kprobes(struct list_head *oplist)
text_poke_smp_batch(jump_poke_params, c);
}
+static void __kprobes setup_unoptimize_kprobe(struct text_poke_param *tprm,
+ u8 *insn_buf,
+ struct optimized_kprobe *op)
+{
+ /* Set int3 to first byte for kprobes */
+ insn_buf[0] = BREAKPOINT_INSTRUCTION;
+ memcpy(insn_buf + 1, op->optinsn.copied_insn, RELATIVE_ADDR_SIZE);
+
+ tprm->addr = op->kp.addr;
+ tprm->opcode = insn_buf;
+ tprm->len = RELATIVEJUMP_SIZE;
+}
+
+/*
+ * Recover original instructions and breakpoints from relative jumps.
+ * Caller must call with locking kprobe_mutex.
+ */
+extern void arch_unoptimize_kprobes(struct list_head *oplist,
+ struct list_head *done_list)
+{
+ struct optimized_kprobe *op, *tmp;
+ int c = 0;
+
+ list_for_each_entry_safe(op, tmp, oplist, list) {
+ /* Setup param */
+ setup_unoptimize_kprobe(&jump_poke_params[c],
+ jump_poke_bufs[c].buf, op);
+ list_del_init(&op->list);
+ list_add(&op->list, done_list);
+ if (++c >= MAX_OPTIMIZE_PROBES)
+ break;
+ }
+
+ /*
+ * text_poke_smp doesn't support NMI/MCE code modifying.
+ * However, since kprobes itself also doesn't support NMI/MCE
+ * code probing, it's not a problem.
+ */
+ text_poke_smp_batch(jump_poke_params, c);
+}
+
/* Replace a relative jump with a breakpoint (int3). */
void __kprobes arch_unoptimize_kprobe(struct optimized_kprobe *op)
{
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index fe157ba..b78edb5 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -276,6 +276,8 @@ 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 void arch_optimize_kprobes(struct list_head *oplist);
+extern void arch_unoptimize_kprobes(struct list_head *oplist,
+ struct list_head *done_list);
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);
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 6bd2e53..ec57d08 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -353,6 +353,13 @@ static inline int kprobe_aggrprobe(struct kprobe *p)
return p->pre_handler == aggr_pre_handler;
}
+/* Return true(!0) if the kprobe is unused */
+static inline int kprobe_unused(struct kprobe *p)
+{
+ return kprobe_aggrprobe(p) && kprobe_disabled(p) &&
+ list_empty(&p->list);
+}
+
/*
* Keep all fields in the kprobe consistent
*/
@@ -383,6 +390,17 @@ void __kprobes opt_pre_handler(struct kprobe *p, struct pt_regs *regs)
}
}
+/* 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);
+ arch_remove_kprobe(p);
+ kfree(op);
+}
+
/* Return true(!0) if the kprobe is ready for optimization. */
static inline int kprobe_optready(struct kprobe *p)
{
@@ -396,6 +414,20 @@ static inline int kprobe_optready(struct kprobe *p)
return 0;
}
+/* Return true(!0) if the kprobe is disarmed. Note: p must be on hash list */
+static inline int kprobe_disarmed(struct kprobe *p)
+{
+ struct optimized_kprobe *op;
+
+ /* If kprobe is not aggr/opt probe, just return kprobe is disabled */
+ if (!kprobe_aggrprobe(p))
+ return kprobe_disabled(p);
+
+ op = container_of(p, struct optimized_kprobe, kp);
+
+ return kprobe_disabled(p) && list_empty(&op->list);
+}
+
/*
* Return an optimized kprobe whose optimizing code replaces
* instructions including addr (exclude breakpoint).
@@ -421,29 +453,19 @@ struct kprobe *__kprobes get_optimized_kprobe(unsigned long addr)
/* Optimization staging list, protected by kprobe_mutex */
static LIST_HEAD(optimizing_list);
+static LIST_HEAD(unoptimizing_list);
static void kprobe_optimizer(struct work_struct *work);
static DECLARE_DELAYED_WORK(optimizing_work, kprobe_optimizer);
#define OPTIMIZE_DELAY 5
+static DECLARE_COMPLETION(optimizer_comp);
-/* Kprobe jump optimizer */
-static __kprobes void kprobe_optimizer(struct work_struct *work)
+static __kprobes void do_unoptimize_kprobes(struct list_head *free_list)
{
- /* Lock modules while optimizing kprobes */
- mutex_lock(&module_mutex);
- mutex_lock(&kprobe_mutex);
- if (kprobes_all_disarmed || !kprobes_allow_optimization)
- 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();
+ struct optimized_kprobe *op, *tmp;
+ if (list_empty(&unoptimizing_list))
+ return;
/*
* The optimization/unoptimization refers online_cpus via
* stop_machine() and cpu-hotplug modifies online_cpus.
@@ -456,14 +478,93 @@ static __kprobes void kprobe_optimizer(struct work_struct *work)
*/
get_online_cpus();
mutex_lock(&text_mutex);
+ /* Do batch unoptimization */
+ arch_unoptimize_kprobes(&unoptimizing_list, free_list);
+ mutex_unlock(&text_mutex);
+ put_online_cpus();
+
+ list_for_each_entry_safe(op, tmp, free_list, list) {
+ /* Disarm probes if disabled */
+ if (kprobe_disabled(&op->kp))
+ arch_disarm_kprobe(&op->kp);
+ /* leave only unused probes on free_list */
+ if (!kprobe_unused(&op->kp))
+ list_del_init(&op->list);
+ else
+ /*
+ * Remove unused probe from hash list. After waiting
+ * for synchronization, this probe can be freed safely
+ * (which will be freed by do_free_unused_kprobes.)
+ */
+ hlist_del_rcu(&op->kp.hlist);
+ }
+}
+
+static __kprobes void do_free_unused_kprobes(struct list_head *free_list)
+{
+ struct optimized_kprobe *op, *tmp;
+
+ list_for_each_entry_safe(op, tmp, free_list, list) {
+ list_del_init(&op->list);
+ free_aggr_kprobe(&op->kp);
+ }
+}
+
+static __kprobes void do_optimize_kprobes(void)
+{
+ /* Optimization never allowed when disarmed */
+ if (kprobes_all_disarmed || !kprobes_allow_optimization ||
+ list_empty(&optimizing_list))
+ return;
+
+ get_online_cpus();
+ mutex_lock(&text_mutex);
arch_optimize_kprobes(&optimizing_list);
mutex_unlock(&text_mutex);
put_online_cpus();
- if (!list_empty(&optimizing_list))
- schedule_delayed_work(&optimizing_work, OPTIMIZE_DELAY);
-end:
+}
+
+/* Kprobe jump optimize/unoptimize and collect unused aggrprobes */
+static __kprobes void kprobe_optimizer(struct work_struct *work)
+{
+ LIST_HEAD(free_list);
+
+ /* Lock modules while optimizing kprobes */
+ mutex_lock(&module_mutex);
+ mutex_lock(&kprobe_mutex);
+
+ do_unoptimize_kprobes(&free_list);
+
+ /*
+ * 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();
+
+ do_optimize_kprobes();
+
+ /* Release all releasable probes */
+ do_free_unused_kprobes(&free_list);
+
mutex_unlock(&kprobe_mutex);
mutex_unlock(&module_mutex);
+
+ if (!list_empty(&optimizing_list) || !list_empty(&unoptimizing_list)) {
+ if (!delayed_work_pending(&optimizing_work))
+ schedule_delayed_work(&optimizing_work, OPTIMIZE_DELAY);
+ } else
+ /* Wake up all waiters */
+ complete_all(&optimizer_comp);
+}
+
+/* Wait for completion */
+static __kprobes void wait_for_optimizer(void)
+{
+ if (delayed_work_pending(&optimizing_work))
+ wait_for_completion(&optimizer_comp);
}
/* Optimize kprobe if p is ready to be optimized */
@@ -476,6 +577,10 @@ static __kprobes void optimize_kprobe(struct kprobe *p)
(kprobe_disabled(p) || kprobes_all_disarmed))
return;
+ /* Check if it is already optimized. */
+ if (kprobe_optimized(p))
+ return;
+
/* Both of break_handler and post_handler are not supported. */
if (p->break_handler || p->post_handler)
return;
@@ -486,45 +591,96 @@ static __kprobes void optimize_kprobe(struct kprobe *p)
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);
+ if (!list_empty(&op->list))
+ /* Unoptimizing probe: dequeue probe */
+ list_del_init(&op->list);
+ else {
+ 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)
+static __kprobes void force_unoptimize_kprobe(struct optimized_kprobe *op)
+{
+ get_online_cpus();
+ arch_unoptimize_kprobe(op);
+ put_online_cpus();
+ if (kprobe_disabled(&op->kp))
+ arch_disarm_kprobe(&op->kp);
+}
+
+/* Unoptimize a kprobe if p is optimized. */
+static __kprobes void unoptimize_kprobe(struct kprobe *p, int force)
{
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 */
+ if (!kprobe_aggrprobe(p) || kprobe_disarmed(p))
+ return; /* This is not an optprobe nor optimized */
+
+ op = container_of(p, struct optimized_kprobe, kp);
+ if (!kprobe_optimized(p)) {
+ /* Unoptimized or unoptimizing */
+ if (force && !list_empty(&op->list)) {
+ /*
+ * If unoptimizing probe and forced option, forcibly
+ * unoptimize it.
+ */
list_del_init(&op->list);
- else
- /* Replace jump with break */
- arch_unoptimize_kprobe(op);
- op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
+ force_unoptimize_kprobe(op);
+ }
+ return;
+ }
+
+ op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
+ if (!list_empty(&op->list)) {
+ /* Dequeue from the optimization queue */
+ list_del_init(&op->list);
+ return;
+ }
+
+ if (force) /* Forcibly update the code: this is a special case */
+ force_unoptimize_kprobe(op);
+ else {
+ list_add(&op->list, &unoptimizing_list);
+ if (!delayed_work_pending(&optimizing_work))
+ schedule_delayed_work(&optimizing_work, OPTIMIZE_DELAY);
}
}
+/* Cancel unoptimizing for reusing */
+static void reuse_unused_kprobe(struct kprobe *ap)
+{
+ struct optimized_kprobe *op;
+
+ BUG_ON(!kprobe_unused(ap));
+ /*
+ * Unused kprobe MUST be on the way of delayed unoptimizing (means
+ * there is still a relative jump) and disabled.
+ */
+ op = container_of(ap, struct optimized_kprobe, kp);
+ if (unlikely(list_empty(&op->list)))
+ printk(KERN_WARNING "Warning: found a stray unused "
+ "aggrprobe@%p\n", ap->addr);
+ /* Enable the probe again */
+ ap->flags &= ~KPROBE_FLAG_DISABLED;
+ /* Optimize it again (remove from op->list) */
+ BUG_ON(!kprobe_optready(ap));
+ optimize_kprobe(ap);
+}
+
/* 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 */
+ if (!list_empty(&op->list))
+ /* Dequeue from the (un)optimization queue */
list_del_init(&op->list);
- op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
- }
- /* Don't unoptimize, because the target code will be freed. */
+ op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
+ /* Don't unoptimize, because the target code is already freed. */
arch_remove_optimized_kprobe(op);
}
@@ -537,16 +693,6 @@ static __kprobes void prepare_optimized_kprobe(struct kprobe *p)
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)
{
@@ -626,20 +772,15 @@ static void __kprobes unoptimize_all_kprobes(void)
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);
+ unoptimize_kprobe(p, 0);
}
}
-
- mutex_unlock(&text_mutex);
- put_online_cpus();
- /* Allow all currently running kprobes to complete */
- synchronize_sched();
+ /* Wait for unoptimizing completion */
+ wait_for_optimizer();
}
int sysctl_kprobes_optimization;
@@ -670,37 +811,64 @@ static void __kprobes __arm_kprobe(struct kprobe *p)
/* 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 */
+ unoptimize_kprobe(old_p, 1);/* Fallback to unoptimized kprobe */
arch_arm_kprobe(p);
optimize_kprobe(p); /* Try to optimize (add kprobe to a list) */
}
+/* Return true(!0) if the probe is queued on (un)optimizing lists */
+static int __kprobes kprobe_queued(struct kprobe *p)
+{
+ struct optimized_kprobe *op;
+
+ if (kprobe_aggrprobe(p)) {
+ op = container_of(p, struct optimized_kprobe, kp);
+ if (!list_empty(&op->list))
+ return 1;
+ }
+ return 0;
+}
+
static void __kprobes __disarm_kprobe(struct kprobe *p)
{
struct kprobe *old_p;
- unoptimize_kprobe(p); /* Try to unoptimize */
- arch_disarm_kprobe(p);
+ /* Delayed unoptimizing if needed (add kprobe to a list) */
+ if (kprobe_optimized(p))
+ unoptimize_kprobe(p, 0);
- /* If another kprobe was blocked, optimize it. */
- old_p = get_optimized_kprobe((unsigned long)p->addr);
- if (unlikely(old_p))
- optimize_kprobe(old_p);
+ if (!kprobe_queued(p)) {
+ arch_disarm_kprobe(p);
+ /* 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 /* !CONFIG_OPTPROBES */
#define optimize_kprobe(p) do {} while (0)
-#define unoptimize_kprobe(p) do {} while (0)
+#define unoptimize_kprobe(p, f) 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)
#define __arm_kprobe(p) arch_arm_kprobe(p)
#define __disarm_kprobe(p) arch_disarm_kprobe(p)
+#define kprobe_disarmed(p) kprobe_disabled(p)
+#define wait_for_optimizer() do {} while (0)
+
+/* There should be no unused kprobes can be reused without optimization */
+static void reuse_unused_kprobe(struct kprobe *ap)
+{
+ printk(KERN_ERR "Error: There should be no unused kprobe here.\n");
+ BUG_ON(kprobe_unused(ap));
+}
static __kprobes void free_aggr_kprobe(struct kprobe *p)
{
+ arch_remove_kprobe(ap);
kfree(p);
}
@@ -723,16 +891,6 @@ static void __kprobes arm_kprobe(struct 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);
- __disarm_kprobe(kp);
- mutex_unlock(&text_mutex);
- put_online_cpus();
-}
-
/*
* Aggregate handlers for multiple kprobes support - these handlers
* take care of invoking the individual kprobe handlers on p->list
@@ -931,7 +1089,7 @@ 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 */
+ unoptimize_kprobe(ap, 1);/* Fall back to normal kprobe */
if (p->break_handler) {
if (ap->break_handler)
@@ -994,7 +1152,9 @@ static int __kprobes register_aggr_kprobe(struct kprobe *old_p,
if (!ap)
return -ENOMEM;
init_aggr_kprobe(ap, old_p);
- }
+ } else if (kprobe_unused(ap))
+ /* This probe is going to die. Rescue it */
+ reuse_unused_kprobe(ap);
if (kprobe_gone(ap)) {
/*
@@ -1028,23 +1188,6 @@ static int __kprobes register_aggr_kprobe(struct kprobe *old_p,
return add_new_kprobe(ap, p);
}
-/* Try to disable aggr_kprobe, and return 1 if succeeded.*/
-static int __kprobes try_to_disable_aggr_kprobe(struct kprobe *p)
-{
- struct kprobe *kp;
-
- list_for_each_entry_rcu(kp, &p->list, list) {
- if (!kprobe_disabled(kp))
- /*
- * There is an active probe on the list.
- * We can't disable aggr_kprobe.
- */
- return 0;
- }
- p->flags |= KPROBE_FLAG_DISABLED;
- return 1;
-}
-
static int __kprobes in_kprobes_functions(unsigned long addr)
{
struct kprobe_blackpoint *kb;
@@ -1211,6 +1354,45 @@ out:
}
EXPORT_SYMBOL_GPL(register_kprobe);
+/* Check if all probes on the aggrprobe are disabled */
+static int __kprobes aggr_kprobe_disabled(struct kprobe *ap)
+{
+ struct kprobe *kp;
+
+ list_for_each_entry_rcu(kp, &ap->list, list) {
+ if (!kprobe_disabled(kp))
+ /*
+ * There is an active probe on the list.
+ * We can't disable aggr_kprobe.
+ */
+ return 0;
+ }
+ return 1;
+}
+
+/* Disable one kprobe: Make sure called under kprobe_mutex is locked */
+static struct kprobe *__kprobes __disable_kprobe(struct kprobe *p)
+{
+ struct kprobe *orig_p;
+
+ /* Check whether specified probe is valid, and get real probe */
+ orig_p = __get_valid_kprobe(p);
+ if (unlikely(orig_p == NULL))
+ return NULL;
+
+ if (!kprobe_disabled(p)) {
+ if (p != orig_p)
+ p->flags |= KPROBE_FLAG_DISABLED;
+
+ if (p == orig_p || aggr_kprobe_disabled(orig_p)) {
+ __disarm_kprobe(orig_p);
+ orig_p->flags |= KPROBE_FLAG_DISABLED;
+ }
+ }
+
+ return orig_p;
+}
+
/*
* Unregister a kprobe without a scheduler synchronization.
*/
@@ -1218,22 +1400,18 @@ static int __kprobes __unregister_kprobe_top(struct kprobe *p)
{
struct kprobe *old_p, *list_p;
- old_p = __get_valid_kprobe(p);
+ /* Disable kprobe. This will disarm it if needed. */
+ old_p = __disable_kprobe(p);
if (old_p == NULL)
return -EINVAL;
if (old_p == p ||
(kprobe_aggrprobe(old_p) &&
- list_is_singular(&old_p->list))) {
- /*
- * Only probe on the hash list. Disarm only if kprobes are
- * enabled and not gone - otherwise, the breakpoint would
- * already have been removed. We save on flushing icache.
- */
- if (!kprobes_all_disarmed && !kprobe_disabled(old_p))
- disarm_kprobe(old_p);
+ list_is_singular(&old_p->list) && kprobe_disarmed(old_p)))
+ /* Only disarmed probe on the hash list or on aggr_probe */
hlist_del_rcu(&old_p->hlist);
- } else {
+ else {
+ /* This probe is on an aggr_probe */
if (p->break_handler && !kprobe_gone(p))
old_p->break_handler = NULL;
if (p->post_handler && !kprobe_gone(p)) {
@@ -1245,16 +1423,12 @@ static int __kprobes __unregister_kprobe_top(struct kprobe *p)
}
noclean:
list_del_rcu(&p->list);
- if (!kprobe_disabled(old_p)) {
- try_to_disable_aggr_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);
- }
- }
+ if (!kprobe_disabled(old_p) && !kprobes_all_disarmed)
+ /*
+ * Try to optimize this probe again,
+ * because post hander may be changed.
+ */
+ optimize_kprobe(old_p);
}
return 0;
}
@@ -1263,13 +1437,13 @@ static void __kprobes __unregister_kprobe_bottom(struct kprobe *p)
{
struct kprobe *old_p;
+ /* If this probe was removed from hlist, remove kprobes insn buffer */
if (list_empty(&p->list))
arch_remove_kprobe(p);
else if (list_is_singular(&p->list)) {
/* "p" is the last child of an aggr_kprobe */
old_p = list_entry(p->list.next, struct kprobe, list);
list_del(&p->list);
- arch_remove_kprobe(old_p);
free_aggr_kprobe(old_p);
}
}
@@ -1311,6 +1485,7 @@ void __kprobes unregister_kprobes(struct kprobe **kps, int num)
mutex_unlock(&kprobe_mutex);
synchronize_sched();
+
for (i = 0; i < num; i++)
if (kps[i]->addr)
__unregister_kprobe_bottom(kps[i]);
@@ -1588,29 +1763,10 @@ static void __kprobes kill_kprobe(struct kprobe *p)
int __kprobes disable_kprobe(struct kprobe *kp)
{
int ret = 0;
- struct kprobe *p;
mutex_lock(&kprobe_mutex);
-
- /* Check whether specified probe is valid. */
- p = __get_valid_kprobe(kp);
- if (unlikely(p == NULL)) {
+ if (__disable_kprobe(kp) == NULL)
ret = -EINVAL;
- goto out;
- }
-
- /* If the probe is already disabled (or gone), just return */
- if (kprobe_disabled(kp))
- goto out;
-
- kp->flags |= KPROBE_FLAG_DISABLED;
- if (p != kp)
- /* When kp != p, p is always enabled. */
- try_to_disable_aggr_kprobe(p);
-
- if (!kprobes_all_disarmed && kprobe_disabled(p))
- disarm_kprobe(p);
-out:
mutex_unlock(&kprobe_mutex);
return ret;
}
@@ -1931,8 +2087,8 @@ static void __kprobes disarm_all_kprobes(void)
mutex_unlock(&text_mutex);
put_online_cpus();
mutex_unlock(&kprobe_mutex);
- /* Allow all currently running kprobes to complete */
- synchronize_sched();
+
+ wait_for_optimizer();
return;
already_disabled:
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-05-19 16:48:40
|
Introduce text_poke_smp_batch(). This function modifies several
text areas with one stop_machine() on SMPr. Because calling
stop_machine() is heavy task, it is better to aggregate text_poke
requests.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ananth N Mavinakayanahalli <an...@in...>
Cc: Ingo Molnar <mi...@el...>
Cc: Jim Keniston <jke...@us...>
Cc: Jason Baron <jb...@re...>
Cc: Mathieu Desnoyers <mat...@ef...>
---
arch/x86/include/asm/alternative.h | 7 +++++
arch/x86/kernel/alternative.c | 49 +++++++++++++++++++++++++++++-------
2 files changed, 47 insertions(+), 9 deletions(-)
diff --git a/arch/x86/include/asm/alternative.h b/arch/x86/include/asm/alternative.h
index 03b6bb5..9a08773 100644
--- a/arch/x86/include/asm/alternative.h
+++ b/arch/x86/include/asm/alternative.h
@@ -176,7 +176,14 @@ static inline void apply_paravirt(struct paravirt_patch_site *start,
* On the local CPU you need to be protected again NMI or MCE handlers seeing an
* inconsistent instruction while you patch.
*/
+struct text_poke_param {
+ void *addr;
+ const void *opcode;
+ size_t len;
+};
+
extern void *text_poke(void *addr, const void *opcode, size_t len);
extern void *text_poke_smp(void *addr, const void *opcode, size_t len);
+extern void text_poke_smp_batch(struct text_poke_param *params, int n);
#endif /* _ASM_X86_ALTERNATIVE_H */
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 7023773..7256800 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -590,17 +590,21 @@ static atomic_t stop_machine_first;
static int wrote_text;
struct text_poke_params {
- void *addr;
- const void *opcode;
- size_t len;
+ struct text_poke_param *params;
+ int nparams;
};
static int __kprobes stop_machine_text_poke(void *data)
{
struct text_poke_params *tpp = data;
+ struct text_poke_param *p;
+ int i;
if (atomic_dec_and_test(&stop_machine_first)) {
- text_poke(tpp->addr, tpp->opcode, tpp->len);
+ for (i = 0; i < tpp->nparams; i++) {
+ p = &tpp->params[i];
+ text_poke(p->addr, p->opcode, p->len);
+ }
smp_wmb(); /* Make sure other cpus see that this has run */
wrote_text = 1;
} else {
@@ -609,8 +613,12 @@ static int __kprobes stop_machine_text_poke(void *data)
smp_mb(); /* Load wrote_text before following execution */
}
- flush_icache_range((unsigned long)tpp->addr,
- (unsigned long)tpp->addr + tpp->len);
+ for (i = 0; i < tpp->nparams; i++) {
+ p = &tpp->params[i];
+ flush_icache_range((unsigned long)p->addr,
+ (unsigned long)p->addr + p->len);
+ }
+
return 0;
}
@@ -630,13 +638,36 @@ static int __kprobes stop_machine_text_poke(void *data)
void *__kprobes text_poke_smp(void *addr, const void *opcode, size_t len)
{
struct text_poke_params tpp;
+ struct text_poke_param p;
- tpp.addr = addr;
- tpp.opcode = opcode;
- tpp.len = len;
+ p.addr = addr;
+ p.opcode = opcode;
+ p.len = len;
+ tpp.params = &p;
+ tpp.nparams = 1;
atomic_set(&stop_machine_first, 1);
wrote_text = 0;
stop_machine(stop_machine_text_poke, (void *)&tpp, NULL);
return addr;
}
+/**
+ * text_poke_smp_batch - Update instructions on a live kernel on SMP
+ * @params: an array of text_poke parameters
+ * @n: the number of elements in params.
+ *
+ * Modify multi-byte instruction by using stop_machine() on SMP. Since the
+ * stop_machine() is heavy task, it is better to aggregate text_poke requests
+ * and do it once if possible.
+ *
+ * Note: Must be called under get_online_cpus() and text_mutex.
+ */
+void __kprobes text_poke_smp_batch(struct text_poke_param *params, int n)
+{
+ struct text_poke_params tpp = {.params = params, .nparams = n};
+
+ atomic_set(&stop_machine_first, 1);
+ wrote_text = 0;
+ stop_machine(stop_machine_text_poke, (void *)&tpp, NULL);
+}
+
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-05-19 16:48:28
|
Use text_poke_smp_batch() in optimization path for reducing
the number of stop_machine() issues. If the number of optimizing
probes is more than MAX_OPTIMIZE_PROBES(=256), kprobes optimizes
first MAX_OPTIMIZE_PROBES probes and kicks optimizer for remaining
probes.
Changes in v2:
- Allocate code buffer and parameters in arch_init_kprobes()
instead of using static arraies.
- Merge previous max optimization limit patch into this patch.
So, this patch introduces upper limit of optimization at
once.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ananth N Mavinakayanahalli <an...@in...>
Cc: Ingo Molnar <mi...@el...>
Cc: Jim Keniston <jke...@us...>
Cc: Jason Baron <jb...@re...>
Cc: Mathieu Desnoyers <mat...@ef...>
---
arch/x86/kernel/kprobes.c | 69 ++++++++++++++++++++++++++++++++++++++++-----
include/linux/kprobes.h | 2 +
kernel/kprobes.c | 11 ++-----
3 files changed, 65 insertions(+), 17 deletions(-)
diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c
index 345a4b1..50cf5d4 100644
--- a/arch/x86/kernel/kprobes.c
+++ b/arch/x86/kernel/kprobes.c
@@ -1385,10 +1385,16 @@ int __kprobes arch_prepare_optimized_kprobe(struct optimized_kprobe *op)
return 0;
}
-/* Replace a breakpoint (int3) with a relative jump. */
-int __kprobes arch_optimize_kprobe(struct optimized_kprobe *op)
+#define MAX_OPTIMIZE_PROBES 256
+static struct text_poke_param *jump_poke_params;
+static struct jump_poke_buffer {
+ u8 buf[RELATIVEJUMP_SIZE];
+} *jump_poke_bufs;
+
+static void __kprobes setup_optimize_kprobe(struct text_poke_param *tprm,
+ u8 *insn_buf,
+ struct optimized_kprobe *op)
{
- unsigned char jmp_code[RELATIVEJUMP_SIZE];
s32 rel = (s32)((long)op->optinsn.insn -
((long)op->kp.addr + RELATIVEJUMP_SIZE));
@@ -1396,16 +1402,39 @@ int __kprobes arch_optimize_kprobe(struct optimized_kprobe *op)
memcpy(op->optinsn.copied_insn, op->kp.addr + INT3_SIZE,
RELATIVE_ADDR_SIZE);
- jmp_code[0] = RELATIVEJUMP_OPCODE;
- *(s32 *)(&jmp_code[1]) = rel;
+ insn_buf[0] = RELATIVEJUMP_OPCODE;
+ *(s32 *)(&insn_buf[1]) = rel;
+
+ tprm->addr = op->kp.addr;
+ tprm->opcode = insn_buf;
+ tprm->len = RELATIVEJUMP_SIZE;
+}
+
+/*
+ * Replace breakpoints (int3) with relative jumps.
+ * Caller must call with locking kprobe_mutex.
+ */
+void __kprobes arch_optimize_kprobes(struct list_head *oplist)
+{
+ struct optimized_kprobe *op, *tmp;
+ int c = 0;
+
+ list_for_each_entry_safe(op, tmp, oplist, list) {
+ WARN_ON(kprobe_disabled(&op->kp));
+ /* Setup param */
+ setup_optimize_kprobe(&jump_poke_params[c],
+ jump_poke_bufs[c].buf, op);
+ list_del_init(&op->list);
+ if (++c >= MAX_OPTIMIZE_PROBES)
+ break;
+ }
/*
* text_poke_smp doesn't support NMI/MCE code modifying.
* However, since kprobes itself also doesn't support NMI/MCE
* code probing, it's not a problem.
*/
- text_poke_smp(op->kp.addr, jmp_code, RELATIVEJUMP_SIZE);
- return 0;
+ text_poke_smp_batch(jump_poke_params, c);
}
/* Replace a relative jump with a breakpoint (int3). */
@@ -1437,11 +1466,35 @@ static int __kprobes setup_detour_execution(struct kprobe *p,
}
return 0;
}
+
+static int __kprobes init_poke_params(void)
+{
+ /* Allocate code buffer and parameter array */
+ jump_poke_bufs = kmalloc(sizeof(struct jump_poke_buffer) *
+ MAX_OPTIMIZE_PROBES, GFP_KERNEL);
+ if (!jump_poke_bufs)
+ return -ENOMEM;
+
+ jump_poke_params = kmalloc(sizeof(struct text_poke_param) *
+ MAX_OPTIMIZE_PROBES, GFP_KERNEL);
+ if (!jump_poke_params) {
+ kfree(jump_poke_bufs);
+ jump_poke_bufs = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+#else /* !CONFIG_OPTPROBES */
+static int __kprobes init_poke_params(void)
+{
+ return 0;
+}
#endif
int __init arch_init_kprobes(void)
{
- return 0;
+ return init_poke_params();
}
int __kprobes arch_trampoline_kprobe(struct kprobe *p)
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index e7d1b2e..fe157ba 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -275,7 +275,7 @@ 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_optimize_kprobes(struct list_head *oplist);
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);
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index f1351f2..6bd2e53 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -429,8 +429,6 @@ static DECLARE_DELAYED_WORK(optimizing_work, kprobe_optimizer);
/* 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);
@@ -458,14 +456,11 @@ static __kprobes void kprobe_optimizer(struct work_struct *work)
*/
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);
- }
+ arch_optimize_kprobes(&optimizing_list);
mutex_unlock(&text_mutex);
put_online_cpus();
+ if (!list_empty(&optimizing_list))
+ schedule_delayed_work(&optimizing_work, OPTIMIZE_DELAY);
end:
mutex_unlock(&kprobe_mutex);
mutex_unlock(&module_mutex);
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-05-19 16:48:13
|
Fix selftest to clear flags field for reusing probes
because the flags field can be modified by Kprobes.
Changes in v3:
- Set kp.addr = NULL according to Ananth's comment.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ananth N Mavinakayanahalli <an...@in...>
Cc: Ingo Molnar <mi...@el...>
Cc: Jim Keniston <jke...@us...>
Cc: Jason Baron <jb...@re...>
Cc: Mathieu Desnoyers <mat...@ef...>
---
kernel/test_kprobes.c | 12 +++++++++---
1 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/kernel/test_kprobes.c b/kernel/test_kprobes.c
index 4f10451..f8b11a2 100644
--- a/kernel/test_kprobes.c
+++ b/kernel/test_kprobes.c
@@ -115,7 +115,9 @@ static int test_kprobes(void)
int ret;
struct kprobe *kps[2] = {&kp, &kp2};
- kp.addr = 0; /* addr should be cleard for reusing kprobe. */
+ /* addr and flags should be cleard for reusing kprobe. */
+ kp.addr = NULL;
+ kp.flags = 0;
ret = register_kprobes(kps, 2);
if (ret < 0) {
printk(KERN_ERR "Kprobe smoke test failed: "
@@ -210,7 +212,9 @@ static int test_jprobes(void)
int ret;
struct jprobe *jps[2] = {&jp, &jp2};
- jp.kp.addr = 0; /* addr should be cleard for reusing kprobe. */
+ /* addr and flags should be cleard for reusing kprobe. */
+ jp.kp.addr = NULL;
+ jp.kp.flags = 0;
ret = register_jprobes(jps, 2);
if (ret < 0) {
printk(KERN_ERR "Kprobe smoke test failed: "
@@ -323,7 +327,9 @@ static int test_kretprobes(void)
int ret;
struct kretprobe *rps[2] = {&rp, &rp2};
- rp.kp.addr = 0; /* addr should be cleard for reusing kprobe. */
+ /* addr and flags should be cleard for reusing kprobe. */
+ rp.kp.addr = NULL;
+ rp.kp.flags = 0;
ret = register_kretprobes(rps, 2);
if (ret < 0) {
printk(KERN_ERR "Kprobe smoke test failed: "
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-05-19 16:48:08
|
Remove text_mutex locking in optimize_all_kprobes, because
this function doesn't modify text. It simply queues probes on
optimization list for kprobe_optimizer worker thread.
Changes in v2:
- Update changelog.
- Add comments for (un)optimize_all_kprobes.
- Update kprobe_mutex comment.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ananth N Mavinakayanahalli <an...@in...>
Cc: Ingo Molnar <mi...@el...>
Cc: Jim Keniston <jke...@us...>
Cc: Jason Baron <jb...@re...>
Cc: Mathieu Desnoyers <mat...@ef...>
---
kernel/kprobes.c | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 282035f..f1351f2 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -73,7 +73,8 @@ static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE];
/* NOTE: change this value only with kprobe_mutex held */
static bool kprobes_all_disarmed;
-static DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */
+/* This protects kprobe_table and optimizing_list */
+static DEFINE_MUTEX(kprobe_mutex);
static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL;
static struct {
spinlock_t lock ____cacheline_aligned_in_smp;
@@ -594,6 +595,7 @@ static __kprobes void try_to_optimize_kprobe(struct kprobe *p)
}
#ifdef CONFIG_SYSCTL
+/* This should be called with kprobe_mutex locked */
static void __kprobes optimize_all_kprobes(void)
{
struct hlist_head *head;
@@ -606,17 +608,16 @@ static void __kprobes optimize_all_kprobes(void)
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");
}
+/* This should be called with kprobe_mutex locked */
static void __kprobes unoptimize_all_kprobes(void)
{
struct hlist_head *head;
--
Masami Hiramatsu
e-mail: mhi...@re...
|