From: Ingo Molnar <mingo@elte.hu>
To: Steven Rostedt <rostedt@goodmis.org>
Cc: linux-kernel@vger.kernel.org,
Andrew Morton <akpm@linux-foundation.org>,
Thomas Gleixner <tglx@linutronix.de>,
Peter Zijlstra <peterz@infradead.org>,
Frederic Weisbecker <fweisbec@gmail.com>,
Tim Bird <tim.bird@am.sony.com>,
Steven Rostedt <srostedt@redhat.com>
Subject: Re: [PATCH 1/5][RFC] tracing: add function profiler
Date: Wed, 25 Mar 2009 11:02:52 +0100 [thread overview]
Message-ID: <20090325100252.GH2341@elte.hu> (raw)
In-Reply-To: <20090325040832.251748832@goodmis.org>
(a few minor nits - all delta-fixable.)
* Steven Rostedt <rostedt@goodmis.org> wrote:
> +#ifdef CONFIG_FUNCTION_PROFILER
> +static struct hlist_head *ftrace_profile_hash;
> +static int ftrace_profile_bits;
> +static int ftrace_profile_enabled;
should be __read_mostly.
> +static DEFINE_MUTEX(ftrace_profile_lock);
should have a comment explaining its rules.
> +
> +static void *
> +function_stat_next(void *v, int idx)
> +{
> + struct dyn_ftrace *rec = v;
> + struct ftrace_page *pg;
> +
> + pg = (struct ftrace_page *)((unsigned long)rec & PAGE_MASK);
> +
> + again:
> + rec++;
> + if ((void *)rec >= (void *)&pg->records[pg->index]) {
> + pg = pg->next;
> + if (!pg)
> + return NULL;
> + rec = &pg->records[0];
> + }
> +
> + if (rec->flags & FTRACE_FL_FREE ||
> + rec->flags & FTRACE_FL_FAILED ||
> + !(rec->flags & FTRACE_FL_CONVERTED) ||
> + /* ignore non hit functions */
> + !rec->counter)
> + goto again;
> +
> + return rec;
> +}
> +
> +static void *function_stat_start(struct tracer_stat *trace)
> +{
> + return function_stat_next(&ftrace_pages_start->records[0], 0);
> +}
> +
> +static int function_stat_cmp(void *p1, void *p2)
> +{
> + struct dyn_ftrace *a = p1;
> + struct dyn_ftrace *b = p2;
> +
> + if (a->counter < b->counter)
> + return -1;
> + if (a->counter > b->counter)
> + return 1;
> + else
> + return 0;
> +}
> +
> +static int function_stat_headers(struct seq_file *m)
> +{
> + seq_printf(m, " Function Hit\n"
> + " -------- ---\n");
> + return 0;
> +}
> +
> +static int function_stat_show(struct seq_file *m, void *v)
> +{
> + struct dyn_ftrace *rec = v;
> + char str[KSYM_SYMBOL_LEN];
> +
> + kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
> +
> + seq_printf(m, " %-30.30s %10lu\n", str, rec->counter);
> + return 0;
> +}
> +
> +static struct tracer_stat function_stats = {
> + .name = "functions",
> + .stat_start = function_stat_start,
> + .stat_next = function_stat_next,
> + .stat_cmp = function_stat_cmp,
> + .stat_headers = function_stat_headers,
> + .stat_show = function_stat_show
> +};
vertical spaces please.
> +
> +static void ftrace_profile_init(int nr_funcs)
> +{
> + unsigned long addr;
> + int order;
> + int size;
> +
> + /*
> + * We are profiling all functions, lets make it 1/4th of the
> + * number of functions that are in core kernel. So we have to
> + * iterate 4 times.
> + */
> + order = (sizeof(struct hlist_head) * nr_funcs) / 4;
> + order = get_order(order);
> + size = 1 << (PAGE_SHIFT + order);
> +
> + pr_info("Allocating %d KB for profiler hash\n", size >> 10);
> +
> + addr = __get_free_pages(GFP_KERNEL | __GFP_ZERO, order);
> + if (!addr) {
> + pr_warning("Could not allocate function profiler hash\n");
> + return;
> + }
> +
> + ftrace_profile_hash = (void *)addr;
> +
> + /*
> + * struct hlist_head should be a pointer of 4 or 8 bytes.
> + * And a simple bit manipulation can be done, but if for
> + * some reason struct hlist_head is not a mulitple of 2,
> + * then we play it safe, and simply count. This function
> + * is done once at boot up, so it is not that critical in
> + * performance.
> + */
> +
> + size--;
> + size /= sizeof(struct hlist_head);
> +
> + for (; size; size >>= 1)
> + ftrace_profile_bits++;
> +
> + pr_info("Function profiler has %d hash buckets\n",
> + 1 << ftrace_profile_bits);
> +
> + return;
> +}
stale return.
> +
> +static ssize_t
> +ftrace_profile_read(struct file *filp, char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + char buf[64];
Magic constant.
> + int r;
> +
> + r = sprintf(buf, "%u\n", ftrace_profile_enabled);
> + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
What guarantees that 'cnt' cannot be larger than ~44?
> +}
> +
> +static void ftrace_profile_reset(void)
> +{
> + struct dyn_ftrace *rec;
> + struct ftrace_page *pg;
> +
> + do_for_each_ftrace_rec(pg, rec) {
> + rec->counter = 0;
> + } while_for_each_ftrace_rec();
> +}
> +
> +static struct dyn_ftrace *ftrace_find_profiled_func(unsigned long ip)
> +{
> + struct dyn_ftrace *rec;
> + struct hlist_head *hhd;
> + struct hlist_node *n;
> + unsigned long flags;
> + unsigned long key;
> +
> + if (!ftrace_profile_hash)
> + return NULL;
> +
> + key = hash_long(ip, ftrace_profile_bits);
> + hhd = &ftrace_profile_hash[key];
> +
> + if (hlist_empty(hhd))
> + return NULL;
> +
> + local_irq_save(flags);
> + hlist_for_each_entry_rcu(rec, n, hhd, node) {
> + if (rec->ip == ip)
> + goto out;
> + }
> + rec = NULL;
> + out:
> + local_irq_restore(flags);
> +
> + return rec;
> +}
actually, this hash is a per function attribute, in disguise,
indexed by addressed. Dont we have such a hash already:
ftrace_func_hash?
Wouldnt it be better to refactor all this into a clean per function
attribute hash thing. The profile counter can be allocated via
percpu_alloc(), to keep it scalable.
> +
> +static void
> +function_profile_call(unsigned long ip, unsigned long parent_ip)
> +{
> + struct dyn_ftrace *rec;
> + unsigned long flags;
> +
> + if (!ftrace_profile_enabled)
> + return;
> +
> + local_irq_save(flags);
> + rec = ftrace_find_profiled_func(ip);
> + if (!rec)
> + goto out;
> +
> + rec->counter++;
> + out:
> + local_irq_restore(flags);
> +}
> +
> +static struct ftrace_ops ftrace_profile_ops __read_mostly =
> +{
> + .func = function_profile_call,
(use vertical space consistent with other places in this file
please.)
> +};
> +
> +static ssize_t
> +ftrace_profile_write(struct file *filp, const char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + unsigned long val;
> + char buf[64];
> + int ret;
> +
> + if (!ftrace_profile_hash) {
> + pr_info("Can not enable hash due to earlier problems\n");
> + return -ENODEV;
> + }
> +
> + if (cnt >= sizeof(buf))
> + return -EINVAL;
> +
> + if (copy_from_user(&buf, ubuf, cnt))
> + return -EFAULT;
cnt is always <= 64 ?
> +
> + buf[cnt] = 0;
> +
> + ret = strict_strtoul(buf, 10, &val);
> + if (ret < 0)
> + return ret;
> +
> + val = !!val;
> +
> + mutex_lock(&ftrace_profile_lock);
> + if (ftrace_profile_enabled ^ val) {
> + if (val) {
> + ftrace_profile_reset();
> + register_ftrace_function(&ftrace_profile_ops);
> + ftrace_profile_enabled = 1;
> + } else {
> + ftrace_profile_enabled = 0;
> + unregister_ftrace_function(&ftrace_profile_ops);
> + }
> + }
> + mutex_unlock(&ftrace_profile_lock);
> +
> + filp->f_pos += cnt;
> +
> + return cnt;
these patterns of code are familar too. We do a lot of
integer-option twiddlig. Helper-function potential?
> +}
> +
> +static const struct file_operations ftrace_profile_fops = {
> + .open = tracing_open_generic,
> + .read = ftrace_profile_read,
> + .write = ftrace_profile_write,
> +};
> +
> +static void ftrace_profile_debugfs(struct dentry *d_tracer)
> +{
> + struct dentry *entry;
> + int ret;
> +
> + ret = register_stat_tracer(&function_stats);
> + if (ret) {
> + pr_warning("Warning: could not register "
> + "function stats\n");
> + return;
> + }
> +
> + entry = debugfs_create_file("function_profile_enabled", 0644,
> + d_tracer, NULL, &ftrace_profile_fops);
> + if (!entry)
> + pr_warning("Could not create debugfs "
> + "'function_profile_enabled' entry\n");
this is a reoccuring pattern. Add tracing_create_file() perhaps,
with an implicit pr_warning()? Would clean up quite a lot of code.
> +}
> +
> +static void ftrace_add_profile(struct dyn_ftrace *rec)
> +{
> + unsigned long key;
> +
> + if (!ftrace_profile_hash)
> + return;
> +
> + key = hash_long(rec->ip, ftrace_profile_bits);
> + hlist_add_head_rcu(&rec->node, &ftrace_profile_hash[key]);
> +}
> +
> +static void ftrace_profile_release(struct dyn_ftrace *rec)
> +{
> + mutex_lock(&ftrace_profile_lock);
> + hlist_del(&rec->node);
> + mutex_unlock(&ftrace_profile_lock);
> +}
> +
> +#else /* CONFIG_FUNCTION_PROFILER */
> +static void ftrace_profile_init(int nr_funcs)
> +{
> +}
> +static void ftrace_add_profile(struct dyn_ftrace *rec)
> +{
> +}
> +static void ftrace_profile_debugfs(struct dentry *d_tracer)
> +{
> +}
> +static void ftrace_profile_release(struct dyn_ftrace *rec)
> +{
> +}
> +#endif /* CONFIG_FUNCTION_PROFILER */
> +
> #ifdef CONFIG_KPROBES
>
> static int frozen_record_count;
> @@ -359,8 +660,10 @@ void ftrace_release(void *start, unsigned long size)
> mutex_lock(&ftrace_lock);
> do_for_each_ftrace_rec(pg, rec) {
> if ((rec->ip >= s) && (rec->ip < e) &&
> - !(rec->flags & FTRACE_FL_FREE))
> + !(rec->flags & FTRACE_FL_FREE)) {
> ftrace_free_rec(rec);
> + ftrace_profile_release(rec);
> + }
> } while_for_each_ftrace_rec();
> mutex_unlock(&ftrace_lock);
> }
> @@ -414,6 +717,8 @@ ftrace_record_ip(unsigned long ip)
> rec->newlist = ftrace_new_addrs;
> ftrace_new_addrs = rec;
>
> + ftrace_add_profile(rec);
> +
> return rec;
> }
>
> @@ -2157,6 +2462,8 @@ static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
> "'set_graph_function' entry\n");
> #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
>
> + ftrace_profile_debugfs(d_tracer);
> +
> return 0;
> }
>
> @@ -2225,6 +2532,8 @@ void __init ftrace_init(void)
> if (ret)
> goto failed;
>
> + ftrace_profile_init(count);
> +
> last_ftrace_enabled = ftrace_enabled = 1;
>
> ret = ftrace_convert_nops(NULL,
Neat stuff ...
Ingo
next prev parent reply other threads:[~2009-03-25 10:03 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-03-25 3:56 [PATCH 0/5][RFC] [RFC] function profiler Steven Rostedt
2009-03-25 3:56 ` [PATCH 1/5][RFC] tracing: add " Steven Rostedt
2009-03-25 10:02 ` Ingo Molnar [this message]
2009-03-25 15:42 ` Steven Rostedt
2009-03-25 17:38 ` [PATCH][GIT PULL] tracing: clean up tracing profiler Steven Rostedt
2009-03-25 19:11 ` [PATCH 1/5][RFC] tracing: add function profiler Frederic Weisbecker
2009-03-25 19:31 ` Steven Rostedt
2009-03-25 3:56 ` [PATCH 2/5][RFC] tracing: move function profiler data out of function struct Steven Rostedt
2009-03-25 10:07 ` Ingo Molnar
2009-03-25 15:43 ` Steven Rostedt
2009-03-25 17:44 ` Ingo Molnar
2009-03-25 17:57 ` Steven Rostedt
2009-03-25 18:02 ` Ingo Molnar
2009-03-25 18:16 ` Steven Rostedt
2009-03-25 18:35 ` Ingo Molnar
2009-03-25 18:40 ` Steven Rostedt
2009-03-25 18:46 ` Steven Rostedt
2009-03-25 3:56 ` [PATCH 3/5][RFC] tracing: adding function timings to function profiler Steven Rostedt
2009-03-25 18:30 ` Frederic Weisbecker
2009-03-25 3:56 ` [PATCH 4/5][RFC] tracing: make the function profiler per cpu Steven Rostedt
2009-03-25 3:56 ` [PATCH 5/5][RFC] function-graph: add option to calculate graph time or not Steven Rostedt
2009-03-25 10:11 ` Ingo Molnar
2009-03-25 17:35 ` Steven Rostedt
2009-03-25 9:50 ` [PATCH 0/5][RFC] [RFC] function profiler Ingo Molnar
2009-03-25 9:52 ` Ingo Molnar
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=20090325100252.GH2341@elte.hu \
--to=mingo@elte.hu \
--cc=akpm@linux-foundation.org \
--cc=fweisbec@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=peterz@infradead.org \
--cc=rostedt@goodmis.org \
--cc=srostedt@redhat.com \
--cc=tglx@linutronix.de \
--cc=tim.bird@am.sony.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.