--- /dev/null
+/*
+ * Infrastructure for statistic tracing (histogram output).
+ *
+ * Copyright (C) 2008 Frederic Weisbecker <fweisbec@gmail.com>
+ *
+ * Based on the code from trace_branch.c which is
+ * Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com>
+ *
+ */
+
+
+#include <linux/list.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include "trace.h"
+
+
+/* List of stat entries from a tracer */
+struct trace_stat_list {
+       struct list_head list;
+       void *stat;
+};
+
+static struct trace_stat_list stat_list;
+
+/*
+ * This is a copy of the current tracer to avoid racy
+ * and dangerous output while the current tracer is
+ * switched.
+ */
+static struct tracer current_tracer;
+
+/*
+ * Protect both the current tracer and the global
+ * stat list.
+ */
+static DEFINE_MUTEX(stat_list_mutex);
+
+
+static void reset_stat_list(void)
+{
+       struct trace_stat_list *node;
+       struct list_head *next;
+
+       if (list_empty(&stat_list.list))
+               return;
+
+       node = list_entry(stat_list.list.next, struct trace_stat_list, list);
+       next = node->list.next;
+
+       while (&node->list != next) {
+               kfree(node);
+               node = list_entry(next, struct trace_stat_list, list);
+       }
+       kfree(node);
+
+       INIT_LIST_HEAD(&stat_list.list);
+}
+
+void init_tracer_stat(struct tracer *trace)
+{
+       mutex_lock(&stat_list_mutex);
+       current_tracer = *trace;
+       mutex_unlock(&stat_list_mutex);
+}
+
+/*
+ * For tracers that don't provide a stat_cmp callback.
+ * This one will force an immediate insertion on tail of
+ * the list.
+ */
+static int dummy_cmp(void *p1, void *p2)
+{
+       return 1;
+}
+
+/*
+ * Initialize the stat list at each trace_stat file opening.
+ * All of these copies and sorting are required on all opening
+ * since the stats could have changed between two file sessions.
+ */
+static int stat_seq_init(void)
+{
+       struct trace_stat_list *iter_entry, *new_entry;
+       void *prev_stat;
+       int ret = 0;
+       int i;
+
+       mutex_lock(&stat_list_mutex);
+       reset_stat_list();
+
+       if (!current_tracer.stat_start || !current_tracer.stat_next ||
+                                       !current_tracer.stat_show)
+               goto exit;
+
+       if (!current_tracer.stat_cmp)
+               current_tracer.stat_cmp = dummy_cmp;
+
+       /*
+        * The first entry. Actually this is the second, but the first
+        * one (the stat_list head) is pointless.
+        */
+       new_entry = kmalloc(sizeof(struct trace_stat_list), GFP_KERNEL);
+       if (!new_entry) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       INIT_LIST_HEAD(&new_entry->list);
+       list_add(&new_entry->list, &stat_list.list);
+       new_entry->stat = current_tracer.stat_start();
+
+       prev_stat = new_entry->stat;
+
+       /*
+        * Iterate over the tracer stat entries and store them in a sorted
+        * list.
+        */
+       for (i = 1; ; i++) {
+               new_entry = kmalloc(sizeof(struct trace_stat_list), GFP_KERNEL);
+               if (!new_entry) {
+                       ret = -ENOMEM;
+                       goto exit_free_list;
+               }
+
+               INIT_LIST_HEAD(&new_entry->list);
+               new_entry->stat = current_tracer.stat_next(prev_stat, i);
+
+               /* End of insertion */
+               if (!new_entry->stat)
+                       break;
+
+               list_for_each_entry(iter_entry, &stat_list.list, list) {
+                       /* Insertion with a descendent sorting */
+                       if (current_tracer.stat_cmp(new_entry->stat,
+                                               iter_entry->stat) > 0) {
+
+                               list_add_tail(&new_entry->list,
+                                               &iter_entry->list);
+                               break;
+
+                       /* The current smaller value */
+                       } else if (list_is_last(&iter_entry->list,
+                                               &stat_list.list)) {
+                               list_add(&new_entry->list, &iter_entry->list);
+                               break;
+                       }
+               }
+
+               prev_stat = new_entry->stat;
+       }
+exit:
+       mutex_unlock(&stat_list_mutex);
+       return ret;
+
+exit_free_list:
+       reset_stat_list();
+       mutex_unlock(&stat_list_mutex);
+       return ret;
+}
+
+
+static void *stat_seq_start(struct seq_file *s, loff_t *pos)
+{
+       struct trace_stat_list *l = (struct trace_stat_list *)s->private;
+
+       /* Prevent from tracer switch or stat_list modification */
+       mutex_lock(&stat_list_mutex);
+
+       /* If we are in the beginning of the file, print the headers */
+       if (!*pos && current_tracer.stat_headers)
+               current_tracer.stat_headers(s);
+
+       return seq_list_start(&l->list, *pos);
+}
+
+static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos)
+{
+       struct trace_stat_list *l = (struct trace_stat_list *)s->private;
+
+       return seq_list_next(p, &l->list, pos);
+}
+
+static void stat_seq_stop(struct seq_file *m, void *p)
+{
+       mutex_unlock(&stat_list_mutex);
+}
+
+static int stat_seq_show(struct seq_file *s, void *v)
+{
+       struct trace_stat_list *l = list_entry(v, struct trace_stat_list, list);
+       return current_tracer.stat_show(s, l->stat);
+}
+
+static const struct seq_operations trace_stat_seq_ops = {
+       .start = stat_seq_start,
+       .next = stat_seq_next,
+       .stop = stat_seq_stop,
+       .show = stat_seq_show
+};
+
+static int tracing_stat_open(struct inode *inode, struct file *file)
+{
+       int ret;
+
+       ret = seq_open(file, &trace_stat_seq_ops);
+       if (!ret) {
+               struct seq_file *m = file->private_data;
+               m->private = &stat_list;
+               ret = stat_seq_init();
+       }
+
+       return ret;
+}
+
+
+/*
+ * Avoid consuming memory with our now useless list.
+ */
+static int tracing_stat_release(struct inode *i, struct file *f)
+{
+       mutex_lock(&stat_list_mutex);
+       reset_stat_list();
+       mutex_unlock(&stat_list_mutex);
+       return 0;
+}
+
+static const struct file_operations tracing_stat_fops = {
+       .open           = tracing_stat_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = tracing_stat_release
+};
+
+static int __init tracing_stat_init(void)
+{
+       struct dentry *d_tracing;
+       struct dentry *entry;
+
+       INIT_LIST_HEAD(&stat_list.list);
+       d_tracing = tracing_init_dentry();
+
+       entry = debugfs_create_file("trace_stat", 0444, d_tracing,
+                                       NULL,
+                                   &tracing_stat_fops);
+       if (!entry)
+               pr_warning("Could not create debugfs "
+                          "'trace_stat' entry\n");
+       return 0;
+}
+fs_initcall(tracing_stat_init);