linux/kernel/trace/trace_stat.c
<<
>>
Prefs
   1/*
   2 * Infrastructure for statistic tracing (histogram output).
   3 *
   4 * Copyright (C) 2008-2009 Frederic Weisbecker <fweisbec@gmail.com>
   5 *
   6 * Based on the code from trace_branch.c which is
   7 * Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com>
   8 *
   9 */
  10
  11
  12#include <linux/list.h>
  13#include <linux/slab.h>
  14#include <linux/rbtree.h>
  15#include <linux/tracefs.h>
  16#include "trace_stat.h"
  17#include "trace.h"
  18
  19
  20/*
  21 * List of stat red-black nodes from a tracer
  22 * We use a such tree to sort quickly the stat
  23 * entries from the tracer.
  24 */
  25struct stat_node {
  26        struct rb_node          node;
  27        void                    *stat;
  28};
  29
  30/* A stat session is the stats output in one file */
  31struct stat_session {
  32        struct list_head        session_list;
  33        struct tracer_stat      *ts;
  34        struct rb_root          stat_root;
  35        struct mutex            stat_mutex;
  36        struct dentry           *file;
  37};
  38
  39/* All of the sessions currently in use. Each stat file embed one session */
  40static LIST_HEAD(all_stat_sessions);
  41static DEFINE_MUTEX(all_stat_sessions_mutex);
  42
  43/* The root directory for all stat files */
  44static struct dentry            *stat_dir;
  45
  46static void __reset_stat_session(struct stat_session *session)
  47{
  48        struct stat_node *snode, *n;
  49
  50        rbtree_postorder_for_each_entry_safe(snode, n, &session->stat_root, node) {
  51                if (session->ts->stat_release)
  52                        session->ts->stat_release(snode->stat);
  53                kfree(snode);
  54        }
  55
  56        session->stat_root = RB_ROOT;
  57}
  58
  59static void reset_stat_session(struct stat_session *session)
  60{
  61        mutex_lock(&session->stat_mutex);
  62        __reset_stat_session(session);
  63        mutex_unlock(&session->stat_mutex);
  64}
  65
  66static void destroy_session(struct stat_session *session)
  67{
  68        tracefs_remove(session->file);
  69        __reset_stat_session(session);
  70        mutex_destroy(&session->stat_mutex);
  71        kfree(session);
  72}
  73
  74typedef int (*cmp_stat_t)(void *, void *);
  75
  76static int insert_stat(struct rb_root *root, void *stat, cmp_stat_t cmp)
  77{
  78        struct rb_node **new = &(root->rb_node), *parent = NULL;
  79        struct stat_node *data;
  80
  81        data = kzalloc(sizeof(*data), GFP_KERNEL);
  82        if (!data)
  83                return -ENOMEM;
  84        data->stat = stat;
  85
  86        /*
  87         * Figure out where to put new node
  88         * This is a descendent sorting
  89         */
  90        while (*new) {
  91                struct stat_node *this;
  92                int result;
  93
  94                this = container_of(*new, struct stat_node, node);
  95                result = cmp(data->stat, this->stat);
  96
  97                parent = *new;
  98                if (result >= 0)
  99                        new = &((*new)->rb_left);
 100                else
 101                        new = &((*new)->rb_right);
 102        }
 103
 104        rb_link_node(&data->node, parent, new);
 105        rb_insert_color(&data->node, root);
 106        return 0;
 107}
 108
 109/*
 110 * For tracers that don't provide a stat_cmp callback.
 111 * This one will force an insertion as right-most node
 112 * in the rbtree.
 113 */
 114static int dummy_cmp(void *p1, void *p2)
 115{
 116        return -1;
 117}
 118
 119/*
 120 * Initialize the stat rbtree at each trace_stat file opening.
 121 * All of these copies and sorting are required on all opening
 122 * since the stats could have changed between two file sessions.
 123 */
 124static int stat_seq_init(struct stat_session *session)
 125{
 126        struct tracer_stat *ts = session->ts;
 127        struct rb_root *root = &session->stat_root;
 128        void *stat;
 129        int ret = 0;
 130        int i;
 131
 132        mutex_lock(&session->stat_mutex);
 133        __reset_stat_session(session);
 134
 135        if (!ts->stat_cmp)
 136                ts->stat_cmp = dummy_cmp;
 137
 138        stat = ts->stat_start(ts);
 139        if (!stat)
 140                goto exit;
 141
 142        ret = insert_stat(root, stat, ts->stat_cmp);
 143        if (ret)
 144                goto exit;
 145
 146        /*
 147         * Iterate over the tracer stat entries and store them in an rbtree.
 148         */
 149        for (i = 1; ; i++) {
 150                stat = ts->stat_next(stat, i);
 151
 152                /* End of insertion */
 153                if (!stat)
 154                        break;
 155
 156                ret = insert_stat(root, stat, ts->stat_cmp);
 157                if (ret)
 158                        goto exit_free_rbtree;
 159        }
 160
 161exit:
 162        mutex_unlock(&session->stat_mutex);
 163        return ret;
 164
 165exit_free_rbtree:
 166        __reset_stat_session(session);
 167        mutex_unlock(&session->stat_mutex);
 168        return ret;
 169}
 170
 171
 172static void *stat_seq_start(struct seq_file *s, loff_t *pos)
 173{
 174        struct stat_session *session = s->private;
 175        struct rb_node *node;
 176        int n = *pos;
 177        int i;
 178
 179        /* Prevent from tracer switch or rbtree modification */
 180        mutex_lock(&session->stat_mutex);
 181
 182        /* If we are in the beginning of the file, print the headers */
 183        if (session->ts->stat_headers) {
 184                if (n == 0)
 185                        return SEQ_START_TOKEN;
 186                n--;
 187        }
 188
 189        node = rb_first(&session->stat_root);
 190        for (i = 0; node && i < n; i++)
 191                node = rb_next(node);
 192
 193        return node;
 194}
 195
 196static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos)
 197{
 198        struct stat_session *session = s->private;
 199        struct rb_node *node = p;
 200
 201        (*pos)++;
 202
 203        if (p == SEQ_START_TOKEN)
 204                return rb_first(&session->stat_root);
 205
 206        return rb_next(node);
 207}
 208
 209static void stat_seq_stop(struct seq_file *s, void *p)
 210{
 211        struct stat_session *session = s->private;
 212        mutex_unlock(&session->stat_mutex);
 213}
 214
 215static int stat_seq_show(struct seq_file *s, void *v)
 216{
 217        struct stat_session *session = s->private;
 218        struct stat_node *l = container_of(v, struct stat_node, node);
 219
 220        if (v == SEQ_START_TOKEN)
 221                return session->ts->stat_headers(s);
 222
 223        return session->ts->stat_show(s, l->stat);
 224}
 225
 226static const struct seq_operations trace_stat_seq_ops = {
 227        .start          = stat_seq_start,
 228        .next           = stat_seq_next,
 229        .stop           = stat_seq_stop,
 230        .show           = stat_seq_show
 231};
 232
 233/* The session stat is refilled and resorted at each stat file opening */
 234static int tracing_stat_open(struct inode *inode, struct file *file)
 235{
 236        int ret;
 237        struct seq_file *m;
 238        struct stat_session *session = inode->i_private;
 239
 240        ret = stat_seq_init(session);
 241        if (ret)
 242                return ret;
 243
 244        ret = seq_open(file, &trace_stat_seq_ops);
 245        if (ret) {
 246                reset_stat_session(session);
 247                return ret;
 248        }
 249
 250        m = file->private_data;
 251        m->private = session;
 252        return ret;
 253}
 254
 255/*
 256 * Avoid consuming memory with our now useless rbtree.
 257 */
 258static int tracing_stat_release(struct inode *i, struct file *f)
 259{
 260        struct stat_session *session = i->i_private;
 261
 262        reset_stat_session(session);
 263
 264        return seq_release(i, f);
 265}
 266
 267static const struct file_operations tracing_stat_fops = {
 268        .open           = tracing_stat_open,
 269        .read           = seq_read,
 270        .llseek         = seq_lseek,
 271        .release        = tracing_stat_release
 272};
 273
 274static int tracing_stat_init(void)
 275{
 276        struct dentry *d_tracing;
 277
 278        d_tracing = tracing_init_dentry();
 279        if (IS_ERR(d_tracing))
 280                return 0;
 281
 282        stat_dir = tracefs_create_dir("trace_stat", d_tracing);
 283        if (!stat_dir)
 284                pr_warn("Could not create tracefs 'trace_stat' entry\n");
 285        return 0;
 286}
 287
 288static int init_stat_file(struct stat_session *session)
 289{
 290        if (!stat_dir && tracing_stat_init())
 291                return -ENODEV;
 292
 293        session->file = tracefs_create_file(session->ts->name, 0644,
 294                                            stat_dir,
 295                                            session, &tracing_stat_fops);
 296        if (!session->file)
 297                return -ENOMEM;
 298        return 0;
 299}
 300
 301int register_stat_tracer(struct tracer_stat *trace)
 302{
 303        struct stat_session *session, *node;
 304        int ret;
 305
 306        if (!trace)
 307                return -EINVAL;
 308
 309        if (!trace->stat_start || !trace->stat_next || !trace->stat_show)
 310                return -EINVAL;
 311
 312        /* Already registered? */
 313        mutex_lock(&all_stat_sessions_mutex);
 314        list_for_each_entry(node, &all_stat_sessions, session_list) {
 315                if (node->ts == trace) {
 316                        mutex_unlock(&all_stat_sessions_mutex);
 317                        return -EINVAL;
 318                }
 319        }
 320        mutex_unlock(&all_stat_sessions_mutex);
 321
 322        /* Init the session */
 323        session = kzalloc(sizeof(*session), GFP_KERNEL);
 324        if (!session)
 325                return -ENOMEM;
 326
 327        session->ts = trace;
 328        INIT_LIST_HEAD(&session->session_list);
 329        mutex_init(&session->stat_mutex);
 330
 331        ret = init_stat_file(session);
 332        if (ret) {
 333                destroy_session(session);
 334                return ret;
 335        }
 336
 337        /* Register */
 338        mutex_lock(&all_stat_sessions_mutex);
 339        list_add_tail(&session->session_list, &all_stat_sessions);
 340        mutex_unlock(&all_stat_sessions_mutex);
 341
 342        return 0;
 343}
 344
 345void unregister_stat_tracer(struct tracer_stat *trace)
 346{
 347        struct stat_session *node, *tmp;
 348
 349        mutex_lock(&all_stat_sessions_mutex);
 350        list_for_each_entry_safe(node, tmp, &all_stat_sessions, session_list) {
 351                if (node->ts == trace) {
 352                        list_del(&node->session_list);
 353                        destroy_session(node);
 354                        break;
 355                }
 356        }
 357        mutex_unlock(&all_stat_sessions_mutex);
 358}
 359