From: Frederic Weisbecker <fweisbec@gmail.com>
To: "K.Prasad" <prasad@linux.vnet.ibm.com>
Cc: mingo@elte.hu, Andrew Morton <akpm@linux-foundation.org>,
Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
Alan Stern <stern@rowland.harvard.edu>,
Roland McGrath <roland@redhat.com>
Subject: Re: [patch 11/11] ftrace plugin for kernel symbol tracing using HW Breakpoint interfaces
Date: Thu, 5 Mar 2009 13:28:30 +0100 [thread overview]
Message-ID: <20090305122827.GI5359@nowhere> (raw)
In-Reply-To: <20090305113359.GA25213@in.ibm.com>
On Thu, Mar 05, 2009 at 05:03:59PM +0530, K.Prasad wrote:
> On Thu, Mar 05, 2009 at 07:37:04AM +0100, Frederic Weisbecker wrote:
> > On Thu, Mar 05, 2009 at 10:13:33AM +0530, prasad@linux.vnet.ibm.com wrote:
> > > This patch adds an ftrace plugin to detect and profile memory access over
> > > kernel variables. It uses HW Breakpoint interfaces to 'watch memory
> > > addresses.
> > >
> > > Signed-off-by: K.Prasad <prasad@linux.vnet.ibm.com>
> > > ---
> >
> >
> > Hi,
> >
> > Nice feature. And moreover the standardized hardware breakpoints could
> > be helpful for tracing.
> >
> > Just some comments below.
> >
> >
>
> Hi,
> Thanks for reviewing the code and pointing out the potential memory
> leaks. The next iteration of this code should contain fixes for them.
> I've explained the usage of 'entry' field inline.
>
> > > +struct trace_ksym {
> > > + struct trace_entry ent;
> > > + struct hw_breakpoint *ksym_hbkpt;
> > > + unsigned long ksym_addr;
> > > + unsigned long ip;
> > > + pid_t pid;
> >
> >
> > Just a doubt here.
> > The current pid is automatically recorded on trace_buffer_lock_reserve()
> > (or unlock_commit, don't remember), so if this pid is the current one, you
> > don't need to reserve a room for it, current pid is on struct trace_entry.
> >
>
> It's a carriage from an old version of the code which used the old
> ring-buffer APIs like ring_buffer_lock_reserve(). I will now use the
> "pid" field in "struct trace_entry".
>
> > > +static int process_new_ksym_entry(struct trace_ksym *entry, char *ksymname,
> > > + int op, unsigned long addr)
> > > +{
> > > + if (ksym_filter_entry_count >= KSYM_TRACER_MAX) {
> > > + printk(KERN_ERR "ksym_tracer: Maximum limit:(%d) reached. No"
> > > + " new requests for tracing can be accepted now.\n",
> > > + KSYM_TRACER_MAX);
> > > + return -ENOSPC;
> > > + }
> > > +
> > > + entry = kzalloc(sizeof(struct trace_ksym), GFP_KERNEL);
> >
> >
> > I'm not sure I understand, you passed an allocated entry to that function, no?
> > If your are using entry as a local variable, it doesn't make sense to pass it
> > as a parameter.
> >
> >
> > > + if (!entry)
> > > + return -ENOMEM;
> > >
> > > + entry->ksym_hbkpt = kzalloc(sizeof(struct hw_breakpoint), GFP_KERNEL);
> > > + if (!entry->ksym_hbkpt)
> > > + return -ENOMEM;
> >
> >
> > Ouch, what happens here to the memory pointed by entry?
> >
> >
>
> A potential leak....will fix this and the others you've pointed below.
>
> > > +
> > > + entry->ksym_hbkpt->info.name = ksymname;
> > > + entry->ksym_hbkpt->info.type = op;
> > > + entry->ksym_addr = entry->ksym_hbkpt->info.address = addr;
> > > + entry->ksym_hbkpt->info.len = HW_BREAKPOINT_LEN_4;
> > > + entry->ksym_hbkpt->priority = HW_BREAKPOINT_PRIO_NORMAL;
> > > +
> > > + entry->ksym_hbkpt->installed = (void *)ksym_hbkpt_installed;
> > > + entry->ksym_hbkpt->uninstalled = (void *)ksym_hbkpt_uninstalled;
> > > + entry->ksym_hbkpt->triggered = (void *)ksym_hbkpt_handler;
> > > +
> > > + if ((register_kernel_hw_breakpoint(entry->ksym_hbkpt)) < 0) {
> > > + printk(KERN_INFO "ksym_tracer request failed. Try again"
> > > + " later!!\n");
> > > + kfree(entry);
> > > + return -EAGAIN;
> >
> >
> > You forgot to free entry->ksym_hbkpt
> >
> >
> > > + }
> > > + hlist_add_head(&(entry->ksym_hlist), &ksym_filter_head);
> > > + printk(KERN_INFO "ksym_tracer changes are now effective\n");
> > > +
> > > + ksym_filter_entry_count++;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static ssize_t ksym_trace_filter_read(struct file *filp, char __user *ubuf,
> > > + size_t count, loff_t *ppos)
> > > +{
> > > + struct trace_ksym *entry;
> > > + struct hlist_node *node;
> > > + char buf[KSYM_FILTER_ENTRY_LEN * KSYM_TRACER_MAX];
> > > + ssize_t ret, cnt = 0;
> > > +
> > > + mutex_lock(&ksym_tracer_mutex);
> > > +
> > > + hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) {
> > > + cnt += snprintf(&buf[cnt], KSYM_FILTER_ENTRY_LEN - cnt, "%s:",
> > > + entry->ksym_hbkpt->info.name);
> > > + if (entry->ksym_hbkpt->info.type == HW_BREAKPOINT_WRITE)
> > > + cnt += snprintf(&buf[cnt], KSYM_FILTER_ENTRY_LEN - cnt,
> > > + "-w-\n");
> > > + else if (entry->ksym_hbkpt->info.type == HW_BREAKPOINT_RW)
> > > + cnt += snprintf(&buf[cnt], KSYM_FILTER_ENTRY_LEN - cnt,
> > > + "rw-\n");
> > > + }
> > > + ret = simple_read_from_buffer(ubuf, count, ppos, buf, strlen(buf));
> > > + mutex_unlock(&ksym_tracer_mutex);
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +static ssize_t ksym_trace_filter_write(struct file *file,
> > > + const char __user *buffer,
> > > + size_t count, loff_t *ppos)
> > > +{
> > > + struct trace_ksym *entry;
> > > + struct hlist_node *node;
> > > + char *input_string, *ksymname = NULL;
> > > + unsigned long ksym_addr = 0;
> > > + int ret, op, changed = 0;
> > > +
> > > + input_string = kzalloc(count, GFP_KERNEL);
> > > + if (!input_string)
> > > + return -ENOMEM;
> > > +
> > > + /* Ignore echo "" > ksym_trace_filter */
> > > + if (count == 0)
> > > + return 0;
> >
> >
> > You forgot to free input_string in !count case.
> >
> >
> > > +
> > > + if (copy_from_user(input_string, buffer, count))
> > > + return -EFAULT;
> >
> >
> > Ditto.
> >
> > > + ret = op = parse_ksym_trace_str(input_string, &ksymname, &ksym_addr);
> > > +
> > > + if (ret < 0)
> > > + goto err_ret;
> >
> >
> > Ah, here you didn't forget.
> >
> >
> > > + mutex_lock(&ksym_tracer_mutex);
> > > +
> > > + ret = -EINVAL;
> > > + hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) {
> > > + if (entry->ksym_addr == ksym_addr) {
> > > + /* Check for malformed request: (6) */
> > > + if (entry->ksym_hbkpt->info.type != op)
> > > + changed = 1;
> > > + else
> > > + goto err_ret;
> > > + break;
> > > + }
> > > + }
> > > + if (changed) {
> > > + unregister_kernel_hw_breakpoint(entry->ksym_hbkpt);
> > > + entry->ksym_hbkpt->info.type = op;
> > > + if (op > 0) {
> > > + ret = register_kernel_hw_breakpoint(entry->ksym_hbkpt);
> > > + if (ret > 0) {
> > > + ret = count;
> > > + goto unlock_ret_path;
> > > + }
> > > + if (ret == 0) {
> > > + ret = -ENOSPC;
> > > + unregister_kernel_hw_breakpoint(entry->\
> > > + ksym_hbkpt);
> > > + }
> > > + }
> > > + ksym_filter_entry_count--;
> > > + hlist_del(&(entry->ksym_hlist));
> > > + kfree(entry->ksym_hbkpt);
> > > + kfree(entry);
> > > + ret = count;
> > > + goto err_ret;
> > > + } else {
> > > + /* Check for malformed request: (4) */
> > > + if (op == 0)
> > > + goto err_ret;
> > > +
> > > + ret = process_new_ksym_entry(entry, ksymname, op, ksym_addr);
> >
> >
> > You are passing an allocated entry as a parameter, but later on process_new_ksym_entry()
> > you allocate a new space for entry.
> > I'm confused.
> >
> >
>
> When changed = 1, entry points to the existing instance of 'struct
> trace_ksym' and will be used for changing the type of breakpoint. If the
> input is a new request to ksym_trace_filter file process_new_ksym_entry()
> takes a pointer to 'struct trace_ksym' i.e entry for
> allocation/initialisation rather than use it as a parameter in the true
> sense.
>
> This is similar to the usage of parameters 'ksymname and addr' in
> parse_ksym_trace_str() where they are used to return multiple values.
>
> I hope you find the usage acceptable.
Hmm. I understand the case of ksymname and addr in parse_ksym_trace_str()
But I don't understand the case here.
You pass the "entry" pointer to process_new_ksym_entry() but:
- this is only a pointer of type struct trace_ksym * and not
struct trace_ksym **entry
Once it comes to process_new_ksym_entry() it's not anymore
the same variable than the caller passed. You override
it with kzalloc() but this change will not be done on the caller
which will keep the same address stored on its pointer.
- you are not reusing it on the caller after it called
process_nex_ksym_ntry()
But you use it on the callee because you insert it on the list.
So the code is not wrong, it's just that such only internal
pointer is generally expected to be declared inside the function itself:
static int process_new_ksym_entry(char *ksymname,
int op, unsigned long addr)
{
struct trace_ksym *entry
entry = kzalloc(sizeof(struct trace_ksym), GFP_KERNEL);
...
}
Otherwise when such a parameter is passed, the code reader would expect that
1) this is a value that we will use inside this function (not the case, the value
is immediately overriden).
2) this is a secondary return value (not the case, or we would need a pointer to
a pointer).
Well, sorry perhaps I'm a bit annoying with that :-)
It's just for the code readability...I mean code flow for the reader eyes.
But the code action itself is not broken.
Thanks.
Frederic.
> > > +
> > > +__init static int init_ksym_trace(void)
> > > +{
> > > + struct dentry *d_tracer;
> > > + struct dentry *entry;
> > > +
> > > + d_tracer = tracing_init_dentry();
> > > + ksym_filter_entry_count = 0;
> > > +
> > > + entry = debugfs_create_file("ksym_trace_filter", 0666, d_tracer,
> > > + NULL, &ksym_tracing_fops);
> > > + if (!entry)
> > > + pr_warning("Could not create debugfs "
> > > + "'ksym_trace_filter' file\n");
> > > +
> > > + return register_tracer(&ksym_tracer);
> > > +
> > > +}
> > > +device_initcall(init_ksym_trace);
> >
> >
> > Well, the rest looks good.
> >
> >
>
> Thanks again for your comments.
>
> -- K.Prasad
next prev parent reply other threads:[~2009-03-05 12:28 UTC|newest]
Thread overview: 68+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20090305043440.189041194@linux.vnet.ibm.com>
2009-03-05 4:37 ` [patch 01/11] Introducing generic hardware breakpoint handler interfaces prasad
2009-03-10 13:50 ` Ingo Molnar
2009-03-10 14:19 ` Alan Stern
2009-03-10 14:50 ` Ingo Molnar
2009-03-11 12:57 ` K.Prasad
2009-03-11 13:35 ` Ingo Molnar
2009-03-05 4:38 ` [patch 02/11] x86 architecture implementation of Hardware Breakpoint interfaces prasad
2009-03-10 14:09 ` Ingo Molnar
2009-03-10 14:59 ` Alan Stern
2009-03-10 15:18 ` Ingo Molnar
2009-03-10 17:11 ` Alan Stern
2009-03-10 17:26 ` Ingo Molnar
2009-03-10 20:30 ` Alan Stern
2009-03-11 12:12 ` Ingo Molnar
2009-03-11 12:50 ` K.Prasad
2009-03-11 13:10 ` Ingo Molnar
2009-03-14 3:46 ` Benjamin Herrenschmidt
2009-03-11 16:39 ` Alan Stern
2009-03-11 16:32 ` Alan Stern
2009-03-11 17:41 ` K.Prasad
2009-03-14 3:47 ` Benjamin Herrenschmidt
2009-03-14 3:43 ` Benjamin Herrenschmidt
2009-03-14 3:41 ` Benjamin Herrenschmidt
2009-03-14 3:40 ` Benjamin Herrenschmidt
2009-03-12 2:46 ` Roland McGrath
2009-03-13 3:43 ` Ingo Molnar
2009-03-13 14:04 ` Alan Stern
2009-03-13 14:13 ` Ingo Molnar
2009-03-13 19:01 ` K.Prasad
2009-03-13 21:21 ` Alan Stern
2009-03-14 12:24 ` Ingo Molnar
2009-03-14 16:10 ` Alan Stern
2009-03-14 16:39 ` Ingo Molnar
2009-03-14 3:51 ` Benjamin Herrenschmidt
2009-03-05 4:38 ` [patch 03/11] Modifying generic debug exception to use virtual debug registers prasad
2009-03-05 4:38 ` [patch 04/11] Introduce virtual debug register in thread_struct and wrapper-routines around process related functions prasad
2009-03-10 14:35 ` Ingo Molnar
2009-03-10 15:53 ` Alan Stern
2009-03-10 17:06 ` Ingo Molnar
2009-03-12 2:26 ` Roland McGrath
2009-03-05 4:38 ` [patch 05/11] Use wrapper routines around debug registers in processor " prasad
2009-03-05 4:40 ` [patch 06/11] Use virtual debug registers in process/thread handling code prasad
2009-03-10 14:49 ` Ingo Molnar
2009-03-10 16:05 ` Alan Stern
2009-03-10 16:58 ` Ingo Molnar
2009-03-10 17:07 ` Ingo Molnar
2009-03-10 20:10 ` Alan Stern
2009-03-11 11:53 ` Ingo Molnar
2009-03-05 4:40 ` [patch 07/11] Modify signal handling code to refrain from re-enabling HW Breakpoints prasad
2009-03-05 4:40 ` [patch 08/11] Modify Ptrace routines to access breakpoint registers prasad
2009-03-10 14:40 ` Ingo Molnar
2009-03-10 15:54 ` Alan Stern
2009-03-12 3:14 ` Roland McGrath
2009-03-05 4:41 ` [patch 09/11] Cleanup HW Breakpoint registers before kexec prasad
2009-03-10 14:42 ` Ingo Molnar
2009-03-05 4:41 ` [patch 10/11] Sample HW breakpoint over kernel data address prasad
2009-03-05 4:43 ` prasad
2009-03-05 4:43 ` [patch 11/11] ftrace plugin for kernel symbol tracing using HW Breakpoint interfaces prasad
2009-03-05 6:37 ` Frederic Weisbecker
2009-03-05 9:16 ` Ingo Molnar
2009-03-05 13:15 ` K.Prasad
2009-03-05 13:28 ` Ingo Molnar
2009-03-05 11:33 ` K.Prasad
2009-03-05 12:19 ` K.Prasad
2009-03-05 12:30 ` Frederic Weisbecker
2009-03-05 12:28 ` Frederic Weisbecker [this message]
2009-03-05 15:00 ` Steven Rostedt
2009-03-05 14:54 ` Steven Rostedt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20090305122827.GI5359@nowhere \
--to=fweisbec@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@elte.hu \
--cc=prasad@linux.vnet.ibm.com \
--cc=roland@redhat.com \
--cc=stern@rowland.harvard.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox