Logo Search packages:      
Sourcecode: linux version File versions  Download package

sputrace.c

/*
 * Copyright (C) 2007 IBM Deutschland Entwicklung GmbH
 *    Released under GPL v2.
 *
 * Partially based on net/ipv4/tcp_probe.c.
 *
 * Simple tracing facility for spu contexts.
 */
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/marker.h>
#include <linux/proc_fs.h>
#include <linux/wait.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include "spufs.h"

struct spu_probe {
      const char *name;
      const char *format;
      marker_probe_func *probe_func;
};

struct sputrace {
      ktime_t tstamp;
      int owner_tid; /* owner */
      int curr_tid;
      const char *name;
      int number;
};

static int bufsize __read_mostly = 16384;
MODULE_PARM_DESC(bufsize, "Log buffer size (number of records)");
module_param(bufsize, int, 0);


static DEFINE_SPINLOCK(sputrace_lock);
static DECLARE_WAIT_QUEUE_HEAD(sputrace_wait);
static ktime_t sputrace_start;
static unsigned long sputrace_head, sputrace_tail;
static struct sputrace *sputrace_log;

static int sputrace_used(void)
{
      return (sputrace_head - sputrace_tail) % bufsize;
}

static inline int sputrace_avail(void)
{
      return bufsize - sputrace_used();
}

static int sputrace_sprint(char *tbuf, int n)
{
      const struct sputrace *t = sputrace_log + sputrace_tail % bufsize;
      struct timespec tv =
            ktime_to_timespec(ktime_sub(t->tstamp, sputrace_start));

      return snprintf(tbuf, n,
            "[%lu.%09lu] %d: %s (ctxthread = %d, spu = %d)\n",
            (unsigned long) tv.tv_sec,
            (unsigned long) tv.tv_nsec,
            t->curr_tid,
            t->name,
            t->owner_tid,
            t->number);
}

static ssize_t sputrace_read(struct file *file, char __user *buf,
            size_t len, loff_t *ppos)
{
      int error = 0, cnt = 0;

      if (!buf || len < 0)
            return -EINVAL;

      while (cnt < len) {
            char tbuf[128];
            int width;

            error = wait_event_interruptible(sputrace_wait,
                                     sputrace_used() > 0);
            if (error)
                  break;

            spin_lock(&sputrace_lock);
            if (sputrace_head == sputrace_tail) {
                  spin_unlock(&sputrace_lock);
                  continue;
            }

            width = sputrace_sprint(tbuf, sizeof(tbuf));
            if (width < len)
                  sputrace_tail = (sputrace_tail + 1) % bufsize;
            spin_unlock(&sputrace_lock);

            if (width >= len)
                  break;

            error = copy_to_user(buf + cnt, tbuf, width);
            if (error)
                  break;
            cnt += width;
      }

      return cnt == 0 ? error : cnt;
}

static int sputrace_open(struct inode *inode, struct file *file)
{
      spin_lock(&sputrace_lock);
      sputrace_head = sputrace_tail = 0;
      sputrace_start = ktime_get();
      spin_unlock(&sputrace_lock);

      return 0;
}

static const struct file_operations sputrace_fops = {
      .owner      = THIS_MODULE,
      .open = sputrace_open,
      .read = sputrace_read,
};

static void sputrace_log_item(const char *name, struct spu_context *ctx,
            struct spu *spu)
{
      spin_lock(&sputrace_lock);
      if (sputrace_avail() > 1) {
            struct sputrace *t = sputrace_log + sputrace_head;

            t->tstamp = ktime_get();
            t->owner_tid = ctx->tid;
            t->name = name;
            t->curr_tid = current->pid;
            t->number = spu ? spu->number : -1;

            sputrace_head = (sputrace_head + 1) % bufsize;
      } else {
            printk(KERN_WARNING
                   "sputrace: lost samples due to full buffer.\n");
      }
      spin_unlock(&sputrace_lock);

      wake_up(&sputrace_wait);
}

static void spu_context_event(void *probe_private, void *call_data,
            const char *format, va_list *args)
{
      struct spu_probe *p = probe_private;
      struct spu_context *ctx;
      struct spu *spu;

      ctx = va_arg(*args, struct spu_context *);
      spu = va_arg(*args, struct spu *);

      sputrace_log_item(p->name, ctx, spu);
}

static void spu_context_nospu_event(void *probe_private, void *call_data,
            const char *format, va_list *args)
{
      struct spu_probe *p = probe_private;
      struct spu_context *ctx;

      ctx = va_arg(*args, struct spu_context *);

      sputrace_log_item(p->name, ctx, NULL);
}

struct spu_probe spu_probes[] = {
      { "spu_bind_context__enter", "ctx %p spu %p", spu_context_event },
      { "spu_unbind_context__enter", "ctx %p spu %p", spu_context_event },
      { "spu_get_idle__enter", "ctx %p", spu_context_nospu_event },
      { "spu_get_idle__found", "ctx %p spu %p", spu_context_event },
      { "spu_get_idle__not_found", "ctx %p", spu_context_nospu_event },
      { "spu_find_victim__enter", "ctx %p", spu_context_nospu_event },
      { "spusched_tick__preempt", "ctx %p spu %p", spu_context_event },
      { "spusched_tick__newslice", "ctx %p", spu_context_nospu_event },
      { "spu_yield__enter", "ctx %p", spu_context_nospu_event },
      { "spu_deactivate__enter", "ctx %p", spu_context_nospu_event },
      { "__spu_deactivate__unload", "ctx %p spu %p", spu_context_event },
      { "spufs_ps_fault__enter", "ctx %p", spu_context_nospu_event },
      { "spufs_ps_fault__sleep", "ctx %p", spu_context_nospu_event },
      { "spufs_ps_fault__wake", "ctx %p spu %p", spu_context_event },
      { "spufs_ps_fault__insert", "ctx %p spu %p", spu_context_event },
      { "spu_acquire_saved__enter", "ctx %p", spu_context_nospu_event },
      { "destroy_spu_context__enter", "ctx %p", spu_context_nospu_event },
      { "spufs_stop_callback__enter", "ctx %p spu %p", spu_context_event },
};

static int __init sputrace_init(void)
{
      struct proc_dir_entry *entry;
      int i, error = -ENOMEM;

      sputrace_log = kcalloc(bufsize, sizeof(struct sputrace), GFP_KERNEL);
      if (!sputrace_log)
            goto out;

      entry = proc_create("sputrace", S_IRUSR, NULL, &sputrace_fops);
      if (!entry)
            goto out_free_log;

      for (i = 0; i < ARRAY_SIZE(spu_probes); i++) {
            struct spu_probe *p = &spu_probes[i];

            error = marker_probe_register(p->name, p->format,
                                    p->probe_func, p);
            if (error)
                  printk(KERN_INFO "Unable to register probe %s\n",
                              p->name);
      }

      return 0;

out_free_log:
      kfree(sputrace_log);
out:
      return -ENOMEM;
}

static void __exit sputrace_exit(void)
{
      int i;

      for (i = 0; i < ARRAY_SIZE(spu_probes); i++)
            marker_probe_unregister(spu_probes[i].name,
                  spu_probes[i].probe_func, &spu_probes[i]);

      remove_proc_entry("sputrace", NULL);
      kfree(sputrace_log);
}

module_init(sputrace_init);
module_exit(sputrace_exit);

MODULE_LICENSE("GPL");

Generated by  Doxygen 1.6.0   Back to index